Project import
diff --git a/arm-linux-a5s.cache b/arm-linux-a5s.cache
new file mode 100644
index 0000000..633c969
--- /dev/null
+++ b/arm-linux-a5s.cache
@@ -0,0 +1,4 @@
+glib_cv_stack_grows=no
+glib_cv_uscore=no
+ac_cv_func_posix_getpwuid_r=yes
+ac_cv_func_posix_getgrgid_r=yes
diff --git a/bluez/AUTHORS b/bluez/AUTHORS
new file mode 100644
index 0000000..4cb3d63
--- /dev/null
+++ b/bluez/AUTHORS
@@ -0,0 +1,82 @@
+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@tieto.com>
+Syam Sidhardhan <s.syam@samsung.com>
+Paulo Alcantara <pcacjr@gmail.com>
+Jefferson Delfes <jefferson.delfes@openbossa.org>
+Andrzej Kaczmarek <andrzej.kaczmarek@tieto.com>
+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>
diff --git a/bluez/COPYING b/bluez/COPYING
new file mode 100644
index 0000000..6d45519
--- /dev/null
+++ b/bluez/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/COPYING.LIB b/bluez/COPYING.LIB
new file mode 100644
index 0000000..1f7c8cc
--- /dev/null
+++ b/bluez/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/ChangeLog b/bluez/ChangeLog
new file mode 100644
index 0000000..989c8a3
--- /dev/null
+++ b/bluez/ChangeLog
@@ -0,0 +1,1907 @@
+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/INSTALL b/bluez/INSTALL
new file mode 100644
index 0000000..56b077d
--- /dev/null
+++ b/bluez/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/Makefile.am b/bluez/Makefile.am
new file mode 100644
index 0000000..f96c700
--- /dev/null
+++ b/bluez/Makefile.am
@@ -0,0 +1,383 @@
+
+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 20:8:17
+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
+
+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/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/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/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-dbus.h src/gatt-dbus.c \
+			src/gatt.h src/gatt.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/shared/io.h src/shared/io-glib.c \
+			src/shared/timeout.h src/shared/timeout-glib.c \
+			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_bluetoothd_LDADD = lib/libbluetooth-internal.la gdbus/libgdbus-internal.la \
+			@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/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/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
+
+EXTRA_DIST += doc/alert-api.txt \
+		doc/proximity-api.txt doc/heartrate-api.txt \
+		doc/thermometer-api.txt doc/cyclingspeed-api.txt
+
+EXTRA_DIST += doc/obex-api.txt doc/obex-agent-api.txt
+
+EXTRA_DIST += tools/magic.btsnoop
+
+AM_CFLAGS += @DBUS_CFLAGS@ @GLIB_CFLAGS@
+
+AM_CPPFLAGS = -I$(builddir)/lib -I$(srcdir)/gdbus
+
+
+unit_tests += unit/test-eir unit/test-uuid unit/test-textfile unit/test-crc
+
+unit_test_eir_SOURCES = unit/test-eir.c src/eir.c src/uuid-helper.c
+unit_test_eir_LDADD = lib/libbluetooth-internal.la @GLIB_LIBS@
+
+unit_test_uuid_SOURCES = unit/test-uuid.c
+unit_test_uuid_LDADD = lib/libbluetooth-internal.la @GLIB_LIBS@
+
+unit_test_textfile_SOURCES = unit/test-textfile.c src/textfile.h src/textfile.c
+unit_test_textfile_LDADD = @GLIB_LIBS@
+
+unit_test_crc_SOURCES = unit/test-crc.c monitor/crc.h monitor/crc.c
+unit_test_crc_LDADD = @GLIB_LIBS@
+
+unit_tests += unit/test-ringbuf unit/test-queue
+
+unit_test_ringbuf_SOURCES = unit/test-ringbuf.c \
+				src/shared/util.h src/shared/util.c \
+				src/shared/ringbuf.h src/shared/ringbuf.c
+unit_test_ringbuf_LDADD = @GLIB_LIBS@
+
+unit_test_queue_SOURCES = unit/test-queue.c \
+				src/shared/util.h src/shared/util.c \
+				src/shared/queue.h src/shared/queue.c
+unit_test_queue_LDADD = @GLIB_LIBS@
+
+unit_tests += unit/test-mgmt
+
+unit_test_mgmt_SOURCES = unit/test-mgmt.c \
+				src/shared/io.h src/shared/io-glib.c \
+				src/shared/queue.h src/shared/queue.c \
+				src/shared/util.h src/shared/util.c \
+				src/shared/mgmt.h src/shared/mgmt.c
+unit_test_mgmt_LDADD = @GLIB_LIBS@
+
+unit_tests += unit/test-sdp
+
+unit_test_sdp_SOURCES = unit/test-sdp.c \
+				src/shared/util.h src/shared/util.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 @GLIB_LIBS@
+
+unit_tests += unit/test-avdtp
+
+unit_test_avdtp_SOURCES = unit/test-avdtp.c \
+				src/shared/util.h src/shared/util.c \
+				src/log.h src/log.c \
+				android/avdtp.c android/avdtp.h
+unit_test_avdtp_LDADD = @GLIB_LIBS@
+
+unit_tests += unit/test-avctp
+
+unit_test_avctp_SOURCES = unit/test-avctp.c \
+				src/shared/util.h src/shared/util.c \
+				src/log.h src/log.c \
+				android/avctp.c android/avctp.h
+unit_test_avctp_LDADD = @GLIB_LIBS@
+
+unit_tests += unit/test-avrcp
+
+unit_test_avrcp_SOURCES = unit/test-avrcp.c \
+				src/shared/util.h src/shared/util.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 = @GLIB_LIBS@ lib/libbluetooth-internal.la
+
+unit_tests += unit/test-hfp
+
+unit_test_hfp_SOURCES = unit/test-hfp.c \
+				src/shared/io.h src/shared/io-glib.c \
+				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/ringbuf.h src/shared/ringbuf.c \
+				src/shared/hfp.h src/shared/hfp.c
+
+unit_test_hfp_LDADD = @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 \
+				@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 = lib/libbluetooth-internal.la @GLIB_LIBS@
+
+noinst_PROGRAMS += $(unit_tests)
+
+TESTS = $(unit_tests)
+
+pkgconfigdir = $(libdir)/pkgconfig
+
+if LIBRARY
+pkgconfig_DATA = lib/bluez.pc
+endif
+
+DISTCHECK_CONFIGURE_FLAGS = --disable-datafiles --enable-library \
+					--disable-systemd --disable-udev
+
+DISTCLEANFILES = $(pkgconfig_DATA)
+
+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)
+
+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)"/$< $@
+
+clean-local:
+	$(RM) -r lib/bluetooth
diff --git a/bluez/Makefile.in b/bluez/Makefile.in
new file mode 100644
index 0000000..769804c
--- /dev/null
+++ b/bluez/Makefile.in
@@ -0,0 +1,8647 @@
+# Makefile.in generated by automake 1.13.3 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994-2013 Free Software Foundation, Inc.
+
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+@SET_MAKE@
+
+
+
+
+
+
+VPATH = @srcdir@
+am__is_gnu_make = test -n '$(MAKEFILE_LIST)' && test -n '$(MAKELEVEL)'
+am__make_running_with_option = \
+  case $${target_option-} in \
+      ?) ;; \
+      *) echo "am__make_running_with_option: internal error: invalid" \
+              "target option '$${target_option-}' specified" >&2; \
+         exit 1;; \
+  esac; \
+  has_opt=no; \
+  sane_makeflags=$$MAKEFLAGS; \
+  if $(am__is_gnu_make); then \
+    sane_makeflags=$$MFLAGS; \
+  else \
+    case $$MAKEFLAGS in \
+      *\\[\ \	]*) \
+        bs=\\; \
+        sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \
+          | sed "s/$$bs$$bs[$$bs $$bs	]*//g"`;; \
+    esac; \
+  fi; \
+  skip_next=no; \
+  strip_trailopt () \
+  { \
+    flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \
+  }; \
+  for flg in $$sane_makeflags; do \
+    test $$skip_next = yes && { skip_next=no; continue; }; \
+    case $$flg in \
+      *=*|--*) continue;; \
+        -*I) strip_trailopt 'I'; skip_next=yes;; \
+      -*I?*) strip_trailopt 'I';; \
+        -*O) strip_trailopt 'O'; skip_next=yes;; \
+      -*O?*) strip_trailopt 'O';; \
+        -*l) strip_trailopt 'l'; skip_next=yes;; \
+      -*l?*) strip_trailopt 'l';; \
+      -[dEDm]) skip_next=yes;; \
+      -[JT]) skip_next=yes;; \
+    esac; \
+    case $$flg in \
+      *$$target_option*) has_opt=yes; break;; \
+    esac; \
+  done; \
+  test $$has_opt = yes
+am__make_dryrun = (target_option=n; $(am__make_running_with_option))
+am__make_keepgoing = (target_option=k; $(am__make_running_with_option))
+pkgdatadir = $(datadir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkglibexecdir = $(libexecdir)/@PACKAGE@
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+build_triplet = @build@
+host_triplet = @host@
+bin_PROGRAMS = $(am__EXEEXT_1) $(am__EXEEXT_2) $(am__EXEEXT_3)
+noinst_PROGRAMS = $(am__EXEEXT_4) $(am__EXEEXT_5) $(am__EXEEXT_6) \
+	$(am__EXEEXT_7) $(am__EXEEXT_9)
+libexec_PROGRAMS = src/bluetoothd$(EXEEXT) obexd/src/obexd$(EXEEXT)
+@LIBRARY_TRUE@am__append_1 = $(lib_headers)
+@LIBRARY_TRUE@am__append_2 = lib/libbluetooth.la
+DIST_COMMON = $(srcdir)/Makefile.plugins $(srcdir)/Makefile.tools \
+	$(srcdir)/Makefile.obexd $(srcdir)/android/Makefile.am \
+	$(srcdir)/Makefile.in $(srcdir)/Makefile.am \
+	$(top_srcdir)/configure $(am__configure_deps) \
+	$(srcdir)/config.h.in $(top_srcdir)/src/bluetoothd.8.in \
+	$(top_srcdir)/lib/bluez.pc.in depcomp $(dist_man_MANS) \
+	$(am__include_HEADERS_DIST) test-driver AUTHORS COPYING \
+	COPYING.LIB ChangeLog INSTALL NEWS README TODO compile \
+	config.guess config.sub install-sh missing ltmain.sh
+@MAINTAINER_MODE_TRUE@am__append_3 = gatt_example
+@MAINTAINER_MODE_TRUE@am__append_4 = plugins/gatt-example.c
+@EXPERIMENTAL_TRUE@am__append_5 = neard sap
+@EXPERIMENTAL_TRUE@am__append_6 = plugins/neard.c profiles/sap/main.c \
+@EXPERIMENTAL_TRUE@	profiles/sap/manager.h \
+@EXPERIMENTAL_TRUE@	profiles/sap/manager.c \
+@EXPERIMENTAL_TRUE@	profiles/sap/server.h profiles/sap/server.c \
+@EXPERIMENTAL_TRUE@	profiles/sap/sap.h profiles/sap/sap-dummy.c
+@EXPERIMENTAL_TRUE@am__append_7 = profiles/sap/libsap.a
+@EXPERIMENTAL_TRUE@am__append_8 = health
+@EXPERIMENTAL_TRUE@am__append_9 = profiles/health/mcap_lib.h profiles/health/mcap_internal.h \
+@EXPERIMENTAL_TRUE@			profiles/health/mcap.h profiles/health/mcap.c \
+@EXPERIMENTAL_TRUE@			profiles/health/mcap_sync.c \
+@EXPERIMENTAL_TRUE@			profiles/health/hdp_main.c profiles/health/hdp_types.h \
+@EXPERIMENTAL_TRUE@			profiles/health/hdp_manager.h \
+@EXPERIMENTAL_TRUE@			profiles/health/hdp_manager.c \
+@EXPERIMENTAL_TRUE@			profiles/health/hdp.h profiles/health/hdp.c \
+@EXPERIMENTAL_TRUE@			profiles/health/hdp_util.h profiles/health/hdp_util.c
+
+@EXPERIMENTAL_TRUE@am__append_10 = alert time proximity thermometer \
+@EXPERIMENTAL_TRUE@	heartrate cyclingspeed
+@EXPERIMENTAL_TRUE@am__append_11 = profiles/alert/server.c \
+@EXPERIMENTAL_TRUE@	profiles/time/server.c \
+@EXPERIMENTAL_TRUE@	profiles/proximity/main.c \
+@EXPERIMENTAL_TRUE@	profiles/proximity/manager.h \
+@EXPERIMENTAL_TRUE@	profiles/proximity/manager.c \
+@EXPERIMENTAL_TRUE@	profiles/proximity/monitor.h \
+@EXPERIMENTAL_TRUE@	profiles/proximity/monitor.c \
+@EXPERIMENTAL_TRUE@	profiles/proximity/reporter.h \
+@EXPERIMENTAL_TRUE@	profiles/proximity/reporter.c \
+@EXPERIMENTAL_TRUE@	profiles/proximity/linkloss.h \
+@EXPERIMENTAL_TRUE@	profiles/proximity/linkloss.c \
+@EXPERIMENTAL_TRUE@	profiles/proximity/immalert.h \
+@EXPERIMENTAL_TRUE@	profiles/proximity/immalert.c \
+@EXPERIMENTAL_TRUE@	profiles/thermometer/thermometer.c \
+@EXPERIMENTAL_TRUE@	profiles/heartrate/heartrate.c \
+@EXPERIMENTAL_TRUE@	profiles/cyclingspeed/cyclingspeed.c
+@SIXAXIS_TRUE@am__append_12 = plugins/sixaxis.la
+@MAINTAINER_MODE_TRUE@am__append_13 = plugins/external-dummy.la
+@CLIENT_TRUE@am__append_14 = client/bluetoothctl
+@MONITOR_TRUE@am__append_15 = monitor/btmon
+@EXPERIMENTAL_TRUE@am__append_16 = emulator/btvirt emulator/b1ee \
+@EXPERIMENTAL_TRUE@	emulator/hfp tools/3dsp tools/mgmt-tester \
+@EXPERIMENTAL_TRUE@	tools/gap-tester tools/l2cap-tester \
+@EXPERIMENTAL_TRUE@	tools/sco-tester tools/smp-tester \
+@EXPERIMENTAL_TRUE@	tools/hci-tester tools/rfcomm-tester \
+@EXPERIMENTAL_TRUE@	tools/bdaddr tools/avinfo tools/avtest \
+@EXPERIMENTAL_TRUE@	tools/scotest tools/amptest tools/hwdb \
+@EXPERIMENTAL_TRUE@	tools/hcieventmask tools/hcisecfilter \
+@EXPERIMENTAL_TRUE@	tools/btmgmt tools/btinfo tools/btattach \
+@EXPERIMENTAL_TRUE@	tools/btsnoop tools/btproxy tools/btiotest \
+@EXPERIMENTAL_TRUE@	tools/mpris-player tools/cltest \
+@EXPERIMENTAL_TRUE@	tools/seq2bseq tools/ibeacon
+@TOOLS_TRUE@am__append_17 = tools/hciattach tools/hciconfig tools/hcitool tools/hcidump \
+@TOOLS_TRUE@			tools/rfcomm tools/rctest tools/l2test tools/l2ping \
+@TOOLS_TRUE@			tools/sdptool tools/ciptool tools/bccmd tools/bluemoon
+
+@TOOLS_TRUE@am__append_18 = tools/hciattach.1 tools/hciconfig.1 \
+@TOOLS_TRUE@			tools/hcitool.1 tools/hcidump.1 \
+@TOOLS_TRUE@			tools/rfcomm.1 tools/rctest.1 tools/l2ping.1 \
+@TOOLS_TRUE@			tools/sdptool.1 tools/ciptool.1 tools/bccmd.1
+
+@TOOLS_FALSE@am__append_19 = tools/hciattach.1 tools/hciconfig.1 \
+@TOOLS_FALSE@			tools/hcitool.1 tools/hcidump.1 \
+@TOOLS_FALSE@			tools/rfcomm.1 tools/rctest.1 tools/l2ping.1 \
+@TOOLS_FALSE@			tools/sdptool.1 tools/ciptool.1 tools/bccmd.1
+
+@HID2HCI_TRUE@udev_PROGRAMS = tools/hid2hci$(EXEEXT)
+@HID2HCI_TRUE@am__append_20 = tools/hid2hci.1
+@HID2HCI_FALSE@am__append_21 = tools/hid2hci.1
+@EXPERIMENTAL_TRUE@am__append_22 = tools/bdaddr.1
+@READLINE_TRUE@am__append_23 = attrib/gatttool \
+@READLINE_TRUE@			tools/obex-client-tool tools/obex-server-tool \
+@READLINE_TRUE@			tools/bluetooth-player tools/obexctl
+
+@EXPERIMENTAL_TRUE@am__append_24 = tools/gatt-service \
+@EXPERIMENTAL_TRUE@	profiles/iap/iapd
+@CUPS_TRUE@cups_PROGRAMS = profiles/cups/bluetooth$(EXEEXT)
+@EXPERIMENTAL_TRUE@am__append_25 = pcsuite
+@EXPERIMENTAL_TRUE@am__append_26 = obexd/plugins/pcsuite.c
+@OBEX_TRUE@am__append_27 = irmc pbap
+@OBEX_TRUE@am__append_28 = obexd/plugins/irmc.c obexd/plugins/pbap.c \
+@OBEX_TRUE@	obexd/plugins/vcard.h obexd/plugins/vcard.c \
+@OBEX_TRUE@	obexd/plugins/phonebook.h \
+@OBEX_TRUE@	obexd/plugins/phonebook-dummy.c
+@ANDROID_TRUE@am__append_29 = android/system-emulator \
+@ANDROID_TRUE@	android/bluetoothd-snoop android/bluetoothd \
+@ANDROID_TRUE@	android/haltest android/android-tester \
+@ANDROID_TRUE@	android/ipc-tester
+@ANDROID_TRUE@am__append_30 = android/bluetooth.default.la \
+@ANDROID_TRUE@	android/audio.a2dp.default.la
+@ANDROID_TRUE@am__append_31 = android/test-ipc
+@HID2HCI_TRUE@am__append_32 = $(rules_DATA)
+TESTS = $(am__EXEEXT_9)
+subdir = .
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/acinclude.m4 \
+	$(top_srcdir)/configure.ac
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+	$(ACLOCAL_M4)
+am__CONFIG_DISTCLEAN_FILES = config.status config.cache config.log \
+ configure.lineno config.status.lineno
+mkinstalldirs = $(install_sh) -d
+CONFIG_HEADER = config.h
+CONFIG_CLEAN_FILES = src/bluetoothd.8 lib/bluez.pc
+CONFIG_CLEAN_VPATH_FILES =
+LIBRARIES = $(noinst_LIBRARIES)
+ARFLAGS = cru
+AM_V_AR = $(am__v_AR_@AM_V@)
+am__v_AR_ = $(am__v_AR_@AM_DEFAULT_V@)
+am__v_AR_0 = @echo "  AR      " $@;
+am__v_AR_1 = 
+profiles_sap_libsap_a_AR = $(AR) $(ARFLAGS)
+profiles_sap_libsap_a_LIBADD =
+am__profiles_sap_libsap_a_SOURCES_DIST = profiles/sap/sap.h \
+	profiles/sap/sap-u8500.c
+am__dirstamp = $(am__leading_dot)dirstamp
+@EXPERIMENTAL_TRUE@am_profiles_sap_libsap_a_OBJECTS =  \
+@EXPERIMENTAL_TRUE@	profiles/sap/sap-u8500.$(OBJEXT)
+profiles_sap_libsap_a_OBJECTS = $(am_profiles_sap_libsap_a_OBJECTS)
+am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`;
+am__vpath_adj = case $$p in \
+    $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \
+    *) f=$$p;; \
+  esac;
+am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`;
+am__install_max = 40
+am__nobase_strip_setup = \
+  srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'`
+am__nobase_strip = \
+  for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||"
+am__nobase_list = $(am__nobase_strip_setup); \
+  for p in $$list; do echo "$$p $$p"; done | \
+  sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \
+  $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \
+    if (++n[$$2] == $(am__install_max)) \
+      { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \
+    END { for (dir in files) print dir, files[dir] }'
+am__base_list = \
+  sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \
+  sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g'
+am__uninstall_files_from_dir = { \
+  test -z "$$files" \
+    || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \
+    || { echo " ( cd '$$dir' && rm -f" $$files ")"; \
+         $(am__cd) "$$dir" && rm -f $$files; }; \
+  }
+am__installdirs = "$(DESTDIR)$(libdir)" "$(DESTDIR)$(plugindir)" \
+	"$(DESTDIR)$(bindir)" "$(DESTDIR)$(cupsdir)" \
+	"$(DESTDIR)$(libexecdir)" "$(DESTDIR)$(udevdir)" \
+	"$(DESTDIR)$(testdir)" "$(DESTDIR)$(man1dir)" \
+	"$(DESTDIR)$(man8dir)" "$(DESTDIR)$(confdir)" \
+	"$(DESTDIR)$(dbusdir)" "$(DESTDIR)$(dbussessionbusdir)" \
+	"$(DESTDIR)$(dbussystembusdir)" "$(DESTDIR)$(pkgconfigdir)" \
+	"$(DESTDIR)$(rulesdir)" "$(DESTDIR)$(statedir)" \
+	"$(DESTDIR)$(systemdsystemunitdir)" \
+	"$(DESTDIR)$(systemduserunitdir)" "$(DESTDIR)$(includedir)"
+LTLIBRARIES = $(lib_LTLIBRARIES) $(noinst_LTLIBRARIES) \
+	$(plugin_LTLIBRARIES)
+android_audio_a2dp_default_la_DEPENDENCIES =
+am__android_audio_a2dp_default_la_SOURCES_DIST = android/audio-msg.h \
+	android/hal-msg.h android/hal-audio.c android/hardware/audio.h \
+	android/hardware/audio_effect.h android/hardware/hardware.h \
+	android/system/audio.h
+@ANDROID_TRUE@am_android_audio_a2dp_default_la_OBJECTS = android/android_audio_a2dp_default_la-hal-audio.lo
+android_audio_a2dp_default_la_OBJECTS =  \
+	$(am_android_audio_a2dp_default_la_OBJECTS)
+AM_V_lt = $(am__v_lt_@AM_V@)
+am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@)
+am__v_lt_0 = --silent
+am__v_lt_1 = 
+android_audio_a2dp_default_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \
+	$(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \
+	$(android_audio_a2dp_default_la_CFLAGS) $(CFLAGS) \
+	$(android_audio_a2dp_default_la_LDFLAGS) $(LDFLAGS) -o $@
+@ANDROID_TRUE@am_android_audio_a2dp_default_la_rpath = -rpath \
+@ANDROID_TRUE@	$(plugindir)
+android_bluetooth_default_la_LIBADD =
+am__android_bluetooth_default_la_SOURCES_DIST = 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-avrcp.c android/hal-handsfree.c \
+	android/hal-gatt.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/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_TRUE@am_android_bluetooth_default_la_OBJECTS = android/android_bluetooth_default_la-hal-bluetooth.lo \
+@ANDROID_TRUE@	android/android_bluetooth_default_la-hal-socket.lo \
+@ANDROID_TRUE@	android/android_bluetooth_default_la-hal-hidhost.lo \
+@ANDROID_TRUE@	android/android_bluetooth_default_la-hal-health.lo \
+@ANDROID_TRUE@	android/android_bluetooth_default_la-hal-pan.lo \
+@ANDROID_TRUE@	android/android_bluetooth_default_la-hal-a2dp.lo \
+@ANDROID_TRUE@	android/android_bluetooth_default_la-hal-avrcp.lo \
+@ANDROID_TRUE@	android/android_bluetooth_default_la-hal-handsfree.lo \
+@ANDROID_TRUE@	android/android_bluetooth_default_la-hal-gatt.lo \
+@ANDROID_TRUE@	android/android_bluetooth_default_la-hal-ipc.lo \
+@ANDROID_TRUE@	android/android_bluetooth_default_la-hal-utils.lo
+android_bluetooth_default_la_OBJECTS =  \
+	$(am_android_bluetooth_default_la_OBJECTS)
+android_bluetooth_default_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \
+	$(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \
+	$(android_bluetooth_default_la_CFLAGS) $(CFLAGS) \
+	$(android_bluetooth_default_la_LDFLAGS) $(LDFLAGS) -o $@
+@ANDROID_TRUE@am_android_bluetooth_default_la_rpath = -rpath \
+@ANDROID_TRUE@	$(plugindir)
+gdbus_libgdbus_internal_la_LIBADD =
+am_gdbus_libgdbus_internal_la_OBJECTS = gdbus/mainloop.lo \
+	gdbus/watch.lo gdbus/object.lo gdbus/client.lo gdbus/polkit.lo
+gdbus_libgdbus_internal_la_OBJECTS =  \
+	$(am_gdbus_libgdbus_internal_la_OBJECTS)
+lib_libbluetooth_internal_la_LIBADD =
+am__objects_1 =
+am__objects_2 = lib/bluetooth.lo lib/hci.lo lib/sdp.lo
+am__objects_3 = lib/uuid.lo
+am_lib_libbluetooth_internal_la_OBJECTS = $(am__objects_1) \
+	$(am__objects_2) $(am__objects_1) $(am__objects_3)
+lib_libbluetooth_internal_la_OBJECTS =  \
+	$(am_lib_libbluetooth_internal_la_OBJECTS)
+lib_libbluetooth_la_LIBADD =
+am__lib_libbluetooth_la_SOURCES_DIST = 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 lib/bluetooth.c \
+	lib/hci.c lib/sdp.c
+@LIBRARY_TRUE@am_lib_libbluetooth_la_OBJECTS = $(am__objects_1) \
+@LIBRARY_TRUE@	$(am__objects_2)
+lib_libbluetooth_la_OBJECTS = $(am_lib_libbluetooth_la_OBJECTS)
+lib_libbluetooth_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \
+	$(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \
+	$(AM_CFLAGS) $(CFLAGS) $(lib_libbluetooth_la_LDFLAGS) \
+	$(LDFLAGS) -o $@
+@LIBRARY_TRUE@am_lib_libbluetooth_la_rpath = -rpath $(libdir)
+plugins_external_dummy_la_LIBADD =
+am__plugins_external_dummy_la_SOURCES_DIST = plugins/external-dummy.c
+@MAINTAINER_MODE_TRUE@am_plugins_external_dummy_la_OBJECTS = plugins/plugins_external_dummy_la-external-dummy.lo
+plugins_external_dummy_la_OBJECTS =  \
+	$(am_plugins_external_dummy_la_OBJECTS)
+plugins_external_dummy_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \
+	$(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \
+	$(plugins_external_dummy_la_CFLAGS) $(CFLAGS) \
+	$(plugins_external_dummy_la_LDFLAGS) $(LDFLAGS) -o $@
+@MAINTAINER_MODE_TRUE@am_plugins_external_dummy_la_rpath = -rpath \
+@MAINTAINER_MODE_TRUE@	$(plugindir)
+plugins_sixaxis_la_LIBADD =
+am__plugins_sixaxis_la_SOURCES_DIST = plugins/sixaxis.c
+@SIXAXIS_TRUE@am_plugins_sixaxis_la_OBJECTS =  \
+@SIXAXIS_TRUE@	plugins/plugins_sixaxis_la-sixaxis.lo
+plugins_sixaxis_la_OBJECTS = $(am_plugins_sixaxis_la_OBJECTS)
+plugins_sixaxis_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \
+	$(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \
+	$(plugins_sixaxis_la_CFLAGS) $(CFLAGS) \
+	$(plugins_sixaxis_la_LDFLAGS) $(LDFLAGS) -o $@
+@SIXAXIS_TRUE@am_plugins_sixaxis_la_rpath = -rpath $(plugindir)
+@CLIENT_TRUE@am__EXEEXT_1 = client/bluetoothctl$(EXEEXT)
+@MONITOR_TRUE@am__EXEEXT_2 = monitor/btmon$(EXEEXT)
+@TOOLS_TRUE@am__EXEEXT_3 = tools/hciattach$(EXEEXT) \
+@TOOLS_TRUE@	tools/hciconfig$(EXEEXT) tools/hcitool$(EXEEXT) \
+@TOOLS_TRUE@	tools/hcidump$(EXEEXT) tools/rfcomm$(EXEEXT) \
+@TOOLS_TRUE@	tools/rctest$(EXEEXT) tools/l2test$(EXEEXT) \
+@TOOLS_TRUE@	tools/l2ping$(EXEEXT) tools/sdptool$(EXEEXT) \
+@TOOLS_TRUE@	tools/ciptool$(EXEEXT) tools/bccmd$(EXEEXT) \
+@TOOLS_TRUE@	tools/bluemoon$(EXEEXT)
+@EXPERIMENTAL_TRUE@am__EXEEXT_4 = emulator/btvirt$(EXEEXT) \
+@EXPERIMENTAL_TRUE@	emulator/b1ee$(EXEEXT) \
+@EXPERIMENTAL_TRUE@	emulator/hfp$(EXEEXT) tools/3dsp$(EXEEXT) \
+@EXPERIMENTAL_TRUE@	tools/mgmt-tester$(EXEEXT) \
+@EXPERIMENTAL_TRUE@	tools/gap-tester$(EXEEXT) \
+@EXPERIMENTAL_TRUE@	tools/l2cap-tester$(EXEEXT) \
+@EXPERIMENTAL_TRUE@	tools/sco-tester$(EXEEXT) \
+@EXPERIMENTAL_TRUE@	tools/smp-tester$(EXEEXT) \
+@EXPERIMENTAL_TRUE@	tools/hci-tester$(EXEEXT) \
+@EXPERIMENTAL_TRUE@	tools/rfcomm-tester$(EXEEXT) \
+@EXPERIMENTAL_TRUE@	tools/bdaddr$(EXEEXT) tools/avinfo$(EXEEXT) \
+@EXPERIMENTAL_TRUE@	tools/avtest$(EXEEXT) \
+@EXPERIMENTAL_TRUE@	tools/scotest$(EXEEXT) \
+@EXPERIMENTAL_TRUE@	tools/amptest$(EXEEXT) tools/hwdb$(EXEEXT) \
+@EXPERIMENTAL_TRUE@	tools/hcieventmask$(EXEEXT) \
+@EXPERIMENTAL_TRUE@	tools/hcisecfilter$(EXEEXT) \
+@EXPERIMENTAL_TRUE@	tools/btmgmt$(EXEEXT) tools/btinfo$(EXEEXT) \
+@EXPERIMENTAL_TRUE@	tools/btattach$(EXEEXT) \
+@EXPERIMENTAL_TRUE@	tools/btsnoop$(EXEEXT) \
+@EXPERIMENTAL_TRUE@	tools/btproxy$(EXEEXT) \
+@EXPERIMENTAL_TRUE@	tools/btiotest$(EXEEXT) \
+@EXPERIMENTAL_TRUE@	tools/mpris-player$(EXEEXT) \
+@EXPERIMENTAL_TRUE@	tools/cltest$(EXEEXT) \
+@EXPERIMENTAL_TRUE@	tools/seq2bseq$(EXEEXT) \
+@EXPERIMENTAL_TRUE@	tools/ibeacon$(EXEEXT)
+@READLINE_TRUE@am__EXEEXT_5 = attrib/gatttool$(EXEEXT) \
+@READLINE_TRUE@	tools/obex-client-tool$(EXEEXT) \
+@READLINE_TRUE@	tools/obex-server-tool$(EXEEXT) \
+@READLINE_TRUE@	tools/bluetooth-player$(EXEEXT) \
+@READLINE_TRUE@	tools/obexctl$(EXEEXT)
+@EXPERIMENTAL_TRUE@am__EXEEXT_6 = tools/gatt-service$(EXEEXT) \
+@EXPERIMENTAL_TRUE@	profiles/iap/iapd$(EXEEXT)
+@ANDROID_TRUE@am__EXEEXT_7 = android/system-emulator$(EXEEXT) \
+@ANDROID_TRUE@	android/bluetoothd-snoop$(EXEEXT) \
+@ANDROID_TRUE@	android/bluetoothd$(EXEEXT) \
+@ANDROID_TRUE@	android/haltest$(EXEEXT) \
+@ANDROID_TRUE@	android/android-tester$(EXEEXT) \
+@ANDROID_TRUE@	android/ipc-tester$(EXEEXT)
+@ANDROID_TRUE@am__EXEEXT_8 = android/test-ipc$(EXEEXT)
+am__EXEEXT_9 = $(am__EXEEXT_8) unit/test-eir$(EXEEXT) \
+	unit/test-uuid$(EXEEXT) unit/test-textfile$(EXEEXT) \
+	unit/test-crc$(EXEEXT) unit/test-ringbuf$(EXEEXT) \
+	unit/test-queue$(EXEEXT) unit/test-mgmt$(EXEEXT) \
+	unit/test-sdp$(EXEEXT) unit/test-avdtp$(EXEEXT) \
+	unit/test-avctp$(EXEEXT) unit/test-avrcp$(EXEEXT) \
+	unit/test-hfp$(EXEEXT) unit/test-gdbus-client$(EXEEXT) \
+	unit/test-gobex-header$(EXEEXT) \
+	unit/test-gobex-packet$(EXEEXT) unit/test-gobex$(EXEEXT) \
+	unit/test-gobex-transfer$(EXEEXT) \
+	unit/test-gobex-apparam$(EXEEXT) unit/test-lib$(EXEEXT)
+PROGRAMS = $(bin_PROGRAMS) $(cups_PROGRAMS) $(libexec_PROGRAMS) \
+	$(noinst_PROGRAMS) $(udev_PROGRAMS)
+am__android_android_tester_SOURCES_DIST = emulator/btdev.h \
+	emulator/btdev.c emulator/bthost.h emulator/bthost.c \
+	emulator/smp.c src/shared/crypto.h src/shared/crypto.c \
+	src/shared/io.h src/shared/io-glib.c 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/hciemu.h \
+	src/shared/hciemu.c src/shared/tester.h src/shared/tester.c \
+	src/shared/timeout.h src/shared/timeout-glib.c \
+	monitor/rfcomm.h android/hardware/hardware.c \
+	android/android-tester.c
+@ANDROID_TRUE@am_android_android_tester_OBJECTS =  \
+@ANDROID_TRUE@	emulator/android_android_tester-btdev.$(OBJEXT) \
+@ANDROID_TRUE@	emulator/android_android_tester-bthost.$(OBJEXT) \
+@ANDROID_TRUE@	emulator/android_android_tester-smp.$(OBJEXT) \
+@ANDROID_TRUE@	src/shared/android_android_tester-crypto.$(OBJEXT) \
+@ANDROID_TRUE@	src/shared/android_android_tester-io-glib.$(OBJEXT) \
+@ANDROID_TRUE@	src/shared/android_android_tester-queue.$(OBJEXT) \
+@ANDROID_TRUE@	src/shared/android_android_tester-util.$(OBJEXT) \
+@ANDROID_TRUE@	src/shared/android_android_tester-mgmt.$(OBJEXT) \
+@ANDROID_TRUE@	src/shared/android_android_tester-hciemu.$(OBJEXT) \
+@ANDROID_TRUE@	src/shared/android_android_tester-tester.$(OBJEXT) \
+@ANDROID_TRUE@	src/shared/android_android_tester-timeout-glib.$(OBJEXT) \
+@ANDROID_TRUE@	android/hardware/android_android_tester-hardware.$(OBJEXT) \
+@ANDROID_TRUE@	android/android_android_tester-android-tester.$(OBJEXT)
+android_android_tester_OBJECTS = $(am_android_android_tester_OBJECTS)
+@ANDROID_TRUE@android_android_tester_DEPENDENCIES =  \
+@ANDROID_TRUE@	lib/libbluetooth-internal.la
+android_android_tester_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \
+	$(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \
+	$(android_android_tester_CFLAGS) $(CFLAGS) \
+	$(android_android_tester_LDFLAGS) $(LDFLAGS) -o $@
+am__android_bluetoothd_SOURCES_DIST = android/main.c src/log.c \
+	android/hal-msg.h android/audio-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 src/shared/io.h src/shared/io-glib.c \
+	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/ringbuf.h src/shared/ringbuf.c src/shared/hfp.h \
+	src/shared/hfp.c android/bluetooth.h android/bluetooth.c \
+	android/hidhost.h android/hidhost.c android/ipc-common.h \
+	android/ipc.h android/ipc.c android/avdtp.h android/avdtp.c \
+	android/a2dp.h android/a2dp.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/pan.h android/pan.c android/handsfree.h \
+	android/handsfree.c android/gatt.h android/gatt.c \
+	android/health.h android/health.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_TRUE@am_android_bluetoothd_OBJECTS = android/main.$(OBJEXT) \
+@ANDROID_TRUE@	src/log.$(OBJEXT) src/sdpd-database.$(OBJEXT) \
+@ANDROID_TRUE@	src/sdpd-server.$(OBJEXT) \
+@ANDROID_TRUE@	src/sdpd-service.$(OBJEXT) \
+@ANDROID_TRUE@	src/sdpd-request.$(OBJEXT) \
+@ANDROID_TRUE@	src/uuid-helper.$(OBJEXT) src/eir.$(OBJEXT) \
+@ANDROID_TRUE@	src/shared/io-glib.$(OBJEXT) \
+@ANDROID_TRUE@	src/shared/queue.$(OBJEXT) \
+@ANDROID_TRUE@	src/shared/util.$(OBJEXT) \
+@ANDROID_TRUE@	src/shared/mgmt.$(OBJEXT) \
+@ANDROID_TRUE@	src/shared/ringbuf.$(OBJEXT) \
+@ANDROID_TRUE@	src/shared/hfp.$(OBJEXT) \
+@ANDROID_TRUE@	android/bluetooth.$(OBJEXT) \
+@ANDROID_TRUE@	android/hidhost.$(OBJEXT) android/ipc.$(OBJEXT) \
+@ANDROID_TRUE@	android/avdtp.$(OBJEXT) android/a2dp.$(OBJEXT) \
+@ANDROID_TRUE@	android/avctp.$(OBJEXT) android/avrcp.$(OBJEXT) \
+@ANDROID_TRUE@	android/avrcp-lib.$(OBJEXT) \
+@ANDROID_TRUE@	android/socket.$(OBJEXT) android/pan.$(OBJEXT) \
+@ANDROID_TRUE@	android/handsfree.$(OBJEXT) \
+@ANDROID_TRUE@	android/gatt.$(OBJEXT) android/health.$(OBJEXT) \
+@ANDROID_TRUE@	attrib/att.$(OBJEXT) attrib/gatt.$(OBJEXT) \
+@ANDROID_TRUE@	attrib/gattrib.$(OBJEXT) btio/btio.$(OBJEXT) \
+@ANDROID_TRUE@	src/sdp-client.$(OBJEXT) \
+@ANDROID_TRUE@	profiles/network/bnep.$(OBJEXT)
+android_bluetoothd_OBJECTS = $(am_android_bluetoothd_OBJECTS)
+@ANDROID_TRUE@android_bluetoothd_DEPENDENCIES =  \
+@ANDROID_TRUE@	lib/libbluetooth-internal.la
+am__android_bluetoothd_snoop_SOURCES_DIST =  \
+	android/bluetoothd-snoop.c monitor/mainloop.h \
+	monitor/mainloop.c src/shared/btsnoop.h src/shared/btsnoop.c
+@ANDROID_TRUE@am_android_bluetoothd_snoop_OBJECTS =  \
+@ANDROID_TRUE@	android/bluetoothd-snoop.$(OBJEXT) \
+@ANDROID_TRUE@	monitor/mainloop.$(OBJEXT) \
+@ANDROID_TRUE@	src/shared/btsnoop.$(OBJEXT)
+android_bluetoothd_snoop_OBJECTS =  \
+	$(am_android_bluetoothd_snoop_OBJECTS)
+android_bluetoothd_snoop_LDADD = $(LDADD)
+am__android_haltest_SOURCES_DIST = 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-rc.c \
+	android/client/if-bt.c android/client/if-gatt.c \
+	android/client/if-hf.c android/client/if-hh.c \
+	android/client/if-pan.c android/client/if-sock.c \
+	android/client/if-audio.c android/hardware/hardware.c \
+	android/hal-utils.h android/hal-utils.c
+@ANDROID_TRUE@am_android_haltest_OBJECTS = android/client/android_haltest-haltest.$(OBJEXT) \
+@ANDROID_TRUE@	android/client/android_haltest-pollhandler.$(OBJEXT) \
+@ANDROID_TRUE@	android/client/android_haltest-terminal.$(OBJEXT) \
+@ANDROID_TRUE@	android/client/android_haltest-history.$(OBJEXT) \
+@ANDROID_TRUE@	android/client/android_haltest-tabcompletion.$(OBJEXT) \
+@ANDROID_TRUE@	android/client/android_haltest-if-av.$(OBJEXT) \
+@ANDROID_TRUE@	android/client/android_haltest-if-rc.$(OBJEXT) \
+@ANDROID_TRUE@	android/client/android_haltest-if-bt.$(OBJEXT) \
+@ANDROID_TRUE@	android/client/android_haltest-if-gatt.$(OBJEXT) \
+@ANDROID_TRUE@	android/client/android_haltest-if-hf.$(OBJEXT) \
+@ANDROID_TRUE@	android/client/android_haltest-if-hh.$(OBJEXT) \
+@ANDROID_TRUE@	android/client/android_haltest-if-pan.$(OBJEXT) \
+@ANDROID_TRUE@	android/client/android_haltest-if-sock.$(OBJEXT) \
+@ANDROID_TRUE@	android/client/android_haltest-if-audio.$(OBJEXT) \
+@ANDROID_TRUE@	android/hardware/android_haltest-hardware.$(OBJEXT) \
+@ANDROID_TRUE@	android/android_haltest-hal-utils.$(OBJEXT)
+android_haltest_OBJECTS = $(am_android_haltest_OBJECTS)
+android_haltest_LDADD = $(LDADD)
+android_haltest_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \
+	$(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \
+	$(android_haltest_CFLAGS) $(CFLAGS) $(android_haltest_LDFLAGS) \
+	$(LDFLAGS) -o $@
+am__android_ipc_tester_SOURCES_DIST = emulator/btdev.h \
+	emulator/btdev.c emulator/bthost.h emulator/bthost.c \
+	emulator/smp.c src/shared/crypto.h src/shared/crypto.c \
+	src/shared/io.h src/shared/io-glib.c 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/hciemu.h \
+	src/shared/hciemu.c src/shared/tester.h src/shared/tester.c \
+	src/shared/timeout.h src/shared/timeout-glib.c \
+	android/hal-utils.h android/hal-utils.c android/ipc-common.h \
+	android/ipc-tester.c
+@ANDROID_TRUE@am_android_ipc_tester_OBJECTS =  \
+@ANDROID_TRUE@	emulator/android_ipc_tester-btdev.$(OBJEXT) \
+@ANDROID_TRUE@	emulator/android_ipc_tester-bthost.$(OBJEXT) \
+@ANDROID_TRUE@	emulator/android_ipc_tester-smp.$(OBJEXT) \
+@ANDROID_TRUE@	src/shared/android_ipc_tester-crypto.$(OBJEXT) \
+@ANDROID_TRUE@	src/shared/android_ipc_tester-io-glib.$(OBJEXT) \
+@ANDROID_TRUE@	src/shared/android_ipc_tester-queue.$(OBJEXT) \
+@ANDROID_TRUE@	src/shared/android_ipc_tester-util.$(OBJEXT) \
+@ANDROID_TRUE@	src/shared/android_ipc_tester-mgmt.$(OBJEXT) \
+@ANDROID_TRUE@	src/shared/android_ipc_tester-hciemu.$(OBJEXT) \
+@ANDROID_TRUE@	src/shared/android_ipc_tester-tester.$(OBJEXT) \
+@ANDROID_TRUE@	src/shared/android_ipc_tester-timeout-glib.$(OBJEXT) \
+@ANDROID_TRUE@	android/android_ipc_tester-hal-utils.$(OBJEXT) \
+@ANDROID_TRUE@	android/android_ipc_tester-ipc-tester.$(OBJEXT)
+android_ipc_tester_OBJECTS = $(am_android_ipc_tester_OBJECTS)
+@ANDROID_TRUE@android_ipc_tester_DEPENDENCIES =  \
+@ANDROID_TRUE@	lib/libbluetooth-internal.la
+android_ipc_tester_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \
+	$(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \
+	$(android_ipc_tester_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) \
+	$(LDFLAGS) -o $@
+am__android_system_emulator_SOURCES_DIST = android/system-emulator.c \
+	monitor/mainloop.h monitor/mainloop.c
+@ANDROID_TRUE@am_android_system_emulator_OBJECTS =  \
+@ANDROID_TRUE@	android/system-emulator.$(OBJEXT) \
+@ANDROID_TRUE@	monitor/mainloop.$(OBJEXT)
+android_system_emulator_OBJECTS =  \
+	$(am_android_system_emulator_OBJECTS)
+android_system_emulator_LDADD = $(LDADD)
+am__android_test_ipc_SOURCES_DIST = android/test-ipc.c \
+	src/shared/util.h src/shared/util.c src/log.h src/log.c \
+	android/ipc-common.h android/ipc.c android/ipc.h
+@ANDROID_TRUE@am_android_test_ipc_OBJECTS =  \
+@ANDROID_TRUE@	android/test-ipc.$(OBJEXT) \
+@ANDROID_TRUE@	src/shared/util.$(OBJEXT) src/log.$(OBJEXT) \
+@ANDROID_TRUE@	android/ipc.$(OBJEXT)
+android_test_ipc_OBJECTS = $(am_android_test_ipc_OBJECTS)
+android_test_ipc_DEPENDENCIES =
+am__attrib_gatttool_SOURCES_DIST = 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
+@READLINE_TRUE@am_attrib_gatttool_OBJECTS = attrib/gatttool.$(OBJEXT) \
+@READLINE_TRUE@	attrib/att.$(OBJEXT) attrib/gatt.$(OBJEXT) \
+@READLINE_TRUE@	attrib/gattrib.$(OBJEXT) btio/btio.$(OBJEXT) \
+@READLINE_TRUE@	attrib/interactive.$(OBJEXT) \
+@READLINE_TRUE@	attrib/utils.$(OBJEXT) src/log.$(OBJEXT) \
+@READLINE_TRUE@	client/display.$(OBJEXT)
+attrib_gatttool_OBJECTS = $(am_attrib_gatttool_OBJECTS)
+@READLINE_TRUE@attrib_gatttool_DEPENDENCIES =  \
+@READLINE_TRUE@	lib/libbluetooth-internal.la
+am__client_bluetoothctl_SOURCES_DIST = client/main.c client/display.h \
+	client/display.c client/agent.h client/agent.c monitor/uuid.h \
+	monitor/uuid.c
+@CLIENT_TRUE@am_client_bluetoothctl_OBJECTS = client/main.$(OBJEXT) \
+@CLIENT_TRUE@	client/display.$(OBJEXT) client/agent.$(OBJEXT) \
+@CLIENT_TRUE@	monitor/uuid.$(OBJEXT)
+client_bluetoothctl_OBJECTS = $(am_client_bluetoothctl_OBJECTS)
+@CLIENT_TRUE@client_bluetoothctl_DEPENDENCIES =  \
+@CLIENT_TRUE@	gdbus/libgdbus-internal.la
+am__emulator_b1ee_SOURCES_DIST = emulator/b1ee.c monitor/mainloop.h \
+	monitor/mainloop.c
+@EXPERIMENTAL_TRUE@am_emulator_b1ee_OBJECTS = emulator/b1ee.$(OBJEXT) \
+@EXPERIMENTAL_TRUE@	monitor/mainloop.$(OBJEXT)
+emulator_b1ee_OBJECTS = $(am_emulator_b1ee_OBJECTS)
+emulator_b1ee_LDADD = $(LDADD)
+am__emulator_btvirt_SOURCES_DIST = emulator/main.c monitor/bt.h \
+	monitor/mainloop.h monitor/mainloop.c src/shared/timeout.h \
+	src/shared/timeout-mainloop.c src/shared/util.h \
+	src/shared/util.c src/shared/crypto.h src/shared/crypto.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/amp.h emulator/amp.c emulator/le.h emulator/le.c
+@EXPERIMENTAL_TRUE@am_emulator_btvirt_OBJECTS =  \
+@EXPERIMENTAL_TRUE@	emulator/main.$(OBJEXT) \
+@EXPERIMENTAL_TRUE@	monitor/mainloop.$(OBJEXT) \
+@EXPERIMENTAL_TRUE@	src/shared/timeout-mainloop.$(OBJEXT) \
+@EXPERIMENTAL_TRUE@	src/shared/util.$(OBJEXT) \
+@EXPERIMENTAL_TRUE@	src/shared/crypto.$(OBJEXT) \
+@EXPERIMENTAL_TRUE@	emulator/server.$(OBJEXT) \
+@EXPERIMENTAL_TRUE@	emulator/vhci.$(OBJEXT) \
+@EXPERIMENTAL_TRUE@	emulator/btdev.$(OBJEXT) \
+@EXPERIMENTAL_TRUE@	emulator/bthost.$(OBJEXT) \
+@EXPERIMENTAL_TRUE@	emulator/smp.$(OBJEXT) \
+@EXPERIMENTAL_TRUE@	emulator/amp.$(OBJEXT) \
+@EXPERIMENTAL_TRUE@	emulator/le.$(OBJEXT)
+emulator_btvirt_OBJECTS = $(am_emulator_btvirt_OBJECTS)
+@EXPERIMENTAL_TRUE@emulator_btvirt_DEPENDENCIES =  \
+@EXPERIMENTAL_TRUE@	lib/libbluetooth-internal.la
+am__emulator_hfp_SOURCES_DIST = emulator/hfp.c monitor/mainloop.h \
+	monitor/mainloop.c src/shared/io.h src/shared/io-mainloop.c \
+	src/shared/util.h src/shared/util.c src/shared/queue.h \
+	src/shared/queue.c src/shared/ringbuf.h src/shared/ringbuf.c \
+	src/shared/hfp.h src/shared/hfp.c
+@EXPERIMENTAL_TRUE@am_emulator_hfp_OBJECTS = emulator/hfp.$(OBJEXT) \
+@EXPERIMENTAL_TRUE@	monitor/mainloop.$(OBJEXT) \
+@EXPERIMENTAL_TRUE@	src/shared/io-mainloop.$(OBJEXT) \
+@EXPERIMENTAL_TRUE@	src/shared/util.$(OBJEXT) \
+@EXPERIMENTAL_TRUE@	src/shared/queue.$(OBJEXT) \
+@EXPERIMENTAL_TRUE@	src/shared/ringbuf.$(OBJEXT) \
+@EXPERIMENTAL_TRUE@	src/shared/hfp.$(OBJEXT)
+emulator_hfp_OBJECTS = $(am_emulator_hfp_OBJECTS)
+emulator_hfp_LDADD = $(LDADD)
+am__monitor_btmon_SOURCES_DIST = monitor/main.c monitor/bt.h \
+	monitor/mainloop.h monitor/mainloop.c 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/uuid.h monitor/uuid.c monitor/hwdb.h monitor/hwdb.c \
+	monitor/keys.h monitor/keys.c monitor/analyze.h \
+	monitor/analyze.c src/shared/util.h src/shared/util.c \
+	src/shared/queue.h src/shared/queue.c src/shared/crypto.h \
+	src/shared/crypto.c src/shared/btsnoop.h src/shared/btsnoop.c
+@MONITOR_TRUE@am_monitor_btmon_OBJECTS = monitor/main.$(OBJEXT) \
+@MONITOR_TRUE@	monitor/mainloop.$(OBJEXT) \
+@MONITOR_TRUE@	monitor/display.$(OBJEXT) \
+@MONITOR_TRUE@	monitor/hcidump.$(OBJEXT) \
+@MONITOR_TRUE@	monitor/ellisys.$(OBJEXT) \
+@MONITOR_TRUE@	monitor/control.$(OBJEXT) \
+@MONITOR_TRUE@	monitor/packet.$(OBJEXT) \
+@MONITOR_TRUE@	monitor/vendor.$(OBJEXT) monitor/lmp.$(OBJEXT) \
+@MONITOR_TRUE@	monitor/crc.$(OBJEXT) monitor/ll.$(OBJEXT) \
+@MONITOR_TRUE@	monitor/l2cap.$(OBJEXT) monitor/sdp.$(OBJEXT) \
+@MONITOR_TRUE@	monitor/uuid.$(OBJEXT) monitor/hwdb.$(OBJEXT) \
+@MONITOR_TRUE@	monitor/keys.$(OBJEXT) monitor/analyze.$(OBJEXT) \
+@MONITOR_TRUE@	src/shared/util.$(OBJEXT) \
+@MONITOR_TRUE@	src/shared/queue.$(OBJEXT) \
+@MONITOR_TRUE@	src/shared/crypto.$(OBJEXT) \
+@MONITOR_TRUE@	src/shared/btsnoop.$(OBJEXT)
+monitor_btmon_OBJECTS = $(am_monitor_btmon_OBJECTS)
+@MONITOR_TRUE@monitor_btmon_DEPENDENCIES =  \
+@MONITOR_TRUE@	lib/libbluetooth-internal.la
+am__obexd_src_obexd_SOURCES_DIST = btio/btio.h btio/btio.c \
+	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 \
+	obexd/plugins/filesystem.c obexd/plugins/filesystem.h \
+	obexd/plugins/bluetooth.c obexd/plugins/pcsuite.c \
+	obexd/plugins/opp.c obexd/plugins/ftp.c obexd/plugins/ftp.h \
+	obexd/plugins/irmc.c obexd/plugins/pbap.c \
+	obexd/plugins/vcard.h obexd/plugins/vcard.c \
+	obexd/plugins/phonebook.h obexd/plugins/phonebook-dummy.c \
+	obexd/plugins/mas.c obexd/src/map_ap.h \
+	obexd/plugins/messages.h obexd/plugins/messages-dummy.c \
+	obexd/client/mns.c obexd/client/map-event.h 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.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
+am__objects_4 = btio/obexd-btio.$(OBJEXT)
+am__objects_5 = gobex/obexd-gobex.$(OBJEXT) \
+	gobex/obexd-gobex-defs.$(OBJEXT) \
+	gobex/obexd-gobex-packet.$(OBJEXT) \
+	gobex/obexd-gobex-header.$(OBJEXT) \
+	gobex/obexd-gobex-transfer.$(OBJEXT) \
+	gobex/obexd-gobex-apparam.$(OBJEXT)
+@EXPERIMENTAL_TRUE@am__objects_6 =  \
+@EXPERIMENTAL_TRUE@	obexd/plugins/obexd-pcsuite.$(OBJEXT)
+@OBEX_TRUE@am__objects_7 = obexd/plugins/obexd-irmc.$(OBJEXT) \
+@OBEX_TRUE@	obexd/plugins/obexd-pbap.$(OBJEXT) \
+@OBEX_TRUE@	obexd/plugins/obexd-vcard.$(OBJEXT) \
+@OBEX_TRUE@	obexd/plugins/obexd-phonebook-dummy.$(OBJEXT)
+am__objects_8 = obexd/plugins/obexd-filesystem.$(OBJEXT) \
+	obexd/plugins/obexd-bluetooth.$(OBJEXT) $(am__objects_6) \
+	obexd/plugins/obexd-opp.$(OBJEXT) \
+	obexd/plugins/obexd-ftp.$(OBJEXT) $(am__objects_7) \
+	obexd/plugins/obexd-mas.$(OBJEXT) \
+	obexd/plugins/obexd-messages-dummy.$(OBJEXT) \
+	obexd/client/obexd-mns.$(OBJEXT)
+am_obexd_src_obexd_OBJECTS = $(am__objects_4) $(am__objects_5) \
+	$(am__objects_8) obexd/src/obexd-main.$(OBJEXT) \
+	obexd/src/obexd-plugin.$(OBJEXT) obexd/src/obexd-log.$(OBJEXT) \
+	obexd/src/obexd-manager.$(OBJEXT) \
+	obexd/src/obexd-obex.$(OBJEXT) \
+	obexd/src/obexd-mimetype.$(OBJEXT) \
+	obexd/src/obexd-service.$(OBJEXT) \
+	obexd/src/obexd-transport.$(OBJEXT) \
+	obexd/src/obexd-server.$(OBJEXT) \
+	obexd/client/obexd-manager.$(OBJEXT) \
+	obexd/client/obexd-session.$(OBJEXT) \
+	obexd/client/obexd-bluetooth.$(OBJEXT) \
+	obexd/client/obexd-sync.$(OBJEXT) \
+	obexd/client/obexd-pbap.$(OBJEXT) \
+	obexd/client/obexd-ftp.$(OBJEXT) \
+	obexd/client/obexd-opp.$(OBJEXT) \
+	obexd/client/obexd-map.$(OBJEXT) \
+	obexd/client/obexd-map-event.$(OBJEXT) \
+	obexd/client/obexd-transfer.$(OBJEXT) \
+	obexd/client/obexd-transport.$(OBJEXT) \
+	obexd/client/obexd-dbus.$(OBJEXT) \
+	obexd/client/obexd-driver.$(OBJEXT)
+am__objects_9 = $(am__objects_1)
+nodist_obexd_src_obexd_OBJECTS = $(am__objects_9)
+obexd_src_obexd_OBJECTS = $(am_obexd_src_obexd_OBJECTS) \
+	$(nodist_obexd_src_obexd_OBJECTS)
+obexd_src_obexd_DEPENDENCIES = lib/libbluetooth-internal.la \
+	gdbus/libgdbus-internal.la
+obexd_src_obexd_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \
+	$(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \
+	$(obexd_src_obexd_CFLAGS) $(CFLAGS) $(obexd_src_obexd_LDFLAGS) \
+	$(LDFLAGS) -o $@
+am__profiles_cups_bluetooth_SOURCES_DIST = profiles/cups/main.c \
+	profiles/cups/cups.h profiles/cups/sdp.c profiles/cups/spp.c \
+	profiles/cups/hcrp.c
+@CUPS_TRUE@am_profiles_cups_bluetooth_OBJECTS =  \
+@CUPS_TRUE@	profiles/cups/main.$(OBJEXT) \
+@CUPS_TRUE@	profiles/cups/sdp.$(OBJEXT) \
+@CUPS_TRUE@	profiles/cups/spp.$(OBJEXT) \
+@CUPS_TRUE@	profiles/cups/hcrp.$(OBJEXT)
+profiles_cups_bluetooth_OBJECTS =  \
+	$(am_profiles_cups_bluetooth_OBJECTS)
+@CUPS_TRUE@profiles_cups_bluetooth_DEPENDENCIES =  \
+@CUPS_TRUE@	lib/libbluetooth-internal.la \
+@CUPS_TRUE@	gdbus/libgdbus-internal.la
+am__profiles_iap_iapd_SOURCES_DIST = profiles/iap/main.c
+@EXPERIMENTAL_TRUE@am_profiles_iap_iapd_OBJECTS =  \
+@EXPERIMENTAL_TRUE@	profiles/iap/main.$(OBJEXT)
+profiles_iap_iapd_OBJECTS = $(am_profiles_iap_iapd_OBJECTS)
+@EXPERIMENTAL_TRUE@profiles_iap_iapd_DEPENDENCIES =  \
+@EXPERIMENTAL_TRUE@	gdbus/libgdbus-internal.la
+am__src_bluetoothd_SOURCES_DIST = plugins/hostname.c plugins/wiimote.c \
+	plugins/autopair.c plugins/dropcam.c plugins/policy.c \
+	plugins/gatt-example.c plugins/neard.c 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 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 \
+	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 \
+	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 profiles/input/manager.c \
+	profiles/input/server.h profiles/input/server.c \
+	profiles/input/device.h profiles/input/device.c \
+	profiles/input/hog.c profiles/input/uhid_copy.h \
+	profiles/input/suspend.h profiles/input/suspend-dummy.c \
+	profiles/health/mcap_lib.h profiles/health/mcap_internal.h \
+	profiles/health/mcap.h profiles/health/mcap.c \
+	profiles/health/mcap_sync.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 profiles/gatt/gas.c \
+	profiles/scanparam/scan.c profiles/deviceinfo/deviceinfo.c \
+	profiles/alert/server.c profiles/time/server.c \
+	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 \
+	profiles/thermometer/thermometer.c \
+	profiles/heartrate/heartrate.c \
+	profiles/cyclingspeed/cyclingspeed.c 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/btio.h btio/btio.c \
+	src/bluetooth.ver src/main.c src/log.h src/log.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/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/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-dbus.h src/gatt-dbus.c src/gatt.h src/gatt.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/shared/io.h \
+	src/shared/io-glib.c src/shared/timeout.h \
+	src/shared/timeout-glib.c src/shared/queue.h \
+	src/shared/queue.c src/shared/util.h src/shared/util.c \
+	src/shared/mgmt.h src/shared/mgmt.c
+@MAINTAINER_MODE_TRUE@am__objects_10 = plugins/bluetoothd-gatt-example.$(OBJEXT)
+@EXPERIMENTAL_TRUE@am__objects_11 =  \
+@EXPERIMENTAL_TRUE@	plugins/bluetoothd-neard.$(OBJEXT) \
+@EXPERIMENTAL_TRUE@	profiles/sap/bluetoothd-main.$(OBJEXT) \
+@EXPERIMENTAL_TRUE@	profiles/sap/bluetoothd-manager.$(OBJEXT) \
+@EXPERIMENTAL_TRUE@	profiles/sap/bluetoothd-server.$(OBJEXT) \
+@EXPERIMENTAL_TRUE@	profiles/sap/bluetoothd-sap-dummy.$(OBJEXT)
+@EXPERIMENTAL_TRUE@am__objects_12 =  \
+@EXPERIMENTAL_TRUE@	profiles/health/bluetoothd-mcap.$(OBJEXT) \
+@EXPERIMENTAL_TRUE@	profiles/health/bluetoothd-mcap_sync.$(OBJEXT) \
+@EXPERIMENTAL_TRUE@	profiles/health/bluetoothd-hdp_main.$(OBJEXT) \
+@EXPERIMENTAL_TRUE@	profiles/health/bluetoothd-hdp_manager.$(OBJEXT) \
+@EXPERIMENTAL_TRUE@	profiles/health/bluetoothd-hdp.$(OBJEXT) \
+@EXPERIMENTAL_TRUE@	profiles/health/bluetoothd-hdp_util.$(OBJEXT)
+@EXPERIMENTAL_TRUE@am__objects_13 =  \
+@EXPERIMENTAL_TRUE@	profiles/alert/bluetoothd-server.$(OBJEXT) \
+@EXPERIMENTAL_TRUE@	profiles/time/bluetoothd-server.$(OBJEXT) \
+@EXPERIMENTAL_TRUE@	profiles/proximity/bluetoothd-main.$(OBJEXT) \
+@EXPERIMENTAL_TRUE@	profiles/proximity/bluetoothd-manager.$(OBJEXT) \
+@EXPERIMENTAL_TRUE@	profiles/proximity/bluetoothd-monitor.$(OBJEXT) \
+@EXPERIMENTAL_TRUE@	profiles/proximity/bluetoothd-reporter.$(OBJEXT) \
+@EXPERIMENTAL_TRUE@	profiles/proximity/bluetoothd-linkloss.$(OBJEXT) \
+@EXPERIMENTAL_TRUE@	profiles/proximity/bluetoothd-immalert.$(OBJEXT) \
+@EXPERIMENTAL_TRUE@	profiles/thermometer/bluetoothd-thermometer.$(OBJEXT) \
+@EXPERIMENTAL_TRUE@	profiles/heartrate/bluetoothd-heartrate.$(OBJEXT) \
+@EXPERIMENTAL_TRUE@	profiles/cyclingspeed/bluetoothd-cyclingspeed.$(OBJEXT)
+am__objects_14 = plugins/bluetoothd-hostname.$(OBJEXT) \
+	plugins/bluetoothd-wiimote.$(OBJEXT) \
+	plugins/bluetoothd-autopair.$(OBJEXT) \
+	plugins/bluetoothd-dropcam.$(OBJEXT) \
+	plugins/bluetoothd-policy.$(OBJEXT) $(am__objects_10) \
+	$(am__objects_11) profiles/audio/bluetoothd-source.$(OBJEXT) \
+	profiles/audio/bluetoothd-sink.$(OBJEXT) \
+	profiles/audio/bluetoothd-a2dp.$(OBJEXT) \
+	profiles/audio/bluetoothd-avdtp.$(OBJEXT) \
+	profiles/audio/bluetoothd-media.$(OBJEXT) \
+	profiles/audio/bluetoothd-transport.$(OBJEXT) \
+	profiles/audio/bluetoothd-control.$(OBJEXT) \
+	profiles/audio/bluetoothd-avctp.$(OBJEXT) \
+	profiles/audio/bluetoothd-avrcp.$(OBJEXT) \
+	profiles/audio/bluetoothd-player.$(OBJEXT) \
+	profiles/network/bluetoothd-manager.$(OBJEXT) \
+	profiles/network/bluetoothd-bnep.$(OBJEXT) \
+	profiles/network/bluetoothd-server.$(OBJEXT) \
+	profiles/network/bluetoothd-connection.$(OBJEXT) \
+	profiles/input/bluetoothd-manager.$(OBJEXT) \
+	profiles/input/bluetoothd-server.$(OBJEXT) \
+	profiles/input/bluetoothd-device.$(OBJEXT) \
+	profiles/input/bluetoothd-hog.$(OBJEXT) \
+	profiles/input/bluetoothd-suspend-dummy.$(OBJEXT) \
+	$(am__objects_12) profiles/gatt/bluetoothd-gas.$(OBJEXT) \
+	profiles/scanparam/bluetoothd-scan.$(OBJEXT) \
+	profiles/deviceinfo/bluetoothd-deviceinfo.$(OBJEXT) \
+	$(am__objects_13)
+am__objects_15 = attrib/bluetoothd-att.$(OBJEXT) \
+	attrib/bluetoothd-gatt.$(OBJEXT) \
+	attrib/bluetoothd-gattrib.$(OBJEXT) \
+	attrib/bluetoothd-gatt-service.$(OBJEXT)
+am__objects_16 = btio/bluetoothd-btio.$(OBJEXT)
+am_src_bluetoothd_OBJECTS = $(am__objects_14) $(am__objects_15) \
+	$(am__objects_16) src/bluetoothd-main.$(OBJEXT) \
+	src/bluetoothd-log.$(OBJEXT) src/bluetoothd-systemd.$(OBJEXT) \
+	src/bluetoothd-rfkill.$(OBJEXT) \
+	src/bluetoothd-sdpd-server.$(OBJEXT) \
+	src/bluetoothd-sdpd-request.$(OBJEXT) \
+	src/bluetoothd-sdpd-service.$(OBJEXT) \
+	src/bluetoothd-sdpd-database.$(OBJEXT) \
+	src/bluetoothd-attrib-server.$(OBJEXT) \
+	src/bluetoothd-sdp-xml.$(OBJEXT) \
+	src/bluetoothd-sdp-client.$(OBJEXT) \
+	src/bluetoothd-textfile.$(OBJEXT) \
+	src/bluetoothd-uuid-helper.$(OBJEXT) \
+	src/bluetoothd-plugin.$(OBJEXT) \
+	src/bluetoothd-storage.$(OBJEXT) \
+	src/bluetoothd-agent.$(OBJEXT) src/bluetoothd-error.$(OBJEXT) \
+	src/bluetoothd-adapter.$(OBJEXT) \
+	src/bluetoothd-profile.$(OBJEXT) \
+	src/bluetoothd-service.$(OBJEXT) \
+	src/bluetoothd-gatt-dbus.$(OBJEXT) \
+	src/bluetoothd-gatt.$(OBJEXT) src/bluetoothd-device.$(OBJEXT) \
+	src/bluetoothd-dbus-common.$(OBJEXT) \
+	src/bluetoothd-eir.$(OBJEXT) \
+	src/shared/bluetoothd-io-glib.$(OBJEXT) \
+	src/shared/bluetoothd-timeout-glib.$(OBJEXT) \
+	src/shared/bluetoothd-queue.$(OBJEXT) \
+	src/shared/bluetoothd-util.$(OBJEXT) \
+	src/shared/bluetoothd-mgmt.$(OBJEXT)
+nodist_src_bluetoothd_OBJECTS = $(am__objects_9)
+src_bluetoothd_OBJECTS = $(am_src_bluetoothd_OBJECTS) \
+	$(nodist_src_bluetoothd_OBJECTS)
+src_bluetoothd_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \
+	$(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \
+	$(src_bluetoothd_CFLAGS) $(CFLAGS) $(src_bluetoothd_LDFLAGS) \
+	$(LDFLAGS) -o $@
+am__tools_3dsp_SOURCES_DIST = tools/3dsp.c monitor/bt.h \
+	monitor/mainloop.h monitor/mainloop.c src/shared/io.h \
+	src/shared/io-mainloop.c src/shared/timeout.h \
+	src/shared/timeout-mainloop.c src/shared/hci.h \
+	src/shared/hci.c src/shared/util.h src/shared/util.c \
+	src/shared/queue.h src/shared/queue.c
+@EXPERIMENTAL_TRUE@am_tools_3dsp_OBJECTS = tools/3dsp.$(OBJEXT) \
+@EXPERIMENTAL_TRUE@	monitor/mainloop.$(OBJEXT) \
+@EXPERIMENTAL_TRUE@	src/shared/io-mainloop.$(OBJEXT) \
+@EXPERIMENTAL_TRUE@	src/shared/timeout-mainloop.$(OBJEXT) \
+@EXPERIMENTAL_TRUE@	src/shared/hci.$(OBJEXT) \
+@EXPERIMENTAL_TRUE@	src/shared/util.$(OBJEXT) \
+@EXPERIMENTAL_TRUE@	src/shared/queue.$(OBJEXT)
+tools_3dsp_OBJECTS = $(am_tools_3dsp_OBJECTS)
+tools_3dsp_LDADD = $(LDADD)
+tools_amptest_SOURCES = tools/amptest.c
+tools_amptest_OBJECTS = tools/amptest.$(OBJEXT)
+@EXPERIMENTAL_TRUE@tools_amptest_DEPENDENCIES =  \
+@EXPERIMENTAL_TRUE@	lib/libbluetooth-internal.la
+tools_avinfo_SOURCES = tools/avinfo.c
+tools_avinfo_OBJECTS = tools/avinfo.$(OBJEXT)
+@EXPERIMENTAL_TRUE@tools_avinfo_DEPENDENCIES =  \
+@EXPERIMENTAL_TRUE@	lib/libbluetooth-internal.la
+tools_avtest_SOURCES = tools/avtest.c
+tools_avtest_OBJECTS = tools/avtest.$(OBJEXT)
+@EXPERIMENTAL_TRUE@tools_avtest_DEPENDENCIES =  \
+@EXPERIMENTAL_TRUE@	lib/libbluetooth-internal.la
+am__tools_bccmd_SOURCES_DIST = 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_TRUE@am_tools_bccmd_OBJECTS = tools/bccmd.$(OBJEXT) \
+@TOOLS_TRUE@	tools/csr.$(OBJEXT) tools/csr_hci.$(OBJEXT) \
+@TOOLS_TRUE@	tools/csr_usb.$(OBJEXT) tools/csr_h4.$(OBJEXT) \
+@TOOLS_TRUE@	tools/csr_3wire.$(OBJEXT) tools/csr_bcsp.$(OBJEXT) \
+@TOOLS_TRUE@	tools/ubcsp.$(OBJEXT)
+tools_bccmd_OBJECTS = $(am_tools_bccmd_OBJECTS)
+@TOOLS_TRUE@tools_bccmd_DEPENDENCIES = lib/libbluetooth-internal.la
+am__tools_bdaddr_SOURCES_DIST = tools/bdaddr.c src/oui.h src/oui.c
+@EXPERIMENTAL_TRUE@am_tools_bdaddr_OBJECTS = tools/bdaddr.$(OBJEXT) \
+@EXPERIMENTAL_TRUE@	src/oui.$(OBJEXT)
+tools_bdaddr_OBJECTS = $(am_tools_bdaddr_OBJECTS)
+@EXPERIMENTAL_TRUE@tools_bdaddr_DEPENDENCIES =  \
+@EXPERIMENTAL_TRUE@	lib/libbluetooth-internal.la
+am__tools_bluemoon_SOURCES_DIST = tools/bluemoon.c monitor/bt.h \
+	monitor/mainloop.h monitor/mainloop.c src/shared/io.h \
+	src/shared/io-mainloop.c src/shared/hci.h src/shared/hci.c \
+	src/shared/util.h src/shared/util.c src/shared/queue.h \
+	src/shared/queue.c
+@TOOLS_TRUE@am_tools_bluemoon_OBJECTS = tools/bluemoon.$(OBJEXT) \
+@TOOLS_TRUE@	monitor/mainloop.$(OBJEXT) \
+@TOOLS_TRUE@	src/shared/io-mainloop.$(OBJEXT) \
+@TOOLS_TRUE@	src/shared/hci.$(OBJEXT) src/shared/util.$(OBJEXT) \
+@TOOLS_TRUE@	src/shared/queue.$(OBJEXT)
+tools_bluemoon_OBJECTS = $(am_tools_bluemoon_OBJECTS)
+tools_bluemoon_LDADD = $(LDADD)
+am__tools_bluetooth_player_SOURCES_DIST = tools/bluetooth-player.c \
+	client/display.h client/display.c
+@READLINE_TRUE@am_tools_bluetooth_player_OBJECTS =  \
+@READLINE_TRUE@	tools/bluetooth-player.$(OBJEXT) \
+@READLINE_TRUE@	client/display.$(OBJEXT)
+tools_bluetooth_player_OBJECTS = $(am_tools_bluetooth_player_OBJECTS)
+@READLINE_TRUE@tools_bluetooth_player_DEPENDENCIES =  \
+@READLINE_TRUE@	gdbus/libgdbus-internal.la
+tools_btattach_SOURCES = tools/btattach.c
+tools_btattach_OBJECTS = tools/btattach.$(OBJEXT)
+tools_btattach_LDADD = $(LDADD)
+am__tools_btinfo_SOURCES_DIST = tools/btinfo.c monitor/bt.h \
+	monitor/mainloop.h monitor/mainloop.c src/shared/io.h \
+	src/shared/io-mainloop.c src/shared/timeout.h \
+	src/shared/timeout-mainloop.c src/shared/hci.h \
+	src/shared/hci.c src/shared/util.h src/shared/util.c \
+	src/shared/queue.h src/shared/queue.c
+@EXPERIMENTAL_TRUE@am_tools_btinfo_OBJECTS = tools/btinfo.$(OBJEXT) \
+@EXPERIMENTAL_TRUE@	monitor/mainloop.$(OBJEXT) \
+@EXPERIMENTAL_TRUE@	src/shared/io-mainloop.$(OBJEXT) \
+@EXPERIMENTAL_TRUE@	src/shared/timeout-mainloop.$(OBJEXT) \
+@EXPERIMENTAL_TRUE@	src/shared/hci.$(OBJEXT) \
+@EXPERIMENTAL_TRUE@	src/shared/util.$(OBJEXT) \
+@EXPERIMENTAL_TRUE@	src/shared/queue.$(OBJEXT)
+tools_btinfo_OBJECTS = $(am_tools_btinfo_OBJECTS)
+tools_btinfo_LDADD = $(LDADD)
+am__tools_btiotest_SOURCES_DIST = tools/btiotest.c btio/btio.h \
+	btio/btio.c
+@EXPERIMENTAL_TRUE@am_tools_btiotest_OBJECTS =  \
+@EXPERIMENTAL_TRUE@	tools/btiotest.$(OBJEXT) \
+@EXPERIMENTAL_TRUE@	btio/btio.$(OBJEXT)
+tools_btiotest_OBJECTS = $(am_tools_btiotest_OBJECTS)
+@EXPERIMENTAL_TRUE@tools_btiotest_DEPENDENCIES =  \
+@EXPERIMENTAL_TRUE@	lib/libbluetooth-internal.la
+am__tools_btmgmt_SOURCES_DIST = tools/btmgmt.c src/uuid-helper.c \
+	monitor/mainloop.h monitor/mainloop.c src/shared/io.h \
+	src/shared/io-mainloop.c src/shared/queue.h src/shared/queue.c \
+	src/shared/util.h src/shared/util.c src/shared/mgmt.h \
+	src/shared/mgmt.c
+@EXPERIMENTAL_TRUE@am_tools_btmgmt_OBJECTS = tools/btmgmt.$(OBJEXT) \
+@EXPERIMENTAL_TRUE@	src/uuid-helper.$(OBJEXT) \
+@EXPERIMENTAL_TRUE@	monitor/mainloop.$(OBJEXT) \
+@EXPERIMENTAL_TRUE@	src/shared/io-mainloop.$(OBJEXT) \
+@EXPERIMENTAL_TRUE@	src/shared/queue.$(OBJEXT) \
+@EXPERIMENTAL_TRUE@	src/shared/util.$(OBJEXT) \
+@EXPERIMENTAL_TRUE@	src/shared/mgmt.$(OBJEXT)
+tools_btmgmt_OBJECTS = $(am_tools_btmgmt_OBJECTS)
+@EXPERIMENTAL_TRUE@tools_btmgmt_DEPENDENCIES =  \
+@EXPERIMENTAL_TRUE@	lib/libbluetooth-internal.la
+am__tools_btproxy_SOURCES_DIST = tools/btproxy.c monitor/bt.h \
+	monitor/mainloop.h monitor/mainloop.c src/shared/util.h \
+	src/shared/util.c
+@EXPERIMENTAL_TRUE@am_tools_btproxy_OBJECTS = tools/btproxy.$(OBJEXT) \
+@EXPERIMENTAL_TRUE@	monitor/mainloop.$(OBJEXT) \
+@EXPERIMENTAL_TRUE@	src/shared/util.$(OBJEXT)
+tools_btproxy_OBJECTS = $(am_tools_btproxy_OBJECTS)
+tools_btproxy_LDADD = $(LDADD)
+am__tools_btsnoop_SOURCES_DIST = tools/btsnoop.c src/shared/pcap.h \
+	src/shared/pcap.c src/shared/btsnoop.h src/shared/btsnoop.c
+@EXPERIMENTAL_TRUE@am_tools_btsnoop_OBJECTS = tools/btsnoop.$(OBJEXT) \
+@EXPERIMENTAL_TRUE@	src/shared/pcap.$(OBJEXT) \
+@EXPERIMENTAL_TRUE@	src/shared/btsnoop.$(OBJEXT)
+tools_btsnoop_OBJECTS = $(am_tools_btsnoop_OBJECTS)
+tools_btsnoop_LDADD = $(LDADD)
+tools_ciptool_SOURCES = tools/ciptool.c
+tools_ciptool_OBJECTS = tools/ciptool.$(OBJEXT)
+@TOOLS_TRUE@tools_ciptool_DEPENDENCIES = lib/libbluetooth-internal.la
+am__tools_cltest_SOURCES_DIST = tools/cltest.c monitor/mainloop.h \
+	monitor/mainloop.c
+@EXPERIMENTAL_TRUE@am_tools_cltest_OBJECTS = tools/cltest.$(OBJEXT) \
+@EXPERIMENTAL_TRUE@	monitor/mainloop.$(OBJEXT)
+tools_cltest_OBJECTS = $(am_tools_cltest_OBJECTS)
+@EXPERIMENTAL_TRUE@tools_cltest_DEPENDENCIES =  \
+@EXPERIMENTAL_TRUE@	lib/libbluetooth-internal.la
+am__tools_gap_tester_SOURCES_DIST = tools/gap-tester.c monitor/bt.h \
+	emulator/btdev.h emulator/btdev.c emulator/bthost.h \
+	emulator/bthost.c emulator/smp.c src/shared/crypto.h \
+	src/shared/crypto.c src/shared/util.h src/shared/util.c \
+	src/shared/queue.h src/shared/queue.c src/shared/hciemu.h \
+	src/shared/hciemu.c src/shared/tester.h src/shared/tester.c \
+	src/shared/timeout.h src/shared/timeout-glib.c
+@EXPERIMENTAL_TRUE@am_tools_gap_tester_OBJECTS =  \
+@EXPERIMENTAL_TRUE@	tools/gap-tester.$(OBJEXT) \
+@EXPERIMENTAL_TRUE@	emulator/btdev.$(OBJEXT) \
+@EXPERIMENTAL_TRUE@	emulator/bthost.$(OBJEXT) \
+@EXPERIMENTAL_TRUE@	emulator/smp.$(OBJEXT) \
+@EXPERIMENTAL_TRUE@	src/shared/crypto.$(OBJEXT) \
+@EXPERIMENTAL_TRUE@	src/shared/util.$(OBJEXT) \
+@EXPERIMENTAL_TRUE@	src/shared/queue.$(OBJEXT) \
+@EXPERIMENTAL_TRUE@	src/shared/hciemu.$(OBJEXT) \
+@EXPERIMENTAL_TRUE@	src/shared/tester.$(OBJEXT) \
+@EXPERIMENTAL_TRUE@	src/shared/timeout-glib.$(OBJEXT)
+tools_gap_tester_OBJECTS = $(am_tools_gap_tester_OBJECTS)
+@EXPERIMENTAL_TRUE@tools_gap_tester_DEPENDENCIES =  \
+@EXPERIMENTAL_TRUE@	lib/libbluetooth-internal.la \
+@EXPERIMENTAL_TRUE@	gdbus/libgdbus-internal.la
+am__tools_gatt_service_SOURCES_DIST = tools/gatt-service.c
+@EXPERIMENTAL_TRUE@am_tools_gatt_service_OBJECTS =  \
+@EXPERIMENTAL_TRUE@	tools/gatt-service.$(OBJEXT)
+tools_gatt_service_OBJECTS = $(am_tools_gatt_service_OBJECTS)
+@EXPERIMENTAL_TRUE@tools_gatt_service_DEPENDENCIES =  \
+@EXPERIMENTAL_TRUE@	gdbus/libgdbus-internal.la
+am__tools_hci_tester_SOURCES_DIST = tools/hci-tester.c monitor/bt.h \
+	src/shared/io.h src/shared/io-glib.c src/shared/hci.h \
+	src/shared/hci.c src/shared/util.h src/shared/util.c \
+	src/shared/queue.h src/shared/queue.c src/shared/tester.h \
+	src/shared/tester.c
+@EXPERIMENTAL_TRUE@am_tools_hci_tester_OBJECTS =  \
+@EXPERIMENTAL_TRUE@	tools/hci-tester.$(OBJEXT) \
+@EXPERIMENTAL_TRUE@	src/shared/io-glib.$(OBJEXT) \
+@EXPERIMENTAL_TRUE@	src/shared/hci.$(OBJEXT) \
+@EXPERIMENTAL_TRUE@	src/shared/util.$(OBJEXT) \
+@EXPERIMENTAL_TRUE@	src/shared/queue.$(OBJEXT) \
+@EXPERIMENTAL_TRUE@	src/shared/tester.$(OBJEXT)
+tools_hci_tester_OBJECTS = $(am_tools_hci_tester_OBJECTS)
+tools_hci_tester_DEPENDENCIES =
+am__tools_hciattach_SOURCES_DIST = 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_TRUE@am_tools_hciattach_OBJECTS = tools/hciattach.$(OBJEXT) \
+@TOOLS_TRUE@	tools/hciattach_st.$(OBJEXT) \
+@TOOLS_TRUE@	tools/hciattach_ti.$(OBJEXT) \
+@TOOLS_TRUE@	tools/hciattach_tialt.$(OBJEXT) \
+@TOOLS_TRUE@	tools/hciattach_ath3k.$(OBJEXT) \
+@TOOLS_TRUE@	tools/hciattach_qualcomm.$(OBJEXT) \
+@TOOLS_TRUE@	tools/hciattach_intel.$(OBJEXT)
+tools_hciattach_OBJECTS = $(am_tools_hciattach_OBJECTS)
+@TOOLS_TRUE@tools_hciattach_DEPENDENCIES =  \
+@TOOLS_TRUE@	lib/libbluetooth-internal.la
+am__tools_hciconfig_SOURCES_DIST = tools/hciconfig.c tools/csr.h \
+	tools/csr.c
+@TOOLS_TRUE@am_tools_hciconfig_OBJECTS = tools/hciconfig.$(OBJEXT) \
+@TOOLS_TRUE@	tools/csr.$(OBJEXT)
+tools_hciconfig_OBJECTS = $(am_tools_hciconfig_OBJECTS)
+@TOOLS_TRUE@tools_hciconfig_DEPENDENCIES =  \
+@TOOLS_TRUE@	lib/libbluetooth-internal.la
+am__tools_hcidump_SOURCES_DIST = 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_TRUE@am_tools_hcidump_OBJECTS = tools/hcidump.$(OBJEXT) \
+@TOOLS_TRUE@	tools/parser/parser.$(OBJEXT) \
+@TOOLS_TRUE@	tools/parser/lmp.$(OBJEXT) \
+@TOOLS_TRUE@	tools/parser/hci.$(OBJEXT) \
+@TOOLS_TRUE@	tools/parser/l2cap.$(OBJEXT) \
+@TOOLS_TRUE@	tools/parser/amp.$(OBJEXT) \
+@TOOLS_TRUE@	tools/parser/smp.$(OBJEXT) \
+@TOOLS_TRUE@	tools/parser/att.$(OBJEXT) \
+@TOOLS_TRUE@	tools/parser/sdp.$(OBJEXT) \
+@TOOLS_TRUE@	tools/parser/rfcomm.$(OBJEXT) \
+@TOOLS_TRUE@	tools/parser/bnep.$(OBJEXT) \
+@TOOLS_TRUE@	tools/parser/cmtp.$(OBJEXT) \
+@TOOLS_TRUE@	tools/parser/hidp.$(OBJEXT) \
+@TOOLS_TRUE@	tools/parser/hcrp.$(OBJEXT) \
+@TOOLS_TRUE@	tools/parser/avdtp.$(OBJEXT) \
+@TOOLS_TRUE@	tools/parser/avctp.$(OBJEXT) \
+@TOOLS_TRUE@	tools/parser/avrcp.$(OBJEXT) \
+@TOOLS_TRUE@	tools/parser/sap.$(OBJEXT) \
+@TOOLS_TRUE@	tools/parser/obex.$(OBJEXT) \
+@TOOLS_TRUE@	tools/parser/capi.$(OBJEXT) \
+@TOOLS_TRUE@	tools/parser/ppp.$(OBJEXT) \
+@TOOLS_TRUE@	tools/parser/tcpip.$(OBJEXT) \
+@TOOLS_TRUE@	tools/parser/ericsson.$(OBJEXT) \
+@TOOLS_TRUE@	tools/parser/csr.$(OBJEXT) \
+@TOOLS_TRUE@	tools/parser/bpa.$(OBJEXT)
+tools_hcidump_OBJECTS = $(am_tools_hcidump_OBJECTS)
+@TOOLS_TRUE@tools_hcidump_DEPENDENCIES = lib/libbluetooth-internal.la
+tools_hcieventmask_SOURCES = tools/hcieventmask.c
+tools_hcieventmask_OBJECTS = tools/hcieventmask.$(OBJEXT)
+@EXPERIMENTAL_TRUE@tools_hcieventmask_DEPENDENCIES =  \
+@EXPERIMENTAL_TRUE@	lib/libbluetooth-internal.la
+tools_hcisecfilter_SOURCES = tools/hcisecfilter.c
+tools_hcisecfilter_OBJECTS = tools/hcisecfilter.$(OBJEXT)
+tools_hcisecfilter_LDADD = $(LDADD)
+am__tools_hcitool_SOURCES_DIST = tools/hcitool.c src/oui.h src/oui.c
+@TOOLS_TRUE@am_tools_hcitool_OBJECTS = tools/hcitool.$(OBJEXT) \
+@TOOLS_TRUE@	src/oui.$(OBJEXT)
+tools_hcitool_OBJECTS = $(am_tools_hcitool_OBJECTS)
+@TOOLS_TRUE@tools_hcitool_DEPENDENCIES = lib/libbluetooth-internal.la
+tools_hid2hci_SOURCES = tools/hid2hci.c
+tools_hid2hci_OBJECTS = tools/hid2hci.$(OBJEXT)
+tools_hid2hci_DEPENDENCIES =
+tools_hwdb_SOURCES = tools/hwdb.c
+tools_hwdb_OBJECTS = tools/hwdb.$(OBJEXT)
+@EXPERIMENTAL_TRUE@tools_hwdb_DEPENDENCIES =  \
+@EXPERIMENTAL_TRUE@	lib/libbluetooth-internal.la
+am__tools_ibeacon_SOURCES_DIST = tools/ibeacon.c monitor/bt.h \
+	monitor/mainloop.h monitor/mainloop.c src/shared/io.h \
+	src/shared/io-mainloop.c src/shared/timeout.h \
+	src/shared/timeout-mainloop.c src/shared/hci.h \
+	src/shared/hci.c src/shared/util.h src/shared/util.c \
+	src/shared/queue.h src/shared/queue.c
+@EXPERIMENTAL_TRUE@am_tools_ibeacon_OBJECTS = tools/ibeacon.$(OBJEXT) \
+@EXPERIMENTAL_TRUE@	monitor/mainloop.$(OBJEXT) \
+@EXPERIMENTAL_TRUE@	src/shared/io-mainloop.$(OBJEXT) \
+@EXPERIMENTAL_TRUE@	src/shared/timeout-mainloop.$(OBJEXT) \
+@EXPERIMENTAL_TRUE@	src/shared/hci.$(OBJEXT) \
+@EXPERIMENTAL_TRUE@	src/shared/util.$(OBJEXT) \
+@EXPERIMENTAL_TRUE@	src/shared/queue.$(OBJEXT)
+tools_ibeacon_OBJECTS = $(am_tools_ibeacon_OBJECTS)
+tools_ibeacon_LDADD = $(LDADD)
+am__tools_l2cap_tester_SOURCES_DIST = tools/l2cap-tester.c \
+	monitor/bt.h emulator/btdev.h emulator/btdev.c \
+	emulator/bthost.h emulator/bthost.c emulator/smp.c \
+	src/shared/crypto.h src/shared/crypto.c src/shared/io.h \
+	src/shared/io-glib.c 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/hciemu.h src/shared/hciemu.c \
+	src/shared/tester.h src/shared/tester.c src/shared/timeout.h \
+	src/shared/timeout-glib.c
+@EXPERIMENTAL_TRUE@am_tools_l2cap_tester_OBJECTS =  \
+@EXPERIMENTAL_TRUE@	tools/l2cap-tester.$(OBJEXT) \
+@EXPERIMENTAL_TRUE@	emulator/btdev.$(OBJEXT) \
+@EXPERIMENTAL_TRUE@	emulator/bthost.$(OBJEXT) \
+@EXPERIMENTAL_TRUE@	emulator/smp.$(OBJEXT) \
+@EXPERIMENTAL_TRUE@	src/shared/crypto.$(OBJEXT) \
+@EXPERIMENTAL_TRUE@	src/shared/io-glib.$(OBJEXT) \
+@EXPERIMENTAL_TRUE@	src/shared/queue.$(OBJEXT) \
+@EXPERIMENTAL_TRUE@	src/shared/util.$(OBJEXT) \
+@EXPERIMENTAL_TRUE@	src/shared/mgmt.$(OBJEXT) \
+@EXPERIMENTAL_TRUE@	src/shared/hciemu.$(OBJEXT) \
+@EXPERIMENTAL_TRUE@	src/shared/tester.$(OBJEXT) \
+@EXPERIMENTAL_TRUE@	src/shared/timeout-glib.$(OBJEXT)
+tools_l2cap_tester_OBJECTS = $(am_tools_l2cap_tester_OBJECTS)
+@EXPERIMENTAL_TRUE@tools_l2cap_tester_DEPENDENCIES =  \
+@EXPERIMENTAL_TRUE@	lib/libbluetooth-internal.la
+tools_l2ping_SOURCES = tools/l2ping.c
+tools_l2ping_OBJECTS = tools/l2ping.$(OBJEXT)
+@TOOLS_TRUE@tools_l2ping_DEPENDENCIES = lib/libbluetooth-internal.la
+tools_l2test_SOURCES = tools/l2test.c
+tools_l2test_OBJECTS = tools/l2test.$(OBJEXT)
+@TOOLS_TRUE@tools_l2test_DEPENDENCIES = lib/libbluetooth-internal.la
+am__tools_mgmt_tester_SOURCES_DIST = tools/mgmt-tester.c monitor/bt.h \
+	emulator/btdev.h emulator/btdev.c emulator/bthost.h \
+	emulator/bthost.c emulator/smp.c src/shared/crypto.h \
+	src/shared/crypto.c src/shared/io.h src/shared/io-glib.c \
+	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/hciemu.h src/shared/hciemu.c src/shared/tester.h \
+	src/shared/tester.c src/shared/timeout.h \
+	src/shared/timeout-glib.c
+@EXPERIMENTAL_TRUE@am_tools_mgmt_tester_OBJECTS =  \
+@EXPERIMENTAL_TRUE@	tools/mgmt-tester.$(OBJEXT) \
+@EXPERIMENTAL_TRUE@	emulator/btdev.$(OBJEXT) \
+@EXPERIMENTAL_TRUE@	emulator/bthost.$(OBJEXT) \
+@EXPERIMENTAL_TRUE@	emulator/smp.$(OBJEXT) \
+@EXPERIMENTAL_TRUE@	src/shared/crypto.$(OBJEXT) \
+@EXPERIMENTAL_TRUE@	src/shared/io-glib.$(OBJEXT) \
+@EXPERIMENTAL_TRUE@	src/shared/queue.$(OBJEXT) \
+@EXPERIMENTAL_TRUE@	src/shared/util.$(OBJEXT) \
+@EXPERIMENTAL_TRUE@	src/shared/mgmt.$(OBJEXT) \
+@EXPERIMENTAL_TRUE@	src/shared/hciemu.$(OBJEXT) \
+@EXPERIMENTAL_TRUE@	src/shared/tester.$(OBJEXT) \
+@EXPERIMENTAL_TRUE@	src/shared/timeout-glib.$(OBJEXT)
+tools_mgmt_tester_OBJECTS = $(am_tools_mgmt_tester_OBJECTS)
+@EXPERIMENTAL_TRUE@tools_mgmt_tester_DEPENDENCIES =  \
+@EXPERIMENTAL_TRUE@	lib/libbluetooth-internal.la
+am__tools_mpris_player_SOURCES_DIST = tools/mpris-player.c
+@EXPERIMENTAL_TRUE@am_tools_mpris_player_OBJECTS =  \
+@EXPERIMENTAL_TRUE@	tools/mpris-player.$(OBJEXT)
+tools_mpris_player_OBJECTS = $(am_tools_mpris_player_OBJECTS)
+@EXPERIMENTAL_TRUE@tools_mpris_player_DEPENDENCIES =  \
+@EXPERIMENTAL_TRUE@	gdbus/libgdbus-internal.la
+am__tools_obex_client_tool_SOURCES_DIST = 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 btio/btio.h \
+	btio/btio.c tools/obex-client-tool.c
+am__objects_17 = gobex/gobex.$(OBJEXT) gobex/gobex-defs.$(OBJEXT) \
+	gobex/gobex-packet.$(OBJEXT) gobex/gobex-header.$(OBJEXT) \
+	gobex/gobex-transfer.$(OBJEXT) gobex/gobex-apparam.$(OBJEXT)
+am__objects_18 = btio/btio.$(OBJEXT)
+@READLINE_TRUE@am_tools_obex_client_tool_OBJECTS = $(am__objects_17) \
+@READLINE_TRUE@	$(am__objects_18) \
+@READLINE_TRUE@	tools/obex-client-tool.$(OBJEXT)
+tools_obex_client_tool_OBJECTS = $(am_tools_obex_client_tool_OBJECTS)
+@READLINE_TRUE@tools_obex_client_tool_DEPENDENCIES =  \
+@READLINE_TRUE@	lib/libbluetooth-internal.la
+am__tools_obex_server_tool_SOURCES_DIST = 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 btio/btio.h \
+	btio/btio.c tools/obex-server-tool.c
+@READLINE_TRUE@am_tools_obex_server_tool_OBJECTS = $(am__objects_17) \
+@READLINE_TRUE@	$(am__objects_18) \
+@READLINE_TRUE@	tools/obex-server-tool.$(OBJEXT)
+tools_obex_server_tool_OBJECTS = $(am_tools_obex_server_tool_OBJECTS)
+@READLINE_TRUE@tools_obex_server_tool_DEPENDENCIES =  \
+@READLINE_TRUE@	lib/libbluetooth-internal.la
+am__tools_obexctl_SOURCES_DIST = tools/obexctl.c client/display.h \
+	client/display.c
+@READLINE_TRUE@am_tools_obexctl_OBJECTS = tools/obexctl.$(OBJEXT) \
+@READLINE_TRUE@	client/display.$(OBJEXT)
+tools_obexctl_OBJECTS = $(am_tools_obexctl_OBJECTS)
+@READLINE_TRUE@tools_obexctl_DEPENDENCIES =  \
+@READLINE_TRUE@	gdbus/libgdbus-internal.la
+tools_rctest_SOURCES = tools/rctest.c
+tools_rctest_OBJECTS = tools/rctest.$(OBJEXT)
+@TOOLS_TRUE@tools_rctest_DEPENDENCIES = lib/libbluetooth-internal.la
+tools_rfcomm_SOURCES = tools/rfcomm.c
+tools_rfcomm_OBJECTS = tools/rfcomm.$(OBJEXT)
+@TOOLS_TRUE@tools_rfcomm_DEPENDENCIES = lib/libbluetooth-internal.la
+am__tools_rfcomm_tester_SOURCES_DIST = tools/rfcomm-tester.c \
+	monitor/bt.h emulator/btdev.h emulator/btdev.c \
+	emulator/bthost.h emulator/bthost.c emulator/smp.c \
+	src/shared/crypto.h src/shared/crypto.c src/shared/io.h \
+	src/shared/io-glib.c 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/hciemu.h src/shared/hciemu.c \
+	src/shared/tester.h src/shared/tester.c src/shared/timeout.h \
+	src/shared/timeout-glib.c
+@EXPERIMENTAL_TRUE@am_tools_rfcomm_tester_OBJECTS =  \
+@EXPERIMENTAL_TRUE@	tools/rfcomm-tester.$(OBJEXT) \
+@EXPERIMENTAL_TRUE@	emulator/btdev.$(OBJEXT) \
+@EXPERIMENTAL_TRUE@	emulator/bthost.$(OBJEXT) \
+@EXPERIMENTAL_TRUE@	emulator/smp.$(OBJEXT) \
+@EXPERIMENTAL_TRUE@	src/shared/crypto.$(OBJEXT) \
+@EXPERIMENTAL_TRUE@	src/shared/io-glib.$(OBJEXT) \
+@EXPERIMENTAL_TRUE@	src/shared/queue.$(OBJEXT) \
+@EXPERIMENTAL_TRUE@	src/shared/util.$(OBJEXT) \
+@EXPERIMENTAL_TRUE@	src/shared/mgmt.$(OBJEXT) \
+@EXPERIMENTAL_TRUE@	src/shared/hciemu.$(OBJEXT) \
+@EXPERIMENTAL_TRUE@	src/shared/tester.$(OBJEXT) \
+@EXPERIMENTAL_TRUE@	src/shared/timeout-glib.$(OBJEXT)
+tools_rfcomm_tester_OBJECTS = $(am_tools_rfcomm_tester_OBJECTS)
+@EXPERIMENTAL_TRUE@tools_rfcomm_tester_DEPENDENCIES =  \
+@EXPERIMENTAL_TRUE@	lib/libbluetooth-internal.la
+am__tools_sco_tester_SOURCES_DIST = tools/sco-tester.c monitor/bt.h \
+	emulator/btdev.h emulator/btdev.c emulator/bthost.h \
+	emulator/bthost.c emulator/smp.c src/shared/crypto.h \
+	src/shared/crypto.c src/shared/io.h src/shared/io-glib.c \
+	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/hciemu.h src/shared/hciemu.c src/shared/tester.h \
+	src/shared/tester.c src/shared/timeout.h \
+	src/shared/timeout-glib.c
+@EXPERIMENTAL_TRUE@am_tools_sco_tester_OBJECTS =  \
+@EXPERIMENTAL_TRUE@	tools/sco-tester.$(OBJEXT) \
+@EXPERIMENTAL_TRUE@	emulator/btdev.$(OBJEXT) \
+@EXPERIMENTAL_TRUE@	emulator/bthost.$(OBJEXT) \
+@EXPERIMENTAL_TRUE@	emulator/smp.$(OBJEXT) \
+@EXPERIMENTAL_TRUE@	src/shared/crypto.$(OBJEXT) \
+@EXPERIMENTAL_TRUE@	src/shared/io-glib.$(OBJEXT) \
+@EXPERIMENTAL_TRUE@	src/shared/queue.$(OBJEXT) \
+@EXPERIMENTAL_TRUE@	src/shared/util.$(OBJEXT) \
+@EXPERIMENTAL_TRUE@	src/shared/mgmt.$(OBJEXT) \
+@EXPERIMENTAL_TRUE@	src/shared/hciemu.$(OBJEXT) \
+@EXPERIMENTAL_TRUE@	src/shared/tester.$(OBJEXT) \
+@EXPERIMENTAL_TRUE@	src/shared/timeout-glib.$(OBJEXT)
+tools_sco_tester_OBJECTS = $(am_tools_sco_tester_OBJECTS)
+@EXPERIMENTAL_TRUE@tools_sco_tester_DEPENDENCIES =  \
+@EXPERIMENTAL_TRUE@	lib/libbluetooth-internal.la
+tools_scotest_SOURCES = tools/scotest.c
+tools_scotest_OBJECTS = tools/scotest.$(OBJEXT)
+@EXPERIMENTAL_TRUE@tools_scotest_DEPENDENCIES =  \
+@EXPERIMENTAL_TRUE@	lib/libbluetooth-internal.la
+am__tools_sdptool_SOURCES_DIST = tools/sdptool.c src/sdp-xml.h \
+	src/sdp-xml.c
+@TOOLS_TRUE@am_tools_sdptool_OBJECTS = tools/sdptool.$(OBJEXT) \
+@TOOLS_TRUE@	src/sdp-xml.$(OBJEXT)
+tools_sdptool_OBJECTS = $(am_tools_sdptool_OBJECTS)
+@TOOLS_TRUE@tools_sdptool_DEPENDENCIES = lib/libbluetooth-internal.la
+am__tools_seq2bseq_SOURCES_DIST = tools/seq2bseq.c
+@EXPERIMENTAL_TRUE@am_tools_seq2bseq_OBJECTS =  \
+@EXPERIMENTAL_TRUE@	tools/seq2bseq.$(OBJEXT)
+tools_seq2bseq_OBJECTS = $(am_tools_seq2bseq_OBJECTS)
+tools_seq2bseq_LDADD = $(LDADD)
+am__tools_smp_tester_SOURCES_DIST = tools/smp-tester.c monitor/bt.h \
+	emulator/btdev.h emulator/btdev.c emulator/bthost.h \
+	emulator/bthost.c emulator/smp.c src/shared/crypto.h \
+	src/shared/crypto.c src/shared/io.h src/shared/io-glib.c \
+	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/hciemu.h src/shared/hciemu.c src/shared/tester.h \
+	src/shared/tester.c src/shared/timeout.h \
+	src/shared/timeout-glib.c
+@EXPERIMENTAL_TRUE@am_tools_smp_tester_OBJECTS =  \
+@EXPERIMENTAL_TRUE@	tools/smp-tester.$(OBJEXT) \
+@EXPERIMENTAL_TRUE@	emulator/btdev.$(OBJEXT) \
+@EXPERIMENTAL_TRUE@	emulator/bthost.$(OBJEXT) \
+@EXPERIMENTAL_TRUE@	emulator/smp.$(OBJEXT) \
+@EXPERIMENTAL_TRUE@	src/shared/crypto.$(OBJEXT) \
+@EXPERIMENTAL_TRUE@	src/shared/io-glib.$(OBJEXT) \
+@EXPERIMENTAL_TRUE@	src/shared/queue.$(OBJEXT) \
+@EXPERIMENTAL_TRUE@	src/shared/util.$(OBJEXT) \
+@EXPERIMENTAL_TRUE@	src/shared/mgmt.$(OBJEXT) \
+@EXPERIMENTAL_TRUE@	src/shared/hciemu.$(OBJEXT) \
+@EXPERIMENTAL_TRUE@	src/shared/tester.$(OBJEXT) \
+@EXPERIMENTAL_TRUE@	src/shared/timeout-glib.$(OBJEXT)
+tools_smp_tester_OBJECTS = $(am_tools_smp_tester_OBJECTS)
+@EXPERIMENTAL_TRUE@tools_smp_tester_DEPENDENCIES =  \
+@EXPERIMENTAL_TRUE@	lib/libbluetooth-internal.la
+am_unit_test_avctp_OBJECTS = unit/test-avctp.$(OBJEXT) \
+	src/shared/util.$(OBJEXT) src/log.$(OBJEXT) \
+	android/avctp.$(OBJEXT)
+unit_test_avctp_OBJECTS = $(am_unit_test_avctp_OBJECTS)
+unit_test_avctp_DEPENDENCIES =
+am_unit_test_avdtp_OBJECTS = unit/test-avdtp.$(OBJEXT) \
+	src/shared/util.$(OBJEXT) src/log.$(OBJEXT) \
+	android/avdtp.$(OBJEXT)
+unit_test_avdtp_OBJECTS = $(am_unit_test_avdtp_OBJECTS)
+unit_test_avdtp_DEPENDENCIES =
+am_unit_test_avrcp_OBJECTS = unit/test-avrcp.$(OBJEXT) \
+	src/shared/util.$(OBJEXT) src/log.$(OBJEXT) \
+	android/avctp.$(OBJEXT) android/avrcp-lib.$(OBJEXT)
+unit_test_avrcp_OBJECTS = $(am_unit_test_avrcp_OBJECTS)
+unit_test_avrcp_DEPENDENCIES = lib/libbluetooth-internal.la
+am_unit_test_crc_OBJECTS = unit/test-crc.$(OBJEXT) \
+	monitor/crc.$(OBJEXT)
+unit_test_crc_OBJECTS = $(am_unit_test_crc_OBJECTS)
+unit_test_crc_DEPENDENCIES =
+am_unit_test_eir_OBJECTS = unit/test-eir.$(OBJEXT) src/eir.$(OBJEXT) \
+	src/uuid-helper.$(OBJEXT)
+unit_test_eir_OBJECTS = $(am_unit_test_eir_OBJECTS)
+unit_test_eir_DEPENDENCIES = lib/libbluetooth-internal.la
+am_unit_test_gdbus_client_OBJECTS = unit/test-gdbus-client.$(OBJEXT)
+unit_test_gdbus_client_OBJECTS = $(am_unit_test_gdbus_client_OBJECTS)
+unit_test_gdbus_client_DEPENDENCIES = gdbus/libgdbus-internal.la
+am_unit_test_gobex_OBJECTS = $(am__objects_17) unit/util.$(OBJEXT) \
+	unit/test-gobex.$(OBJEXT)
+unit_test_gobex_OBJECTS = $(am_unit_test_gobex_OBJECTS)
+unit_test_gobex_DEPENDENCIES =
+am_unit_test_gobex_apparam_OBJECTS = $(am__objects_17) \
+	unit/util.$(OBJEXT) unit/test-gobex-apparam.$(OBJEXT)
+unit_test_gobex_apparam_OBJECTS =  \
+	$(am_unit_test_gobex_apparam_OBJECTS)
+unit_test_gobex_apparam_DEPENDENCIES =
+am_unit_test_gobex_header_OBJECTS = $(am__objects_17) \
+	unit/util.$(OBJEXT) unit/test-gobex-header.$(OBJEXT)
+unit_test_gobex_header_OBJECTS = $(am_unit_test_gobex_header_OBJECTS)
+unit_test_gobex_header_DEPENDENCIES =
+am_unit_test_gobex_packet_OBJECTS = $(am__objects_17) \
+	unit/util.$(OBJEXT) unit/test-gobex-packet.$(OBJEXT)
+unit_test_gobex_packet_OBJECTS = $(am_unit_test_gobex_packet_OBJECTS)
+unit_test_gobex_packet_DEPENDENCIES =
+am_unit_test_gobex_transfer_OBJECTS = $(am__objects_17) \
+	unit/util.$(OBJEXT) unit/test-gobex-transfer.$(OBJEXT)
+unit_test_gobex_transfer_OBJECTS =  \
+	$(am_unit_test_gobex_transfer_OBJECTS)
+unit_test_gobex_transfer_DEPENDENCIES =
+am_unit_test_hfp_OBJECTS = unit/test-hfp.$(OBJEXT) \
+	src/shared/io-glib.$(OBJEXT) src/shared/queue.$(OBJEXT) \
+	src/shared/util.$(OBJEXT) src/shared/mgmt.$(OBJEXT) \
+	src/shared/ringbuf.$(OBJEXT) src/shared/hfp.$(OBJEXT)
+unit_test_hfp_OBJECTS = $(am_unit_test_hfp_OBJECTS)
+unit_test_hfp_DEPENDENCIES =
+am_unit_test_lib_OBJECTS = unit/test-lib.$(OBJEXT)
+unit_test_lib_OBJECTS = $(am_unit_test_lib_OBJECTS)
+unit_test_lib_DEPENDENCIES = lib/libbluetooth-internal.la
+am_unit_test_mgmt_OBJECTS = unit/test-mgmt.$(OBJEXT) \
+	src/shared/io-glib.$(OBJEXT) src/shared/queue.$(OBJEXT) \
+	src/shared/util.$(OBJEXT) src/shared/mgmt.$(OBJEXT)
+unit_test_mgmt_OBJECTS = $(am_unit_test_mgmt_OBJECTS)
+unit_test_mgmt_DEPENDENCIES =
+am_unit_test_queue_OBJECTS = unit/test-queue.$(OBJEXT) \
+	src/shared/util.$(OBJEXT) src/shared/queue.$(OBJEXT)
+unit_test_queue_OBJECTS = $(am_unit_test_queue_OBJECTS)
+unit_test_queue_DEPENDENCIES =
+am_unit_test_ringbuf_OBJECTS = unit/test-ringbuf.$(OBJEXT) \
+	src/shared/util.$(OBJEXT) src/shared/ringbuf.$(OBJEXT)
+unit_test_ringbuf_OBJECTS = $(am_unit_test_ringbuf_OBJECTS)
+unit_test_ringbuf_DEPENDENCIES =
+am_unit_test_sdp_OBJECTS = unit/test-sdp.$(OBJEXT) \
+	src/shared/util.$(OBJEXT) src/sdpd-database.$(OBJEXT) \
+	src/log.$(OBJEXT) src/sdpd-service.$(OBJEXT) \
+	src/sdpd-request.$(OBJEXT)
+unit_test_sdp_OBJECTS = $(am_unit_test_sdp_OBJECTS)
+unit_test_sdp_DEPENDENCIES = lib/libbluetooth-internal.la
+am_unit_test_textfile_OBJECTS = unit/test-textfile.$(OBJEXT) \
+	src/textfile.$(OBJEXT)
+unit_test_textfile_OBJECTS = $(am_unit_test_textfile_OBJECTS)
+unit_test_textfile_DEPENDENCIES =
+am_unit_test_uuid_OBJECTS = unit/test-uuid.$(OBJEXT)
+unit_test_uuid_OBJECTS = $(am_unit_test_uuid_OBJECTS)
+unit_test_uuid_DEPENDENCIES = lib/libbluetooth-internal.la
+SCRIPTS = $(test_SCRIPTS)
+AM_V_P = $(am__v_P_@AM_V@)
+am__v_P_ = $(am__v_P_@AM_DEFAULT_V@)
+am__v_P_0 = false
+am__v_P_1 = :
+AM_V_GEN = $(am__v_GEN_@AM_V@)
+am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@)
+am__v_GEN_0 = @echo "  GEN     " $@;
+am__v_GEN_1 = 
+AM_V_at = $(am__v_at_@AM_V@)
+am__v_at_ = $(am__v_at_@AM_DEFAULT_V@)
+am__v_at_0 = @
+am__v_at_1 = 
+DEFAULT_INCLUDES = -I.@am__isrc@
+depcomp = $(SHELL) $(top_srcdir)/depcomp
+am__depfiles_maybe = depfiles
+am__mv = mv -f
+COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
+	$(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+	$(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \
+	$(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \
+	$(AM_CFLAGS) $(CFLAGS)
+AM_V_CC = $(am__v_CC_@AM_V@)
+am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@)
+am__v_CC_0 = @echo "  CC      " $@;
+am__v_CC_1 = 
+CCLD = $(CC)
+LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+	$(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+	$(AM_LDFLAGS) $(LDFLAGS) -o $@
+AM_V_CCLD = $(am__v_CCLD_@AM_V@)
+am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@)
+am__v_CCLD_0 = @echo "  CCLD    " $@;
+am__v_CCLD_1 = 
+SOURCES = $(profiles_sap_libsap_a_SOURCES) \
+	$(android_audio_a2dp_default_la_SOURCES) \
+	$(android_bluetooth_default_la_SOURCES) \
+	$(gdbus_libgdbus_internal_la_SOURCES) \
+	$(lib_libbluetooth_internal_la_SOURCES) \
+	$(lib_libbluetooth_la_SOURCES) \
+	$(plugins_external_dummy_la_SOURCES) \
+	$(plugins_sixaxis_la_SOURCES) \
+	$(android_android_tester_SOURCES) \
+	$(android_bluetoothd_SOURCES) \
+	$(android_bluetoothd_snoop_SOURCES) $(android_haltest_SOURCES) \
+	$(android_ipc_tester_SOURCES) \
+	$(android_system_emulator_SOURCES) $(android_test_ipc_SOURCES) \
+	$(attrib_gatttool_SOURCES) $(client_bluetoothctl_SOURCES) \
+	$(emulator_b1ee_SOURCES) $(emulator_btvirt_SOURCES) \
+	$(emulator_hfp_SOURCES) $(monitor_btmon_SOURCES) \
+	$(obexd_src_obexd_SOURCES) $(nodist_obexd_src_obexd_SOURCES) \
+	$(profiles_cups_bluetooth_SOURCES) \
+	$(profiles_iap_iapd_SOURCES) $(src_bluetoothd_SOURCES) \
+	$(nodist_src_bluetoothd_SOURCES) $(tools_3dsp_SOURCES) \
+	tools/amptest.c tools/avinfo.c tools/avtest.c \
+	$(tools_bccmd_SOURCES) $(tools_bdaddr_SOURCES) \
+	$(tools_bluemoon_SOURCES) $(tools_bluetooth_player_SOURCES) \
+	tools/btattach.c $(tools_btinfo_SOURCES) \
+	$(tools_btiotest_SOURCES) $(tools_btmgmt_SOURCES) \
+	$(tools_btproxy_SOURCES) $(tools_btsnoop_SOURCES) \
+	tools/ciptool.c $(tools_cltest_SOURCES) \
+	$(tools_gap_tester_SOURCES) $(tools_gatt_service_SOURCES) \
+	$(tools_hci_tester_SOURCES) $(tools_hciattach_SOURCES) \
+	$(tools_hciconfig_SOURCES) $(tools_hcidump_SOURCES) \
+	tools/hcieventmask.c tools/hcisecfilter.c \
+	$(tools_hcitool_SOURCES) tools/hid2hci.c tools/hwdb.c \
+	$(tools_ibeacon_SOURCES) $(tools_l2cap_tester_SOURCES) \
+	tools/l2ping.c tools/l2test.c $(tools_mgmt_tester_SOURCES) \
+	$(tools_mpris_player_SOURCES) \
+	$(tools_obex_client_tool_SOURCES) \
+	$(tools_obex_server_tool_SOURCES) $(tools_obexctl_SOURCES) \
+	tools/rctest.c tools/rfcomm.c $(tools_rfcomm_tester_SOURCES) \
+	$(tools_sco_tester_SOURCES) tools/scotest.c \
+	$(tools_sdptool_SOURCES) $(tools_seq2bseq_SOURCES) \
+	$(tools_smp_tester_SOURCES) $(unit_test_avctp_SOURCES) \
+	$(unit_test_avdtp_SOURCES) $(unit_test_avrcp_SOURCES) \
+	$(unit_test_crc_SOURCES) $(unit_test_eir_SOURCES) \
+	$(unit_test_gdbus_client_SOURCES) $(unit_test_gobex_SOURCES) \
+	$(unit_test_gobex_apparam_SOURCES) \
+	$(unit_test_gobex_header_SOURCES) \
+	$(unit_test_gobex_packet_SOURCES) \
+	$(unit_test_gobex_transfer_SOURCES) $(unit_test_hfp_SOURCES) \
+	$(unit_test_lib_SOURCES) $(unit_test_mgmt_SOURCES) \
+	$(unit_test_queue_SOURCES) $(unit_test_ringbuf_SOURCES) \
+	$(unit_test_sdp_SOURCES) $(unit_test_textfile_SOURCES) \
+	$(unit_test_uuid_SOURCES)
+DIST_SOURCES = $(am__profiles_sap_libsap_a_SOURCES_DIST) \
+	$(am__android_audio_a2dp_default_la_SOURCES_DIST) \
+	$(am__android_bluetooth_default_la_SOURCES_DIST) \
+	$(gdbus_libgdbus_internal_la_SOURCES) \
+	$(lib_libbluetooth_internal_la_SOURCES) \
+	$(am__lib_libbluetooth_la_SOURCES_DIST) \
+	$(am__plugins_external_dummy_la_SOURCES_DIST) \
+	$(am__plugins_sixaxis_la_SOURCES_DIST) \
+	$(am__android_android_tester_SOURCES_DIST) \
+	$(am__android_bluetoothd_SOURCES_DIST) \
+	$(am__android_bluetoothd_snoop_SOURCES_DIST) \
+	$(am__android_haltest_SOURCES_DIST) \
+	$(am__android_ipc_tester_SOURCES_DIST) \
+	$(am__android_system_emulator_SOURCES_DIST) \
+	$(am__android_test_ipc_SOURCES_DIST) \
+	$(am__attrib_gatttool_SOURCES_DIST) \
+	$(am__client_bluetoothctl_SOURCES_DIST) \
+	$(am__emulator_b1ee_SOURCES_DIST) \
+	$(am__emulator_btvirt_SOURCES_DIST) \
+	$(am__emulator_hfp_SOURCES_DIST) \
+	$(am__monitor_btmon_SOURCES_DIST) \
+	$(am__obexd_src_obexd_SOURCES_DIST) \
+	$(am__profiles_cups_bluetooth_SOURCES_DIST) \
+	$(am__profiles_iap_iapd_SOURCES_DIST) \
+	$(am__src_bluetoothd_SOURCES_DIST) \
+	$(am__tools_3dsp_SOURCES_DIST) tools/amptest.c tools/avinfo.c \
+	tools/avtest.c $(am__tools_bccmd_SOURCES_DIST) \
+	$(am__tools_bdaddr_SOURCES_DIST) \
+	$(am__tools_bluemoon_SOURCES_DIST) \
+	$(am__tools_bluetooth_player_SOURCES_DIST) tools/btattach.c \
+	$(am__tools_btinfo_SOURCES_DIST) \
+	$(am__tools_btiotest_SOURCES_DIST) \
+	$(am__tools_btmgmt_SOURCES_DIST) \
+	$(am__tools_btproxy_SOURCES_DIST) \
+	$(am__tools_btsnoop_SOURCES_DIST) tools/ciptool.c \
+	$(am__tools_cltest_SOURCES_DIST) \
+	$(am__tools_gap_tester_SOURCES_DIST) \
+	$(am__tools_gatt_service_SOURCES_DIST) \
+	$(am__tools_hci_tester_SOURCES_DIST) \
+	$(am__tools_hciattach_SOURCES_DIST) \
+	$(am__tools_hciconfig_SOURCES_DIST) \
+	$(am__tools_hcidump_SOURCES_DIST) tools/hcieventmask.c \
+	tools/hcisecfilter.c $(am__tools_hcitool_SOURCES_DIST) \
+	tools/hid2hci.c tools/hwdb.c $(am__tools_ibeacon_SOURCES_DIST) \
+	$(am__tools_l2cap_tester_SOURCES_DIST) tools/l2ping.c \
+	tools/l2test.c $(am__tools_mgmt_tester_SOURCES_DIST) \
+	$(am__tools_mpris_player_SOURCES_DIST) \
+	$(am__tools_obex_client_tool_SOURCES_DIST) \
+	$(am__tools_obex_server_tool_SOURCES_DIST) \
+	$(am__tools_obexctl_SOURCES_DIST) tools/rctest.c \
+	tools/rfcomm.c $(am__tools_rfcomm_tester_SOURCES_DIST) \
+	$(am__tools_sco_tester_SOURCES_DIST) tools/scotest.c \
+	$(am__tools_sdptool_SOURCES_DIST) \
+	$(am__tools_seq2bseq_SOURCES_DIST) \
+	$(am__tools_smp_tester_SOURCES_DIST) \
+	$(unit_test_avctp_SOURCES) $(unit_test_avdtp_SOURCES) \
+	$(unit_test_avrcp_SOURCES) $(unit_test_crc_SOURCES) \
+	$(unit_test_eir_SOURCES) $(unit_test_gdbus_client_SOURCES) \
+	$(unit_test_gobex_SOURCES) $(unit_test_gobex_apparam_SOURCES) \
+	$(unit_test_gobex_header_SOURCES) \
+	$(unit_test_gobex_packet_SOURCES) \
+	$(unit_test_gobex_transfer_SOURCES) $(unit_test_hfp_SOURCES) \
+	$(unit_test_lib_SOURCES) $(unit_test_mgmt_SOURCES) \
+	$(unit_test_queue_SOURCES) $(unit_test_ringbuf_SOURCES) \
+	$(unit_test_sdp_SOURCES) $(unit_test_textfile_SOURCES) \
+	$(unit_test_uuid_SOURCES)
+am__can_run_installinfo = \
+  case $$AM_UPDATE_INFO_DIR in \
+    n|no|NO) false;; \
+    *) (install-info --version) >/dev/null 2>&1;; \
+  esac
+man1dir = $(mandir)/man1
+man8dir = $(mandir)/man8
+NROFF = nroff
+MANS = $(dist_man_MANS) $(man_MANS)
+DATA = $(conf_DATA) $(dbus_DATA) $(dbussessionbus_DATA) \
+	$(dbussystembus_DATA) $(pkgconfig_DATA) $(rules_DATA) \
+	$(state_DATA) $(systemdsystemunit_DATA) \
+	$(systemduserunit_DATA)
+am__include_HEADERS_DIST = 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
+HEADERS = $(include_HEADERS)
+am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) \
+	$(LISP)config.h.in
+# Read a list of newline-separated strings from the standard input,
+# and print each of them once, without duplicates.  Input order is
+# *not* preserved.
+am__uniquify_input = $(AWK) '\
+  BEGIN { nonempty = 0; } \
+  { items[$$0] = 1; nonempty = 1; } \
+  END { if (nonempty) { for (i in items) print i; }; } \
+'
+# Make sure the list of sources is unique.  This is necessary because,
+# e.g., the same source file might be shared among _SOURCES variables
+# for different programs/libraries.
+am__define_uniq_tagged_files = \
+  list='$(am__tagged_files)'; \
+  unique=`for i in $$list; do \
+    if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+  done | $(am__uniquify_input)`
+ETAGS = etags
+CTAGS = ctags
+CSCOPE = cscope
+AM_RECURSIVE_TARGETS = cscope check recheck
+am__tty_colors_dummy = \
+  mgn= red= grn= lgn= blu= brg= std=; \
+  am__color_tests=no
+am__tty_colors = { \
+  $(am__tty_colors_dummy); \
+  if test "X$(AM_COLOR_TESTS)" = Xno; then \
+    am__color_tests=no; \
+  elif test "X$(AM_COLOR_TESTS)" = Xalways; then \
+    am__color_tests=yes; \
+  elif test "X$$TERM" != Xdumb && { test -t 1; } 2>/dev/null; then \
+    am__color_tests=yes; \
+  fi; \
+  if test $$am__color_tests = yes; then \
+    red=''; \
+    grn=''; \
+    lgn=''; \
+    blu=''; \
+    mgn=''; \
+    brg=''; \
+    std=''; \
+  fi; \
+}
+am__recheck_rx = ^[ 	]*:recheck:[ 	]*
+am__global_test_result_rx = ^[ 	]*:global-test-result:[ 	]*
+am__copy_in_global_log_rx = ^[ 	]*:copy-in-global-log:[ 	]*
+# A command that, given a newline-separated list of test names on the
+# standard input, print the name of the tests that are to be re-run
+# upon "make recheck".
+am__list_recheck_tests = $(AWK) '{ \
+  recheck = 1; \
+  while ((rc = (getline line < ($$0 ".trs"))) != 0) \
+    { \
+      if (rc < 0) \
+        { \
+          if ((getline line2 < ($$0 ".log")) < 0) \
+	    recheck = 0; \
+          break; \
+        } \
+      else if (line ~ /$(am__recheck_rx)[nN][Oo]/) \
+        { \
+          recheck = 0; \
+          break; \
+        } \
+      else if (line ~ /$(am__recheck_rx)[yY][eE][sS]/) \
+        { \
+          break; \
+        } \
+    }; \
+  if (recheck) \
+    print $$0; \
+  close ($$0 ".trs"); \
+  close ($$0 ".log"); \
+}'
+# A command that, given a newline-separated list of test names on the
+# standard input, create the global log from their .trs and .log files.
+am__create_global_log = $(AWK) ' \
+function fatal(msg) \
+{ \
+  print "fatal: making $@: " msg | "cat >&2"; \
+  exit 1; \
+} \
+function rst_section(header) \
+{ \
+  print header; \
+  len = length(header); \
+  for (i = 1; i <= len; i = i + 1) \
+    printf "="; \
+  printf "\n\n"; \
+} \
+{ \
+  copy_in_global_log = 1; \
+  global_test_result = "RUN"; \
+  while ((rc = (getline line < ($$0 ".trs"))) != 0) \
+    { \
+      if (rc < 0) \
+         fatal("failed to read from " $$0 ".trs"); \
+      if (line ~ /$(am__global_test_result_rx)/) \
+        { \
+          sub("$(am__global_test_result_rx)", "", line); \
+          sub("[ 	]*$$", "", line); \
+          global_test_result = line; \
+        } \
+      else if (line ~ /$(am__copy_in_global_log_rx)[nN][oO]/) \
+        copy_in_global_log = 0; \
+    }; \
+  if (copy_in_global_log) \
+    { \
+      rst_section(global_test_result ": " $$0); \
+      while ((rc = (getline line < ($$0 ".log"))) != 0) \
+      { \
+        if (rc < 0) \
+          fatal("failed to read from " $$0 ".log"); \
+        print line; \
+      }; \
+      printf "\n"; \
+    }; \
+  close ($$0 ".trs"); \
+  close ($$0 ".log"); \
+}'
+# Restructured Text title.
+am__rst_title = { sed 's/.*/   &   /;h;s/./=/g;p;x;s/ *$$//;p;g' && echo; }
+# Solaris 10 'make', and several other traditional 'make' implementations,
+# pass "-e" to $(SHELL), and POSIX 2008 even requires this.  Work around it
+# by disabling -e (using the XSI extension "set +e") if it's set.
+am__sh_e_setup = case $$- in *e*) set +e;; esac
+# Default flags passed to test drivers.
+am__common_driver_flags = \
+  --color-tests "$$am__color_tests" \
+  --enable-hard-errors "$$am__enable_hard_errors" \
+  --expect-failure "$$am__expect_failure"
+# To be inserted before the command running the test.  Creates the
+# directory for the log if needed.  Stores in $dir the directory
+# containing $f, in $tst the test, in $log the log.  Executes the
+# developer- defined test setup AM_TESTS_ENVIRONMENT (if any), and
+# passes TESTS_ENVIRONMENT.  Set up options for the wrapper that
+# will run the test scripts (or their associated LOG_COMPILER, if
+# thy have one).
+am__check_pre = \
+$(am__sh_e_setup);					\
+$(am__vpath_adj_setup) $(am__vpath_adj)			\
+$(am__tty_colors);					\
+srcdir=$(srcdir); export srcdir;			\
+case "$@" in						\
+  */*) am__odir=`echo "./$@" | sed 's|/[^/]*$$||'`;;	\
+    *) am__odir=.;; 					\
+esac;							\
+test "x$$am__odir" = x"." || test -d "$$am__odir" 	\
+  || $(MKDIR_P) "$$am__odir" || exit $$?;		\
+if test -f "./$$f"; then dir=./;			\
+elif test -f "$$f"; then dir=;				\
+else dir="$(srcdir)/"; fi;				\
+tst=$$dir$$f; log='$@'; 				\
+if test -n '$(DISABLE_HARD_ERRORS)'; then		\
+  am__enable_hard_errors=no; 				\
+else							\
+  am__enable_hard_errors=yes; 				\
+fi; 							\
+case " $(XFAIL_TESTS) " in				\
+  *[\ \	]$$f[\ \	]* | *[\ \	]$$dir$$f[\ \	]*) \
+    am__expect_failure=yes;;				\
+  *)							\
+    am__expect_failure=no;;				\
+esac; 							\
+$(AM_TESTS_ENVIRONMENT) $(TESTS_ENVIRONMENT)
+# A shell command to get the names of the tests scripts with any registered
+# extension removed (i.e., equivalently, the names of the test logs, with
+# the '.log' extension removed).  The result is saved in the shell variable
+# '$bases'.  This honors runtime overriding of TESTS and TEST_LOGS.  Sadly,
+# we cannot use something simpler, involving e.g., "$(TEST_LOGS:.log=)",
+# since that might cause problem with VPATH rewrites for suffix-less tests.
+# See also 'test-harness-vpath-rewrite.sh' and 'test-trs-basic.sh'.
+am__set_TESTS_bases = \
+  bases='$(TEST_LOGS)'; \
+  bases=`for i in $$bases; do echo $$i; done | sed 's/\.log$$//'`; \
+  bases=`echo $$bases`
+RECHECK_LOGS = $(TEST_LOGS)
+TEST_SUITE_LOG = test-suite.log
+TEST_EXTENSIONS = @EXEEXT@ .test
+LOG_DRIVER = $(SHELL) $(top_srcdir)/test-driver
+LOG_COMPILE = $(LOG_COMPILER) $(AM_LOG_FLAGS) $(LOG_FLAGS)
+am__set_b = \
+  case '$@' in \
+    */*) \
+      case '$*' in \
+        */*) b='$*';; \
+          *) b=`echo '$@' | sed 's/\.log$$//'`; \
+       esac;; \
+    *) \
+      b='$*';; \
+  esac
+am__test_logs1 = $(TESTS:=.log)
+am__test_logs2 = $(am__test_logs1:@EXEEXT@.log=.log)
+TEST_LOGS = $(am__test_logs2:.test.log=.log)
+TEST_LOG_DRIVER = $(SHELL) $(top_srcdir)/test-driver
+TEST_LOG_COMPILE = $(TEST_LOG_COMPILER) $(AM_TEST_LOG_FLAGS) \
+	$(TEST_LOG_FLAGS)
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+distdir = $(PACKAGE)-$(VERSION)
+top_distdir = $(distdir)
+am__remove_distdir = \
+  if test -d "$(distdir)"; then \
+    find "$(distdir)" -type d ! -perm -200 -exec chmod u+w {} ';' \
+      && rm -rf "$(distdir)" \
+      || { sleep 5 && rm -rf "$(distdir)"; }; \
+  else :; fi
+am__post_remove_distdir = $(am__remove_distdir)
+GZIP_ENV = --best
+DIST_ARCHIVES = $(distdir).tar.xz
+DIST_TARGETS = dist-xz
+distuninstallcheck_listfiles = find . -type f -print
+am__distuninstallcheck_listfiles = $(distuninstallcheck_listfiles) \
+  | sed 's|^\./|$(prefix)/|' | grep -v '$(infodir)/dir$$'
+distcleancheck_listfiles = find . -type f -print
+ACLOCAL = @ACLOCAL@
+AMTAR = @AMTAR@
+AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
+AR = @AR@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+CC = @CC@
+CCDEPMODE = @CCDEPMODE@
+CFLAGS = @CFLAGS@
+CONFIGDIR = @CONFIGDIR@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CYGPATH_W = @CYGPATH_W@
+DBUS_CFLAGS = @DBUS_CFLAGS@
+DBUS_CONFDIR = @DBUS_CONFDIR@
+DBUS_LIBS = @DBUS_LIBS@
+DBUS_SESSIONBUSDIR = @DBUS_SESSIONBUSDIR@
+DBUS_SYSTEMBUSDIR = @DBUS_SYSTEMBUSDIR@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+DLLTOOL = @DLLTOOL@
+DSYMUTIL = @DSYMUTIL@
+DUMPBIN = @DUMPBIN@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+EXEEXT = @EXEEXT@
+FGREP = @FGREP@
+GLIB_CFLAGS = @GLIB_CFLAGS@
+GLIB_LIBS = @GLIB_LIBS@
+GREP = @GREP@
+GTHREAD_CFLAGS = @GTHREAD_CFLAGS@
+GTHREAD_LIBS = @GTHREAD_LIBS@
+ICAL_CFLAGS = @ICAL_CFLAGS@
+ICAL_LIBS = @ICAL_LIBS@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+LD = @LD@
+LDFLAGS = @LDFLAGS@
+LIBOBJS = @LIBOBJS@
+LIBS = @LIBS@
+LIBTOOL = @LIBTOOL@
+LIPO = @LIPO@
+LN_S = @LN_S@
+LTLIBOBJS = @LTLIBOBJS@
+MAINT = @MAINT@
+MAKEINFO = @MAKEINFO@
+MANIFEST_TOOL = @MANIFEST_TOOL@
+MISC_CFLAGS = @MISC_CFLAGS@
+MISC_LDFLAGS = @MISC_LDFLAGS@
+MKDIR_P = @MKDIR_P@
+NM = @NM@
+NMEDIT = @NMEDIT@
+OBJDUMP = @OBJDUMP@
+OBJEXT = @OBJEXT@
+OTOOL = @OTOOL@
+OTOOL64 = @OTOOL64@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_URL = @PACKAGE_URL@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+PKG_CONFIG = @PKG_CONFIG@
+PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@
+PKG_CONFIG_PATH = @PKG_CONFIG_PATH@
+RANLIB = @RANLIB@
+SBC_CFLAGS = @SBC_CFLAGS@
+SBC_LIBS = @SBC_LIBS@
+SED = @SED@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+STRIP = @STRIP@
+SYSTEMD_SYSTEMUNITDIR = @SYSTEMD_SYSTEMUNITDIR@
+SYSTEMD_USERUNITDIR = @SYSTEMD_USERUNITDIR@
+UDEV_CFLAGS = @UDEV_CFLAGS@
+UDEV_DIR = @UDEV_DIR@
+UDEV_LIBS = @UDEV_LIBS@
+VERSION = @VERSION@
+WARNING_CFLAGS = @WARNING_CFLAGS@
+abs_builddir = @abs_builddir@
+abs_srcdir = @abs_srcdir@
+abs_top_builddir = @abs_top_builddir@
+abs_top_srcdir = @abs_top_srcdir@
+ac_ct_AR = @ac_ct_AR@
+ac_ct_CC = @ac_ct_CC@
+ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+bindir = @bindir@
+build = @build@
+build_alias = @build_alias@
+build_cpu = @build_cpu@
+build_os = @build_os@
+build_vendor = @build_vendor@
+builddir = @builddir@
+datadir = @datadir@
+datarootdir = @datarootdir@
+docdir = @docdir@
+dvidir = @dvidir@
+exec_prefix = @exec_prefix@
+host = @host@
+host_alias = @host_alias@
+host_cpu = @host_cpu@
+host_os = @host_os@
+host_vendor = @host_vendor@
+htmldir = @htmldir@
+includedir = @includedir@/bluetooth
+infodir = @infodir@
+install_sh = @install_sh@
+libdir = @libdir@
+libexecdir = @libexecdir@/bluetooth
+localedir = @localedir@
+localstatedir = @localstatedir@
+mandir = @mandir@
+mkdir_p = @mkdir_p@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+srcdir = @srcdir@
+sysconfdir = @sysconfdir@
+target_alias = @target_alias@
+top_build_prefix = @top_build_prefix@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+AM_MAKEFLAGS = --no-print-directory
+lib_LTLIBRARIES = $(am__append_2)
+noinst_LIBRARIES = $(am__append_7)
+noinst_LTLIBRARIES = lib/libbluetooth-internal.la \
+	gdbus/libgdbus-internal.la
+dist_man_MANS = $(am__append_18) $(am__append_20)
+dist_noinst_MANS = 
+CLEANFILES = $(builtin_files) src/bluetooth.service \
+	obexd/src/builtin.h $(builtin_files) obexd/src/obex.service \
+	$(am__append_32)
+EXTRA_DIST = src/bluetooth.service.in src/org.bluez.service \
+	src/genbuiltin src/bluetooth.conf src/main.conf \
+	profiles/network/network.conf profiles/input/input.conf \
+	profiles/proximity/proximity.conf $(am__append_19) \
+	$(am__append_21) $(am__append_22) obexd/src/obex.service.in \
+	obexd/src/org.bluez.obex.service obexd/src/genbuiltin \
+	android/Android.mk android/README android/init.bluetooth.rc \
+	android/hal-ipc-api.txt android/audio-ipc-api.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/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/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-hsp.txt tools/hid2hci.rules $(test_scripts) \
+	doc/assigned-numbers.txt doc/supported-features.txt \
+	doc/test-coverage.txt doc/settings-storage.txt \
+	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/alert-api.txt doc/proximity-api.txt doc/heartrate-api.txt \
+	doc/thermometer-api.txt doc/cyclingspeed-api.txt \
+	doc/obex-api.txt doc/obex-agent-api.txt tools/magic.btsnoop
+include_HEADERS = $(am__append_1)
+AM_CFLAGS = $(WARNING_CFLAGS) $(MISC_CFLAGS) @DBUS_CFLAGS@ \
+	@GLIB_CFLAGS@ $(am__empty)
+AM_LDFLAGS = $(MISC_LDFLAGS)
+@DATAFILES_TRUE@dbusdir = @DBUS_CONFDIR@/dbus-1/system.d
+@DATAFILES_TRUE@dbus_DATA = src/bluetooth.conf
+@DATAFILES_TRUE@confdir = $(sysconfdir)/bluetooth
+@DATAFILES_TRUE@conf_DATA = 
+@DATAFILES_TRUE@statedir = $(localstatedir)/lib/bluetooth
+@DATAFILES_TRUE@state_DATA = 
+@SYSTEMD_TRUE@systemdsystemunitdir = @SYSTEMD_SYSTEMUNITDIR@
+@SYSTEMD_TRUE@systemdsystemunit_DATA = src/bluetooth.service
+@SYSTEMD_TRUE@dbussystembusdir = @DBUS_SYSTEMBUSDIR@
+@SYSTEMD_TRUE@dbussystembus_DATA = src/org.bluez.service
+plugindir = $(libdir)/bluetooth/plugins
+@MAINTAINER_MODE_FALSE@build_plugindir = $(plugindir)
+@MAINTAINER_MODE_TRUE@build_plugindir = $(abs_top_srcdir)/plugins/.libs
+plugin_LTLIBRARIES = $(am__append_12) $(am__append_13) \
+	$(am__append_30)
+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 obexd/src/builtin.h
+@LIBRARY_TRUE@lib_libbluetooth_la_SOURCES = $(lib_headers) $(lib_sources)
+@LIBRARY_TRUE@lib_libbluetooth_la_LDFLAGS = $(AM_LDFLAGS) -version-info 20:8:17
+@LIBRARY_TRUE@lib_libbluetooth_la_DEPENDENCIES = $(local_headers)
+lib_libbluetooth_internal_la_SOURCES = $(lib_headers) $(lib_sources) \
+					$(extra_headers) $(extra_sources)
+
+gdbus_libgdbus_internal_la_SOURCES = gdbus/gdbus.h \
+				gdbus/mainloop.c gdbus/watch.c \
+				gdbus/object.c gdbus/client.c gdbus/polkit.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 = hostname wiimote autopair dropcam policy \
+	$(am__append_3) $(am__append_5) a2dp avrcp network input hog \
+	$(am__append_8) gatt scanparam deviceinfo $(am__append_10)
+builtin_sources = plugins/hostname.c plugins/wiimote.c \
+	plugins/autopair.c plugins/dropcam.c plugins/policy.c \
+	$(am__append_4) $(am__append_6) 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 \
+	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 \
+	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 profiles/input/manager.c \
+	profiles/input/server.h profiles/input/server.c \
+	profiles/input/device.h profiles/input/device.c \
+	profiles/input/hog.c profiles/input/uhid_copy.h \
+	profiles/input/suspend.h profiles/input/suspend-dummy.c \
+	$(am__append_9) profiles/gatt/gas.c profiles/scanparam/scan.c \
+	profiles/deviceinfo/deviceinfo.c $(am__append_11)
+builtin_nodist = 
+@EXPERIMENTAL_TRUE@profiles_sap_libsap_a_SOURCES = profiles/sap/sap.h profiles/sap/sap-u8500.c
+@SIXAXIS_TRUE@plugins_sixaxis_la_SOURCES = plugins/sixaxis.c
+@SIXAXIS_TRUE@plugins_sixaxis_la_LDFLAGS = $(AM_LDFLAGS) -module -avoid-version \
+@SIXAXIS_TRUE@						-no-undefined @UDEV_LIBS@
+
+@SIXAXIS_TRUE@plugins_sixaxis_la_CFLAGS = $(AM_CFLAGS) -fvisibility=hidden @UDEV_CFLAGS@
+@MAINTAINER_MODE_TRUE@plugins_external_dummy_la_SOURCES = plugins/external-dummy.c
+@MAINTAINER_MODE_TRUE@plugins_external_dummy_la_LDFLAGS = $(AM_LDFLAGS) -module -avoid-version \
+@MAINTAINER_MODE_TRUE@				    -no-undefined
+
+@MAINTAINER_MODE_TRUE@plugins_external_dummy_la_CFLAGS = $(AM_CFLAGS) -fvisibility=hidden
+src_bluetoothd_SOURCES = $(builtin_sources) \
+			$(attrib_sources) $(btio_sources) \
+			src/bluetooth.ver \
+			src/main.c src/log.h src/log.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/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/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-dbus.h src/gatt-dbus.c \
+			src/gatt.h src/gatt.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/shared/io.h src/shared/io-glib.c \
+			src/shared/timeout.h src/shared/timeout-glib.c \
+			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_bluetoothd_LDADD = lib/libbluetooth-internal.la gdbus/libgdbus-internal.la \
+			@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/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)
+man_MANS = src/bluetoothd.8
+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-service 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
+unit_tests = $(am__append_31) unit/test-eir unit/test-uuid \
+	unit/test-textfile unit/test-crc unit/test-ringbuf \
+	unit/test-queue unit/test-mgmt unit/test-sdp unit/test-avdtp \
+	unit/test-avctp unit/test-avrcp unit/test-hfp \
+	unit/test-gdbus-client unit/test-gobex-header \
+	unit/test-gobex-packet unit/test-gobex \
+	unit/test-gobex-transfer unit/test-gobex-apparam unit/test-lib
+@CLIENT_TRUE@client_bluetoothctl_SOURCES = client/main.c \
+@CLIENT_TRUE@					client/display.h client/display.c \
+@CLIENT_TRUE@					client/agent.h client/agent.c \
+@CLIENT_TRUE@					monitor/uuid.h monitor/uuid.c
+
+@CLIENT_TRUE@client_bluetoothctl_LDADD = gdbus/libgdbus-internal.la @GLIB_LIBS@ @DBUS_LIBS@ \
+@CLIENT_TRUE@				-lreadline
+
+@MONITOR_TRUE@monitor_btmon_SOURCES = monitor/main.c monitor/bt.h \
+@MONITOR_TRUE@				monitor/mainloop.h monitor/mainloop.c \
+@MONITOR_TRUE@				monitor/display.h monitor/display.c \
+@MONITOR_TRUE@				monitor/hcidump.h monitor/hcidump.c \
+@MONITOR_TRUE@				monitor/ellisys.h monitor/ellisys.c \
+@MONITOR_TRUE@				monitor/control.h monitor/control.c \
+@MONITOR_TRUE@				monitor/packet.h monitor/packet.c \
+@MONITOR_TRUE@				monitor/vendor.h monitor/vendor.c \
+@MONITOR_TRUE@				monitor/lmp.h monitor/lmp.c \
+@MONITOR_TRUE@				monitor/crc.h monitor/crc.c \
+@MONITOR_TRUE@				monitor/ll.h monitor/ll.c \
+@MONITOR_TRUE@				monitor/l2cap.h monitor/l2cap.c \
+@MONITOR_TRUE@				monitor/sdp.h monitor/sdp.c \
+@MONITOR_TRUE@				monitor/uuid.h monitor/uuid.c \
+@MONITOR_TRUE@				monitor/hwdb.h monitor/hwdb.c \
+@MONITOR_TRUE@				monitor/keys.h monitor/keys.c \
+@MONITOR_TRUE@				monitor/analyze.h monitor/analyze.c \
+@MONITOR_TRUE@				src/shared/util.h src/shared/util.c \
+@MONITOR_TRUE@				src/shared/queue.h src/shared/queue.c \
+@MONITOR_TRUE@				src/shared/crypto.h src/shared/crypto.c \
+@MONITOR_TRUE@				src/shared/btsnoop.h src/shared/btsnoop.c
+
+@MONITOR_TRUE@monitor_btmon_LDADD = lib/libbluetooth-internal.la @UDEV_LIBS@
+@EXPERIMENTAL_TRUE@emulator_btvirt_SOURCES = emulator/main.c monitor/bt.h \
+@EXPERIMENTAL_TRUE@				monitor/mainloop.h monitor/mainloop.c \
+@EXPERIMENTAL_TRUE@				src/shared/timeout.h \
+@EXPERIMENTAL_TRUE@				src/shared/timeout-mainloop.c \
+@EXPERIMENTAL_TRUE@				src/shared/util.h src/shared/util.c \
+@EXPERIMENTAL_TRUE@				src/shared/crypto.h src/shared/crypto.c \
+@EXPERIMENTAL_TRUE@				emulator/server.h emulator/server.c \
+@EXPERIMENTAL_TRUE@				emulator/vhci.h emulator/vhci.c \
+@EXPERIMENTAL_TRUE@				emulator/btdev.h emulator/btdev.c \
+@EXPERIMENTAL_TRUE@				emulator/bthost.h emulator/bthost.c \
+@EXPERIMENTAL_TRUE@				emulator/smp.c \
+@EXPERIMENTAL_TRUE@				emulator/amp.h emulator/amp.c \
+@EXPERIMENTAL_TRUE@				emulator/le.h emulator/le.c
+
+@EXPERIMENTAL_TRUE@emulator_btvirt_LDADD = lib/libbluetooth-internal.la
+@EXPERIMENTAL_TRUE@emulator_b1ee_SOURCES = emulator/b1ee.c monitor/mainloop.h monitor/mainloop.c
+@EXPERIMENTAL_TRUE@emulator_hfp_SOURCES = emulator/hfp.c \
+@EXPERIMENTAL_TRUE@				monitor/mainloop.h monitor/mainloop.c \
+@EXPERIMENTAL_TRUE@				src/shared/io.h src/shared/io-mainloop.c \
+@EXPERIMENTAL_TRUE@				src/shared/util.h src/shared/util.c \
+@EXPERIMENTAL_TRUE@				src/shared/queue.h src/shared/queue.c \
+@EXPERIMENTAL_TRUE@				src/shared/ringbuf.h src/shared/ringbuf.c \
+@EXPERIMENTAL_TRUE@				src/shared/hfp.h src/shared/hfp.c
+
+@EXPERIMENTAL_TRUE@tools_3dsp_SOURCES = tools/3dsp.c monitor/bt.h \
+@EXPERIMENTAL_TRUE@				monitor/mainloop.h monitor/mainloop.c \
+@EXPERIMENTAL_TRUE@				src/shared/io.h src/shared/io-mainloop.c \
+@EXPERIMENTAL_TRUE@				src/shared/timeout.h \
+@EXPERIMENTAL_TRUE@				src/shared/timeout-mainloop.c \
+@EXPERIMENTAL_TRUE@				src/shared/hci.h src/shared/hci.c \
+@EXPERIMENTAL_TRUE@				src/shared/util.h src/shared/util.c \
+@EXPERIMENTAL_TRUE@				src/shared/queue.h src/shared/queue.c
+
+@EXPERIMENTAL_TRUE@tools_mgmt_tester_SOURCES = tools/mgmt-tester.c monitor/bt.h \
+@EXPERIMENTAL_TRUE@				emulator/btdev.h emulator/btdev.c \
+@EXPERIMENTAL_TRUE@				emulator/bthost.h emulator/bthost.c \
+@EXPERIMENTAL_TRUE@				emulator/smp.c \
+@EXPERIMENTAL_TRUE@				src/shared/crypto.h src/shared/crypto.c \
+@EXPERIMENTAL_TRUE@				src/shared/io.h src/shared/io-glib.c \
+@EXPERIMENTAL_TRUE@				src/shared/queue.h src/shared/queue.c \
+@EXPERIMENTAL_TRUE@				src/shared/util.h src/shared/util.c \
+@EXPERIMENTAL_TRUE@				src/shared/mgmt.h src/shared/mgmt.c \
+@EXPERIMENTAL_TRUE@				src/shared/hciemu.h src/shared/hciemu.c \
+@EXPERIMENTAL_TRUE@				src/shared/tester.h src/shared/tester.c \
+@EXPERIMENTAL_TRUE@				src/shared/timeout.h src/shared/timeout-glib.c
+
+@EXPERIMENTAL_TRUE@tools_mgmt_tester_LDADD = lib/libbluetooth-internal.la @GLIB_LIBS@
+@EXPERIMENTAL_TRUE@tools_l2cap_tester_SOURCES = tools/l2cap-tester.c monitor/bt.h \
+@EXPERIMENTAL_TRUE@				emulator/btdev.h emulator/btdev.c \
+@EXPERIMENTAL_TRUE@				emulator/bthost.h emulator/bthost.c \
+@EXPERIMENTAL_TRUE@				emulator/smp.c \
+@EXPERIMENTAL_TRUE@				src/shared/crypto.h src/shared/crypto.c \
+@EXPERIMENTAL_TRUE@				src/shared/io.h src/shared/io-glib.c \
+@EXPERIMENTAL_TRUE@				src/shared/queue.h src/shared/queue.c \
+@EXPERIMENTAL_TRUE@				src/shared/util.h src/shared/util.c \
+@EXPERIMENTAL_TRUE@				src/shared/mgmt.h src/shared/mgmt.c \
+@EXPERIMENTAL_TRUE@				src/shared/hciemu.h src/shared/hciemu.c \
+@EXPERIMENTAL_TRUE@				src/shared/tester.h src/shared/tester.c \
+@EXPERIMENTAL_TRUE@				src/shared/timeout.h src/shared/timeout-glib.c
+
+@EXPERIMENTAL_TRUE@tools_l2cap_tester_LDADD = lib/libbluetooth-internal.la @GLIB_LIBS@
+@EXPERIMENTAL_TRUE@tools_rfcomm_tester_SOURCES = tools/rfcomm-tester.c monitor/bt.h \
+@EXPERIMENTAL_TRUE@				emulator/btdev.h emulator/btdev.c \
+@EXPERIMENTAL_TRUE@				emulator/bthost.h emulator/bthost.c \
+@EXPERIMENTAL_TRUE@				emulator/smp.c \
+@EXPERIMENTAL_TRUE@				src/shared/crypto.h src/shared/crypto.c \
+@EXPERIMENTAL_TRUE@				src/shared/io.h src/shared/io-glib.c \
+@EXPERIMENTAL_TRUE@				src/shared/queue.h src/shared/queue.c \
+@EXPERIMENTAL_TRUE@				src/shared/util.h src/shared/util.c \
+@EXPERIMENTAL_TRUE@				src/shared/mgmt.h src/shared/mgmt.c \
+@EXPERIMENTAL_TRUE@				src/shared/hciemu.h src/shared/hciemu.c \
+@EXPERIMENTAL_TRUE@				src/shared/tester.h src/shared/tester.c \
+@EXPERIMENTAL_TRUE@				src/shared/timeout.h src/shared/timeout-glib.c
+
+@EXPERIMENTAL_TRUE@tools_rfcomm_tester_LDADD = lib/libbluetooth-internal.la @GLIB_LIBS@
+@EXPERIMENTAL_TRUE@tools_smp_tester_SOURCES = tools/smp-tester.c monitor/bt.h \
+@EXPERIMENTAL_TRUE@				emulator/btdev.h emulator/btdev.c \
+@EXPERIMENTAL_TRUE@				emulator/bthost.h emulator/bthost.c \
+@EXPERIMENTAL_TRUE@				emulator/smp.c \
+@EXPERIMENTAL_TRUE@				src/shared/crypto.h src/shared/crypto.c \
+@EXPERIMENTAL_TRUE@				src/shared/io.h src/shared/io-glib.c \
+@EXPERIMENTAL_TRUE@				src/shared/queue.h src/shared/queue.c \
+@EXPERIMENTAL_TRUE@				src/shared/util.h src/shared/util.c \
+@EXPERIMENTAL_TRUE@				src/shared/mgmt.h src/shared/mgmt.c \
+@EXPERIMENTAL_TRUE@				src/shared/hciemu.h src/shared/hciemu.c \
+@EXPERIMENTAL_TRUE@				src/shared/tester.h src/shared/tester.c \
+@EXPERIMENTAL_TRUE@				src/shared/timeout.h src/shared/timeout-glib.c
+
+@EXPERIMENTAL_TRUE@tools_smp_tester_LDADD = lib/libbluetooth-internal.la @GLIB_LIBS@
+@EXPERIMENTAL_TRUE@tools_gap_tester_SOURCES = tools/gap-tester.c monitor/bt.h \
+@EXPERIMENTAL_TRUE@				emulator/btdev.h emulator/btdev.c \
+@EXPERIMENTAL_TRUE@				emulator/bthost.h emulator/bthost.c \
+@EXPERIMENTAL_TRUE@				emulator/smp.c \
+@EXPERIMENTAL_TRUE@				src/shared/crypto.h src/shared/crypto.c \
+@EXPERIMENTAL_TRUE@				src/shared/util.h src/shared/util.c \
+@EXPERIMENTAL_TRUE@				src/shared/queue.h src/shared/queue.c \
+@EXPERIMENTAL_TRUE@				src/shared/hciemu.h src/shared/hciemu.c \
+@EXPERIMENTAL_TRUE@				src/shared/tester.h src/shared/tester.c \
+@EXPERIMENTAL_TRUE@				src/shared/timeout.h src/shared/timeout-glib.c
+
+@EXPERIMENTAL_TRUE@tools_gap_tester_LDADD = lib/libbluetooth-internal.la \
+@EXPERIMENTAL_TRUE@				gdbus/libgdbus-internal.la \
+@EXPERIMENTAL_TRUE@				@GLIB_LIBS@ @DBUS_LIBS@
+
+@EXPERIMENTAL_TRUE@tools_sco_tester_SOURCES = tools/sco-tester.c monitor/bt.h \
+@EXPERIMENTAL_TRUE@				emulator/btdev.h emulator/btdev.c \
+@EXPERIMENTAL_TRUE@				emulator/bthost.h emulator/bthost.c \
+@EXPERIMENTAL_TRUE@				emulator/smp.c \
+@EXPERIMENTAL_TRUE@				src/shared/crypto.h src/shared/crypto.c \
+@EXPERIMENTAL_TRUE@				src/shared/io.h src/shared/io-glib.c \
+@EXPERIMENTAL_TRUE@				src/shared/queue.h src/shared/queue.c \
+@EXPERIMENTAL_TRUE@				src/shared/util.h src/shared/util.c \
+@EXPERIMENTAL_TRUE@				src/shared/mgmt.h src/shared/mgmt.c \
+@EXPERIMENTAL_TRUE@				src/shared/hciemu.h src/shared/hciemu.c \
+@EXPERIMENTAL_TRUE@				src/shared/tester.h src/shared/tester.c \
+@EXPERIMENTAL_TRUE@				src/shared/timeout.h src/shared/timeout-glib.c
+
+@EXPERIMENTAL_TRUE@tools_sco_tester_LDADD = lib/libbluetooth-internal.la @GLIB_LIBS@
+@EXPERIMENTAL_TRUE@tools_hci_tester_SOURCES = tools/hci-tester.c monitor/bt.h \
+@EXPERIMENTAL_TRUE@				src/shared/io.h src/shared/io-glib.c \
+@EXPERIMENTAL_TRUE@				src/shared/hci.h src/shared/hci.c \
+@EXPERIMENTAL_TRUE@				src/shared/util.h src/shared/util.c \
+@EXPERIMENTAL_TRUE@				src/shared/queue.h src/shared/queue.c \
+@EXPERIMENTAL_TRUE@				src/shared/tester.h src/shared/tester.c
+
+@EXPERIMENTAL_TRUE@tools_hci_tester_LDADD = @GLIB_LIBS@
+@TOOLS_TRUE@tools_hciattach_SOURCES = tools/hciattach.c tools/hciattach.h \
+@TOOLS_TRUE@						tools/hciattach_st.c \
+@TOOLS_TRUE@						tools/hciattach_ti.c \
+@TOOLS_TRUE@						tools/hciattach_tialt.c \
+@TOOLS_TRUE@						tools/hciattach_ath3k.c \
+@TOOLS_TRUE@						tools/hciattach_qualcomm.c \
+@TOOLS_TRUE@						tools/hciattach_intel.c
+
+@TOOLS_TRUE@tools_hciattach_LDADD = lib/libbluetooth-internal.la
+@TOOLS_TRUE@tools_hciconfig_SOURCES = tools/hciconfig.c tools/csr.h tools/csr.c
+@TOOLS_TRUE@tools_hciconfig_LDADD = lib/libbluetooth-internal.la
+@TOOLS_TRUE@tools_hcitool_SOURCES = tools/hcitool.c src/oui.h src/oui.c
+@TOOLS_TRUE@tools_hcitool_LDADD = lib/libbluetooth-internal.la @GLIB_LIBS@ @UDEV_LIBS@
+@TOOLS_TRUE@tools_hcidump_SOURCES = tools/hcidump.c \
+@TOOLS_TRUE@				tools/parser/parser.h tools/parser/parser.c \
+@TOOLS_TRUE@				tools/parser/lmp.c \
+@TOOLS_TRUE@				tools/parser/hci.c \
+@TOOLS_TRUE@				tools/parser/l2cap.h tools/parser/l2cap.c \
+@TOOLS_TRUE@				tools/parser/amp.c \
+@TOOLS_TRUE@				tools/parser/smp.c \
+@TOOLS_TRUE@				tools/parser/att.c \
+@TOOLS_TRUE@				tools/parser/sdp.h tools/parser/sdp.c \
+@TOOLS_TRUE@				tools/parser/rfcomm.h tools/parser/rfcomm.c \
+@TOOLS_TRUE@				tools/parser/bnep.c \
+@TOOLS_TRUE@				tools/parser/cmtp.c \
+@TOOLS_TRUE@				tools/parser/hidp.c \
+@TOOLS_TRUE@				tools/parser/hcrp.c \
+@TOOLS_TRUE@				tools/parser/avdtp.c \
+@TOOLS_TRUE@				tools/parser/avctp.c \
+@TOOLS_TRUE@				tools/parser/avrcp.c \
+@TOOLS_TRUE@				tools/parser/sap.c \
+@TOOLS_TRUE@				tools/parser/obex.c \
+@TOOLS_TRUE@				tools/parser/capi.c \
+@TOOLS_TRUE@				tools/parser/ppp.c \
+@TOOLS_TRUE@				tools/parser/tcpip.c \
+@TOOLS_TRUE@				tools/parser/ericsson.c \
+@TOOLS_TRUE@				tools/parser/csr.c \
+@TOOLS_TRUE@				tools/parser/bpa.c
+
+@TOOLS_TRUE@tools_hcidump_LDADD = lib/libbluetooth-internal.la
+@TOOLS_TRUE@tools_rfcomm_LDADD = lib/libbluetooth-internal.la
+@TOOLS_TRUE@tools_rctest_LDADD = lib/libbluetooth-internal.la
+@TOOLS_TRUE@tools_l2test_LDADD = lib/libbluetooth-internal.la
+@TOOLS_TRUE@tools_l2ping_LDADD = lib/libbluetooth-internal.la
+@TOOLS_TRUE@tools_sdptool_SOURCES = tools/sdptool.c src/sdp-xml.h src/sdp-xml.c
+@TOOLS_TRUE@tools_sdptool_LDADD = lib/libbluetooth-internal.la @GLIB_LIBS@
+@TOOLS_TRUE@tools_ciptool_LDADD = lib/libbluetooth-internal.la
+@TOOLS_TRUE@tools_bccmd_SOURCES = tools/bccmd.c tools/csr.h tools/csr.c \
+@TOOLS_TRUE@			tools/csr_hci.c tools/csr_usb.c \
+@TOOLS_TRUE@			tools/csr_h4.c tools/csr_3wire.c \
+@TOOLS_TRUE@			tools/csr_bcsp.c tools/ubcsp.h tools/ubcsp.c
+
+@TOOLS_TRUE@tools_bccmd_LDADD = lib/libbluetooth-internal.la
+@TOOLS_TRUE@tools_bluemoon_SOURCES = tools/bluemoon.c monitor/bt.h \
+@TOOLS_TRUE@				monitor/mainloop.h monitor/mainloop.c \
+@TOOLS_TRUE@				src/shared/io.h src/shared/io-mainloop.c \
+@TOOLS_TRUE@				src/shared/hci.h src/shared/hci.c \
+@TOOLS_TRUE@				src/shared/util.h src/shared/util.c \
+@TOOLS_TRUE@				src/shared/queue.h src/shared/queue.c
+
+@HID2HCI_TRUE@udevdir = @UDEV_DIR@
+@HID2HCI_TRUE@tools_hid2hci_LDADD = @UDEV_LIBS@
+@EXPERIMENTAL_TRUE@tools_bdaddr_SOURCES = tools/bdaddr.c src/oui.h src/oui.c
+@EXPERIMENTAL_TRUE@tools_bdaddr_LDADD = lib/libbluetooth-internal.la @UDEV_LIBS@
+@EXPERIMENTAL_TRUE@tools_avinfo_LDADD = lib/libbluetooth-internal.la
+@EXPERIMENTAL_TRUE@tools_avtest_LDADD = lib/libbluetooth-internal.la
+@EXPERIMENTAL_TRUE@tools_scotest_LDADD = lib/libbluetooth-internal.la
+@EXPERIMENTAL_TRUE@tools_amptest_LDADD = lib/libbluetooth-internal.la
+@EXPERIMENTAL_TRUE@tools_hwdb_LDADD = lib/libbluetooth-internal.la
+@EXPERIMENTAL_TRUE@tools_hcieventmask_LDADD = lib/libbluetooth-internal.la
+@EXPERIMENTAL_TRUE@tools_btmgmt_SOURCES = tools/btmgmt.c src/uuid-helper.c \
+@EXPERIMENTAL_TRUE@				monitor/mainloop.h monitor/mainloop.c \
+@EXPERIMENTAL_TRUE@				src/shared/io.h src/shared/io-mainloop.c \
+@EXPERIMENTAL_TRUE@				src/shared/queue.h src/shared/queue.c \
+@EXPERIMENTAL_TRUE@				src/shared/util.h src/shared/util.c \
+@EXPERIMENTAL_TRUE@				src/shared/mgmt.h src/shared/mgmt.c
+
+@EXPERIMENTAL_TRUE@tools_btmgmt_LDADD = lib/libbluetooth-internal.la
+@EXPERIMENTAL_TRUE@tools_btinfo_SOURCES = tools/btinfo.c monitor/bt.h \
+@EXPERIMENTAL_TRUE@				monitor/mainloop.h monitor/mainloop.c \
+@EXPERIMENTAL_TRUE@				src/shared/io.h src/shared/io-mainloop.c \
+@EXPERIMENTAL_TRUE@				src/shared/timeout.h \
+@EXPERIMENTAL_TRUE@				src/shared/timeout-mainloop.c \
+@EXPERIMENTAL_TRUE@				src/shared/hci.h src/shared/hci.c \
+@EXPERIMENTAL_TRUE@				src/shared/util.h src/shared/util.c \
+@EXPERIMENTAL_TRUE@				src/shared/queue.h src/shared/queue.c
+
+@EXPERIMENTAL_TRUE@tools_btsnoop_SOURCES = tools/btsnoop.c \
+@EXPERIMENTAL_TRUE@				src/shared/pcap.h src/shared/pcap.c \
+@EXPERIMENTAL_TRUE@				src/shared/btsnoop.h src/shared/btsnoop.c
+
+@EXPERIMENTAL_TRUE@tools_btproxy_SOURCES = tools/btproxy.c monitor/bt.h \
+@EXPERIMENTAL_TRUE@				monitor/mainloop.h monitor/mainloop.c \
+@EXPERIMENTAL_TRUE@				src/shared/util.h src/shared/util.c
+
+@EXPERIMENTAL_TRUE@tools_btiotest_SOURCES = tools/btiotest.c btio/btio.h btio/btio.c
+@EXPERIMENTAL_TRUE@tools_btiotest_LDADD = lib/libbluetooth-internal.la @GLIB_LIBS@
+@EXPERIMENTAL_TRUE@tools_mpris_player_SOURCES = tools/mpris-player.c
+@EXPERIMENTAL_TRUE@tools_mpris_player_LDADD = gdbus/libgdbus-internal.la @GLIB_LIBS@ @DBUS_LIBS@
+@EXPERIMENTAL_TRUE@tools_cltest_SOURCES = tools/cltest.c monitor/mainloop.h monitor/mainloop.c
+@EXPERIMENTAL_TRUE@tools_cltest_LDADD = lib/libbluetooth-internal.la
+@EXPERIMENTAL_TRUE@tools_seq2bseq_SOURCES = tools/seq2bseq.c
+@EXPERIMENTAL_TRUE@tools_ibeacon_SOURCES = tools/ibeacon.c monitor/bt.h \
+@EXPERIMENTAL_TRUE@				monitor/mainloop.h monitor/mainloop.c \
+@EXPERIMENTAL_TRUE@				src/shared/io.h src/shared/io-mainloop.c \
+@EXPERIMENTAL_TRUE@				src/shared/timeout.h \
+@EXPERIMENTAL_TRUE@				src/shared/timeout-mainloop.c \
+@EXPERIMENTAL_TRUE@				src/shared/hci.h src/shared/hci.c \
+@EXPERIMENTAL_TRUE@				src/shared/util.h src/shared/util.c \
+@EXPERIMENTAL_TRUE@				src/shared/queue.h src/shared/queue.c
+
+@READLINE_TRUE@attrib_gatttool_SOURCES = attrib/gatttool.c attrib/att.c attrib/gatt.c \
+@READLINE_TRUE@				attrib/gattrib.c btio/btio.c \
+@READLINE_TRUE@				attrib/gatttool.h attrib/interactive.c \
+@READLINE_TRUE@				attrib/utils.c src/log.c client/display.c \
+@READLINE_TRUE@				client/display.h
+
+@READLINE_TRUE@attrib_gatttool_LDADD = lib/libbluetooth-internal.la @GLIB_LIBS@ -lreadline
+@READLINE_TRUE@tools_obex_client_tool_SOURCES = $(gobex_sources) $(btio_sources) \
+@READLINE_TRUE@						tools/obex-client-tool.c
+
+@READLINE_TRUE@tools_obex_client_tool_LDADD = lib/libbluetooth-internal.la \
+@READLINE_TRUE@						@GLIB_LIBS@ -lreadline
+
+@READLINE_TRUE@tools_obex_server_tool_SOURCES = $(gobex_sources) $(btio_sources) \
+@READLINE_TRUE@						tools/obex-server-tool.c
+
+@READLINE_TRUE@tools_obex_server_tool_LDADD = lib/libbluetooth-internal.la @GLIB_LIBS@
+@READLINE_TRUE@tools_bluetooth_player_SOURCES = tools/bluetooth-player.c \
+@READLINE_TRUE@				client/display.h client/display.c
+
+@READLINE_TRUE@tools_bluetooth_player_LDADD = gdbus/libgdbus-internal.la \
+@READLINE_TRUE@				@GLIB_LIBS@ @DBUS_LIBS@ -lreadline
+
+@READLINE_TRUE@tools_obexctl_SOURCES = tools/obexctl.c \
+@READLINE_TRUE@				client/display.h client/display.c
+
+@READLINE_TRUE@tools_obexctl_LDADD = gdbus/libgdbus-internal.la \
+@READLINE_TRUE@				@GLIB_LIBS@ @DBUS_LIBS@ -lreadline
+
+@EXPERIMENTAL_TRUE@tools_gatt_service_SOURCES = tools/gatt-service.c
+@EXPERIMENTAL_TRUE@tools_gatt_service_LDADD = @GLIB_LIBS@ @DBUS_LIBS@ gdbus/libgdbus-internal.la
+@EXPERIMENTAL_TRUE@profiles_iap_iapd_SOURCES = profiles/iap/main.c
+@EXPERIMENTAL_TRUE@profiles_iap_iapd_LDADD = gdbus/libgdbus-internal.la @GLIB_LIBS@ @DBUS_LIBS@
+@CUPS_TRUE@cupsdir = $(libdir)/cups/backend
+@CUPS_TRUE@profiles_cups_bluetooth_SOURCES = profiles/cups/main.c \
+@CUPS_TRUE@					profiles/cups/cups.h \
+@CUPS_TRUE@					profiles/cups/sdp.c \
+@CUPS_TRUE@					profiles/cups/spp.c \
+@CUPS_TRUE@					profiles/cups/hcrp.c
+
+@CUPS_TRUE@profiles_cups_bluetooth_LDADD = @GLIB_LIBS@ @DBUS_LIBS@ \
+@CUPS_TRUE@				lib/libbluetooth-internal.la \
+@CUPS_TRUE@				gdbus/libgdbus-internal.la
+
+@SYSTEMD_TRUE@systemduserunitdir = @SYSTEMD_USERUNITDIR@
+@SYSTEMD_TRUE@systemduserunit_DATA = obexd/src/obex.service
+@SYSTEMD_TRUE@dbussessionbusdir = @DBUS_SESSIONBUSDIR@
+@SYSTEMD_TRUE@dbussessionbus_DATA = obexd/src/org.bluez.obex.service
+obex_plugindir = $(libdir)/obex/plugins
+obexd_builtin_modules = filesystem bluetooth $(am__append_25) opp ftp \
+	$(am__append_27) mas mns
+obexd_builtin_sources = obexd/plugins/filesystem.c \
+	obexd/plugins/filesystem.h obexd/plugins/bluetooth.c \
+	$(am__append_26) obexd/plugins/opp.c obexd/plugins/ftp.c \
+	obexd/plugins/ftp.h $(am__append_28) obexd/plugins/mas.c \
+	obexd/src/map_ap.h obexd/plugins/messages.h \
+	obexd/plugins/messages-dummy.c obexd/client/mns.c \
+	obexd/src/map_ap.h obexd/client/map-event.h
+obexd_builtin_nodist = 
+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  \
+				-I$(srcdir)/obexd/src -I$(srcdir)/btio \
+				-I$(srcdir)/gobex -I$(srcdir)/gdbus
+
+obexd_src_obexd_SHORTNAME = obexd
+obexd_builtin_files = obexd/src/builtin.h $(obexd_builtin_nodist)
+nodist_obexd_src_obexd_SOURCES = $(obexd_builtin_files)
+@ANDROID_TRUE@android_plugindir = $(abs_top_srcdir)/android/.libs
+@ANDROID_TRUE@android_system_emulator_SOURCES = android/system-emulator.c \
+@ANDROID_TRUE@					monitor/mainloop.h monitor/mainloop.c
+
+@ANDROID_TRUE@android_bluetoothd_snoop_SOURCES = android/bluetoothd-snoop.c \
+@ANDROID_TRUE@				monitor/mainloop.h monitor/mainloop.c \
+@ANDROID_TRUE@				src/shared/btsnoop.h src/shared/btsnoop.c
+
+@ANDROID_TRUE@android_bluetoothd_SOURCES = android/main.c \
+@ANDROID_TRUE@				src/log.c \
+@ANDROID_TRUE@				android/hal-msg.h \
+@ANDROID_TRUE@				android/audio-msg.h \
+@ANDROID_TRUE@				android/utils.h \
+@ANDROID_TRUE@				src/sdpd-database.c src/sdpd-server.c \
+@ANDROID_TRUE@				src/sdpd-service.c src/sdpd-request.c \
+@ANDROID_TRUE@				src/uuid-helper.h src/uuid-helper.c \
+@ANDROID_TRUE@				src/eir.h src/eir.c \
+@ANDROID_TRUE@				src/shared/io.h src/shared/io-glib.c \
+@ANDROID_TRUE@				src/shared/queue.h src/shared/queue.c \
+@ANDROID_TRUE@				src/shared/util.h src/shared/util.c \
+@ANDROID_TRUE@				src/shared/mgmt.h src/shared/mgmt.c \
+@ANDROID_TRUE@				src/shared/ringbuf.h src/shared/ringbuf.c \
+@ANDROID_TRUE@				src/shared/hfp.h src/shared/hfp.c \
+@ANDROID_TRUE@				android/bluetooth.h android/bluetooth.c \
+@ANDROID_TRUE@				android/hidhost.h android/hidhost.c \
+@ANDROID_TRUE@				android/ipc-common.h \
+@ANDROID_TRUE@				android/ipc.h android/ipc.c \
+@ANDROID_TRUE@				android/avdtp.h android/avdtp.c \
+@ANDROID_TRUE@				android/a2dp.h android/a2dp.c \
+@ANDROID_TRUE@				android/avctp.h android/avctp.c \
+@ANDROID_TRUE@				android/avrcp.h android/avrcp.c \
+@ANDROID_TRUE@				android/avrcp-lib.h android/avrcp-lib.c \
+@ANDROID_TRUE@				android/socket.h android/socket.c \
+@ANDROID_TRUE@				android/pan.h android/pan.c \
+@ANDROID_TRUE@				android/handsfree.h android/handsfree.c \
+@ANDROID_TRUE@				android/gatt.h android/gatt.c \
+@ANDROID_TRUE@				android/health.h android/health.c \
+@ANDROID_TRUE@				attrib/att.c attrib/att.h \
+@ANDROID_TRUE@				attrib/gatt.c attrib/gatt.h \
+@ANDROID_TRUE@				attrib/gattrib.c attrib/gattrib.h \
+@ANDROID_TRUE@				btio/btio.h btio/btio.c \
+@ANDROID_TRUE@				src/sdp-client.h src/sdp-client.c \
+@ANDROID_TRUE@				profiles/network/bnep.h profiles/network/bnep.c
+
+@ANDROID_TRUE@android_bluetoothd_LDADD = lib/libbluetooth-internal.la @GLIB_LIBS@
+@ANDROID_TRUE@android_bluetooth_default_la_SOURCES = android/hal.h android/hal-bluetooth.c \
+@ANDROID_TRUE@					android/hal-socket.c \
+@ANDROID_TRUE@					android/hal-hidhost.c \
+@ANDROID_TRUE@					android/hal-health.c \
+@ANDROID_TRUE@					android/hal-pan.c \
+@ANDROID_TRUE@					android/hal-a2dp.c \
+@ANDROID_TRUE@					android/hal-avrcp.c \
+@ANDROID_TRUE@					android/hal-handsfree.c \
+@ANDROID_TRUE@					android/hal-gatt.c \
+@ANDROID_TRUE@					android/hardware/bluetooth.h \
+@ANDROID_TRUE@					android/hardware/bt_av.h \
+@ANDROID_TRUE@					android/hardware/bt_gatt.h \
+@ANDROID_TRUE@					android/hardware/bt_gatt_client.h \
+@ANDROID_TRUE@					android/hardware/bt_gatt_server.h \
+@ANDROID_TRUE@					android/hardware/bt_gatt_types.h \
+@ANDROID_TRUE@					android/hardware/bt_hf.h \
+@ANDROID_TRUE@					android/hardware/bt_hh.h \
+@ANDROID_TRUE@					android/hardware/bt_hl.h \
+@ANDROID_TRUE@					android/hardware/bt_pan.h \
+@ANDROID_TRUE@					android/hardware/bt_rc.h \
+@ANDROID_TRUE@					android/hardware/bt_sock.h \
+@ANDROID_TRUE@					android/hardware/hardware.h \
+@ANDROID_TRUE@					android/cutils/properties.h \
+@ANDROID_TRUE@					android/ipc-common.h \
+@ANDROID_TRUE@					android/hal-log.h \
+@ANDROID_TRUE@					android/hal-ipc.h android/hal-ipc.c \
+@ANDROID_TRUE@					android/hal-utils.h android/hal-utils.c
+
+@ANDROID_TRUE@android_bluetooth_default_la_CFLAGS = $(AM_CFLAGS) -I$(srcdir)/android
+@ANDROID_TRUE@android_bluetooth_default_la_LDFLAGS = $(AM_LDFLAGS) -module -avoid-version \
+@ANDROID_TRUE@					-no-undefined
+
+@ANDROID_TRUE@android_haltest_SOURCES = android/client/haltest.c \
+@ANDROID_TRUE@				android/client/pollhandler.h \
+@ANDROID_TRUE@				android/client/pollhandler.c \
+@ANDROID_TRUE@				android/client/terminal.h \
+@ANDROID_TRUE@				android/client/terminal.c \
+@ANDROID_TRUE@				android/client/history.h \
+@ANDROID_TRUE@				android/client/history.c \
+@ANDROID_TRUE@				android/client/tabcompletion.c \
+@ANDROID_TRUE@				android/client/if-main.h \
+@ANDROID_TRUE@				android/client/if-av.c \
+@ANDROID_TRUE@				android/client/if-rc.c \
+@ANDROID_TRUE@				android/client/if-bt.c \
+@ANDROID_TRUE@				android/client/if-gatt.c \
+@ANDROID_TRUE@				android/client/if-hf.c \
+@ANDROID_TRUE@				android/client/if-hh.c \
+@ANDROID_TRUE@				android/client/if-pan.c \
+@ANDROID_TRUE@				android/client/if-sock.c \
+@ANDROID_TRUE@				android/client/if-audio.c \
+@ANDROID_TRUE@				android/hardware/hardware.c \
+@ANDROID_TRUE@				android/hal-utils.h android/hal-utils.c
+
+@ANDROID_TRUE@android_haltest_CFLAGS = $(AM_CFLAGS) -I$(srcdir)/android \
+@ANDROID_TRUE@				-DPLUGINDIR=\""$(android_plugindir)"\"
+
+@ANDROID_TRUE@android_haltest_LDFLAGS = -pthread -ldl -lm
+@ANDROID_TRUE@android_android_tester_SOURCES = emulator/btdev.h emulator/btdev.c \
+@ANDROID_TRUE@				emulator/bthost.h emulator/bthost.c \
+@ANDROID_TRUE@				emulator/smp.c \
+@ANDROID_TRUE@				src/shared/crypto.h src/shared/crypto.c \
+@ANDROID_TRUE@				src/shared/io.h src/shared/io-glib.c \
+@ANDROID_TRUE@				src/shared/queue.h src/shared/queue.c \
+@ANDROID_TRUE@				src/shared/util.h src/shared/util.c \
+@ANDROID_TRUE@				src/shared/mgmt.h src/shared/mgmt.c \
+@ANDROID_TRUE@				src/shared/hciemu.h src/shared/hciemu.c \
+@ANDROID_TRUE@				src/shared/tester.h src/shared/tester.c \
+@ANDROID_TRUE@				src/shared/timeout.h src/shared/timeout-glib.c \
+@ANDROID_TRUE@				monitor/rfcomm.h \
+@ANDROID_TRUE@				android/hardware/hardware.c \
+@ANDROID_TRUE@				android/android-tester.c
+
+@ANDROID_TRUE@android_android_tester_CFLAGS = $(AM_CFLAGS) -I$(srcdir)/android \
+@ANDROID_TRUE@				-DPLUGINDIR=\""$(android_plugindir)"\"
+
+@ANDROID_TRUE@android_android_tester_LDADD = lib/libbluetooth-internal.la @GLIB_LIBS@
+@ANDROID_TRUE@android_android_tester_LDFLAGS = -pthread -ldl
+@ANDROID_TRUE@android_ipc_tester_SOURCES = emulator/btdev.h emulator/btdev.c \
+@ANDROID_TRUE@				emulator/bthost.h emulator/bthost.c \
+@ANDROID_TRUE@				emulator/smp.c \
+@ANDROID_TRUE@				src/shared/crypto.h src/shared/crypto.c \
+@ANDROID_TRUE@				src/shared/io.h src/shared/io-glib.c \
+@ANDROID_TRUE@				src/shared/queue.h src/shared/queue.c \
+@ANDROID_TRUE@				src/shared/util.h src/shared/util.c \
+@ANDROID_TRUE@				src/shared/mgmt.h src/shared/mgmt.c \
+@ANDROID_TRUE@				src/shared/hciemu.h src/shared/hciemu.c \
+@ANDROID_TRUE@				src/shared/tester.h src/shared/tester.c \
+@ANDROID_TRUE@				src/shared/timeout.h src/shared/timeout-glib.c \
+@ANDROID_TRUE@				android/hal-utils.h android/hal-utils.c \
+@ANDROID_TRUE@				android/ipc-common.h android/ipc-tester.c
+
+@ANDROID_TRUE@android_ipc_tester_CFLAGS = $(AM_CFLAGS) -I$(srcdir)/android
+@ANDROID_TRUE@android_ipc_tester_LDADD = lib/libbluetooth-internal.la @GLIB_LIBS@
+@ANDROID_TRUE@android_audio_a2dp_default_la_SOURCES = android/audio-msg.h \
+@ANDROID_TRUE@					android/hal-msg.h \
+@ANDROID_TRUE@					android/hal-audio.c \
+@ANDROID_TRUE@					android/hardware/audio.h \
+@ANDROID_TRUE@					android/hardware/audio_effect.h \
+@ANDROID_TRUE@					android/hardware/hardware.h \
+@ANDROID_TRUE@					android/system/audio.h
+
+@ANDROID_TRUE@android_test_ipc_SOURCES = android/test-ipc.c \
+@ANDROID_TRUE@				src/shared/util.h src/shared/util.c \
+@ANDROID_TRUE@				src/log.h src/log.c \
+@ANDROID_TRUE@				android/ipc-common.h \
+@ANDROID_TRUE@				android/ipc.c android/ipc.h
+
+@ANDROID_TRUE@android_test_ipc_LDADD = @GLIB_LIBS@
+@ANDROID_TRUE@android_audio_a2dp_default_la_CFLAGS = $(AM_CFLAGS) -I$(srcdir)/android
+@ANDROID_TRUE@android_audio_a2dp_default_la_LIBADD = @SBC_LIBS@
+@ANDROID_TRUE@android_audio_a2dp_default_la_LDFLAGS = $(AM_LDFLAGS) -module -avoid-version \
+@ANDROID_TRUE@					-no-undefined -pthread -lrt
+
+@HID2HCI_TRUE@rulesdir = @UDEV_DIR@/rules.d
+@HID2HCI_TRUE@rules_DATA = tools/97-hid2hci.rules
+@TEST_TRUE@testdir = $(pkglibdir)/test
+@TEST_TRUE@test_SCRIPTS = $(test_scripts)
+AM_CPPFLAGS = -I$(builddir)/lib -I$(srcdir)/gdbus
+unit_test_eir_SOURCES = unit/test-eir.c src/eir.c src/uuid-helper.c
+unit_test_eir_LDADD = lib/libbluetooth-internal.la @GLIB_LIBS@
+unit_test_uuid_SOURCES = unit/test-uuid.c
+unit_test_uuid_LDADD = lib/libbluetooth-internal.la @GLIB_LIBS@
+unit_test_textfile_SOURCES = unit/test-textfile.c src/textfile.h src/textfile.c
+unit_test_textfile_LDADD = @GLIB_LIBS@
+unit_test_crc_SOURCES = unit/test-crc.c monitor/crc.h monitor/crc.c
+unit_test_crc_LDADD = @GLIB_LIBS@
+unit_test_ringbuf_SOURCES = unit/test-ringbuf.c \
+				src/shared/util.h src/shared/util.c \
+				src/shared/ringbuf.h src/shared/ringbuf.c
+
+unit_test_ringbuf_LDADD = @GLIB_LIBS@
+unit_test_queue_SOURCES = unit/test-queue.c \
+				src/shared/util.h src/shared/util.c \
+				src/shared/queue.h src/shared/queue.c
+
+unit_test_queue_LDADD = @GLIB_LIBS@
+unit_test_mgmt_SOURCES = unit/test-mgmt.c \
+				src/shared/io.h src/shared/io-glib.c \
+				src/shared/queue.h src/shared/queue.c \
+				src/shared/util.h src/shared/util.c \
+				src/shared/mgmt.h src/shared/mgmt.c
+
+unit_test_mgmt_LDADD = @GLIB_LIBS@
+unit_test_sdp_SOURCES = unit/test-sdp.c \
+				src/shared/util.h src/shared/util.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 @GLIB_LIBS@
+unit_test_avdtp_SOURCES = unit/test-avdtp.c \
+				src/shared/util.h src/shared/util.c \
+				src/log.h src/log.c \
+				android/avdtp.c android/avdtp.h
+
+unit_test_avdtp_LDADD = @GLIB_LIBS@
+unit_test_avctp_SOURCES = unit/test-avctp.c \
+				src/shared/util.h src/shared/util.c \
+				src/log.h src/log.c \
+				android/avctp.c android/avctp.h
+
+unit_test_avctp_LDADD = @GLIB_LIBS@
+unit_test_avrcp_SOURCES = unit/test-avrcp.c \
+				src/shared/util.h src/shared/util.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 = @GLIB_LIBS@ lib/libbluetooth-internal.la
+unit_test_hfp_SOURCES = unit/test-hfp.c \
+				src/shared/io.h src/shared/io-glib.c \
+				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/ringbuf.h src/shared/ringbuf.c \
+				src/shared/hfp.h src/shared/hfp.c
+
+unit_test_hfp_LDADD = @GLIB_LIBS@
+unit_test_gdbus_client_SOURCES = unit/test-gdbus-client.c
+unit_test_gdbus_client_LDADD = gdbus/libgdbus-internal.la \
+				@GLIB_LIBS@ @DBUS_LIBS@
+
+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_test_lib_SOURCES = unit/test-lib.c
+unit_test_lib_LDADD = lib/libbluetooth-internal.la @GLIB_LIBS@
+pkgconfigdir = $(libdir)/pkgconfig
+@LIBRARY_TRUE@pkgconfig_DATA = lib/bluez.pc
+DISTCHECK_CONFIGURE_FLAGS = --disable-datafiles --enable-library \
+					--disable-systemd --disable-udev
+
+DISTCLEANFILES = $(pkgconfig_DATA)
+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' \
+		< $< > $@
+
+all: $(BUILT_SOURCES) config.h
+	$(MAKE) $(AM_MAKEFLAGS) all-am
+
+.SUFFIXES:
+.SUFFIXES: .c .lo .log .o .obj .test .test$(EXEEXT) .trs
+am--refresh: Makefile
+	@:
+$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(srcdir)/Makefile.plugins $(srcdir)/Makefile.tools $(srcdir)/Makefile.obexd $(srcdir)/android/Makefile.am $(am__configure_deps)
+	@for dep in $?; do \
+	  case '$(am__configure_deps)' in \
+	    *$$dep*) \
+	      echo ' cd $(srcdir) && $(AUTOMAKE) --foreign'; \
+	      $(am__cd) $(srcdir) && $(AUTOMAKE) --foreign \
+		&& exit 0; \
+	      exit 1;; \
+	  esac; \
+	done; \
+	echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign Makefile'; \
+	$(am__cd) $(top_srcdir) && \
+	  $(AUTOMAKE) --foreign Makefile
+.PRECIOUS: Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+	@case '$?' in \
+	  *config.status*) \
+	    echo ' $(SHELL) ./config.status'; \
+	    $(SHELL) ./config.status;; \
+	  *) \
+	    echo ' cd $(top_builddir) && $(SHELL) ./config.status $@ $(am__depfiles_maybe)'; \
+	    cd $(top_builddir) && $(SHELL) ./config.status $@ $(am__depfiles_maybe);; \
+	esac;
+$(srcdir)/Makefile.plugins $(srcdir)/Makefile.tools $(srcdir)/Makefile.obexd $(srcdir)/android/Makefile.am:
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+	$(SHELL) ./config.status --recheck
+
+$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps)
+	$(am__cd) $(srcdir) && $(AUTOCONF)
+$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps)
+	$(am__cd) $(srcdir) && $(ACLOCAL) $(ACLOCAL_AMFLAGS)
+$(am__aclocal_m4_deps):
+
+config.h: stamp-h1
+	@if test ! -f $@; then rm -f stamp-h1; else :; fi
+	@if test ! -f $@; then $(MAKE) $(AM_MAKEFLAGS) stamp-h1; else :; fi
+
+stamp-h1: $(srcdir)/config.h.in $(top_builddir)/config.status
+	@rm -f stamp-h1
+	cd $(top_builddir) && $(SHELL) ./config.status config.h
+$(srcdir)/config.h.in: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) 
+	($(am__cd) $(top_srcdir) && $(AUTOHEADER))
+	rm -f stamp-h1
+	touch $@
+
+distclean-hdr:
+	-rm -f config.h stamp-h1
+src/bluetoothd.8: $(top_builddir)/config.status $(top_srcdir)/src/bluetoothd.8.in
+	cd $(top_builddir) && $(SHELL) ./config.status $@
+lib/bluez.pc: $(top_builddir)/config.status $(top_srcdir)/lib/bluez.pc.in
+	cd $(top_builddir) && $(SHELL) ./config.status $@
+
+clean-noinstLIBRARIES:
+	-test -z "$(noinst_LIBRARIES)" || rm -f $(noinst_LIBRARIES)
+profiles/sap/$(am__dirstamp):
+	@$(MKDIR_P) profiles/sap
+	@: > profiles/sap/$(am__dirstamp)
+profiles/sap/$(DEPDIR)/$(am__dirstamp):
+	@$(MKDIR_P) profiles/sap/$(DEPDIR)
+	@: > profiles/sap/$(DEPDIR)/$(am__dirstamp)
+profiles/sap/sap-u8500.$(OBJEXT): profiles/sap/$(am__dirstamp) \
+	profiles/sap/$(DEPDIR)/$(am__dirstamp)
+
+profiles/sap/libsap.a: $(profiles_sap_libsap_a_OBJECTS) $(profiles_sap_libsap_a_DEPENDENCIES) $(EXTRA_profiles_sap_libsap_a_DEPENDENCIES) profiles/sap/$(am__dirstamp)
+	$(AM_V_at)-rm -f profiles/sap/libsap.a
+	$(AM_V_AR)$(profiles_sap_libsap_a_AR) profiles/sap/libsap.a $(profiles_sap_libsap_a_OBJECTS) $(profiles_sap_libsap_a_LIBADD)
+	$(AM_V_at)$(RANLIB) profiles/sap/libsap.a
+
+install-libLTLIBRARIES: $(lib_LTLIBRARIES)
+	@$(NORMAL_INSTALL)
+	@list='$(lib_LTLIBRARIES)'; test -n "$(libdir)" || list=; \
+	list2=; for p in $$list; do \
+	  if test -f $$p; then \
+	    list2="$$list2 $$p"; \
+	  else :; fi; \
+	done; \
+	test -z "$$list2" || { \
+	  echo " $(MKDIR_P) '$(DESTDIR)$(libdir)'"; \
+	  $(MKDIR_P) "$(DESTDIR)$(libdir)" || exit 1; \
+	  echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(libdir)'"; \
+	  $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(libdir)"; \
+	}
+
+uninstall-libLTLIBRARIES:
+	@$(NORMAL_UNINSTALL)
+	@list='$(lib_LTLIBRARIES)'; test -n "$(libdir)" || list=; \
+	for p in $$list; do \
+	  $(am__strip_dir) \
+	  echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f '$(DESTDIR)$(libdir)/$$f'"; \
+	  $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f "$(DESTDIR)$(libdir)/$$f"; \
+	done
+
+clean-libLTLIBRARIES:
+	-test -z "$(lib_LTLIBRARIES)" || rm -f $(lib_LTLIBRARIES)
+	@list='$(lib_LTLIBRARIES)'; \
+	locs=`for p in $$list; do echo $$p; done | \
+	      sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \
+	      sort -u`; \
+	test -z "$$locs" || { \
+	  echo rm -f $${locs}; \
+	  rm -f $${locs}; \
+	}
+
+clean-noinstLTLIBRARIES:
+	-test -z "$(noinst_LTLIBRARIES)" || rm -f $(noinst_LTLIBRARIES)
+	@list='$(noinst_LTLIBRARIES)'; \
+	locs=`for p in $$list; do echo $$p; done | \
+	      sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \
+	      sort -u`; \
+	test -z "$$locs" || { \
+	  echo rm -f $${locs}; \
+	  rm -f $${locs}; \
+	}
+
+install-pluginLTLIBRARIES: $(plugin_LTLIBRARIES)
+	@$(NORMAL_INSTALL)
+	@list='$(plugin_LTLIBRARIES)'; test -n "$(plugindir)" || list=; \
+	list2=; for p in $$list; do \
+	  if test -f $$p; then \
+	    list2="$$list2 $$p"; \
+	  else :; fi; \
+	done; \
+	test -z "$$list2" || { \
+	  echo " $(MKDIR_P) '$(DESTDIR)$(plugindir)'"; \
+	  $(MKDIR_P) "$(DESTDIR)$(plugindir)" || exit 1; \
+	  echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(plugindir)'"; \
+	  $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(plugindir)"; \
+	}
+
+uninstall-pluginLTLIBRARIES:
+	@$(NORMAL_UNINSTALL)
+	@list='$(plugin_LTLIBRARIES)'; test -n "$(plugindir)" || list=; \
+	for p in $$list; do \
+	  $(am__strip_dir) \
+	  echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f '$(DESTDIR)$(plugindir)/$$f'"; \
+	  $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f "$(DESTDIR)$(plugindir)/$$f"; \
+	done
+
+clean-pluginLTLIBRARIES:
+	-test -z "$(plugin_LTLIBRARIES)" || rm -f $(plugin_LTLIBRARIES)
+	@list='$(plugin_LTLIBRARIES)'; \
+	locs=`for p in $$list; do echo $$p; done | \
+	      sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \
+	      sort -u`; \
+	test -z "$$locs" || { \
+	  echo rm -f $${locs}; \
+	  rm -f $${locs}; \
+	}
+android/$(am__dirstamp):
+	@$(MKDIR_P) android
+	@: > android/$(am__dirstamp)
+android/$(DEPDIR)/$(am__dirstamp):
+	@$(MKDIR_P) android/$(DEPDIR)
+	@: > android/$(DEPDIR)/$(am__dirstamp)
+android/android_audio_a2dp_default_la-hal-audio.lo:  \
+	android/$(am__dirstamp) android/$(DEPDIR)/$(am__dirstamp)
+
+android/audio.a2dp.default.la: $(android_audio_a2dp_default_la_OBJECTS) $(android_audio_a2dp_default_la_DEPENDENCIES) $(EXTRA_android_audio_a2dp_default_la_DEPENDENCIES) android/$(am__dirstamp)
+	$(AM_V_CCLD)$(android_audio_a2dp_default_la_LINK) $(am_android_audio_a2dp_default_la_rpath) $(android_audio_a2dp_default_la_OBJECTS) $(android_audio_a2dp_default_la_LIBADD) $(LIBS)
+android/android_bluetooth_default_la-hal-bluetooth.lo:  \
+	android/$(am__dirstamp) android/$(DEPDIR)/$(am__dirstamp)
+android/android_bluetooth_default_la-hal-socket.lo:  \
+	android/$(am__dirstamp) android/$(DEPDIR)/$(am__dirstamp)
+android/android_bluetooth_default_la-hal-hidhost.lo:  \
+	android/$(am__dirstamp) android/$(DEPDIR)/$(am__dirstamp)
+android/android_bluetooth_default_la-hal-health.lo:  \
+	android/$(am__dirstamp) android/$(DEPDIR)/$(am__dirstamp)
+android/android_bluetooth_default_la-hal-pan.lo:  \
+	android/$(am__dirstamp) android/$(DEPDIR)/$(am__dirstamp)
+android/android_bluetooth_default_la-hal-a2dp.lo:  \
+	android/$(am__dirstamp) android/$(DEPDIR)/$(am__dirstamp)
+android/android_bluetooth_default_la-hal-avrcp.lo:  \
+	android/$(am__dirstamp) android/$(DEPDIR)/$(am__dirstamp)
+android/android_bluetooth_default_la-hal-handsfree.lo:  \
+	android/$(am__dirstamp) android/$(DEPDIR)/$(am__dirstamp)
+android/android_bluetooth_default_la-hal-gatt.lo:  \
+	android/$(am__dirstamp) android/$(DEPDIR)/$(am__dirstamp)
+android/android_bluetooth_default_la-hal-ipc.lo:  \
+	android/$(am__dirstamp) android/$(DEPDIR)/$(am__dirstamp)
+android/android_bluetooth_default_la-hal-utils.lo:  \
+	android/$(am__dirstamp) android/$(DEPDIR)/$(am__dirstamp)
+
+android/bluetooth.default.la: $(android_bluetooth_default_la_OBJECTS) $(android_bluetooth_default_la_DEPENDENCIES) $(EXTRA_android_bluetooth_default_la_DEPENDENCIES) android/$(am__dirstamp)
+	$(AM_V_CCLD)$(android_bluetooth_default_la_LINK) $(am_android_bluetooth_default_la_rpath) $(android_bluetooth_default_la_OBJECTS) $(android_bluetooth_default_la_LIBADD) $(LIBS)
+gdbus/$(am__dirstamp):
+	@$(MKDIR_P) gdbus
+	@: > gdbus/$(am__dirstamp)
+gdbus/$(DEPDIR)/$(am__dirstamp):
+	@$(MKDIR_P) gdbus/$(DEPDIR)
+	@: > gdbus/$(DEPDIR)/$(am__dirstamp)
+gdbus/mainloop.lo: gdbus/$(am__dirstamp) \
+	gdbus/$(DEPDIR)/$(am__dirstamp)
+gdbus/watch.lo: gdbus/$(am__dirstamp) gdbus/$(DEPDIR)/$(am__dirstamp)
+gdbus/object.lo: gdbus/$(am__dirstamp) gdbus/$(DEPDIR)/$(am__dirstamp)
+gdbus/client.lo: gdbus/$(am__dirstamp) gdbus/$(DEPDIR)/$(am__dirstamp)
+gdbus/polkit.lo: gdbus/$(am__dirstamp) gdbus/$(DEPDIR)/$(am__dirstamp)
+
+gdbus/libgdbus-internal.la: $(gdbus_libgdbus_internal_la_OBJECTS) $(gdbus_libgdbus_internal_la_DEPENDENCIES) $(EXTRA_gdbus_libgdbus_internal_la_DEPENDENCIES) gdbus/$(am__dirstamp)
+	$(AM_V_CCLD)$(LINK)  $(gdbus_libgdbus_internal_la_OBJECTS) $(gdbus_libgdbus_internal_la_LIBADD) $(LIBS)
+lib/$(am__dirstamp):
+	@$(MKDIR_P) lib
+	@: > lib/$(am__dirstamp)
+lib/$(DEPDIR)/$(am__dirstamp):
+	@$(MKDIR_P) lib/$(DEPDIR)
+	@: > lib/$(DEPDIR)/$(am__dirstamp)
+lib/bluetooth.lo: lib/$(am__dirstamp) lib/$(DEPDIR)/$(am__dirstamp)
+lib/hci.lo: lib/$(am__dirstamp) lib/$(DEPDIR)/$(am__dirstamp)
+lib/sdp.lo: lib/$(am__dirstamp) lib/$(DEPDIR)/$(am__dirstamp)
+lib/uuid.lo: lib/$(am__dirstamp) lib/$(DEPDIR)/$(am__dirstamp)
+
+lib/libbluetooth-internal.la: $(lib_libbluetooth_internal_la_OBJECTS) $(lib_libbluetooth_internal_la_DEPENDENCIES) $(EXTRA_lib_libbluetooth_internal_la_DEPENDENCIES) lib/$(am__dirstamp)
+	$(AM_V_CCLD)$(LINK)  $(lib_libbluetooth_internal_la_OBJECTS) $(lib_libbluetooth_internal_la_LIBADD) $(LIBS)
+
+lib/libbluetooth.la: $(lib_libbluetooth_la_OBJECTS) $(lib_libbluetooth_la_DEPENDENCIES) $(EXTRA_lib_libbluetooth_la_DEPENDENCIES) lib/$(am__dirstamp)
+	$(AM_V_CCLD)$(lib_libbluetooth_la_LINK) $(am_lib_libbluetooth_la_rpath) $(lib_libbluetooth_la_OBJECTS) $(lib_libbluetooth_la_LIBADD) $(LIBS)
+plugins/$(am__dirstamp):
+	@$(MKDIR_P) plugins
+	@: > plugins/$(am__dirstamp)
+plugins/$(DEPDIR)/$(am__dirstamp):
+	@$(MKDIR_P) plugins/$(DEPDIR)
+	@: > plugins/$(DEPDIR)/$(am__dirstamp)
+plugins/plugins_external_dummy_la-external-dummy.lo:  \
+	plugins/$(am__dirstamp) plugins/$(DEPDIR)/$(am__dirstamp)
+
+plugins/external-dummy.la: $(plugins_external_dummy_la_OBJECTS) $(plugins_external_dummy_la_DEPENDENCIES) $(EXTRA_plugins_external_dummy_la_DEPENDENCIES) plugins/$(am__dirstamp)
+	$(AM_V_CCLD)$(plugins_external_dummy_la_LINK) $(am_plugins_external_dummy_la_rpath) $(plugins_external_dummy_la_OBJECTS) $(plugins_external_dummy_la_LIBADD) $(LIBS)
+plugins/plugins_sixaxis_la-sixaxis.lo: plugins/$(am__dirstamp) \
+	plugins/$(DEPDIR)/$(am__dirstamp)
+
+plugins/sixaxis.la: $(plugins_sixaxis_la_OBJECTS) $(plugins_sixaxis_la_DEPENDENCIES) $(EXTRA_plugins_sixaxis_la_DEPENDENCIES) plugins/$(am__dirstamp)
+	$(AM_V_CCLD)$(plugins_sixaxis_la_LINK) $(am_plugins_sixaxis_la_rpath) $(plugins_sixaxis_la_OBJECTS) $(plugins_sixaxis_la_LIBADD) $(LIBS)
+install-binPROGRAMS: $(bin_PROGRAMS)
+	@$(NORMAL_INSTALL)
+	@list='$(bin_PROGRAMS)'; test -n "$(bindir)" || list=; \
+	if test -n "$$list"; then \
+	  echo " $(MKDIR_P) '$(DESTDIR)$(bindir)'"; \
+	  $(MKDIR_P) "$(DESTDIR)$(bindir)" || exit 1; \
+	fi; \
+	for p in $$list; do echo "$$p $$p"; done | \
+	sed 's/$(EXEEXT)$$//' | \
+	while read p p1; do if test -f $$p \
+	 || test -f $$p1 \
+	  ; then echo "$$p"; echo "$$p"; else :; fi; \
+	done | \
+	sed -e 'p;s,.*/,,;n;h' \
+	    -e 's|.*|.|' \
+	    -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \
+	sed 'N;N;N;s,\n, ,g' | \
+	$(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \
+	  { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \
+	    if ($$2 == $$4) files[d] = files[d] " " $$1; \
+	    else { print "f", $$3 "/" $$4, $$1; } } \
+	  END { for (d in files) print "f", d, files[d] }' | \
+	while read type dir files; do \
+	    if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \
+	    test -z "$$files" || { \
+	    echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(bindir)$$dir'"; \
+	    $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(bindir)$$dir" || exit $$?; \
+	    } \
+	; done
+
+uninstall-binPROGRAMS:
+	@$(NORMAL_UNINSTALL)
+	@list='$(bin_PROGRAMS)'; test -n "$(bindir)" || list=; \
+	files=`for p in $$list; do echo "$$p"; done | \
+	  sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \
+	      -e 's/$$/$(EXEEXT)/' \
+	`; \
+	test -n "$$list" || exit 0; \
+	echo " ( cd '$(DESTDIR)$(bindir)' && rm -f" $$files ")"; \
+	cd "$(DESTDIR)$(bindir)" && rm -f $$files
+
+clean-binPROGRAMS:
+	@list='$(bin_PROGRAMS)'; test -n "$$list" || exit 0; \
+	echo " rm -f" $$list; \
+	rm -f $$list || exit $$?; \
+	test -n "$(EXEEXT)" || exit 0; \
+	list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \
+	echo " rm -f" $$list; \
+	rm -f $$list
+install-cupsPROGRAMS: $(cups_PROGRAMS)
+	@$(NORMAL_INSTALL)
+	@list='$(cups_PROGRAMS)'; test -n "$(cupsdir)" || list=; \
+	if test -n "$$list"; then \
+	  echo " $(MKDIR_P) '$(DESTDIR)$(cupsdir)'"; \
+	  $(MKDIR_P) "$(DESTDIR)$(cupsdir)" || exit 1; \
+	fi; \
+	for p in $$list; do echo "$$p $$p"; done | \
+	sed 's/$(EXEEXT)$$//' | \
+	while read p p1; do if test -f $$p \
+	 || test -f $$p1 \
+	  ; then echo "$$p"; echo "$$p"; else :; fi; \
+	done | \
+	sed -e 'p;s,.*/,,;n;h' \
+	    -e 's|.*|.|' \
+	    -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \
+	sed 'N;N;N;s,\n, ,g' | \
+	$(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \
+	  { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \
+	    if ($$2 == $$4) files[d] = files[d] " " $$1; \
+	    else { print "f", $$3 "/" $$4, $$1; } } \
+	  END { for (d in files) print "f", d, files[d] }' | \
+	while read type dir files; do \
+	    if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \
+	    test -z "$$files" || { \
+	    echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(cupsdir)$$dir'"; \
+	    $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(cupsdir)$$dir" || exit $$?; \
+	    } \
+	; done
+
+uninstall-cupsPROGRAMS:
+	@$(NORMAL_UNINSTALL)
+	@list='$(cups_PROGRAMS)'; test -n "$(cupsdir)" || list=; \
+	files=`for p in $$list; do echo "$$p"; done | \
+	  sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \
+	      -e 's/$$/$(EXEEXT)/' \
+	`; \
+	test -n "$$list" || exit 0; \
+	echo " ( cd '$(DESTDIR)$(cupsdir)' && rm -f" $$files ")"; \
+	cd "$(DESTDIR)$(cupsdir)" && rm -f $$files
+
+clean-cupsPROGRAMS:
+	@list='$(cups_PROGRAMS)'; test -n "$$list" || exit 0; \
+	echo " rm -f" $$list; \
+	rm -f $$list || exit $$?; \
+	test -n "$(EXEEXT)" || exit 0; \
+	list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \
+	echo " rm -f" $$list; \
+	rm -f $$list
+install-libexecPROGRAMS: $(libexec_PROGRAMS)
+	@$(NORMAL_INSTALL)
+	@list='$(libexec_PROGRAMS)'; test -n "$(libexecdir)" || list=; \
+	if test -n "$$list"; then \
+	  echo " $(MKDIR_P) '$(DESTDIR)$(libexecdir)'"; \
+	  $(MKDIR_P) "$(DESTDIR)$(libexecdir)" || exit 1; \
+	fi; \
+	for p in $$list; do echo "$$p $$p"; done | \
+	sed 's/$(EXEEXT)$$//' | \
+	while read p p1; do if test -f $$p \
+	 || test -f $$p1 \
+	  ; then echo "$$p"; echo "$$p"; else :; fi; \
+	done | \
+	sed -e 'p;s,.*/,,;n;h' \
+	    -e 's|.*|.|' \
+	    -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \
+	sed 'N;N;N;s,\n, ,g' | \
+	$(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \
+	  { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \
+	    if ($$2 == $$4) files[d] = files[d] " " $$1; \
+	    else { print "f", $$3 "/" $$4, $$1; } } \
+	  END { for (d in files) print "f", d, files[d] }' | \
+	while read type dir files; do \
+	    if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \
+	    test -z "$$files" || { \
+	    echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(libexecdir)$$dir'"; \
+	    $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(libexecdir)$$dir" || exit $$?; \
+	    } \
+	; done
+
+uninstall-libexecPROGRAMS:
+	@$(NORMAL_UNINSTALL)
+	@list='$(libexec_PROGRAMS)'; test -n "$(libexecdir)" || list=; \
+	files=`for p in $$list; do echo "$$p"; done | \
+	  sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \
+	      -e 's/$$/$(EXEEXT)/' \
+	`; \
+	test -n "$$list" || exit 0; \
+	echo " ( cd '$(DESTDIR)$(libexecdir)' && rm -f" $$files ")"; \
+	cd "$(DESTDIR)$(libexecdir)" && rm -f $$files
+
+clean-libexecPROGRAMS:
+	@list='$(libexec_PROGRAMS)'; test -n "$$list" || exit 0; \
+	echo " rm -f" $$list; \
+	rm -f $$list || exit $$?; \
+	test -n "$(EXEEXT)" || exit 0; \
+	list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \
+	echo " rm -f" $$list; \
+	rm -f $$list
+
+clean-noinstPROGRAMS:
+	@list='$(noinst_PROGRAMS)'; test -n "$$list" || exit 0; \
+	echo " rm -f" $$list; \
+	rm -f $$list || exit $$?; \
+	test -n "$(EXEEXT)" || exit 0; \
+	list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \
+	echo " rm -f" $$list; \
+	rm -f $$list
+install-udevPROGRAMS: $(udev_PROGRAMS)
+	@$(NORMAL_INSTALL)
+	@list='$(udev_PROGRAMS)'; test -n "$(udevdir)" || list=; \
+	if test -n "$$list"; then \
+	  echo " $(MKDIR_P) '$(DESTDIR)$(udevdir)'"; \
+	  $(MKDIR_P) "$(DESTDIR)$(udevdir)" || exit 1; \
+	fi; \
+	for p in $$list; do echo "$$p $$p"; done | \
+	sed 's/$(EXEEXT)$$//' | \
+	while read p p1; do if test -f $$p \
+	 || test -f $$p1 \
+	  ; then echo "$$p"; echo "$$p"; else :; fi; \
+	done | \
+	sed -e 'p;s,.*/,,;n;h' \
+	    -e 's|.*|.|' \
+	    -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \
+	sed 'N;N;N;s,\n, ,g' | \
+	$(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \
+	  { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \
+	    if ($$2 == $$4) files[d] = files[d] " " $$1; \
+	    else { print "f", $$3 "/" $$4, $$1; } } \
+	  END { for (d in files) print "f", d, files[d] }' | \
+	while read type dir files; do \
+	    if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \
+	    test -z "$$files" || { \
+	    echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(udevdir)$$dir'"; \
+	    $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(udevdir)$$dir" || exit $$?; \
+	    } \
+	; done
+
+uninstall-udevPROGRAMS:
+	@$(NORMAL_UNINSTALL)
+	@list='$(udev_PROGRAMS)'; test -n "$(udevdir)" || list=; \
+	files=`for p in $$list; do echo "$$p"; done | \
+	  sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \
+	      -e 's/$$/$(EXEEXT)/' \
+	`; \
+	test -n "$$list" || exit 0; \
+	echo " ( cd '$(DESTDIR)$(udevdir)' && rm -f" $$files ")"; \
+	cd "$(DESTDIR)$(udevdir)" && rm -f $$files
+
+clean-udevPROGRAMS:
+	@list='$(udev_PROGRAMS)'; test -n "$$list" || exit 0; \
+	echo " rm -f" $$list; \
+	rm -f $$list || exit $$?; \
+	test -n "$(EXEEXT)" || exit 0; \
+	list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \
+	echo " rm -f" $$list; \
+	rm -f $$list
+emulator/$(am__dirstamp):
+	@$(MKDIR_P) emulator
+	@: > emulator/$(am__dirstamp)
+emulator/$(DEPDIR)/$(am__dirstamp):
+	@$(MKDIR_P) emulator/$(DEPDIR)
+	@: > emulator/$(DEPDIR)/$(am__dirstamp)
+emulator/android_android_tester-btdev.$(OBJEXT):  \
+	emulator/$(am__dirstamp) emulator/$(DEPDIR)/$(am__dirstamp)
+emulator/android_android_tester-bthost.$(OBJEXT):  \
+	emulator/$(am__dirstamp) emulator/$(DEPDIR)/$(am__dirstamp)
+emulator/android_android_tester-smp.$(OBJEXT):  \
+	emulator/$(am__dirstamp) emulator/$(DEPDIR)/$(am__dirstamp)
+src/shared/$(am__dirstamp):
+	@$(MKDIR_P) src/shared
+	@: > src/shared/$(am__dirstamp)
+src/shared/$(DEPDIR)/$(am__dirstamp):
+	@$(MKDIR_P) src/shared/$(DEPDIR)
+	@: > src/shared/$(DEPDIR)/$(am__dirstamp)
+src/shared/android_android_tester-crypto.$(OBJEXT):  \
+	src/shared/$(am__dirstamp) \
+	src/shared/$(DEPDIR)/$(am__dirstamp)
+src/shared/android_android_tester-io-glib.$(OBJEXT):  \
+	src/shared/$(am__dirstamp) \
+	src/shared/$(DEPDIR)/$(am__dirstamp)
+src/shared/android_android_tester-queue.$(OBJEXT):  \
+	src/shared/$(am__dirstamp) \
+	src/shared/$(DEPDIR)/$(am__dirstamp)
+src/shared/android_android_tester-util.$(OBJEXT):  \
+	src/shared/$(am__dirstamp) \
+	src/shared/$(DEPDIR)/$(am__dirstamp)
+src/shared/android_android_tester-mgmt.$(OBJEXT):  \
+	src/shared/$(am__dirstamp) \
+	src/shared/$(DEPDIR)/$(am__dirstamp)
+src/shared/android_android_tester-hciemu.$(OBJEXT):  \
+	src/shared/$(am__dirstamp) \
+	src/shared/$(DEPDIR)/$(am__dirstamp)
+src/shared/android_android_tester-tester.$(OBJEXT):  \
+	src/shared/$(am__dirstamp) \
+	src/shared/$(DEPDIR)/$(am__dirstamp)
+src/shared/android_android_tester-timeout-glib.$(OBJEXT):  \
+	src/shared/$(am__dirstamp) \
+	src/shared/$(DEPDIR)/$(am__dirstamp)
+android/hardware/$(am__dirstamp):
+	@$(MKDIR_P) android/hardware
+	@: > android/hardware/$(am__dirstamp)
+android/hardware/$(DEPDIR)/$(am__dirstamp):
+	@$(MKDIR_P) android/hardware/$(DEPDIR)
+	@: > android/hardware/$(DEPDIR)/$(am__dirstamp)
+android/hardware/android_android_tester-hardware.$(OBJEXT):  \
+	android/hardware/$(am__dirstamp) \
+	android/hardware/$(DEPDIR)/$(am__dirstamp)
+android/android_android_tester-android-tester.$(OBJEXT):  \
+	android/$(am__dirstamp) android/$(DEPDIR)/$(am__dirstamp)
+
+android/android-tester$(EXEEXT): $(android_android_tester_OBJECTS) $(android_android_tester_DEPENDENCIES) $(EXTRA_android_android_tester_DEPENDENCIES) android/$(am__dirstamp)
+	@rm -f android/android-tester$(EXEEXT)
+	$(AM_V_CCLD)$(android_android_tester_LINK) $(android_android_tester_OBJECTS) $(android_android_tester_LDADD) $(LIBS)
+android/main.$(OBJEXT): android/$(am__dirstamp) \
+	android/$(DEPDIR)/$(am__dirstamp)
+src/$(am__dirstamp):
+	@$(MKDIR_P) src
+	@: > src/$(am__dirstamp)
+src/$(DEPDIR)/$(am__dirstamp):
+	@$(MKDIR_P) src/$(DEPDIR)
+	@: > src/$(DEPDIR)/$(am__dirstamp)
+src/log.$(OBJEXT): src/$(am__dirstamp) src/$(DEPDIR)/$(am__dirstamp)
+src/sdpd-database.$(OBJEXT): src/$(am__dirstamp) \
+	src/$(DEPDIR)/$(am__dirstamp)
+src/sdpd-server.$(OBJEXT): src/$(am__dirstamp) \
+	src/$(DEPDIR)/$(am__dirstamp)
+src/sdpd-service.$(OBJEXT): src/$(am__dirstamp) \
+	src/$(DEPDIR)/$(am__dirstamp)
+src/sdpd-request.$(OBJEXT): src/$(am__dirstamp) \
+	src/$(DEPDIR)/$(am__dirstamp)
+src/uuid-helper.$(OBJEXT): src/$(am__dirstamp) \
+	src/$(DEPDIR)/$(am__dirstamp)
+src/eir.$(OBJEXT): src/$(am__dirstamp) src/$(DEPDIR)/$(am__dirstamp)
+src/shared/io-glib.$(OBJEXT): src/shared/$(am__dirstamp) \
+	src/shared/$(DEPDIR)/$(am__dirstamp)
+src/shared/queue.$(OBJEXT): src/shared/$(am__dirstamp) \
+	src/shared/$(DEPDIR)/$(am__dirstamp)
+src/shared/util.$(OBJEXT): src/shared/$(am__dirstamp) \
+	src/shared/$(DEPDIR)/$(am__dirstamp)
+src/shared/mgmt.$(OBJEXT): src/shared/$(am__dirstamp) \
+	src/shared/$(DEPDIR)/$(am__dirstamp)
+src/shared/ringbuf.$(OBJEXT): src/shared/$(am__dirstamp) \
+	src/shared/$(DEPDIR)/$(am__dirstamp)
+src/shared/hfp.$(OBJEXT): src/shared/$(am__dirstamp) \
+	src/shared/$(DEPDIR)/$(am__dirstamp)
+android/bluetooth.$(OBJEXT): android/$(am__dirstamp) \
+	android/$(DEPDIR)/$(am__dirstamp)
+android/hidhost.$(OBJEXT): android/$(am__dirstamp) \
+	android/$(DEPDIR)/$(am__dirstamp)
+android/ipc.$(OBJEXT): android/$(am__dirstamp) \
+	android/$(DEPDIR)/$(am__dirstamp)
+android/avdtp.$(OBJEXT): android/$(am__dirstamp) \
+	android/$(DEPDIR)/$(am__dirstamp)
+android/a2dp.$(OBJEXT): android/$(am__dirstamp) \
+	android/$(DEPDIR)/$(am__dirstamp)
+android/avctp.$(OBJEXT): android/$(am__dirstamp) \
+	android/$(DEPDIR)/$(am__dirstamp)
+android/avrcp.$(OBJEXT): android/$(am__dirstamp) \
+	android/$(DEPDIR)/$(am__dirstamp)
+android/avrcp-lib.$(OBJEXT): android/$(am__dirstamp) \
+	android/$(DEPDIR)/$(am__dirstamp)
+android/socket.$(OBJEXT): android/$(am__dirstamp) \
+	android/$(DEPDIR)/$(am__dirstamp)
+android/pan.$(OBJEXT): android/$(am__dirstamp) \
+	android/$(DEPDIR)/$(am__dirstamp)
+android/handsfree.$(OBJEXT): android/$(am__dirstamp) \
+	android/$(DEPDIR)/$(am__dirstamp)
+android/gatt.$(OBJEXT): android/$(am__dirstamp) \
+	android/$(DEPDIR)/$(am__dirstamp)
+android/health.$(OBJEXT): android/$(am__dirstamp) \
+	android/$(DEPDIR)/$(am__dirstamp)
+attrib/$(am__dirstamp):
+	@$(MKDIR_P) attrib
+	@: > attrib/$(am__dirstamp)
+attrib/$(DEPDIR)/$(am__dirstamp):
+	@$(MKDIR_P) attrib/$(DEPDIR)
+	@: > attrib/$(DEPDIR)/$(am__dirstamp)
+attrib/att.$(OBJEXT): attrib/$(am__dirstamp) \
+	attrib/$(DEPDIR)/$(am__dirstamp)
+attrib/gatt.$(OBJEXT): attrib/$(am__dirstamp) \
+	attrib/$(DEPDIR)/$(am__dirstamp)
+attrib/gattrib.$(OBJEXT): attrib/$(am__dirstamp) \
+	attrib/$(DEPDIR)/$(am__dirstamp)
+btio/$(am__dirstamp):
+	@$(MKDIR_P) btio
+	@: > btio/$(am__dirstamp)
+btio/$(DEPDIR)/$(am__dirstamp):
+	@$(MKDIR_P) btio/$(DEPDIR)
+	@: > btio/$(DEPDIR)/$(am__dirstamp)
+btio/btio.$(OBJEXT): btio/$(am__dirstamp) \
+	btio/$(DEPDIR)/$(am__dirstamp)
+src/sdp-client.$(OBJEXT): src/$(am__dirstamp) \
+	src/$(DEPDIR)/$(am__dirstamp)
+profiles/network/$(am__dirstamp):
+	@$(MKDIR_P) profiles/network
+	@: > profiles/network/$(am__dirstamp)
+profiles/network/$(DEPDIR)/$(am__dirstamp):
+	@$(MKDIR_P) profiles/network/$(DEPDIR)
+	@: > profiles/network/$(DEPDIR)/$(am__dirstamp)
+profiles/network/bnep.$(OBJEXT): profiles/network/$(am__dirstamp) \
+	profiles/network/$(DEPDIR)/$(am__dirstamp)
+
+android/bluetoothd$(EXEEXT): $(android_bluetoothd_OBJECTS) $(android_bluetoothd_DEPENDENCIES) $(EXTRA_android_bluetoothd_DEPENDENCIES) android/$(am__dirstamp)
+	@rm -f android/bluetoothd$(EXEEXT)
+	$(AM_V_CCLD)$(LINK) $(android_bluetoothd_OBJECTS) $(android_bluetoothd_LDADD) $(LIBS)
+android/bluetoothd-snoop.$(OBJEXT): android/$(am__dirstamp) \
+	android/$(DEPDIR)/$(am__dirstamp)
+monitor/$(am__dirstamp):
+	@$(MKDIR_P) monitor
+	@: > monitor/$(am__dirstamp)
+monitor/$(DEPDIR)/$(am__dirstamp):
+	@$(MKDIR_P) monitor/$(DEPDIR)
+	@: > monitor/$(DEPDIR)/$(am__dirstamp)
+monitor/mainloop.$(OBJEXT): monitor/$(am__dirstamp) \
+	monitor/$(DEPDIR)/$(am__dirstamp)
+src/shared/btsnoop.$(OBJEXT): src/shared/$(am__dirstamp) \
+	src/shared/$(DEPDIR)/$(am__dirstamp)
+
+android/bluetoothd-snoop$(EXEEXT): $(android_bluetoothd_snoop_OBJECTS) $(android_bluetoothd_snoop_DEPENDENCIES) $(EXTRA_android_bluetoothd_snoop_DEPENDENCIES) android/$(am__dirstamp)
+	@rm -f android/bluetoothd-snoop$(EXEEXT)
+	$(AM_V_CCLD)$(LINK) $(android_bluetoothd_snoop_OBJECTS) $(android_bluetoothd_snoop_LDADD) $(LIBS)
+android/client/$(am__dirstamp):
+	@$(MKDIR_P) android/client
+	@: > android/client/$(am__dirstamp)
+android/client/$(DEPDIR)/$(am__dirstamp):
+	@$(MKDIR_P) android/client/$(DEPDIR)
+	@: > android/client/$(DEPDIR)/$(am__dirstamp)
+android/client/android_haltest-haltest.$(OBJEXT):  \
+	android/client/$(am__dirstamp) \
+	android/client/$(DEPDIR)/$(am__dirstamp)
+android/client/android_haltest-pollhandler.$(OBJEXT):  \
+	android/client/$(am__dirstamp) \
+	android/client/$(DEPDIR)/$(am__dirstamp)
+android/client/android_haltest-terminal.$(OBJEXT):  \
+	android/client/$(am__dirstamp) \
+	android/client/$(DEPDIR)/$(am__dirstamp)
+android/client/android_haltest-history.$(OBJEXT):  \
+	android/client/$(am__dirstamp) \
+	android/client/$(DEPDIR)/$(am__dirstamp)
+android/client/android_haltest-tabcompletion.$(OBJEXT):  \
+	android/client/$(am__dirstamp) \
+	android/client/$(DEPDIR)/$(am__dirstamp)
+android/client/android_haltest-if-av.$(OBJEXT):  \
+	android/client/$(am__dirstamp) \
+	android/client/$(DEPDIR)/$(am__dirstamp)
+android/client/android_haltest-if-rc.$(OBJEXT):  \
+	android/client/$(am__dirstamp) \
+	android/client/$(DEPDIR)/$(am__dirstamp)
+android/client/android_haltest-if-bt.$(OBJEXT):  \
+	android/client/$(am__dirstamp) \
+	android/client/$(DEPDIR)/$(am__dirstamp)
+android/client/android_haltest-if-gatt.$(OBJEXT):  \
+	android/client/$(am__dirstamp) \
+	android/client/$(DEPDIR)/$(am__dirstamp)
+android/client/android_haltest-if-hf.$(OBJEXT):  \
+	android/client/$(am__dirstamp) \
+	android/client/$(DEPDIR)/$(am__dirstamp)
+android/client/android_haltest-if-hh.$(OBJEXT):  \
+	android/client/$(am__dirstamp) \
+	android/client/$(DEPDIR)/$(am__dirstamp)
+android/client/android_haltest-if-pan.$(OBJEXT):  \
+	android/client/$(am__dirstamp) \
+	android/client/$(DEPDIR)/$(am__dirstamp)
+android/client/android_haltest-if-sock.$(OBJEXT):  \
+	android/client/$(am__dirstamp) \
+	android/client/$(DEPDIR)/$(am__dirstamp)
+android/client/android_haltest-if-audio.$(OBJEXT):  \
+	android/client/$(am__dirstamp) \
+	android/client/$(DEPDIR)/$(am__dirstamp)
+android/hardware/android_haltest-hardware.$(OBJEXT):  \
+	android/hardware/$(am__dirstamp) \
+	android/hardware/$(DEPDIR)/$(am__dirstamp)
+android/android_haltest-hal-utils.$(OBJEXT): android/$(am__dirstamp) \
+	android/$(DEPDIR)/$(am__dirstamp)
+
+android/haltest$(EXEEXT): $(android_haltest_OBJECTS) $(android_haltest_DEPENDENCIES) $(EXTRA_android_haltest_DEPENDENCIES) android/$(am__dirstamp)
+	@rm -f android/haltest$(EXEEXT)
+	$(AM_V_CCLD)$(android_haltest_LINK) $(android_haltest_OBJECTS) $(android_haltest_LDADD) $(LIBS)
+emulator/android_ipc_tester-btdev.$(OBJEXT): emulator/$(am__dirstamp) \
+	emulator/$(DEPDIR)/$(am__dirstamp)
+emulator/android_ipc_tester-bthost.$(OBJEXT):  \
+	emulator/$(am__dirstamp) emulator/$(DEPDIR)/$(am__dirstamp)
+emulator/android_ipc_tester-smp.$(OBJEXT): emulator/$(am__dirstamp) \
+	emulator/$(DEPDIR)/$(am__dirstamp)
+src/shared/android_ipc_tester-crypto.$(OBJEXT):  \
+	src/shared/$(am__dirstamp) \
+	src/shared/$(DEPDIR)/$(am__dirstamp)
+src/shared/android_ipc_tester-io-glib.$(OBJEXT):  \
+	src/shared/$(am__dirstamp) \
+	src/shared/$(DEPDIR)/$(am__dirstamp)
+src/shared/android_ipc_tester-queue.$(OBJEXT):  \
+	src/shared/$(am__dirstamp) \
+	src/shared/$(DEPDIR)/$(am__dirstamp)
+src/shared/android_ipc_tester-util.$(OBJEXT):  \
+	src/shared/$(am__dirstamp) \
+	src/shared/$(DEPDIR)/$(am__dirstamp)
+src/shared/android_ipc_tester-mgmt.$(OBJEXT):  \
+	src/shared/$(am__dirstamp) \
+	src/shared/$(DEPDIR)/$(am__dirstamp)
+src/shared/android_ipc_tester-hciemu.$(OBJEXT):  \
+	src/shared/$(am__dirstamp) \
+	src/shared/$(DEPDIR)/$(am__dirstamp)
+src/shared/android_ipc_tester-tester.$(OBJEXT):  \
+	src/shared/$(am__dirstamp) \
+	src/shared/$(DEPDIR)/$(am__dirstamp)
+src/shared/android_ipc_tester-timeout-glib.$(OBJEXT):  \
+	src/shared/$(am__dirstamp) \
+	src/shared/$(DEPDIR)/$(am__dirstamp)
+android/android_ipc_tester-hal-utils.$(OBJEXT):  \
+	android/$(am__dirstamp) android/$(DEPDIR)/$(am__dirstamp)
+android/android_ipc_tester-ipc-tester.$(OBJEXT):  \
+	android/$(am__dirstamp) android/$(DEPDIR)/$(am__dirstamp)
+
+android/ipc-tester$(EXEEXT): $(android_ipc_tester_OBJECTS) $(android_ipc_tester_DEPENDENCIES) $(EXTRA_android_ipc_tester_DEPENDENCIES) android/$(am__dirstamp)
+	@rm -f android/ipc-tester$(EXEEXT)
+	$(AM_V_CCLD)$(android_ipc_tester_LINK) $(android_ipc_tester_OBJECTS) $(android_ipc_tester_LDADD) $(LIBS)
+android/system-emulator.$(OBJEXT): android/$(am__dirstamp) \
+	android/$(DEPDIR)/$(am__dirstamp)
+
+android/system-emulator$(EXEEXT): $(android_system_emulator_OBJECTS) $(android_system_emulator_DEPENDENCIES) $(EXTRA_android_system_emulator_DEPENDENCIES) android/$(am__dirstamp)
+	@rm -f android/system-emulator$(EXEEXT)
+	$(AM_V_CCLD)$(LINK) $(android_system_emulator_OBJECTS) $(android_system_emulator_LDADD) $(LIBS)
+android/test-ipc.$(OBJEXT): android/$(am__dirstamp) \
+	android/$(DEPDIR)/$(am__dirstamp)
+
+android/test-ipc$(EXEEXT): $(android_test_ipc_OBJECTS) $(android_test_ipc_DEPENDENCIES) $(EXTRA_android_test_ipc_DEPENDENCIES) android/$(am__dirstamp)
+	@rm -f android/test-ipc$(EXEEXT)
+	$(AM_V_CCLD)$(LINK) $(android_test_ipc_OBJECTS) $(android_test_ipc_LDADD) $(LIBS)
+attrib/gatttool.$(OBJEXT): attrib/$(am__dirstamp) \
+	attrib/$(DEPDIR)/$(am__dirstamp)
+attrib/interactive.$(OBJEXT): attrib/$(am__dirstamp) \
+	attrib/$(DEPDIR)/$(am__dirstamp)
+attrib/utils.$(OBJEXT): attrib/$(am__dirstamp) \
+	attrib/$(DEPDIR)/$(am__dirstamp)
+client/$(am__dirstamp):
+	@$(MKDIR_P) client
+	@: > client/$(am__dirstamp)
+client/$(DEPDIR)/$(am__dirstamp):
+	@$(MKDIR_P) client/$(DEPDIR)
+	@: > client/$(DEPDIR)/$(am__dirstamp)
+client/display.$(OBJEXT): client/$(am__dirstamp) \
+	client/$(DEPDIR)/$(am__dirstamp)
+
+attrib/gatttool$(EXEEXT): $(attrib_gatttool_OBJECTS) $(attrib_gatttool_DEPENDENCIES) $(EXTRA_attrib_gatttool_DEPENDENCIES) attrib/$(am__dirstamp)
+	@rm -f attrib/gatttool$(EXEEXT)
+	$(AM_V_CCLD)$(LINK) $(attrib_gatttool_OBJECTS) $(attrib_gatttool_LDADD) $(LIBS)
+client/main.$(OBJEXT): client/$(am__dirstamp) \
+	client/$(DEPDIR)/$(am__dirstamp)
+client/agent.$(OBJEXT): client/$(am__dirstamp) \
+	client/$(DEPDIR)/$(am__dirstamp)
+monitor/uuid.$(OBJEXT): monitor/$(am__dirstamp) \
+	monitor/$(DEPDIR)/$(am__dirstamp)
+
+client/bluetoothctl$(EXEEXT): $(client_bluetoothctl_OBJECTS) $(client_bluetoothctl_DEPENDENCIES) $(EXTRA_client_bluetoothctl_DEPENDENCIES) client/$(am__dirstamp)
+	@rm -f client/bluetoothctl$(EXEEXT)
+	$(AM_V_CCLD)$(LINK) $(client_bluetoothctl_OBJECTS) $(client_bluetoothctl_LDADD) $(LIBS)
+emulator/b1ee.$(OBJEXT): emulator/$(am__dirstamp) \
+	emulator/$(DEPDIR)/$(am__dirstamp)
+
+emulator/b1ee$(EXEEXT): $(emulator_b1ee_OBJECTS) $(emulator_b1ee_DEPENDENCIES) $(EXTRA_emulator_b1ee_DEPENDENCIES) emulator/$(am__dirstamp)
+	@rm -f emulator/b1ee$(EXEEXT)
+	$(AM_V_CCLD)$(LINK) $(emulator_b1ee_OBJECTS) $(emulator_b1ee_LDADD) $(LIBS)
+emulator/main.$(OBJEXT): emulator/$(am__dirstamp) \
+	emulator/$(DEPDIR)/$(am__dirstamp)
+src/shared/timeout-mainloop.$(OBJEXT): src/shared/$(am__dirstamp) \
+	src/shared/$(DEPDIR)/$(am__dirstamp)
+src/shared/crypto.$(OBJEXT): src/shared/$(am__dirstamp) \
+	src/shared/$(DEPDIR)/$(am__dirstamp)
+emulator/server.$(OBJEXT): emulator/$(am__dirstamp) \
+	emulator/$(DEPDIR)/$(am__dirstamp)
+emulator/vhci.$(OBJEXT): emulator/$(am__dirstamp) \
+	emulator/$(DEPDIR)/$(am__dirstamp)
+emulator/btdev.$(OBJEXT): emulator/$(am__dirstamp) \
+	emulator/$(DEPDIR)/$(am__dirstamp)
+emulator/bthost.$(OBJEXT): emulator/$(am__dirstamp) \
+	emulator/$(DEPDIR)/$(am__dirstamp)
+emulator/smp.$(OBJEXT): emulator/$(am__dirstamp) \
+	emulator/$(DEPDIR)/$(am__dirstamp)
+emulator/amp.$(OBJEXT): emulator/$(am__dirstamp) \
+	emulator/$(DEPDIR)/$(am__dirstamp)
+emulator/le.$(OBJEXT): emulator/$(am__dirstamp) \
+	emulator/$(DEPDIR)/$(am__dirstamp)
+
+emulator/btvirt$(EXEEXT): $(emulator_btvirt_OBJECTS) $(emulator_btvirt_DEPENDENCIES) $(EXTRA_emulator_btvirt_DEPENDENCIES) emulator/$(am__dirstamp)
+	@rm -f emulator/btvirt$(EXEEXT)
+	$(AM_V_CCLD)$(LINK) $(emulator_btvirt_OBJECTS) $(emulator_btvirt_LDADD) $(LIBS)
+emulator/hfp.$(OBJEXT): emulator/$(am__dirstamp) \
+	emulator/$(DEPDIR)/$(am__dirstamp)
+src/shared/io-mainloop.$(OBJEXT): src/shared/$(am__dirstamp) \
+	src/shared/$(DEPDIR)/$(am__dirstamp)
+
+emulator/hfp$(EXEEXT): $(emulator_hfp_OBJECTS) $(emulator_hfp_DEPENDENCIES) $(EXTRA_emulator_hfp_DEPENDENCIES) emulator/$(am__dirstamp)
+	@rm -f emulator/hfp$(EXEEXT)
+	$(AM_V_CCLD)$(LINK) $(emulator_hfp_OBJECTS) $(emulator_hfp_LDADD) $(LIBS)
+monitor/main.$(OBJEXT): monitor/$(am__dirstamp) \
+	monitor/$(DEPDIR)/$(am__dirstamp)
+monitor/display.$(OBJEXT): monitor/$(am__dirstamp) \
+	monitor/$(DEPDIR)/$(am__dirstamp)
+monitor/hcidump.$(OBJEXT): monitor/$(am__dirstamp) \
+	monitor/$(DEPDIR)/$(am__dirstamp)
+monitor/ellisys.$(OBJEXT): monitor/$(am__dirstamp) \
+	monitor/$(DEPDIR)/$(am__dirstamp)
+monitor/control.$(OBJEXT): monitor/$(am__dirstamp) \
+	monitor/$(DEPDIR)/$(am__dirstamp)
+monitor/packet.$(OBJEXT): monitor/$(am__dirstamp) \
+	monitor/$(DEPDIR)/$(am__dirstamp)
+monitor/vendor.$(OBJEXT): monitor/$(am__dirstamp) \
+	monitor/$(DEPDIR)/$(am__dirstamp)
+monitor/lmp.$(OBJEXT): monitor/$(am__dirstamp) \
+	monitor/$(DEPDIR)/$(am__dirstamp)
+monitor/crc.$(OBJEXT): monitor/$(am__dirstamp) \
+	monitor/$(DEPDIR)/$(am__dirstamp)
+monitor/ll.$(OBJEXT): monitor/$(am__dirstamp) \
+	monitor/$(DEPDIR)/$(am__dirstamp)
+monitor/l2cap.$(OBJEXT): monitor/$(am__dirstamp) \
+	monitor/$(DEPDIR)/$(am__dirstamp)
+monitor/sdp.$(OBJEXT): monitor/$(am__dirstamp) \
+	monitor/$(DEPDIR)/$(am__dirstamp)
+monitor/hwdb.$(OBJEXT): monitor/$(am__dirstamp) \
+	monitor/$(DEPDIR)/$(am__dirstamp)
+monitor/keys.$(OBJEXT): monitor/$(am__dirstamp) \
+	monitor/$(DEPDIR)/$(am__dirstamp)
+monitor/analyze.$(OBJEXT): monitor/$(am__dirstamp) \
+	monitor/$(DEPDIR)/$(am__dirstamp)
+
+monitor/btmon$(EXEEXT): $(monitor_btmon_OBJECTS) $(monitor_btmon_DEPENDENCIES) $(EXTRA_monitor_btmon_DEPENDENCIES) monitor/$(am__dirstamp)
+	@rm -f monitor/btmon$(EXEEXT)
+	$(AM_V_CCLD)$(LINK) $(monitor_btmon_OBJECTS) $(monitor_btmon_LDADD) $(LIBS)
+btio/obexd-btio.$(OBJEXT): btio/$(am__dirstamp) \
+	btio/$(DEPDIR)/$(am__dirstamp)
+gobex/$(am__dirstamp):
+	@$(MKDIR_P) gobex
+	@: > gobex/$(am__dirstamp)
+gobex/$(DEPDIR)/$(am__dirstamp):
+	@$(MKDIR_P) gobex/$(DEPDIR)
+	@: > gobex/$(DEPDIR)/$(am__dirstamp)
+gobex/obexd-gobex.$(OBJEXT): gobex/$(am__dirstamp) \
+	gobex/$(DEPDIR)/$(am__dirstamp)
+gobex/obexd-gobex-defs.$(OBJEXT): gobex/$(am__dirstamp) \
+	gobex/$(DEPDIR)/$(am__dirstamp)
+gobex/obexd-gobex-packet.$(OBJEXT): gobex/$(am__dirstamp) \
+	gobex/$(DEPDIR)/$(am__dirstamp)
+gobex/obexd-gobex-header.$(OBJEXT): gobex/$(am__dirstamp) \
+	gobex/$(DEPDIR)/$(am__dirstamp)
+gobex/obexd-gobex-transfer.$(OBJEXT): gobex/$(am__dirstamp) \
+	gobex/$(DEPDIR)/$(am__dirstamp)
+gobex/obexd-gobex-apparam.$(OBJEXT): gobex/$(am__dirstamp) \
+	gobex/$(DEPDIR)/$(am__dirstamp)
+obexd/plugins/$(am__dirstamp):
+	@$(MKDIR_P) obexd/plugins
+	@: > obexd/plugins/$(am__dirstamp)
+obexd/plugins/$(DEPDIR)/$(am__dirstamp):
+	@$(MKDIR_P) obexd/plugins/$(DEPDIR)
+	@: > obexd/plugins/$(DEPDIR)/$(am__dirstamp)
+obexd/plugins/obexd-filesystem.$(OBJEXT):  \
+	obexd/plugins/$(am__dirstamp) \
+	obexd/plugins/$(DEPDIR)/$(am__dirstamp)
+obexd/plugins/obexd-bluetooth.$(OBJEXT):  \
+	obexd/plugins/$(am__dirstamp) \
+	obexd/plugins/$(DEPDIR)/$(am__dirstamp)
+obexd/plugins/obexd-pcsuite.$(OBJEXT): obexd/plugins/$(am__dirstamp) \
+	obexd/plugins/$(DEPDIR)/$(am__dirstamp)
+obexd/plugins/obexd-opp.$(OBJEXT): obexd/plugins/$(am__dirstamp) \
+	obexd/plugins/$(DEPDIR)/$(am__dirstamp)
+obexd/plugins/obexd-ftp.$(OBJEXT): obexd/plugins/$(am__dirstamp) \
+	obexd/plugins/$(DEPDIR)/$(am__dirstamp)
+obexd/plugins/obexd-irmc.$(OBJEXT): obexd/plugins/$(am__dirstamp) \
+	obexd/plugins/$(DEPDIR)/$(am__dirstamp)
+obexd/plugins/obexd-pbap.$(OBJEXT): obexd/plugins/$(am__dirstamp) \
+	obexd/plugins/$(DEPDIR)/$(am__dirstamp)
+obexd/plugins/obexd-vcard.$(OBJEXT): obexd/plugins/$(am__dirstamp) \
+	obexd/plugins/$(DEPDIR)/$(am__dirstamp)
+obexd/plugins/obexd-phonebook-dummy.$(OBJEXT):  \
+	obexd/plugins/$(am__dirstamp) \
+	obexd/plugins/$(DEPDIR)/$(am__dirstamp)
+obexd/plugins/obexd-mas.$(OBJEXT): obexd/plugins/$(am__dirstamp) \
+	obexd/plugins/$(DEPDIR)/$(am__dirstamp)
+obexd/plugins/obexd-messages-dummy.$(OBJEXT):  \
+	obexd/plugins/$(am__dirstamp) \
+	obexd/plugins/$(DEPDIR)/$(am__dirstamp)
+obexd/client/$(am__dirstamp):
+	@$(MKDIR_P) obexd/client
+	@: > obexd/client/$(am__dirstamp)
+obexd/client/$(DEPDIR)/$(am__dirstamp):
+	@$(MKDIR_P) obexd/client/$(DEPDIR)
+	@: > obexd/client/$(DEPDIR)/$(am__dirstamp)
+obexd/client/obexd-mns.$(OBJEXT): obexd/client/$(am__dirstamp) \
+	obexd/client/$(DEPDIR)/$(am__dirstamp)
+obexd/src/$(am__dirstamp):
+	@$(MKDIR_P) obexd/src
+	@: > obexd/src/$(am__dirstamp)
+obexd/src/$(DEPDIR)/$(am__dirstamp):
+	@$(MKDIR_P) obexd/src/$(DEPDIR)
+	@: > obexd/src/$(DEPDIR)/$(am__dirstamp)
+obexd/src/obexd-main.$(OBJEXT): obexd/src/$(am__dirstamp) \
+	obexd/src/$(DEPDIR)/$(am__dirstamp)
+obexd/src/obexd-plugin.$(OBJEXT): obexd/src/$(am__dirstamp) \
+	obexd/src/$(DEPDIR)/$(am__dirstamp)
+obexd/src/obexd-log.$(OBJEXT): obexd/src/$(am__dirstamp) \
+	obexd/src/$(DEPDIR)/$(am__dirstamp)
+obexd/src/obexd-manager.$(OBJEXT): obexd/src/$(am__dirstamp) \
+	obexd/src/$(DEPDIR)/$(am__dirstamp)
+obexd/src/obexd-obex.$(OBJEXT): obexd/src/$(am__dirstamp) \
+	obexd/src/$(DEPDIR)/$(am__dirstamp)
+obexd/src/obexd-mimetype.$(OBJEXT): obexd/src/$(am__dirstamp) \
+	obexd/src/$(DEPDIR)/$(am__dirstamp)
+obexd/src/obexd-service.$(OBJEXT): obexd/src/$(am__dirstamp) \
+	obexd/src/$(DEPDIR)/$(am__dirstamp)
+obexd/src/obexd-transport.$(OBJEXT): obexd/src/$(am__dirstamp) \
+	obexd/src/$(DEPDIR)/$(am__dirstamp)
+obexd/src/obexd-server.$(OBJEXT): obexd/src/$(am__dirstamp) \
+	obexd/src/$(DEPDIR)/$(am__dirstamp)
+obexd/client/obexd-manager.$(OBJEXT): obexd/client/$(am__dirstamp) \
+	obexd/client/$(DEPDIR)/$(am__dirstamp)
+obexd/client/obexd-session.$(OBJEXT): obexd/client/$(am__dirstamp) \
+	obexd/client/$(DEPDIR)/$(am__dirstamp)
+obexd/client/obexd-bluetooth.$(OBJEXT): obexd/client/$(am__dirstamp) \
+	obexd/client/$(DEPDIR)/$(am__dirstamp)
+obexd/client/obexd-sync.$(OBJEXT): obexd/client/$(am__dirstamp) \
+	obexd/client/$(DEPDIR)/$(am__dirstamp)
+obexd/client/obexd-pbap.$(OBJEXT): obexd/client/$(am__dirstamp) \
+	obexd/client/$(DEPDIR)/$(am__dirstamp)
+obexd/client/obexd-ftp.$(OBJEXT): obexd/client/$(am__dirstamp) \
+	obexd/client/$(DEPDIR)/$(am__dirstamp)
+obexd/client/obexd-opp.$(OBJEXT): obexd/client/$(am__dirstamp) \
+	obexd/client/$(DEPDIR)/$(am__dirstamp)
+obexd/client/obexd-map.$(OBJEXT): obexd/client/$(am__dirstamp) \
+	obexd/client/$(DEPDIR)/$(am__dirstamp)
+obexd/client/obexd-map-event.$(OBJEXT): obexd/client/$(am__dirstamp) \
+	obexd/client/$(DEPDIR)/$(am__dirstamp)
+obexd/client/obexd-transfer.$(OBJEXT): obexd/client/$(am__dirstamp) \
+	obexd/client/$(DEPDIR)/$(am__dirstamp)
+obexd/client/obexd-transport.$(OBJEXT): obexd/client/$(am__dirstamp) \
+	obexd/client/$(DEPDIR)/$(am__dirstamp)
+obexd/client/obexd-dbus.$(OBJEXT): obexd/client/$(am__dirstamp) \
+	obexd/client/$(DEPDIR)/$(am__dirstamp)
+obexd/client/obexd-driver.$(OBJEXT): obexd/client/$(am__dirstamp) \
+	obexd/client/$(DEPDIR)/$(am__dirstamp)
+
+obexd/src/obexd$(EXEEXT): $(obexd_src_obexd_OBJECTS) $(obexd_src_obexd_DEPENDENCIES) $(EXTRA_obexd_src_obexd_DEPENDENCIES) obexd/src/$(am__dirstamp)
+	@rm -f obexd/src/obexd$(EXEEXT)
+	$(AM_V_CCLD)$(obexd_src_obexd_LINK) $(obexd_src_obexd_OBJECTS) $(obexd_src_obexd_LDADD) $(LIBS)
+profiles/cups/$(am__dirstamp):
+	@$(MKDIR_P) profiles/cups
+	@: > profiles/cups/$(am__dirstamp)
+profiles/cups/$(DEPDIR)/$(am__dirstamp):
+	@$(MKDIR_P) profiles/cups/$(DEPDIR)
+	@: > profiles/cups/$(DEPDIR)/$(am__dirstamp)
+profiles/cups/main.$(OBJEXT): profiles/cups/$(am__dirstamp) \
+	profiles/cups/$(DEPDIR)/$(am__dirstamp)
+profiles/cups/sdp.$(OBJEXT): profiles/cups/$(am__dirstamp) \
+	profiles/cups/$(DEPDIR)/$(am__dirstamp)
+profiles/cups/spp.$(OBJEXT): profiles/cups/$(am__dirstamp) \
+	profiles/cups/$(DEPDIR)/$(am__dirstamp)
+profiles/cups/hcrp.$(OBJEXT): profiles/cups/$(am__dirstamp) \
+	profiles/cups/$(DEPDIR)/$(am__dirstamp)
+
+profiles/cups/bluetooth$(EXEEXT): $(profiles_cups_bluetooth_OBJECTS) $(profiles_cups_bluetooth_DEPENDENCIES) $(EXTRA_profiles_cups_bluetooth_DEPENDENCIES) profiles/cups/$(am__dirstamp)
+	@rm -f profiles/cups/bluetooth$(EXEEXT)
+	$(AM_V_CCLD)$(LINK) $(profiles_cups_bluetooth_OBJECTS) $(profiles_cups_bluetooth_LDADD) $(LIBS)
+profiles/iap/$(am__dirstamp):
+	@$(MKDIR_P) profiles/iap
+	@: > profiles/iap/$(am__dirstamp)
+profiles/iap/$(DEPDIR)/$(am__dirstamp):
+	@$(MKDIR_P) profiles/iap/$(DEPDIR)
+	@: > profiles/iap/$(DEPDIR)/$(am__dirstamp)
+profiles/iap/main.$(OBJEXT): profiles/iap/$(am__dirstamp) \
+	profiles/iap/$(DEPDIR)/$(am__dirstamp)
+
+profiles/iap/iapd$(EXEEXT): $(profiles_iap_iapd_OBJECTS) $(profiles_iap_iapd_DEPENDENCIES) $(EXTRA_profiles_iap_iapd_DEPENDENCIES) profiles/iap/$(am__dirstamp)
+	@rm -f profiles/iap/iapd$(EXEEXT)
+	$(AM_V_CCLD)$(LINK) $(profiles_iap_iapd_OBJECTS) $(profiles_iap_iapd_LDADD) $(LIBS)
+plugins/bluetoothd-hostname.$(OBJEXT): plugins/$(am__dirstamp) \
+	plugins/$(DEPDIR)/$(am__dirstamp)
+plugins/bluetoothd-wiimote.$(OBJEXT): plugins/$(am__dirstamp) \
+	plugins/$(DEPDIR)/$(am__dirstamp)
+plugins/bluetoothd-autopair.$(OBJEXT): plugins/$(am__dirstamp) \
+	plugins/$(DEPDIR)/$(am__dirstamp)
+plugins/bluetoothd-dropcam.$(OBJEXT): plugins/$(am__dirstamp) \
+	plugins/$(DEPDIR)/$(am__dirstamp)
+plugins/bluetoothd-policy.$(OBJEXT): plugins/$(am__dirstamp) \
+	plugins/$(DEPDIR)/$(am__dirstamp)
+plugins/bluetoothd-gatt-example.$(OBJEXT): plugins/$(am__dirstamp) \
+	plugins/$(DEPDIR)/$(am__dirstamp)
+plugins/bluetoothd-neard.$(OBJEXT): plugins/$(am__dirstamp) \
+	plugins/$(DEPDIR)/$(am__dirstamp)
+profiles/sap/bluetoothd-main.$(OBJEXT): profiles/sap/$(am__dirstamp) \
+	profiles/sap/$(DEPDIR)/$(am__dirstamp)
+profiles/sap/bluetoothd-manager.$(OBJEXT):  \
+	profiles/sap/$(am__dirstamp) \
+	profiles/sap/$(DEPDIR)/$(am__dirstamp)
+profiles/sap/bluetoothd-server.$(OBJEXT):  \
+	profiles/sap/$(am__dirstamp) \
+	profiles/sap/$(DEPDIR)/$(am__dirstamp)
+profiles/sap/bluetoothd-sap-dummy.$(OBJEXT):  \
+	profiles/sap/$(am__dirstamp) \
+	profiles/sap/$(DEPDIR)/$(am__dirstamp)
+profiles/audio/$(am__dirstamp):
+	@$(MKDIR_P) profiles/audio
+	@: > profiles/audio/$(am__dirstamp)
+profiles/audio/$(DEPDIR)/$(am__dirstamp):
+	@$(MKDIR_P) profiles/audio/$(DEPDIR)
+	@: > profiles/audio/$(DEPDIR)/$(am__dirstamp)
+profiles/audio/bluetoothd-source.$(OBJEXT):  \
+	profiles/audio/$(am__dirstamp) \
+	profiles/audio/$(DEPDIR)/$(am__dirstamp)
+profiles/audio/bluetoothd-sink.$(OBJEXT):  \
+	profiles/audio/$(am__dirstamp) \
+	profiles/audio/$(DEPDIR)/$(am__dirstamp)
+profiles/audio/bluetoothd-a2dp.$(OBJEXT):  \
+	profiles/audio/$(am__dirstamp) \
+	profiles/audio/$(DEPDIR)/$(am__dirstamp)
+profiles/audio/bluetoothd-avdtp.$(OBJEXT):  \
+	profiles/audio/$(am__dirstamp) \
+	profiles/audio/$(DEPDIR)/$(am__dirstamp)
+profiles/audio/bluetoothd-media.$(OBJEXT):  \
+	profiles/audio/$(am__dirstamp) \
+	profiles/audio/$(DEPDIR)/$(am__dirstamp)
+profiles/audio/bluetoothd-transport.$(OBJEXT):  \
+	profiles/audio/$(am__dirstamp) \
+	profiles/audio/$(DEPDIR)/$(am__dirstamp)
+profiles/audio/bluetoothd-control.$(OBJEXT):  \
+	profiles/audio/$(am__dirstamp) \
+	profiles/audio/$(DEPDIR)/$(am__dirstamp)
+profiles/audio/bluetoothd-avctp.$(OBJEXT):  \
+	profiles/audio/$(am__dirstamp) \
+	profiles/audio/$(DEPDIR)/$(am__dirstamp)
+profiles/audio/bluetoothd-avrcp.$(OBJEXT):  \
+	profiles/audio/$(am__dirstamp) \
+	profiles/audio/$(DEPDIR)/$(am__dirstamp)
+profiles/audio/bluetoothd-player.$(OBJEXT):  \
+	profiles/audio/$(am__dirstamp) \
+	profiles/audio/$(DEPDIR)/$(am__dirstamp)
+profiles/network/bluetoothd-manager.$(OBJEXT):  \
+	profiles/network/$(am__dirstamp) \
+	profiles/network/$(DEPDIR)/$(am__dirstamp)
+profiles/network/bluetoothd-bnep.$(OBJEXT):  \
+	profiles/network/$(am__dirstamp) \
+	profiles/network/$(DEPDIR)/$(am__dirstamp)
+profiles/network/bluetoothd-server.$(OBJEXT):  \
+	profiles/network/$(am__dirstamp) \
+	profiles/network/$(DEPDIR)/$(am__dirstamp)
+profiles/network/bluetoothd-connection.$(OBJEXT):  \
+	profiles/network/$(am__dirstamp) \
+	profiles/network/$(DEPDIR)/$(am__dirstamp)
+profiles/input/$(am__dirstamp):
+	@$(MKDIR_P) profiles/input
+	@: > profiles/input/$(am__dirstamp)
+profiles/input/$(DEPDIR)/$(am__dirstamp):
+	@$(MKDIR_P) profiles/input/$(DEPDIR)
+	@: > profiles/input/$(DEPDIR)/$(am__dirstamp)
+profiles/input/bluetoothd-manager.$(OBJEXT):  \
+	profiles/input/$(am__dirstamp) \
+	profiles/input/$(DEPDIR)/$(am__dirstamp)
+profiles/input/bluetoothd-server.$(OBJEXT):  \
+	profiles/input/$(am__dirstamp) \
+	profiles/input/$(DEPDIR)/$(am__dirstamp)
+profiles/input/bluetoothd-device.$(OBJEXT):  \
+	profiles/input/$(am__dirstamp) \
+	profiles/input/$(DEPDIR)/$(am__dirstamp)
+profiles/input/bluetoothd-hog.$(OBJEXT):  \
+	profiles/input/$(am__dirstamp) \
+	profiles/input/$(DEPDIR)/$(am__dirstamp)
+profiles/input/bluetoothd-suspend-dummy.$(OBJEXT):  \
+	profiles/input/$(am__dirstamp) \
+	profiles/input/$(DEPDIR)/$(am__dirstamp)
+profiles/health/$(am__dirstamp):
+	@$(MKDIR_P) profiles/health
+	@: > profiles/health/$(am__dirstamp)
+profiles/health/$(DEPDIR)/$(am__dirstamp):
+	@$(MKDIR_P) profiles/health/$(DEPDIR)
+	@: > profiles/health/$(DEPDIR)/$(am__dirstamp)
+profiles/health/bluetoothd-mcap.$(OBJEXT):  \
+	profiles/health/$(am__dirstamp) \
+	profiles/health/$(DEPDIR)/$(am__dirstamp)
+profiles/health/bluetoothd-mcap_sync.$(OBJEXT):  \
+	profiles/health/$(am__dirstamp) \
+	profiles/health/$(DEPDIR)/$(am__dirstamp)
+profiles/health/bluetoothd-hdp_main.$(OBJEXT):  \
+	profiles/health/$(am__dirstamp) \
+	profiles/health/$(DEPDIR)/$(am__dirstamp)
+profiles/health/bluetoothd-hdp_manager.$(OBJEXT):  \
+	profiles/health/$(am__dirstamp) \
+	profiles/health/$(DEPDIR)/$(am__dirstamp)
+profiles/health/bluetoothd-hdp.$(OBJEXT):  \
+	profiles/health/$(am__dirstamp) \
+	profiles/health/$(DEPDIR)/$(am__dirstamp)
+profiles/health/bluetoothd-hdp_util.$(OBJEXT):  \
+	profiles/health/$(am__dirstamp) \
+	profiles/health/$(DEPDIR)/$(am__dirstamp)
+profiles/gatt/$(am__dirstamp):
+	@$(MKDIR_P) profiles/gatt
+	@: > profiles/gatt/$(am__dirstamp)
+profiles/gatt/$(DEPDIR)/$(am__dirstamp):
+	@$(MKDIR_P) profiles/gatt/$(DEPDIR)
+	@: > profiles/gatt/$(DEPDIR)/$(am__dirstamp)
+profiles/gatt/bluetoothd-gas.$(OBJEXT): profiles/gatt/$(am__dirstamp) \
+	profiles/gatt/$(DEPDIR)/$(am__dirstamp)
+profiles/scanparam/$(am__dirstamp):
+	@$(MKDIR_P) profiles/scanparam
+	@: > profiles/scanparam/$(am__dirstamp)
+profiles/scanparam/$(DEPDIR)/$(am__dirstamp):
+	@$(MKDIR_P) profiles/scanparam/$(DEPDIR)
+	@: > profiles/scanparam/$(DEPDIR)/$(am__dirstamp)
+profiles/scanparam/bluetoothd-scan.$(OBJEXT):  \
+	profiles/scanparam/$(am__dirstamp) \
+	profiles/scanparam/$(DEPDIR)/$(am__dirstamp)
+profiles/deviceinfo/$(am__dirstamp):
+	@$(MKDIR_P) profiles/deviceinfo
+	@: > profiles/deviceinfo/$(am__dirstamp)
+profiles/deviceinfo/$(DEPDIR)/$(am__dirstamp):
+	@$(MKDIR_P) profiles/deviceinfo/$(DEPDIR)
+	@: > profiles/deviceinfo/$(DEPDIR)/$(am__dirstamp)
+profiles/deviceinfo/bluetoothd-deviceinfo.$(OBJEXT):  \
+	profiles/deviceinfo/$(am__dirstamp) \
+	profiles/deviceinfo/$(DEPDIR)/$(am__dirstamp)
+profiles/alert/$(am__dirstamp):
+	@$(MKDIR_P) profiles/alert
+	@: > profiles/alert/$(am__dirstamp)
+profiles/alert/$(DEPDIR)/$(am__dirstamp):
+	@$(MKDIR_P) profiles/alert/$(DEPDIR)
+	@: > profiles/alert/$(DEPDIR)/$(am__dirstamp)
+profiles/alert/bluetoothd-server.$(OBJEXT):  \
+	profiles/alert/$(am__dirstamp) \
+	profiles/alert/$(DEPDIR)/$(am__dirstamp)
+profiles/time/$(am__dirstamp):
+	@$(MKDIR_P) profiles/time
+	@: > profiles/time/$(am__dirstamp)
+profiles/time/$(DEPDIR)/$(am__dirstamp):
+	@$(MKDIR_P) profiles/time/$(DEPDIR)
+	@: > profiles/time/$(DEPDIR)/$(am__dirstamp)
+profiles/time/bluetoothd-server.$(OBJEXT):  \
+	profiles/time/$(am__dirstamp) \
+	profiles/time/$(DEPDIR)/$(am__dirstamp)
+profiles/proximity/$(am__dirstamp):
+	@$(MKDIR_P) profiles/proximity
+	@: > profiles/proximity/$(am__dirstamp)
+profiles/proximity/$(DEPDIR)/$(am__dirstamp):
+	@$(MKDIR_P) profiles/proximity/$(DEPDIR)
+	@: > profiles/proximity/$(DEPDIR)/$(am__dirstamp)
+profiles/proximity/bluetoothd-main.$(OBJEXT):  \
+	profiles/proximity/$(am__dirstamp) \
+	profiles/proximity/$(DEPDIR)/$(am__dirstamp)
+profiles/proximity/bluetoothd-manager.$(OBJEXT):  \
+	profiles/proximity/$(am__dirstamp) \
+	profiles/proximity/$(DEPDIR)/$(am__dirstamp)
+profiles/proximity/bluetoothd-monitor.$(OBJEXT):  \
+	profiles/proximity/$(am__dirstamp) \
+	profiles/proximity/$(DEPDIR)/$(am__dirstamp)
+profiles/proximity/bluetoothd-reporter.$(OBJEXT):  \
+	profiles/proximity/$(am__dirstamp) \
+	profiles/proximity/$(DEPDIR)/$(am__dirstamp)
+profiles/proximity/bluetoothd-linkloss.$(OBJEXT):  \
+	profiles/proximity/$(am__dirstamp) \
+	profiles/proximity/$(DEPDIR)/$(am__dirstamp)
+profiles/proximity/bluetoothd-immalert.$(OBJEXT):  \
+	profiles/proximity/$(am__dirstamp) \
+	profiles/proximity/$(DEPDIR)/$(am__dirstamp)
+profiles/thermometer/$(am__dirstamp):
+	@$(MKDIR_P) profiles/thermometer
+	@: > profiles/thermometer/$(am__dirstamp)
+profiles/thermometer/$(DEPDIR)/$(am__dirstamp):
+	@$(MKDIR_P) profiles/thermometer/$(DEPDIR)
+	@: > profiles/thermometer/$(DEPDIR)/$(am__dirstamp)
+profiles/thermometer/bluetoothd-thermometer.$(OBJEXT):  \
+	profiles/thermometer/$(am__dirstamp) \
+	profiles/thermometer/$(DEPDIR)/$(am__dirstamp)
+profiles/heartrate/$(am__dirstamp):
+	@$(MKDIR_P) profiles/heartrate
+	@: > profiles/heartrate/$(am__dirstamp)
+profiles/heartrate/$(DEPDIR)/$(am__dirstamp):
+	@$(MKDIR_P) profiles/heartrate/$(DEPDIR)
+	@: > profiles/heartrate/$(DEPDIR)/$(am__dirstamp)
+profiles/heartrate/bluetoothd-heartrate.$(OBJEXT):  \
+	profiles/heartrate/$(am__dirstamp) \
+	profiles/heartrate/$(DEPDIR)/$(am__dirstamp)
+profiles/cyclingspeed/$(am__dirstamp):
+	@$(MKDIR_P) profiles/cyclingspeed
+	@: > profiles/cyclingspeed/$(am__dirstamp)
+profiles/cyclingspeed/$(DEPDIR)/$(am__dirstamp):
+	@$(MKDIR_P) profiles/cyclingspeed/$(DEPDIR)
+	@: > profiles/cyclingspeed/$(DEPDIR)/$(am__dirstamp)
+profiles/cyclingspeed/bluetoothd-cyclingspeed.$(OBJEXT):  \
+	profiles/cyclingspeed/$(am__dirstamp) \
+	profiles/cyclingspeed/$(DEPDIR)/$(am__dirstamp)
+attrib/bluetoothd-att.$(OBJEXT): attrib/$(am__dirstamp) \
+	attrib/$(DEPDIR)/$(am__dirstamp)
+attrib/bluetoothd-gatt.$(OBJEXT): attrib/$(am__dirstamp) \
+	attrib/$(DEPDIR)/$(am__dirstamp)
+attrib/bluetoothd-gattrib.$(OBJEXT): attrib/$(am__dirstamp) \
+	attrib/$(DEPDIR)/$(am__dirstamp)
+attrib/bluetoothd-gatt-service.$(OBJEXT): attrib/$(am__dirstamp) \
+	attrib/$(DEPDIR)/$(am__dirstamp)
+btio/bluetoothd-btio.$(OBJEXT): btio/$(am__dirstamp) \
+	btio/$(DEPDIR)/$(am__dirstamp)
+src/bluetoothd-main.$(OBJEXT): src/$(am__dirstamp) \
+	src/$(DEPDIR)/$(am__dirstamp)
+src/bluetoothd-log.$(OBJEXT): src/$(am__dirstamp) \
+	src/$(DEPDIR)/$(am__dirstamp)
+src/bluetoothd-systemd.$(OBJEXT): src/$(am__dirstamp) \
+	src/$(DEPDIR)/$(am__dirstamp)
+src/bluetoothd-rfkill.$(OBJEXT): src/$(am__dirstamp) \
+	src/$(DEPDIR)/$(am__dirstamp)
+src/bluetoothd-sdpd-server.$(OBJEXT): src/$(am__dirstamp) \
+	src/$(DEPDIR)/$(am__dirstamp)
+src/bluetoothd-sdpd-request.$(OBJEXT): src/$(am__dirstamp) \
+	src/$(DEPDIR)/$(am__dirstamp)
+src/bluetoothd-sdpd-service.$(OBJEXT): src/$(am__dirstamp) \
+	src/$(DEPDIR)/$(am__dirstamp)
+src/bluetoothd-sdpd-database.$(OBJEXT): src/$(am__dirstamp) \
+	src/$(DEPDIR)/$(am__dirstamp)
+src/bluetoothd-attrib-server.$(OBJEXT): src/$(am__dirstamp) \
+	src/$(DEPDIR)/$(am__dirstamp)
+src/bluetoothd-sdp-xml.$(OBJEXT): src/$(am__dirstamp) \
+	src/$(DEPDIR)/$(am__dirstamp)
+src/bluetoothd-sdp-client.$(OBJEXT): src/$(am__dirstamp) \
+	src/$(DEPDIR)/$(am__dirstamp)
+src/bluetoothd-textfile.$(OBJEXT): src/$(am__dirstamp) \
+	src/$(DEPDIR)/$(am__dirstamp)
+src/bluetoothd-uuid-helper.$(OBJEXT): src/$(am__dirstamp) \
+	src/$(DEPDIR)/$(am__dirstamp)
+src/bluetoothd-plugin.$(OBJEXT): src/$(am__dirstamp) \
+	src/$(DEPDIR)/$(am__dirstamp)
+src/bluetoothd-storage.$(OBJEXT): src/$(am__dirstamp) \
+	src/$(DEPDIR)/$(am__dirstamp)
+src/bluetoothd-agent.$(OBJEXT): src/$(am__dirstamp) \
+	src/$(DEPDIR)/$(am__dirstamp)
+src/bluetoothd-error.$(OBJEXT): src/$(am__dirstamp) \
+	src/$(DEPDIR)/$(am__dirstamp)
+src/bluetoothd-adapter.$(OBJEXT): src/$(am__dirstamp) \
+	src/$(DEPDIR)/$(am__dirstamp)
+src/bluetoothd-profile.$(OBJEXT): src/$(am__dirstamp) \
+	src/$(DEPDIR)/$(am__dirstamp)
+src/bluetoothd-service.$(OBJEXT): src/$(am__dirstamp) \
+	src/$(DEPDIR)/$(am__dirstamp)
+src/bluetoothd-gatt-dbus.$(OBJEXT): src/$(am__dirstamp) \
+	src/$(DEPDIR)/$(am__dirstamp)
+src/bluetoothd-gatt.$(OBJEXT): src/$(am__dirstamp) \
+	src/$(DEPDIR)/$(am__dirstamp)
+src/bluetoothd-device.$(OBJEXT): src/$(am__dirstamp) \
+	src/$(DEPDIR)/$(am__dirstamp)
+src/bluetoothd-dbus-common.$(OBJEXT): src/$(am__dirstamp) \
+	src/$(DEPDIR)/$(am__dirstamp)
+src/bluetoothd-eir.$(OBJEXT): src/$(am__dirstamp) \
+	src/$(DEPDIR)/$(am__dirstamp)
+src/shared/bluetoothd-io-glib.$(OBJEXT): src/shared/$(am__dirstamp) \
+	src/shared/$(DEPDIR)/$(am__dirstamp)
+src/shared/bluetoothd-timeout-glib.$(OBJEXT):  \
+	src/shared/$(am__dirstamp) \
+	src/shared/$(DEPDIR)/$(am__dirstamp)
+src/shared/bluetoothd-queue.$(OBJEXT): src/shared/$(am__dirstamp) \
+	src/shared/$(DEPDIR)/$(am__dirstamp)
+src/shared/bluetoothd-util.$(OBJEXT): src/shared/$(am__dirstamp) \
+	src/shared/$(DEPDIR)/$(am__dirstamp)
+src/shared/bluetoothd-mgmt.$(OBJEXT): src/shared/$(am__dirstamp) \
+	src/shared/$(DEPDIR)/$(am__dirstamp)
+
+src/bluetoothd$(EXEEXT): $(src_bluetoothd_OBJECTS) $(src_bluetoothd_DEPENDENCIES) $(EXTRA_src_bluetoothd_DEPENDENCIES) src/$(am__dirstamp)
+	@rm -f src/bluetoothd$(EXEEXT)
+	$(AM_V_CCLD)$(src_bluetoothd_LINK) $(src_bluetoothd_OBJECTS) $(src_bluetoothd_LDADD) $(LIBS)
+tools/$(am__dirstamp):
+	@$(MKDIR_P) tools
+	@: > tools/$(am__dirstamp)
+tools/$(DEPDIR)/$(am__dirstamp):
+	@$(MKDIR_P) tools/$(DEPDIR)
+	@: > tools/$(DEPDIR)/$(am__dirstamp)
+tools/3dsp.$(OBJEXT): tools/$(am__dirstamp) \
+	tools/$(DEPDIR)/$(am__dirstamp)
+src/shared/hci.$(OBJEXT): src/shared/$(am__dirstamp) \
+	src/shared/$(DEPDIR)/$(am__dirstamp)
+
+tools/3dsp$(EXEEXT): $(tools_3dsp_OBJECTS) $(tools_3dsp_DEPENDENCIES) $(EXTRA_tools_3dsp_DEPENDENCIES) tools/$(am__dirstamp)
+	@rm -f tools/3dsp$(EXEEXT)
+	$(AM_V_CCLD)$(LINK) $(tools_3dsp_OBJECTS) $(tools_3dsp_LDADD) $(LIBS)
+tools/amptest.$(OBJEXT): tools/$(am__dirstamp) \
+	tools/$(DEPDIR)/$(am__dirstamp)
+
+tools/amptest$(EXEEXT): $(tools_amptest_OBJECTS) $(tools_amptest_DEPENDENCIES) $(EXTRA_tools_amptest_DEPENDENCIES) tools/$(am__dirstamp)
+	@rm -f tools/amptest$(EXEEXT)
+	$(AM_V_CCLD)$(LINK) $(tools_amptest_OBJECTS) $(tools_amptest_LDADD) $(LIBS)
+tools/avinfo.$(OBJEXT): tools/$(am__dirstamp) \
+	tools/$(DEPDIR)/$(am__dirstamp)
+
+tools/avinfo$(EXEEXT): $(tools_avinfo_OBJECTS) $(tools_avinfo_DEPENDENCIES) $(EXTRA_tools_avinfo_DEPENDENCIES) tools/$(am__dirstamp)
+	@rm -f tools/avinfo$(EXEEXT)
+	$(AM_V_CCLD)$(LINK) $(tools_avinfo_OBJECTS) $(tools_avinfo_LDADD) $(LIBS)
+tools/avtest.$(OBJEXT): tools/$(am__dirstamp) \
+	tools/$(DEPDIR)/$(am__dirstamp)
+
+tools/avtest$(EXEEXT): $(tools_avtest_OBJECTS) $(tools_avtest_DEPENDENCIES) $(EXTRA_tools_avtest_DEPENDENCIES) tools/$(am__dirstamp)
+	@rm -f tools/avtest$(EXEEXT)
+	$(AM_V_CCLD)$(LINK) $(tools_avtest_OBJECTS) $(tools_avtest_LDADD) $(LIBS)
+tools/bccmd.$(OBJEXT): tools/$(am__dirstamp) \
+	tools/$(DEPDIR)/$(am__dirstamp)
+tools/csr.$(OBJEXT): tools/$(am__dirstamp) \
+	tools/$(DEPDIR)/$(am__dirstamp)
+tools/csr_hci.$(OBJEXT): tools/$(am__dirstamp) \
+	tools/$(DEPDIR)/$(am__dirstamp)
+tools/csr_usb.$(OBJEXT): tools/$(am__dirstamp) \
+	tools/$(DEPDIR)/$(am__dirstamp)
+tools/csr_h4.$(OBJEXT): tools/$(am__dirstamp) \
+	tools/$(DEPDIR)/$(am__dirstamp)
+tools/csr_3wire.$(OBJEXT): tools/$(am__dirstamp) \
+	tools/$(DEPDIR)/$(am__dirstamp)
+tools/csr_bcsp.$(OBJEXT): tools/$(am__dirstamp) \
+	tools/$(DEPDIR)/$(am__dirstamp)
+tools/ubcsp.$(OBJEXT): tools/$(am__dirstamp) \
+	tools/$(DEPDIR)/$(am__dirstamp)
+
+tools/bccmd$(EXEEXT): $(tools_bccmd_OBJECTS) $(tools_bccmd_DEPENDENCIES) $(EXTRA_tools_bccmd_DEPENDENCIES) tools/$(am__dirstamp)
+	@rm -f tools/bccmd$(EXEEXT)
+	$(AM_V_CCLD)$(LINK) $(tools_bccmd_OBJECTS) $(tools_bccmd_LDADD) $(LIBS)
+tools/bdaddr.$(OBJEXT): tools/$(am__dirstamp) \
+	tools/$(DEPDIR)/$(am__dirstamp)
+src/oui.$(OBJEXT): src/$(am__dirstamp) src/$(DEPDIR)/$(am__dirstamp)
+
+tools/bdaddr$(EXEEXT): $(tools_bdaddr_OBJECTS) $(tools_bdaddr_DEPENDENCIES) $(EXTRA_tools_bdaddr_DEPENDENCIES) tools/$(am__dirstamp)
+	@rm -f tools/bdaddr$(EXEEXT)
+	$(AM_V_CCLD)$(LINK) $(tools_bdaddr_OBJECTS) $(tools_bdaddr_LDADD) $(LIBS)
+tools/bluemoon.$(OBJEXT): tools/$(am__dirstamp) \
+	tools/$(DEPDIR)/$(am__dirstamp)
+
+tools/bluemoon$(EXEEXT): $(tools_bluemoon_OBJECTS) $(tools_bluemoon_DEPENDENCIES) $(EXTRA_tools_bluemoon_DEPENDENCIES) tools/$(am__dirstamp)
+	@rm -f tools/bluemoon$(EXEEXT)
+	$(AM_V_CCLD)$(LINK) $(tools_bluemoon_OBJECTS) $(tools_bluemoon_LDADD) $(LIBS)
+tools/bluetooth-player.$(OBJEXT): tools/$(am__dirstamp) \
+	tools/$(DEPDIR)/$(am__dirstamp)
+
+tools/bluetooth-player$(EXEEXT): $(tools_bluetooth_player_OBJECTS) $(tools_bluetooth_player_DEPENDENCIES) $(EXTRA_tools_bluetooth_player_DEPENDENCIES) tools/$(am__dirstamp)
+	@rm -f tools/bluetooth-player$(EXEEXT)
+	$(AM_V_CCLD)$(LINK) $(tools_bluetooth_player_OBJECTS) $(tools_bluetooth_player_LDADD) $(LIBS)
+tools/btattach.$(OBJEXT): tools/$(am__dirstamp) \
+	tools/$(DEPDIR)/$(am__dirstamp)
+
+tools/btattach$(EXEEXT): $(tools_btattach_OBJECTS) $(tools_btattach_DEPENDENCIES) $(EXTRA_tools_btattach_DEPENDENCIES) tools/$(am__dirstamp)
+	@rm -f tools/btattach$(EXEEXT)
+	$(AM_V_CCLD)$(LINK) $(tools_btattach_OBJECTS) $(tools_btattach_LDADD) $(LIBS)
+tools/btinfo.$(OBJEXT): tools/$(am__dirstamp) \
+	tools/$(DEPDIR)/$(am__dirstamp)
+
+tools/btinfo$(EXEEXT): $(tools_btinfo_OBJECTS) $(tools_btinfo_DEPENDENCIES) $(EXTRA_tools_btinfo_DEPENDENCIES) tools/$(am__dirstamp)
+	@rm -f tools/btinfo$(EXEEXT)
+	$(AM_V_CCLD)$(LINK) $(tools_btinfo_OBJECTS) $(tools_btinfo_LDADD) $(LIBS)
+tools/btiotest.$(OBJEXT): tools/$(am__dirstamp) \
+	tools/$(DEPDIR)/$(am__dirstamp)
+
+tools/btiotest$(EXEEXT): $(tools_btiotest_OBJECTS) $(tools_btiotest_DEPENDENCIES) $(EXTRA_tools_btiotest_DEPENDENCIES) tools/$(am__dirstamp)
+	@rm -f tools/btiotest$(EXEEXT)
+	$(AM_V_CCLD)$(LINK) $(tools_btiotest_OBJECTS) $(tools_btiotest_LDADD) $(LIBS)
+tools/btmgmt.$(OBJEXT): tools/$(am__dirstamp) \
+	tools/$(DEPDIR)/$(am__dirstamp)
+
+tools/btmgmt$(EXEEXT): $(tools_btmgmt_OBJECTS) $(tools_btmgmt_DEPENDENCIES) $(EXTRA_tools_btmgmt_DEPENDENCIES) tools/$(am__dirstamp)
+	@rm -f tools/btmgmt$(EXEEXT)
+	$(AM_V_CCLD)$(LINK) $(tools_btmgmt_OBJECTS) $(tools_btmgmt_LDADD) $(LIBS)
+tools/btproxy.$(OBJEXT): tools/$(am__dirstamp) \
+	tools/$(DEPDIR)/$(am__dirstamp)
+
+tools/btproxy$(EXEEXT): $(tools_btproxy_OBJECTS) $(tools_btproxy_DEPENDENCIES) $(EXTRA_tools_btproxy_DEPENDENCIES) tools/$(am__dirstamp)
+	@rm -f tools/btproxy$(EXEEXT)
+	$(AM_V_CCLD)$(LINK) $(tools_btproxy_OBJECTS) $(tools_btproxy_LDADD) $(LIBS)
+tools/btsnoop.$(OBJEXT): tools/$(am__dirstamp) \
+	tools/$(DEPDIR)/$(am__dirstamp)
+src/shared/pcap.$(OBJEXT): src/shared/$(am__dirstamp) \
+	src/shared/$(DEPDIR)/$(am__dirstamp)
+
+tools/btsnoop$(EXEEXT): $(tools_btsnoop_OBJECTS) $(tools_btsnoop_DEPENDENCIES) $(EXTRA_tools_btsnoop_DEPENDENCIES) tools/$(am__dirstamp)
+	@rm -f tools/btsnoop$(EXEEXT)
+	$(AM_V_CCLD)$(LINK) $(tools_btsnoop_OBJECTS) $(tools_btsnoop_LDADD) $(LIBS)
+tools/ciptool.$(OBJEXT): tools/$(am__dirstamp) \
+	tools/$(DEPDIR)/$(am__dirstamp)
+
+tools/ciptool$(EXEEXT): $(tools_ciptool_OBJECTS) $(tools_ciptool_DEPENDENCIES) $(EXTRA_tools_ciptool_DEPENDENCIES) tools/$(am__dirstamp)
+	@rm -f tools/ciptool$(EXEEXT)
+	$(AM_V_CCLD)$(LINK) $(tools_ciptool_OBJECTS) $(tools_ciptool_LDADD) $(LIBS)
+tools/cltest.$(OBJEXT): tools/$(am__dirstamp) \
+	tools/$(DEPDIR)/$(am__dirstamp)
+
+tools/cltest$(EXEEXT): $(tools_cltest_OBJECTS) $(tools_cltest_DEPENDENCIES) $(EXTRA_tools_cltest_DEPENDENCIES) tools/$(am__dirstamp)
+	@rm -f tools/cltest$(EXEEXT)
+	$(AM_V_CCLD)$(LINK) $(tools_cltest_OBJECTS) $(tools_cltest_LDADD) $(LIBS)
+tools/gap-tester.$(OBJEXT): tools/$(am__dirstamp) \
+	tools/$(DEPDIR)/$(am__dirstamp)
+src/shared/hciemu.$(OBJEXT): src/shared/$(am__dirstamp) \
+	src/shared/$(DEPDIR)/$(am__dirstamp)
+src/shared/tester.$(OBJEXT): src/shared/$(am__dirstamp) \
+	src/shared/$(DEPDIR)/$(am__dirstamp)
+src/shared/timeout-glib.$(OBJEXT): src/shared/$(am__dirstamp) \
+	src/shared/$(DEPDIR)/$(am__dirstamp)
+
+tools/gap-tester$(EXEEXT): $(tools_gap_tester_OBJECTS) $(tools_gap_tester_DEPENDENCIES) $(EXTRA_tools_gap_tester_DEPENDENCIES) tools/$(am__dirstamp)
+	@rm -f tools/gap-tester$(EXEEXT)
+	$(AM_V_CCLD)$(LINK) $(tools_gap_tester_OBJECTS) $(tools_gap_tester_LDADD) $(LIBS)
+tools/gatt-service.$(OBJEXT): tools/$(am__dirstamp) \
+	tools/$(DEPDIR)/$(am__dirstamp)
+
+tools/gatt-service$(EXEEXT): $(tools_gatt_service_OBJECTS) $(tools_gatt_service_DEPENDENCIES) $(EXTRA_tools_gatt_service_DEPENDENCIES) tools/$(am__dirstamp)
+	@rm -f tools/gatt-service$(EXEEXT)
+	$(AM_V_CCLD)$(LINK) $(tools_gatt_service_OBJECTS) $(tools_gatt_service_LDADD) $(LIBS)
+tools/hci-tester.$(OBJEXT): tools/$(am__dirstamp) \
+	tools/$(DEPDIR)/$(am__dirstamp)
+
+tools/hci-tester$(EXEEXT): $(tools_hci_tester_OBJECTS) $(tools_hci_tester_DEPENDENCIES) $(EXTRA_tools_hci_tester_DEPENDENCIES) tools/$(am__dirstamp)
+	@rm -f tools/hci-tester$(EXEEXT)
+	$(AM_V_CCLD)$(LINK) $(tools_hci_tester_OBJECTS) $(tools_hci_tester_LDADD) $(LIBS)
+tools/hciattach.$(OBJEXT): tools/$(am__dirstamp) \
+	tools/$(DEPDIR)/$(am__dirstamp)
+tools/hciattach_st.$(OBJEXT): tools/$(am__dirstamp) \
+	tools/$(DEPDIR)/$(am__dirstamp)
+tools/hciattach_ti.$(OBJEXT): tools/$(am__dirstamp) \
+	tools/$(DEPDIR)/$(am__dirstamp)
+tools/hciattach_tialt.$(OBJEXT): tools/$(am__dirstamp) \
+	tools/$(DEPDIR)/$(am__dirstamp)
+tools/hciattach_ath3k.$(OBJEXT): tools/$(am__dirstamp) \
+	tools/$(DEPDIR)/$(am__dirstamp)
+tools/hciattach_qualcomm.$(OBJEXT): tools/$(am__dirstamp) \
+	tools/$(DEPDIR)/$(am__dirstamp)
+tools/hciattach_intel.$(OBJEXT): tools/$(am__dirstamp) \
+	tools/$(DEPDIR)/$(am__dirstamp)
+
+tools/hciattach$(EXEEXT): $(tools_hciattach_OBJECTS) $(tools_hciattach_DEPENDENCIES) $(EXTRA_tools_hciattach_DEPENDENCIES) tools/$(am__dirstamp)
+	@rm -f tools/hciattach$(EXEEXT)
+	$(AM_V_CCLD)$(LINK) $(tools_hciattach_OBJECTS) $(tools_hciattach_LDADD) $(LIBS)
+tools/hciconfig.$(OBJEXT): tools/$(am__dirstamp) \
+	tools/$(DEPDIR)/$(am__dirstamp)
+
+tools/hciconfig$(EXEEXT): $(tools_hciconfig_OBJECTS) $(tools_hciconfig_DEPENDENCIES) $(EXTRA_tools_hciconfig_DEPENDENCIES) tools/$(am__dirstamp)
+	@rm -f tools/hciconfig$(EXEEXT)
+	$(AM_V_CCLD)$(LINK) $(tools_hciconfig_OBJECTS) $(tools_hciconfig_LDADD) $(LIBS)
+tools/hcidump.$(OBJEXT): tools/$(am__dirstamp) \
+	tools/$(DEPDIR)/$(am__dirstamp)
+tools/parser/$(am__dirstamp):
+	@$(MKDIR_P) tools/parser
+	@: > tools/parser/$(am__dirstamp)
+tools/parser/$(DEPDIR)/$(am__dirstamp):
+	@$(MKDIR_P) tools/parser/$(DEPDIR)
+	@: > tools/parser/$(DEPDIR)/$(am__dirstamp)
+tools/parser/parser.$(OBJEXT): tools/parser/$(am__dirstamp) \
+	tools/parser/$(DEPDIR)/$(am__dirstamp)
+tools/parser/lmp.$(OBJEXT): tools/parser/$(am__dirstamp) \
+	tools/parser/$(DEPDIR)/$(am__dirstamp)
+tools/parser/hci.$(OBJEXT): tools/parser/$(am__dirstamp) \
+	tools/parser/$(DEPDIR)/$(am__dirstamp)
+tools/parser/l2cap.$(OBJEXT): tools/parser/$(am__dirstamp) \
+	tools/parser/$(DEPDIR)/$(am__dirstamp)
+tools/parser/amp.$(OBJEXT): tools/parser/$(am__dirstamp) \
+	tools/parser/$(DEPDIR)/$(am__dirstamp)
+tools/parser/smp.$(OBJEXT): tools/parser/$(am__dirstamp) \
+	tools/parser/$(DEPDIR)/$(am__dirstamp)
+tools/parser/att.$(OBJEXT): tools/parser/$(am__dirstamp) \
+	tools/parser/$(DEPDIR)/$(am__dirstamp)
+tools/parser/sdp.$(OBJEXT): tools/parser/$(am__dirstamp) \
+	tools/parser/$(DEPDIR)/$(am__dirstamp)
+tools/parser/rfcomm.$(OBJEXT): tools/parser/$(am__dirstamp) \
+	tools/parser/$(DEPDIR)/$(am__dirstamp)
+tools/parser/bnep.$(OBJEXT): tools/parser/$(am__dirstamp) \
+	tools/parser/$(DEPDIR)/$(am__dirstamp)
+tools/parser/cmtp.$(OBJEXT): tools/parser/$(am__dirstamp) \
+	tools/parser/$(DEPDIR)/$(am__dirstamp)
+tools/parser/hidp.$(OBJEXT): tools/parser/$(am__dirstamp) \
+	tools/parser/$(DEPDIR)/$(am__dirstamp)
+tools/parser/hcrp.$(OBJEXT): tools/parser/$(am__dirstamp) \
+	tools/parser/$(DEPDIR)/$(am__dirstamp)
+tools/parser/avdtp.$(OBJEXT): tools/parser/$(am__dirstamp) \
+	tools/parser/$(DEPDIR)/$(am__dirstamp)
+tools/parser/avctp.$(OBJEXT): tools/parser/$(am__dirstamp) \
+	tools/parser/$(DEPDIR)/$(am__dirstamp)
+tools/parser/avrcp.$(OBJEXT): tools/parser/$(am__dirstamp) \
+	tools/parser/$(DEPDIR)/$(am__dirstamp)
+tools/parser/sap.$(OBJEXT): tools/parser/$(am__dirstamp) \
+	tools/parser/$(DEPDIR)/$(am__dirstamp)
+tools/parser/obex.$(OBJEXT): tools/parser/$(am__dirstamp) \
+	tools/parser/$(DEPDIR)/$(am__dirstamp)
+tools/parser/capi.$(OBJEXT): tools/parser/$(am__dirstamp) \
+	tools/parser/$(DEPDIR)/$(am__dirstamp)
+tools/parser/ppp.$(OBJEXT): tools/parser/$(am__dirstamp) \
+	tools/parser/$(DEPDIR)/$(am__dirstamp)
+tools/parser/tcpip.$(OBJEXT): tools/parser/$(am__dirstamp) \
+	tools/parser/$(DEPDIR)/$(am__dirstamp)
+tools/parser/ericsson.$(OBJEXT): tools/parser/$(am__dirstamp) \
+	tools/parser/$(DEPDIR)/$(am__dirstamp)
+tools/parser/csr.$(OBJEXT): tools/parser/$(am__dirstamp) \
+	tools/parser/$(DEPDIR)/$(am__dirstamp)
+tools/parser/bpa.$(OBJEXT): tools/parser/$(am__dirstamp) \
+	tools/parser/$(DEPDIR)/$(am__dirstamp)
+
+tools/hcidump$(EXEEXT): $(tools_hcidump_OBJECTS) $(tools_hcidump_DEPENDENCIES) $(EXTRA_tools_hcidump_DEPENDENCIES) tools/$(am__dirstamp)
+	@rm -f tools/hcidump$(EXEEXT)
+	$(AM_V_CCLD)$(LINK) $(tools_hcidump_OBJECTS) $(tools_hcidump_LDADD) $(LIBS)
+tools/hcieventmask.$(OBJEXT): tools/$(am__dirstamp) \
+	tools/$(DEPDIR)/$(am__dirstamp)
+
+tools/hcieventmask$(EXEEXT): $(tools_hcieventmask_OBJECTS) $(tools_hcieventmask_DEPENDENCIES) $(EXTRA_tools_hcieventmask_DEPENDENCIES) tools/$(am__dirstamp)
+	@rm -f tools/hcieventmask$(EXEEXT)
+	$(AM_V_CCLD)$(LINK) $(tools_hcieventmask_OBJECTS) $(tools_hcieventmask_LDADD) $(LIBS)
+tools/hcisecfilter.$(OBJEXT): tools/$(am__dirstamp) \
+	tools/$(DEPDIR)/$(am__dirstamp)
+
+tools/hcisecfilter$(EXEEXT): $(tools_hcisecfilter_OBJECTS) $(tools_hcisecfilter_DEPENDENCIES) $(EXTRA_tools_hcisecfilter_DEPENDENCIES) tools/$(am__dirstamp)
+	@rm -f tools/hcisecfilter$(EXEEXT)
+	$(AM_V_CCLD)$(LINK) $(tools_hcisecfilter_OBJECTS) $(tools_hcisecfilter_LDADD) $(LIBS)
+tools/hcitool.$(OBJEXT): tools/$(am__dirstamp) \
+	tools/$(DEPDIR)/$(am__dirstamp)
+
+tools/hcitool$(EXEEXT): $(tools_hcitool_OBJECTS) $(tools_hcitool_DEPENDENCIES) $(EXTRA_tools_hcitool_DEPENDENCIES) tools/$(am__dirstamp)
+	@rm -f tools/hcitool$(EXEEXT)
+	$(AM_V_CCLD)$(LINK) $(tools_hcitool_OBJECTS) $(tools_hcitool_LDADD) $(LIBS)
+tools/hid2hci.$(OBJEXT): tools/$(am__dirstamp) \
+	tools/$(DEPDIR)/$(am__dirstamp)
+
+tools/hid2hci$(EXEEXT): $(tools_hid2hci_OBJECTS) $(tools_hid2hci_DEPENDENCIES) $(EXTRA_tools_hid2hci_DEPENDENCIES) tools/$(am__dirstamp)
+	@rm -f tools/hid2hci$(EXEEXT)
+	$(AM_V_CCLD)$(LINK) $(tools_hid2hci_OBJECTS) $(tools_hid2hci_LDADD) $(LIBS)
+tools/hwdb.$(OBJEXT): tools/$(am__dirstamp) \
+	tools/$(DEPDIR)/$(am__dirstamp)
+
+tools/hwdb$(EXEEXT): $(tools_hwdb_OBJECTS) $(tools_hwdb_DEPENDENCIES) $(EXTRA_tools_hwdb_DEPENDENCIES) tools/$(am__dirstamp)
+	@rm -f tools/hwdb$(EXEEXT)
+	$(AM_V_CCLD)$(LINK) $(tools_hwdb_OBJECTS) $(tools_hwdb_LDADD) $(LIBS)
+tools/ibeacon.$(OBJEXT): tools/$(am__dirstamp) \
+	tools/$(DEPDIR)/$(am__dirstamp)
+
+tools/ibeacon$(EXEEXT): $(tools_ibeacon_OBJECTS) $(tools_ibeacon_DEPENDENCIES) $(EXTRA_tools_ibeacon_DEPENDENCIES) tools/$(am__dirstamp)
+	@rm -f tools/ibeacon$(EXEEXT)
+	$(AM_V_CCLD)$(LINK) $(tools_ibeacon_OBJECTS) $(tools_ibeacon_LDADD) $(LIBS)
+tools/l2cap-tester.$(OBJEXT): tools/$(am__dirstamp) \
+	tools/$(DEPDIR)/$(am__dirstamp)
+
+tools/l2cap-tester$(EXEEXT): $(tools_l2cap_tester_OBJECTS) $(tools_l2cap_tester_DEPENDENCIES) $(EXTRA_tools_l2cap_tester_DEPENDENCIES) tools/$(am__dirstamp)
+	@rm -f tools/l2cap-tester$(EXEEXT)
+	$(AM_V_CCLD)$(LINK) $(tools_l2cap_tester_OBJECTS) $(tools_l2cap_tester_LDADD) $(LIBS)
+tools/l2ping.$(OBJEXT): tools/$(am__dirstamp) \
+	tools/$(DEPDIR)/$(am__dirstamp)
+
+tools/l2ping$(EXEEXT): $(tools_l2ping_OBJECTS) $(tools_l2ping_DEPENDENCIES) $(EXTRA_tools_l2ping_DEPENDENCIES) tools/$(am__dirstamp)
+	@rm -f tools/l2ping$(EXEEXT)
+	$(AM_V_CCLD)$(LINK) $(tools_l2ping_OBJECTS) $(tools_l2ping_LDADD) $(LIBS)
+tools/l2test.$(OBJEXT): tools/$(am__dirstamp) \
+	tools/$(DEPDIR)/$(am__dirstamp)
+
+tools/l2test$(EXEEXT): $(tools_l2test_OBJECTS) $(tools_l2test_DEPENDENCIES) $(EXTRA_tools_l2test_DEPENDENCIES) tools/$(am__dirstamp)
+	@rm -f tools/l2test$(EXEEXT)
+	$(AM_V_CCLD)$(LINK) $(tools_l2test_OBJECTS) $(tools_l2test_LDADD) $(LIBS)
+tools/mgmt-tester.$(OBJEXT): tools/$(am__dirstamp) \
+	tools/$(DEPDIR)/$(am__dirstamp)
+
+tools/mgmt-tester$(EXEEXT): $(tools_mgmt_tester_OBJECTS) $(tools_mgmt_tester_DEPENDENCIES) $(EXTRA_tools_mgmt_tester_DEPENDENCIES) tools/$(am__dirstamp)
+	@rm -f tools/mgmt-tester$(EXEEXT)
+	$(AM_V_CCLD)$(LINK) $(tools_mgmt_tester_OBJECTS) $(tools_mgmt_tester_LDADD) $(LIBS)
+tools/mpris-player.$(OBJEXT): tools/$(am__dirstamp) \
+	tools/$(DEPDIR)/$(am__dirstamp)
+
+tools/mpris-player$(EXEEXT): $(tools_mpris_player_OBJECTS) $(tools_mpris_player_DEPENDENCIES) $(EXTRA_tools_mpris_player_DEPENDENCIES) tools/$(am__dirstamp)
+	@rm -f tools/mpris-player$(EXEEXT)
+	$(AM_V_CCLD)$(LINK) $(tools_mpris_player_OBJECTS) $(tools_mpris_player_LDADD) $(LIBS)
+gobex/gobex.$(OBJEXT): gobex/$(am__dirstamp) \
+	gobex/$(DEPDIR)/$(am__dirstamp)
+gobex/gobex-defs.$(OBJEXT): gobex/$(am__dirstamp) \
+	gobex/$(DEPDIR)/$(am__dirstamp)
+gobex/gobex-packet.$(OBJEXT): gobex/$(am__dirstamp) \
+	gobex/$(DEPDIR)/$(am__dirstamp)
+gobex/gobex-header.$(OBJEXT): gobex/$(am__dirstamp) \
+	gobex/$(DEPDIR)/$(am__dirstamp)
+gobex/gobex-transfer.$(OBJEXT): gobex/$(am__dirstamp) \
+	gobex/$(DEPDIR)/$(am__dirstamp)
+gobex/gobex-apparam.$(OBJEXT): gobex/$(am__dirstamp) \
+	gobex/$(DEPDIR)/$(am__dirstamp)
+tools/obex-client-tool.$(OBJEXT): tools/$(am__dirstamp) \
+	tools/$(DEPDIR)/$(am__dirstamp)
+
+tools/obex-client-tool$(EXEEXT): $(tools_obex_client_tool_OBJECTS) $(tools_obex_client_tool_DEPENDENCIES) $(EXTRA_tools_obex_client_tool_DEPENDENCIES) tools/$(am__dirstamp)
+	@rm -f tools/obex-client-tool$(EXEEXT)
+	$(AM_V_CCLD)$(LINK) $(tools_obex_client_tool_OBJECTS) $(tools_obex_client_tool_LDADD) $(LIBS)
+tools/obex-server-tool.$(OBJEXT): tools/$(am__dirstamp) \
+	tools/$(DEPDIR)/$(am__dirstamp)
+
+tools/obex-server-tool$(EXEEXT): $(tools_obex_server_tool_OBJECTS) $(tools_obex_server_tool_DEPENDENCIES) $(EXTRA_tools_obex_server_tool_DEPENDENCIES) tools/$(am__dirstamp)
+	@rm -f tools/obex-server-tool$(EXEEXT)
+	$(AM_V_CCLD)$(LINK) $(tools_obex_server_tool_OBJECTS) $(tools_obex_server_tool_LDADD) $(LIBS)
+tools/obexctl.$(OBJEXT): tools/$(am__dirstamp) \
+	tools/$(DEPDIR)/$(am__dirstamp)
+
+tools/obexctl$(EXEEXT): $(tools_obexctl_OBJECTS) $(tools_obexctl_DEPENDENCIES) $(EXTRA_tools_obexctl_DEPENDENCIES) tools/$(am__dirstamp)
+	@rm -f tools/obexctl$(EXEEXT)
+	$(AM_V_CCLD)$(LINK) $(tools_obexctl_OBJECTS) $(tools_obexctl_LDADD) $(LIBS)
+tools/rctest.$(OBJEXT): tools/$(am__dirstamp) \
+	tools/$(DEPDIR)/$(am__dirstamp)
+
+tools/rctest$(EXEEXT): $(tools_rctest_OBJECTS) $(tools_rctest_DEPENDENCIES) $(EXTRA_tools_rctest_DEPENDENCIES) tools/$(am__dirstamp)
+	@rm -f tools/rctest$(EXEEXT)
+	$(AM_V_CCLD)$(LINK) $(tools_rctest_OBJECTS) $(tools_rctest_LDADD) $(LIBS)
+tools/rfcomm.$(OBJEXT): tools/$(am__dirstamp) \
+	tools/$(DEPDIR)/$(am__dirstamp)
+
+tools/rfcomm$(EXEEXT): $(tools_rfcomm_OBJECTS) $(tools_rfcomm_DEPENDENCIES) $(EXTRA_tools_rfcomm_DEPENDENCIES) tools/$(am__dirstamp)
+	@rm -f tools/rfcomm$(EXEEXT)
+	$(AM_V_CCLD)$(LINK) $(tools_rfcomm_OBJECTS) $(tools_rfcomm_LDADD) $(LIBS)
+tools/rfcomm-tester.$(OBJEXT): tools/$(am__dirstamp) \
+	tools/$(DEPDIR)/$(am__dirstamp)
+
+tools/rfcomm-tester$(EXEEXT): $(tools_rfcomm_tester_OBJECTS) $(tools_rfcomm_tester_DEPENDENCIES) $(EXTRA_tools_rfcomm_tester_DEPENDENCIES) tools/$(am__dirstamp)
+	@rm -f tools/rfcomm-tester$(EXEEXT)
+	$(AM_V_CCLD)$(LINK) $(tools_rfcomm_tester_OBJECTS) $(tools_rfcomm_tester_LDADD) $(LIBS)
+tools/sco-tester.$(OBJEXT): tools/$(am__dirstamp) \
+	tools/$(DEPDIR)/$(am__dirstamp)
+
+tools/sco-tester$(EXEEXT): $(tools_sco_tester_OBJECTS) $(tools_sco_tester_DEPENDENCIES) $(EXTRA_tools_sco_tester_DEPENDENCIES) tools/$(am__dirstamp)
+	@rm -f tools/sco-tester$(EXEEXT)
+	$(AM_V_CCLD)$(LINK) $(tools_sco_tester_OBJECTS) $(tools_sco_tester_LDADD) $(LIBS)
+tools/scotest.$(OBJEXT): tools/$(am__dirstamp) \
+	tools/$(DEPDIR)/$(am__dirstamp)
+
+tools/scotest$(EXEEXT): $(tools_scotest_OBJECTS) $(tools_scotest_DEPENDENCIES) $(EXTRA_tools_scotest_DEPENDENCIES) tools/$(am__dirstamp)
+	@rm -f tools/scotest$(EXEEXT)
+	$(AM_V_CCLD)$(LINK) $(tools_scotest_OBJECTS) $(tools_scotest_LDADD) $(LIBS)
+tools/sdptool.$(OBJEXT): tools/$(am__dirstamp) \
+	tools/$(DEPDIR)/$(am__dirstamp)
+src/sdp-xml.$(OBJEXT): src/$(am__dirstamp) \
+	src/$(DEPDIR)/$(am__dirstamp)
+
+tools/sdptool$(EXEEXT): $(tools_sdptool_OBJECTS) $(tools_sdptool_DEPENDENCIES) $(EXTRA_tools_sdptool_DEPENDENCIES) tools/$(am__dirstamp)
+	@rm -f tools/sdptool$(EXEEXT)
+	$(AM_V_CCLD)$(LINK) $(tools_sdptool_OBJECTS) $(tools_sdptool_LDADD) $(LIBS)
+tools/seq2bseq.$(OBJEXT): tools/$(am__dirstamp) \
+	tools/$(DEPDIR)/$(am__dirstamp)
+
+tools/seq2bseq$(EXEEXT): $(tools_seq2bseq_OBJECTS) $(tools_seq2bseq_DEPENDENCIES) $(EXTRA_tools_seq2bseq_DEPENDENCIES) tools/$(am__dirstamp)
+	@rm -f tools/seq2bseq$(EXEEXT)
+	$(AM_V_CCLD)$(LINK) $(tools_seq2bseq_OBJECTS) $(tools_seq2bseq_LDADD) $(LIBS)
+tools/smp-tester.$(OBJEXT): tools/$(am__dirstamp) \
+	tools/$(DEPDIR)/$(am__dirstamp)
+
+tools/smp-tester$(EXEEXT): $(tools_smp_tester_OBJECTS) $(tools_smp_tester_DEPENDENCIES) $(EXTRA_tools_smp_tester_DEPENDENCIES) tools/$(am__dirstamp)
+	@rm -f tools/smp-tester$(EXEEXT)
+	$(AM_V_CCLD)$(LINK) $(tools_smp_tester_OBJECTS) $(tools_smp_tester_LDADD) $(LIBS)
+unit/$(am__dirstamp):
+	@$(MKDIR_P) unit
+	@: > unit/$(am__dirstamp)
+unit/$(DEPDIR)/$(am__dirstamp):
+	@$(MKDIR_P) unit/$(DEPDIR)
+	@: > unit/$(DEPDIR)/$(am__dirstamp)
+unit/test-avctp.$(OBJEXT): unit/$(am__dirstamp) \
+	unit/$(DEPDIR)/$(am__dirstamp)
+
+unit/test-avctp$(EXEEXT): $(unit_test_avctp_OBJECTS) $(unit_test_avctp_DEPENDENCIES) $(EXTRA_unit_test_avctp_DEPENDENCIES) unit/$(am__dirstamp)
+	@rm -f unit/test-avctp$(EXEEXT)
+	$(AM_V_CCLD)$(LINK) $(unit_test_avctp_OBJECTS) $(unit_test_avctp_LDADD) $(LIBS)
+unit/test-avdtp.$(OBJEXT): unit/$(am__dirstamp) \
+	unit/$(DEPDIR)/$(am__dirstamp)
+
+unit/test-avdtp$(EXEEXT): $(unit_test_avdtp_OBJECTS) $(unit_test_avdtp_DEPENDENCIES) $(EXTRA_unit_test_avdtp_DEPENDENCIES) unit/$(am__dirstamp)
+	@rm -f unit/test-avdtp$(EXEEXT)
+	$(AM_V_CCLD)$(LINK) $(unit_test_avdtp_OBJECTS) $(unit_test_avdtp_LDADD) $(LIBS)
+unit/test-avrcp.$(OBJEXT): unit/$(am__dirstamp) \
+	unit/$(DEPDIR)/$(am__dirstamp)
+
+unit/test-avrcp$(EXEEXT): $(unit_test_avrcp_OBJECTS) $(unit_test_avrcp_DEPENDENCIES) $(EXTRA_unit_test_avrcp_DEPENDENCIES) unit/$(am__dirstamp)
+	@rm -f unit/test-avrcp$(EXEEXT)
+	$(AM_V_CCLD)$(LINK) $(unit_test_avrcp_OBJECTS) $(unit_test_avrcp_LDADD) $(LIBS)
+unit/test-crc.$(OBJEXT): unit/$(am__dirstamp) \
+	unit/$(DEPDIR)/$(am__dirstamp)
+
+unit/test-crc$(EXEEXT): $(unit_test_crc_OBJECTS) $(unit_test_crc_DEPENDENCIES) $(EXTRA_unit_test_crc_DEPENDENCIES) unit/$(am__dirstamp)
+	@rm -f unit/test-crc$(EXEEXT)
+	$(AM_V_CCLD)$(LINK) $(unit_test_crc_OBJECTS) $(unit_test_crc_LDADD) $(LIBS)
+unit/test-eir.$(OBJEXT): unit/$(am__dirstamp) \
+	unit/$(DEPDIR)/$(am__dirstamp)
+
+unit/test-eir$(EXEEXT): $(unit_test_eir_OBJECTS) $(unit_test_eir_DEPENDENCIES) $(EXTRA_unit_test_eir_DEPENDENCIES) unit/$(am__dirstamp)
+	@rm -f unit/test-eir$(EXEEXT)
+	$(AM_V_CCLD)$(LINK) $(unit_test_eir_OBJECTS) $(unit_test_eir_LDADD) $(LIBS)
+unit/test-gdbus-client.$(OBJEXT): unit/$(am__dirstamp) \
+	unit/$(DEPDIR)/$(am__dirstamp)
+
+unit/test-gdbus-client$(EXEEXT): $(unit_test_gdbus_client_OBJECTS) $(unit_test_gdbus_client_DEPENDENCIES) $(EXTRA_unit_test_gdbus_client_DEPENDENCIES) unit/$(am__dirstamp)
+	@rm -f unit/test-gdbus-client$(EXEEXT)
+	$(AM_V_CCLD)$(LINK) $(unit_test_gdbus_client_OBJECTS) $(unit_test_gdbus_client_LDADD) $(LIBS)
+unit/util.$(OBJEXT): unit/$(am__dirstamp) \
+	unit/$(DEPDIR)/$(am__dirstamp)
+unit/test-gobex.$(OBJEXT): unit/$(am__dirstamp) \
+	unit/$(DEPDIR)/$(am__dirstamp)
+
+unit/test-gobex$(EXEEXT): $(unit_test_gobex_OBJECTS) $(unit_test_gobex_DEPENDENCIES) $(EXTRA_unit_test_gobex_DEPENDENCIES) unit/$(am__dirstamp)
+	@rm -f unit/test-gobex$(EXEEXT)
+	$(AM_V_CCLD)$(LINK) $(unit_test_gobex_OBJECTS) $(unit_test_gobex_LDADD) $(LIBS)
+unit/test-gobex-apparam.$(OBJEXT): unit/$(am__dirstamp) \
+	unit/$(DEPDIR)/$(am__dirstamp)
+
+unit/test-gobex-apparam$(EXEEXT): $(unit_test_gobex_apparam_OBJECTS) $(unit_test_gobex_apparam_DEPENDENCIES) $(EXTRA_unit_test_gobex_apparam_DEPENDENCIES) unit/$(am__dirstamp)
+	@rm -f unit/test-gobex-apparam$(EXEEXT)
+	$(AM_V_CCLD)$(LINK) $(unit_test_gobex_apparam_OBJECTS) $(unit_test_gobex_apparam_LDADD) $(LIBS)
+unit/test-gobex-header.$(OBJEXT): unit/$(am__dirstamp) \
+	unit/$(DEPDIR)/$(am__dirstamp)
+
+unit/test-gobex-header$(EXEEXT): $(unit_test_gobex_header_OBJECTS) $(unit_test_gobex_header_DEPENDENCIES) $(EXTRA_unit_test_gobex_header_DEPENDENCIES) unit/$(am__dirstamp)
+	@rm -f unit/test-gobex-header$(EXEEXT)
+	$(AM_V_CCLD)$(LINK) $(unit_test_gobex_header_OBJECTS) $(unit_test_gobex_header_LDADD) $(LIBS)
+unit/test-gobex-packet.$(OBJEXT): unit/$(am__dirstamp) \
+	unit/$(DEPDIR)/$(am__dirstamp)
+
+unit/test-gobex-packet$(EXEEXT): $(unit_test_gobex_packet_OBJECTS) $(unit_test_gobex_packet_DEPENDENCIES) $(EXTRA_unit_test_gobex_packet_DEPENDENCIES) unit/$(am__dirstamp)
+	@rm -f unit/test-gobex-packet$(EXEEXT)
+	$(AM_V_CCLD)$(LINK) $(unit_test_gobex_packet_OBJECTS) $(unit_test_gobex_packet_LDADD) $(LIBS)
+unit/test-gobex-transfer.$(OBJEXT): unit/$(am__dirstamp) \
+	unit/$(DEPDIR)/$(am__dirstamp)
+
+unit/test-gobex-transfer$(EXEEXT): $(unit_test_gobex_transfer_OBJECTS) $(unit_test_gobex_transfer_DEPENDENCIES) $(EXTRA_unit_test_gobex_transfer_DEPENDENCIES) unit/$(am__dirstamp)
+	@rm -f unit/test-gobex-transfer$(EXEEXT)
+	$(AM_V_CCLD)$(LINK) $(unit_test_gobex_transfer_OBJECTS) $(unit_test_gobex_transfer_LDADD) $(LIBS)
+unit/test-hfp.$(OBJEXT): unit/$(am__dirstamp) \
+	unit/$(DEPDIR)/$(am__dirstamp)
+
+unit/test-hfp$(EXEEXT): $(unit_test_hfp_OBJECTS) $(unit_test_hfp_DEPENDENCIES) $(EXTRA_unit_test_hfp_DEPENDENCIES) unit/$(am__dirstamp)
+	@rm -f unit/test-hfp$(EXEEXT)
+	$(AM_V_CCLD)$(LINK) $(unit_test_hfp_OBJECTS) $(unit_test_hfp_LDADD) $(LIBS)
+unit/test-lib.$(OBJEXT): unit/$(am__dirstamp) \
+	unit/$(DEPDIR)/$(am__dirstamp)
+
+unit/test-lib$(EXEEXT): $(unit_test_lib_OBJECTS) $(unit_test_lib_DEPENDENCIES) $(EXTRA_unit_test_lib_DEPENDENCIES) unit/$(am__dirstamp)
+	@rm -f unit/test-lib$(EXEEXT)
+	$(AM_V_CCLD)$(LINK) $(unit_test_lib_OBJECTS) $(unit_test_lib_LDADD) $(LIBS)
+unit/test-mgmt.$(OBJEXT): unit/$(am__dirstamp) \
+	unit/$(DEPDIR)/$(am__dirstamp)
+
+unit/test-mgmt$(EXEEXT): $(unit_test_mgmt_OBJECTS) $(unit_test_mgmt_DEPENDENCIES) $(EXTRA_unit_test_mgmt_DEPENDENCIES) unit/$(am__dirstamp)
+	@rm -f unit/test-mgmt$(EXEEXT)
+	$(AM_V_CCLD)$(LINK) $(unit_test_mgmt_OBJECTS) $(unit_test_mgmt_LDADD) $(LIBS)
+unit/test-queue.$(OBJEXT): unit/$(am__dirstamp) \
+	unit/$(DEPDIR)/$(am__dirstamp)
+
+unit/test-queue$(EXEEXT): $(unit_test_queue_OBJECTS) $(unit_test_queue_DEPENDENCIES) $(EXTRA_unit_test_queue_DEPENDENCIES) unit/$(am__dirstamp)
+	@rm -f unit/test-queue$(EXEEXT)
+	$(AM_V_CCLD)$(LINK) $(unit_test_queue_OBJECTS) $(unit_test_queue_LDADD) $(LIBS)
+unit/test-ringbuf.$(OBJEXT): unit/$(am__dirstamp) \
+	unit/$(DEPDIR)/$(am__dirstamp)
+
+unit/test-ringbuf$(EXEEXT): $(unit_test_ringbuf_OBJECTS) $(unit_test_ringbuf_DEPENDENCIES) $(EXTRA_unit_test_ringbuf_DEPENDENCIES) unit/$(am__dirstamp)
+	@rm -f unit/test-ringbuf$(EXEEXT)
+	$(AM_V_CCLD)$(LINK) $(unit_test_ringbuf_OBJECTS) $(unit_test_ringbuf_LDADD) $(LIBS)
+unit/test-sdp.$(OBJEXT): unit/$(am__dirstamp) \
+	unit/$(DEPDIR)/$(am__dirstamp)
+
+unit/test-sdp$(EXEEXT): $(unit_test_sdp_OBJECTS) $(unit_test_sdp_DEPENDENCIES) $(EXTRA_unit_test_sdp_DEPENDENCIES) unit/$(am__dirstamp)
+	@rm -f unit/test-sdp$(EXEEXT)
+	$(AM_V_CCLD)$(LINK) $(unit_test_sdp_OBJECTS) $(unit_test_sdp_LDADD) $(LIBS)
+unit/test-textfile.$(OBJEXT): unit/$(am__dirstamp) \
+	unit/$(DEPDIR)/$(am__dirstamp)
+src/textfile.$(OBJEXT): src/$(am__dirstamp) \
+	src/$(DEPDIR)/$(am__dirstamp)
+
+unit/test-textfile$(EXEEXT): $(unit_test_textfile_OBJECTS) $(unit_test_textfile_DEPENDENCIES) $(EXTRA_unit_test_textfile_DEPENDENCIES) unit/$(am__dirstamp)
+	@rm -f unit/test-textfile$(EXEEXT)
+	$(AM_V_CCLD)$(LINK) $(unit_test_textfile_OBJECTS) $(unit_test_textfile_LDADD) $(LIBS)
+unit/test-uuid.$(OBJEXT): unit/$(am__dirstamp) \
+	unit/$(DEPDIR)/$(am__dirstamp)
+
+unit/test-uuid$(EXEEXT): $(unit_test_uuid_OBJECTS) $(unit_test_uuid_DEPENDENCIES) $(EXTRA_unit_test_uuid_DEPENDENCIES) unit/$(am__dirstamp)
+	@rm -f unit/test-uuid$(EXEEXT)
+	$(AM_V_CCLD)$(LINK) $(unit_test_uuid_OBJECTS) $(unit_test_uuid_LDADD) $(LIBS)
+install-testSCRIPTS: $(test_SCRIPTS)
+	@$(NORMAL_INSTALL)
+	@list='$(test_SCRIPTS)'; test -n "$(testdir)" || list=; \
+	if test -n "$$list"; then \
+	  echo " $(MKDIR_P) '$(DESTDIR)$(testdir)'"; \
+	  $(MKDIR_P) "$(DESTDIR)$(testdir)" || exit 1; \
+	fi; \
+	for p in $$list; do \
+	  if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \
+	  if test -f "$$d$$p"; then echo "$$d$$p"; echo "$$p"; else :; fi; \
+	done | \
+	sed -e 'p;s,.*/,,;n' \
+	    -e 'h;s|.*|.|' \
+	    -e 'p;x;s,.*/,,;$(transform)' | sed 'N;N;N;s,\n, ,g' | \
+	$(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1; } \
+	  { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \
+	    if ($$2 == $$4) { files[d] = files[d] " " $$1; \
+	      if (++n[d] == $(am__install_max)) { \
+		print "f", d, files[d]; n[d] = 0; files[d] = "" } } \
+	    else { print "f", d "/" $$4, $$1 } } \
+	  END { for (d in files) print "f", d, files[d] }' | \
+	while read type dir files; do \
+	     if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \
+	     test -z "$$files" || { \
+	       echo " $(INSTALL_SCRIPT) $$files '$(DESTDIR)$(testdir)$$dir'"; \
+	       $(INSTALL_SCRIPT) $$files "$(DESTDIR)$(testdir)$$dir" || exit $$?; \
+	     } \
+	; done
+
+uninstall-testSCRIPTS:
+	@$(NORMAL_UNINSTALL)
+	@list='$(test_SCRIPTS)'; test -n "$(testdir)" || exit 0; \
+	files=`for p in $$list; do echo "$$p"; done | \
+	       sed -e 's,.*/,,;$(transform)'`; \
+	dir='$(DESTDIR)$(testdir)'; $(am__uninstall_files_from_dir)
+
+mostlyclean-compile:
+	-rm -f *.$(OBJEXT)
+	-rm -f android/*.$(OBJEXT)
+	-rm -f android/*.lo
+	-rm -f android/client/*.$(OBJEXT)
+	-rm -f android/hardware/*.$(OBJEXT)
+	-rm -f attrib/*.$(OBJEXT)
+	-rm -f btio/*.$(OBJEXT)
+	-rm -f client/*.$(OBJEXT)
+	-rm -f emulator/*.$(OBJEXT)
+	-rm -f gdbus/*.$(OBJEXT)
+	-rm -f gdbus/*.lo
+	-rm -f gobex/*.$(OBJEXT)
+	-rm -f lib/*.$(OBJEXT)
+	-rm -f lib/*.lo
+	-rm -f monitor/*.$(OBJEXT)
+	-rm -f obexd/client/*.$(OBJEXT)
+	-rm -f obexd/plugins/*.$(OBJEXT)
+	-rm -f obexd/src/*.$(OBJEXT)
+	-rm -f plugins/*.$(OBJEXT)
+	-rm -f plugins/*.lo
+	-rm -f profiles/alert/*.$(OBJEXT)
+	-rm -f profiles/audio/*.$(OBJEXT)
+	-rm -f profiles/cups/*.$(OBJEXT)
+	-rm -f profiles/cyclingspeed/*.$(OBJEXT)
+	-rm -f profiles/deviceinfo/*.$(OBJEXT)
+	-rm -f profiles/gatt/*.$(OBJEXT)
+	-rm -f profiles/health/*.$(OBJEXT)
+	-rm -f profiles/heartrate/*.$(OBJEXT)
+	-rm -f profiles/iap/*.$(OBJEXT)
+	-rm -f profiles/input/*.$(OBJEXT)
+	-rm -f profiles/network/*.$(OBJEXT)
+	-rm -f profiles/proximity/*.$(OBJEXT)
+	-rm -f profiles/sap/*.$(OBJEXT)
+	-rm -f profiles/scanparam/*.$(OBJEXT)
+	-rm -f profiles/thermometer/*.$(OBJEXT)
+	-rm -f profiles/time/*.$(OBJEXT)
+	-rm -f src/*.$(OBJEXT)
+	-rm -f src/shared/*.$(OBJEXT)
+	-rm -f tools/*.$(OBJEXT)
+	-rm -f tools/parser/*.$(OBJEXT)
+	-rm -f unit/*.$(OBJEXT)
+
+distclean-compile:
+	-rm -f *.tab.c
+
+@AMDEP_TRUE@@am__include@ @am__quote@android/$(DEPDIR)/a2dp.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@android/$(DEPDIR)/android_android_tester-android-tester.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@android/$(DEPDIR)/android_audio_a2dp_default_la-hal-audio.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@android/$(DEPDIR)/android_bluetooth_default_la-hal-a2dp.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@android/$(DEPDIR)/android_bluetooth_default_la-hal-avrcp.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@android/$(DEPDIR)/android_bluetooth_default_la-hal-bluetooth.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@android/$(DEPDIR)/android_bluetooth_default_la-hal-gatt.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@android/$(DEPDIR)/android_bluetooth_default_la-hal-handsfree.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@android/$(DEPDIR)/android_bluetooth_default_la-hal-health.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@android/$(DEPDIR)/android_bluetooth_default_la-hal-hidhost.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@android/$(DEPDIR)/android_bluetooth_default_la-hal-ipc.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@android/$(DEPDIR)/android_bluetooth_default_la-hal-pan.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@android/$(DEPDIR)/android_bluetooth_default_la-hal-socket.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@android/$(DEPDIR)/android_bluetooth_default_la-hal-utils.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@android/$(DEPDIR)/android_haltest-hal-utils.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@android/$(DEPDIR)/android_ipc_tester-hal-utils.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@android/$(DEPDIR)/android_ipc_tester-ipc-tester.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@android/$(DEPDIR)/avctp.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@android/$(DEPDIR)/avdtp.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@android/$(DEPDIR)/avrcp-lib.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@android/$(DEPDIR)/avrcp.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@android/$(DEPDIR)/bluetooth.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@android/$(DEPDIR)/bluetoothd-snoop.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@android/$(DEPDIR)/gatt.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@android/$(DEPDIR)/handsfree.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@android/$(DEPDIR)/health.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@android/$(DEPDIR)/hidhost.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@android/$(DEPDIR)/ipc.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@android/$(DEPDIR)/main.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@android/$(DEPDIR)/pan.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@android/$(DEPDIR)/socket.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@android/$(DEPDIR)/system-emulator.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@android/$(DEPDIR)/test-ipc.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@android/client/$(DEPDIR)/android_haltest-haltest.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@android/client/$(DEPDIR)/android_haltest-history.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@android/client/$(DEPDIR)/android_haltest-if-audio.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@android/client/$(DEPDIR)/android_haltest-if-av.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@android/client/$(DEPDIR)/android_haltest-if-bt.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@android/client/$(DEPDIR)/android_haltest-if-gatt.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@android/client/$(DEPDIR)/android_haltest-if-hf.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@android/client/$(DEPDIR)/android_haltest-if-hh.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@android/client/$(DEPDIR)/android_haltest-if-pan.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@android/client/$(DEPDIR)/android_haltest-if-rc.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@android/client/$(DEPDIR)/android_haltest-if-sock.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@android/client/$(DEPDIR)/android_haltest-pollhandler.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@android/client/$(DEPDIR)/android_haltest-tabcompletion.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@android/client/$(DEPDIR)/android_haltest-terminal.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@android/hardware/$(DEPDIR)/android_android_tester-hardware.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@android/hardware/$(DEPDIR)/android_haltest-hardware.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@attrib/$(DEPDIR)/att.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@attrib/$(DEPDIR)/bluetoothd-att.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@attrib/$(DEPDIR)/bluetoothd-gatt-service.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@attrib/$(DEPDIR)/bluetoothd-gatt.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@attrib/$(DEPDIR)/bluetoothd-gattrib.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@attrib/$(DEPDIR)/gatt.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@attrib/$(DEPDIR)/gattrib.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@attrib/$(DEPDIR)/gatttool.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@attrib/$(DEPDIR)/interactive.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@attrib/$(DEPDIR)/utils.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@btio/$(DEPDIR)/bluetoothd-btio.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@btio/$(DEPDIR)/btio.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@btio/$(DEPDIR)/obexd-btio.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@client/$(DEPDIR)/agent.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@client/$(DEPDIR)/display.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@client/$(DEPDIR)/main.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@emulator/$(DEPDIR)/amp.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@emulator/$(DEPDIR)/android_android_tester-btdev.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@emulator/$(DEPDIR)/android_android_tester-bthost.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@emulator/$(DEPDIR)/android_android_tester-smp.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@emulator/$(DEPDIR)/android_ipc_tester-btdev.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@emulator/$(DEPDIR)/android_ipc_tester-bthost.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@emulator/$(DEPDIR)/android_ipc_tester-smp.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@emulator/$(DEPDIR)/b1ee.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@emulator/$(DEPDIR)/btdev.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@emulator/$(DEPDIR)/bthost.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@emulator/$(DEPDIR)/hfp.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@emulator/$(DEPDIR)/le.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@emulator/$(DEPDIR)/main.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@emulator/$(DEPDIR)/server.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@emulator/$(DEPDIR)/smp.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@emulator/$(DEPDIR)/vhci.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@gdbus/$(DEPDIR)/client.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@gdbus/$(DEPDIR)/mainloop.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@gdbus/$(DEPDIR)/object.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@gdbus/$(DEPDIR)/polkit.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@gdbus/$(DEPDIR)/watch.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@gobex/$(DEPDIR)/gobex-apparam.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@gobex/$(DEPDIR)/gobex-defs.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@gobex/$(DEPDIR)/gobex-header.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@gobex/$(DEPDIR)/gobex-packet.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@gobex/$(DEPDIR)/gobex-transfer.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@gobex/$(DEPDIR)/gobex.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@gobex/$(DEPDIR)/obexd-gobex-apparam.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@gobex/$(DEPDIR)/obexd-gobex-defs.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@gobex/$(DEPDIR)/obexd-gobex-header.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@gobex/$(DEPDIR)/obexd-gobex-packet.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@gobex/$(DEPDIR)/obexd-gobex-transfer.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@gobex/$(DEPDIR)/obexd-gobex.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@lib/$(DEPDIR)/bluetooth.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@lib/$(DEPDIR)/hci.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@lib/$(DEPDIR)/sdp.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@lib/$(DEPDIR)/uuid.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@monitor/$(DEPDIR)/analyze.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@monitor/$(DEPDIR)/control.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@monitor/$(DEPDIR)/crc.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@monitor/$(DEPDIR)/display.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@monitor/$(DEPDIR)/ellisys.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@monitor/$(DEPDIR)/hcidump.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@monitor/$(DEPDIR)/hwdb.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@monitor/$(DEPDIR)/keys.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@monitor/$(DEPDIR)/l2cap.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@monitor/$(DEPDIR)/ll.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@monitor/$(DEPDIR)/lmp.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@monitor/$(DEPDIR)/main.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@monitor/$(DEPDIR)/mainloop.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@monitor/$(DEPDIR)/packet.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@monitor/$(DEPDIR)/sdp.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@monitor/$(DEPDIR)/uuid.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@monitor/$(DEPDIR)/vendor.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@obexd/client/$(DEPDIR)/obexd-bluetooth.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@obexd/client/$(DEPDIR)/obexd-dbus.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@obexd/client/$(DEPDIR)/obexd-driver.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@obexd/client/$(DEPDIR)/obexd-ftp.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@obexd/client/$(DEPDIR)/obexd-manager.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@obexd/client/$(DEPDIR)/obexd-map-event.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@obexd/client/$(DEPDIR)/obexd-map.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@obexd/client/$(DEPDIR)/obexd-mns.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@obexd/client/$(DEPDIR)/obexd-opp.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@obexd/client/$(DEPDIR)/obexd-pbap.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@obexd/client/$(DEPDIR)/obexd-session.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@obexd/client/$(DEPDIR)/obexd-sync.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@obexd/client/$(DEPDIR)/obexd-transfer.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@obexd/client/$(DEPDIR)/obexd-transport.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@obexd/plugins/$(DEPDIR)/obexd-bluetooth.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@obexd/plugins/$(DEPDIR)/obexd-filesystem.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@obexd/plugins/$(DEPDIR)/obexd-ftp.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@obexd/plugins/$(DEPDIR)/obexd-irmc.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@obexd/plugins/$(DEPDIR)/obexd-mas.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@obexd/plugins/$(DEPDIR)/obexd-messages-dummy.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@obexd/plugins/$(DEPDIR)/obexd-opp.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@obexd/plugins/$(DEPDIR)/obexd-pbap.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@obexd/plugins/$(DEPDIR)/obexd-pcsuite.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@obexd/plugins/$(DEPDIR)/obexd-phonebook-dummy.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@obexd/plugins/$(DEPDIR)/obexd-vcard.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@obexd/src/$(DEPDIR)/obexd-log.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@obexd/src/$(DEPDIR)/obexd-main.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@obexd/src/$(DEPDIR)/obexd-manager.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@obexd/src/$(DEPDIR)/obexd-mimetype.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@obexd/src/$(DEPDIR)/obexd-obex.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@obexd/src/$(DEPDIR)/obexd-plugin.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@obexd/src/$(DEPDIR)/obexd-server.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@obexd/src/$(DEPDIR)/obexd-service.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@obexd/src/$(DEPDIR)/obexd-transport.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@plugins/$(DEPDIR)/bluetoothd-autopair.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@plugins/$(DEPDIR)/bluetoothd-dropcam.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@plugins/$(DEPDIR)/bluetoothd-gatt-example.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@plugins/$(DEPDIR)/bluetoothd-hostname.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@plugins/$(DEPDIR)/bluetoothd-neard.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@plugins/$(DEPDIR)/bluetoothd-policy.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@plugins/$(DEPDIR)/bluetoothd-wiimote.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@plugins/$(DEPDIR)/plugins_external_dummy_la-external-dummy.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@plugins/$(DEPDIR)/plugins_sixaxis_la-sixaxis.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@profiles/alert/$(DEPDIR)/bluetoothd-server.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@profiles/audio/$(DEPDIR)/bluetoothd-a2dp.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@profiles/audio/$(DEPDIR)/bluetoothd-avctp.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@profiles/audio/$(DEPDIR)/bluetoothd-avdtp.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@profiles/audio/$(DEPDIR)/bluetoothd-avrcp.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@profiles/audio/$(DEPDIR)/bluetoothd-control.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@profiles/audio/$(DEPDIR)/bluetoothd-media.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@profiles/audio/$(DEPDIR)/bluetoothd-player.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@profiles/audio/$(DEPDIR)/bluetoothd-sink.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@profiles/audio/$(DEPDIR)/bluetoothd-source.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@profiles/audio/$(DEPDIR)/bluetoothd-transport.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@profiles/cups/$(DEPDIR)/hcrp.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@profiles/cups/$(DEPDIR)/main.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@profiles/cups/$(DEPDIR)/sdp.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@profiles/cups/$(DEPDIR)/spp.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@profiles/cyclingspeed/$(DEPDIR)/bluetoothd-cyclingspeed.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@profiles/deviceinfo/$(DEPDIR)/bluetoothd-deviceinfo.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@profiles/gatt/$(DEPDIR)/bluetoothd-gas.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@profiles/health/$(DEPDIR)/bluetoothd-hdp.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@profiles/health/$(DEPDIR)/bluetoothd-hdp_main.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@profiles/health/$(DEPDIR)/bluetoothd-hdp_manager.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@profiles/health/$(DEPDIR)/bluetoothd-hdp_util.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@profiles/health/$(DEPDIR)/bluetoothd-mcap.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@profiles/health/$(DEPDIR)/bluetoothd-mcap_sync.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@profiles/heartrate/$(DEPDIR)/bluetoothd-heartrate.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@profiles/iap/$(DEPDIR)/main.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@profiles/input/$(DEPDIR)/bluetoothd-device.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@profiles/input/$(DEPDIR)/bluetoothd-hog.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@profiles/input/$(DEPDIR)/bluetoothd-manager.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@profiles/input/$(DEPDIR)/bluetoothd-server.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@profiles/input/$(DEPDIR)/bluetoothd-suspend-dummy.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@profiles/network/$(DEPDIR)/bluetoothd-bnep.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@profiles/network/$(DEPDIR)/bluetoothd-connection.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@profiles/network/$(DEPDIR)/bluetoothd-manager.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@profiles/network/$(DEPDIR)/bluetoothd-server.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@profiles/network/$(DEPDIR)/bnep.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@profiles/proximity/$(DEPDIR)/bluetoothd-immalert.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@profiles/proximity/$(DEPDIR)/bluetoothd-linkloss.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@profiles/proximity/$(DEPDIR)/bluetoothd-main.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@profiles/proximity/$(DEPDIR)/bluetoothd-manager.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@profiles/proximity/$(DEPDIR)/bluetoothd-monitor.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@profiles/proximity/$(DEPDIR)/bluetoothd-reporter.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@profiles/sap/$(DEPDIR)/bluetoothd-main.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@profiles/sap/$(DEPDIR)/bluetoothd-manager.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@profiles/sap/$(DEPDIR)/bluetoothd-sap-dummy.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@profiles/sap/$(DEPDIR)/bluetoothd-server.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@profiles/sap/$(DEPDIR)/sap-u8500.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@profiles/scanparam/$(DEPDIR)/bluetoothd-scan.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@profiles/thermometer/$(DEPDIR)/bluetoothd-thermometer.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@profiles/time/$(DEPDIR)/bluetoothd-server.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/bluetoothd-adapter.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/bluetoothd-agent.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/bluetoothd-attrib-server.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/bluetoothd-dbus-common.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/bluetoothd-device.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/bluetoothd-eir.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/bluetoothd-error.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/bluetoothd-gatt-dbus.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/bluetoothd-gatt.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/bluetoothd-log.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/bluetoothd-main.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/bluetoothd-plugin.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/bluetoothd-profile.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/bluetoothd-rfkill.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/bluetoothd-sdp-client.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/bluetoothd-sdp-xml.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/bluetoothd-sdpd-database.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/bluetoothd-sdpd-request.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/bluetoothd-sdpd-server.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/bluetoothd-sdpd-service.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/bluetoothd-service.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/bluetoothd-storage.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/bluetoothd-systemd.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/bluetoothd-textfile.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/bluetoothd-uuid-helper.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/eir.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/log.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/oui.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/sdp-client.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/sdp-xml.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/sdpd-database.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/sdpd-request.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/sdpd-server.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/sdpd-service.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/textfile.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/uuid-helper.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@src/shared/$(DEPDIR)/android_android_tester-crypto.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@src/shared/$(DEPDIR)/android_android_tester-hciemu.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@src/shared/$(DEPDIR)/android_android_tester-io-glib.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@src/shared/$(DEPDIR)/android_android_tester-mgmt.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@src/shared/$(DEPDIR)/android_android_tester-queue.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@src/shared/$(DEPDIR)/android_android_tester-tester.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@src/shared/$(DEPDIR)/android_android_tester-timeout-glib.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@src/shared/$(DEPDIR)/android_android_tester-util.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@src/shared/$(DEPDIR)/android_ipc_tester-crypto.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@src/shared/$(DEPDIR)/android_ipc_tester-hciemu.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@src/shared/$(DEPDIR)/android_ipc_tester-io-glib.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@src/shared/$(DEPDIR)/android_ipc_tester-mgmt.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@src/shared/$(DEPDIR)/android_ipc_tester-queue.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@src/shared/$(DEPDIR)/android_ipc_tester-tester.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@src/shared/$(DEPDIR)/android_ipc_tester-timeout-glib.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@src/shared/$(DEPDIR)/android_ipc_tester-util.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@src/shared/$(DEPDIR)/bluetoothd-io-glib.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@src/shared/$(DEPDIR)/bluetoothd-mgmt.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@src/shared/$(DEPDIR)/bluetoothd-queue.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@src/shared/$(DEPDIR)/bluetoothd-timeout-glib.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@src/shared/$(DEPDIR)/bluetoothd-util.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@src/shared/$(DEPDIR)/btsnoop.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@src/shared/$(DEPDIR)/crypto.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@src/shared/$(DEPDIR)/hci.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@src/shared/$(DEPDIR)/hciemu.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@src/shared/$(DEPDIR)/hfp.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@src/shared/$(DEPDIR)/io-glib.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@src/shared/$(DEPDIR)/io-mainloop.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@src/shared/$(DEPDIR)/mgmt.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@src/shared/$(DEPDIR)/pcap.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@src/shared/$(DEPDIR)/queue.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@src/shared/$(DEPDIR)/ringbuf.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@src/shared/$(DEPDIR)/tester.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@src/shared/$(DEPDIR)/timeout-glib.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@src/shared/$(DEPDIR)/timeout-mainloop.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@src/shared/$(DEPDIR)/util.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/3dsp.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/amptest.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/avinfo.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/avtest.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/bccmd.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/bdaddr.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/bluemoon.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/bluetooth-player.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/btattach.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/btinfo.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/btiotest.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/btmgmt.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/btproxy.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/btsnoop.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/ciptool.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/cltest.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/csr.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/csr_3wire.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/csr_bcsp.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/csr_h4.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/csr_hci.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/csr_usb.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/gap-tester.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/gatt-service.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/hci-tester.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/hciattach.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/hciattach_ath3k.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/hciattach_intel.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/hciattach_qualcomm.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/hciattach_st.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/hciattach_ti.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/hciattach_tialt.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/hciconfig.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/hcidump.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/hcieventmask.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/hcisecfilter.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/hcitool.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/hid2hci.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/hwdb.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/ibeacon.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/l2cap-tester.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/l2ping.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/l2test.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/mgmt-tester.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/mpris-player.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/obex-client-tool.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/obex-server-tool.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/obexctl.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/rctest.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/rfcomm-tester.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/rfcomm.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/sco-tester.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/scotest.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/sdptool.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/seq2bseq.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/smp-tester.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/ubcsp.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@tools/parser/$(DEPDIR)/amp.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@tools/parser/$(DEPDIR)/att.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@tools/parser/$(DEPDIR)/avctp.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@tools/parser/$(DEPDIR)/avdtp.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@tools/parser/$(DEPDIR)/avrcp.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@tools/parser/$(DEPDIR)/bnep.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@tools/parser/$(DEPDIR)/bpa.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@tools/parser/$(DEPDIR)/capi.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@tools/parser/$(DEPDIR)/cmtp.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@tools/parser/$(DEPDIR)/csr.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@tools/parser/$(DEPDIR)/ericsson.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@tools/parser/$(DEPDIR)/hci.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@tools/parser/$(DEPDIR)/hcrp.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@tools/parser/$(DEPDIR)/hidp.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@tools/parser/$(DEPDIR)/l2cap.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@tools/parser/$(DEPDIR)/lmp.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@tools/parser/$(DEPDIR)/obex.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@tools/parser/$(DEPDIR)/parser.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@tools/parser/$(DEPDIR)/ppp.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@tools/parser/$(DEPDIR)/rfcomm.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@tools/parser/$(DEPDIR)/sap.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@tools/parser/$(DEPDIR)/sdp.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@tools/parser/$(DEPDIR)/smp.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@tools/parser/$(DEPDIR)/tcpip.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@unit/$(DEPDIR)/test-avctp.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@unit/$(DEPDIR)/test-avdtp.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@unit/$(DEPDIR)/test-avrcp.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@unit/$(DEPDIR)/test-crc.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@unit/$(DEPDIR)/test-eir.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@unit/$(DEPDIR)/test-gdbus-client.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@unit/$(DEPDIR)/test-gobex-apparam.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@unit/$(DEPDIR)/test-gobex-header.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@unit/$(DEPDIR)/test-gobex-packet.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@unit/$(DEPDIR)/test-gobex-transfer.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@unit/$(DEPDIR)/test-gobex.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@unit/$(DEPDIR)/test-hfp.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@unit/$(DEPDIR)/test-lib.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@unit/$(DEPDIR)/test-mgmt.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@unit/$(DEPDIR)/test-queue.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@unit/$(DEPDIR)/test-ringbuf.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@unit/$(DEPDIR)/test-sdp.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@unit/$(DEPDIR)/test-textfile.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@unit/$(DEPDIR)/test-uuid.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@unit/$(DEPDIR)/util.Po@am__quote@
+
+.c.o:
+@am__fastdepCC_TRUE@	$(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.o$$||'`;\
+@am__fastdepCC_TRUE@	$(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\
+@am__fastdepCC_TRUE@	$(am__mv) $$depbase.Tpo $$depbase.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $<
+
+.c.obj:
+@am__fastdepCC_TRUE@	$(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.obj$$||'`;\
+@am__fastdepCC_TRUE@	$(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ `$(CYGPATH_W) '$<'` &&\
+@am__fastdepCC_TRUE@	$(am__mv) $$depbase.Tpo $$depbase.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'`
+
+.c.lo:
+@am__fastdepCC_TRUE@	$(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.lo$$||'`;\
+@am__fastdepCC_TRUE@	$(LTCOMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\
+@am__fastdepCC_TRUE@	$(am__mv) $$depbase.Tpo $$depbase.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $<
+
+android/android_audio_a2dp_default_la-hal-audio.lo: android/hal-audio.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_audio_a2dp_default_la_CFLAGS) $(CFLAGS) -MT android/android_audio_a2dp_default_la-hal-audio.lo -MD -MP -MF android/$(DEPDIR)/android_audio_a2dp_default_la-hal-audio.Tpo -c -o android/android_audio_a2dp_default_la-hal-audio.lo `test -f 'android/hal-audio.c' || echo '$(srcdir)/'`android/hal-audio.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) android/$(DEPDIR)/android_audio_a2dp_default_la-hal-audio.Tpo android/$(DEPDIR)/android_audio_a2dp_default_la-hal-audio.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='android/hal-audio.c' object='android/android_audio_a2dp_default_la-hal-audio.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_audio_a2dp_default_la_CFLAGS) $(CFLAGS) -c -o android/android_audio_a2dp_default_la-hal-audio.lo `test -f 'android/hal-audio.c' || echo '$(srcdir)/'`android/hal-audio.c
+
+android/android_bluetooth_default_la-hal-bluetooth.lo: android/hal-bluetooth.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_bluetooth_default_la_CFLAGS) $(CFLAGS) -MT android/android_bluetooth_default_la-hal-bluetooth.lo -MD -MP -MF android/$(DEPDIR)/android_bluetooth_default_la-hal-bluetooth.Tpo -c -o android/android_bluetooth_default_la-hal-bluetooth.lo `test -f 'android/hal-bluetooth.c' || echo '$(srcdir)/'`android/hal-bluetooth.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) android/$(DEPDIR)/android_bluetooth_default_la-hal-bluetooth.Tpo android/$(DEPDIR)/android_bluetooth_default_la-hal-bluetooth.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='android/hal-bluetooth.c' object='android/android_bluetooth_default_la-hal-bluetooth.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_bluetooth_default_la_CFLAGS) $(CFLAGS) -c -o android/android_bluetooth_default_la-hal-bluetooth.lo `test -f 'android/hal-bluetooth.c' || echo '$(srcdir)/'`android/hal-bluetooth.c
+
+android/android_bluetooth_default_la-hal-socket.lo: android/hal-socket.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_bluetooth_default_la_CFLAGS) $(CFLAGS) -MT android/android_bluetooth_default_la-hal-socket.lo -MD -MP -MF android/$(DEPDIR)/android_bluetooth_default_la-hal-socket.Tpo -c -o android/android_bluetooth_default_la-hal-socket.lo `test -f 'android/hal-socket.c' || echo '$(srcdir)/'`android/hal-socket.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) android/$(DEPDIR)/android_bluetooth_default_la-hal-socket.Tpo android/$(DEPDIR)/android_bluetooth_default_la-hal-socket.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='android/hal-socket.c' object='android/android_bluetooth_default_la-hal-socket.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_bluetooth_default_la_CFLAGS) $(CFLAGS) -c -o android/android_bluetooth_default_la-hal-socket.lo `test -f 'android/hal-socket.c' || echo '$(srcdir)/'`android/hal-socket.c
+
+android/android_bluetooth_default_la-hal-hidhost.lo: android/hal-hidhost.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_bluetooth_default_la_CFLAGS) $(CFLAGS) -MT android/android_bluetooth_default_la-hal-hidhost.lo -MD -MP -MF android/$(DEPDIR)/android_bluetooth_default_la-hal-hidhost.Tpo -c -o android/android_bluetooth_default_la-hal-hidhost.lo `test -f 'android/hal-hidhost.c' || echo '$(srcdir)/'`android/hal-hidhost.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) android/$(DEPDIR)/android_bluetooth_default_la-hal-hidhost.Tpo android/$(DEPDIR)/android_bluetooth_default_la-hal-hidhost.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='android/hal-hidhost.c' object='android/android_bluetooth_default_la-hal-hidhost.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_bluetooth_default_la_CFLAGS) $(CFLAGS) -c -o android/android_bluetooth_default_la-hal-hidhost.lo `test -f 'android/hal-hidhost.c' || echo '$(srcdir)/'`android/hal-hidhost.c
+
+android/android_bluetooth_default_la-hal-health.lo: android/hal-health.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_bluetooth_default_la_CFLAGS) $(CFLAGS) -MT android/android_bluetooth_default_la-hal-health.lo -MD -MP -MF android/$(DEPDIR)/android_bluetooth_default_la-hal-health.Tpo -c -o android/android_bluetooth_default_la-hal-health.lo `test -f 'android/hal-health.c' || echo '$(srcdir)/'`android/hal-health.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) android/$(DEPDIR)/android_bluetooth_default_la-hal-health.Tpo android/$(DEPDIR)/android_bluetooth_default_la-hal-health.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='android/hal-health.c' object='android/android_bluetooth_default_la-hal-health.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_bluetooth_default_la_CFLAGS) $(CFLAGS) -c -o android/android_bluetooth_default_la-hal-health.lo `test -f 'android/hal-health.c' || echo '$(srcdir)/'`android/hal-health.c
+
+android/android_bluetooth_default_la-hal-pan.lo: android/hal-pan.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_bluetooth_default_la_CFLAGS) $(CFLAGS) -MT android/android_bluetooth_default_la-hal-pan.lo -MD -MP -MF android/$(DEPDIR)/android_bluetooth_default_la-hal-pan.Tpo -c -o android/android_bluetooth_default_la-hal-pan.lo `test -f 'android/hal-pan.c' || echo '$(srcdir)/'`android/hal-pan.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) android/$(DEPDIR)/android_bluetooth_default_la-hal-pan.Tpo android/$(DEPDIR)/android_bluetooth_default_la-hal-pan.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='android/hal-pan.c' object='android/android_bluetooth_default_la-hal-pan.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_bluetooth_default_la_CFLAGS) $(CFLAGS) -c -o android/android_bluetooth_default_la-hal-pan.lo `test -f 'android/hal-pan.c' || echo '$(srcdir)/'`android/hal-pan.c
+
+android/android_bluetooth_default_la-hal-a2dp.lo: android/hal-a2dp.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_bluetooth_default_la_CFLAGS) $(CFLAGS) -MT android/android_bluetooth_default_la-hal-a2dp.lo -MD -MP -MF android/$(DEPDIR)/android_bluetooth_default_la-hal-a2dp.Tpo -c -o android/android_bluetooth_default_la-hal-a2dp.lo `test -f 'android/hal-a2dp.c' || echo '$(srcdir)/'`android/hal-a2dp.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) android/$(DEPDIR)/android_bluetooth_default_la-hal-a2dp.Tpo android/$(DEPDIR)/android_bluetooth_default_la-hal-a2dp.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='android/hal-a2dp.c' object='android/android_bluetooth_default_la-hal-a2dp.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_bluetooth_default_la_CFLAGS) $(CFLAGS) -c -o android/android_bluetooth_default_la-hal-a2dp.lo `test -f 'android/hal-a2dp.c' || echo '$(srcdir)/'`android/hal-a2dp.c
+
+android/android_bluetooth_default_la-hal-avrcp.lo: android/hal-avrcp.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_bluetooth_default_la_CFLAGS) $(CFLAGS) -MT android/android_bluetooth_default_la-hal-avrcp.lo -MD -MP -MF android/$(DEPDIR)/android_bluetooth_default_la-hal-avrcp.Tpo -c -o android/android_bluetooth_default_la-hal-avrcp.lo `test -f 'android/hal-avrcp.c' || echo '$(srcdir)/'`android/hal-avrcp.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) android/$(DEPDIR)/android_bluetooth_default_la-hal-avrcp.Tpo android/$(DEPDIR)/android_bluetooth_default_la-hal-avrcp.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='android/hal-avrcp.c' object='android/android_bluetooth_default_la-hal-avrcp.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_bluetooth_default_la_CFLAGS) $(CFLAGS) -c -o android/android_bluetooth_default_la-hal-avrcp.lo `test -f 'android/hal-avrcp.c' || echo '$(srcdir)/'`android/hal-avrcp.c
+
+android/android_bluetooth_default_la-hal-handsfree.lo: android/hal-handsfree.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_bluetooth_default_la_CFLAGS) $(CFLAGS) -MT android/android_bluetooth_default_la-hal-handsfree.lo -MD -MP -MF android/$(DEPDIR)/android_bluetooth_default_la-hal-handsfree.Tpo -c -o android/android_bluetooth_default_la-hal-handsfree.lo `test -f 'android/hal-handsfree.c' || echo '$(srcdir)/'`android/hal-handsfree.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) android/$(DEPDIR)/android_bluetooth_default_la-hal-handsfree.Tpo android/$(DEPDIR)/android_bluetooth_default_la-hal-handsfree.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='android/hal-handsfree.c' object='android/android_bluetooth_default_la-hal-handsfree.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_bluetooth_default_la_CFLAGS) $(CFLAGS) -c -o android/android_bluetooth_default_la-hal-handsfree.lo `test -f 'android/hal-handsfree.c' || echo '$(srcdir)/'`android/hal-handsfree.c
+
+android/android_bluetooth_default_la-hal-gatt.lo: android/hal-gatt.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_bluetooth_default_la_CFLAGS) $(CFLAGS) -MT android/android_bluetooth_default_la-hal-gatt.lo -MD -MP -MF android/$(DEPDIR)/android_bluetooth_default_la-hal-gatt.Tpo -c -o android/android_bluetooth_default_la-hal-gatt.lo `test -f 'android/hal-gatt.c' || echo '$(srcdir)/'`android/hal-gatt.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) android/$(DEPDIR)/android_bluetooth_default_la-hal-gatt.Tpo android/$(DEPDIR)/android_bluetooth_default_la-hal-gatt.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='android/hal-gatt.c' object='android/android_bluetooth_default_la-hal-gatt.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_bluetooth_default_la_CFLAGS) $(CFLAGS) -c -o android/android_bluetooth_default_la-hal-gatt.lo `test -f 'android/hal-gatt.c' || echo '$(srcdir)/'`android/hal-gatt.c
+
+android/android_bluetooth_default_la-hal-ipc.lo: android/hal-ipc.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_bluetooth_default_la_CFLAGS) $(CFLAGS) -MT android/android_bluetooth_default_la-hal-ipc.lo -MD -MP -MF android/$(DEPDIR)/android_bluetooth_default_la-hal-ipc.Tpo -c -o android/android_bluetooth_default_la-hal-ipc.lo `test -f 'android/hal-ipc.c' || echo '$(srcdir)/'`android/hal-ipc.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) android/$(DEPDIR)/android_bluetooth_default_la-hal-ipc.Tpo android/$(DEPDIR)/android_bluetooth_default_la-hal-ipc.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='android/hal-ipc.c' object='android/android_bluetooth_default_la-hal-ipc.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_bluetooth_default_la_CFLAGS) $(CFLAGS) -c -o android/android_bluetooth_default_la-hal-ipc.lo `test -f 'android/hal-ipc.c' || echo '$(srcdir)/'`android/hal-ipc.c
+
+android/android_bluetooth_default_la-hal-utils.lo: android/hal-utils.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_bluetooth_default_la_CFLAGS) $(CFLAGS) -MT android/android_bluetooth_default_la-hal-utils.lo -MD -MP -MF android/$(DEPDIR)/android_bluetooth_default_la-hal-utils.Tpo -c -o android/android_bluetooth_default_la-hal-utils.lo `test -f 'android/hal-utils.c' || echo '$(srcdir)/'`android/hal-utils.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) android/$(DEPDIR)/android_bluetooth_default_la-hal-utils.Tpo android/$(DEPDIR)/android_bluetooth_default_la-hal-utils.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='android/hal-utils.c' object='android/android_bluetooth_default_la-hal-utils.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_bluetooth_default_la_CFLAGS) $(CFLAGS) -c -o android/android_bluetooth_default_la-hal-utils.lo `test -f 'android/hal-utils.c' || echo '$(srcdir)/'`android/hal-utils.c
+
+plugins/plugins_external_dummy_la-external-dummy.lo: plugins/external-dummy.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(plugins_external_dummy_la_CFLAGS) $(CFLAGS) -MT plugins/plugins_external_dummy_la-external-dummy.lo -MD -MP -MF plugins/$(DEPDIR)/plugins_external_dummy_la-external-dummy.Tpo -c -o plugins/plugins_external_dummy_la-external-dummy.lo `test -f 'plugins/external-dummy.c' || echo '$(srcdir)/'`plugins/external-dummy.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) plugins/$(DEPDIR)/plugins_external_dummy_la-external-dummy.Tpo plugins/$(DEPDIR)/plugins_external_dummy_la-external-dummy.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='plugins/external-dummy.c' object='plugins/plugins_external_dummy_la-external-dummy.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(plugins_external_dummy_la_CFLAGS) $(CFLAGS) -c -o plugins/plugins_external_dummy_la-external-dummy.lo `test -f 'plugins/external-dummy.c' || echo '$(srcdir)/'`plugins/external-dummy.c
+
+plugins/plugins_sixaxis_la-sixaxis.lo: plugins/sixaxis.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(plugins_sixaxis_la_CFLAGS) $(CFLAGS) -MT plugins/plugins_sixaxis_la-sixaxis.lo -MD -MP -MF plugins/$(DEPDIR)/plugins_sixaxis_la-sixaxis.Tpo -c -o plugins/plugins_sixaxis_la-sixaxis.lo `test -f 'plugins/sixaxis.c' || echo '$(srcdir)/'`plugins/sixaxis.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) plugins/$(DEPDIR)/plugins_sixaxis_la-sixaxis.Tpo plugins/$(DEPDIR)/plugins_sixaxis_la-sixaxis.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='plugins/sixaxis.c' object='plugins/plugins_sixaxis_la-sixaxis.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(plugins_sixaxis_la_CFLAGS) $(CFLAGS) -c -o plugins/plugins_sixaxis_la-sixaxis.lo `test -f 'plugins/sixaxis.c' || echo '$(srcdir)/'`plugins/sixaxis.c
+
+emulator/android_android_tester-btdev.o: emulator/btdev.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_android_tester_CFLAGS) $(CFLAGS) -MT emulator/android_android_tester-btdev.o -MD -MP -MF emulator/$(DEPDIR)/android_android_tester-btdev.Tpo -c -o emulator/android_android_tester-btdev.o `test -f 'emulator/btdev.c' || echo '$(srcdir)/'`emulator/btdev.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) emulator/$(DEPDIR)/android_android_tester-btdev.Tpo emulator/$(DEPDIR)/android_android_tester-btdev.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='emulator/btdev.c' object='emulator/android_android_tester-btdev.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_android_tester_CFLAGS) $(CFLAGS) -c -o emulator/android_android_tester-btdev.o `test -f 'emulator/btdev.c' || echo '$(srcdir)/'`emulator/btdev.c
+
+emulator/android_android_tester-btdev.obj: emulator/btdev.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_android_tester_CFLAGS) $(CFLAGS) -MT emulator/android_android_tester-btdev.obj -MD -MP -MF emulator/$(DEPDIR)/android_android_tester-btdev.Tpo -c -o emulator/android_android_tester-btdev.obj `if test -f 'emulator/btdev.c'; then $(CYGPATH_W) 'emulator/btdev.c'; else $(CYGPATH_W) '$(srcdir)/emulator/btdev.c'; fi`
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) emulator/$(DEPDIR)/android_android_tester-btdev.Tpo emulator/$(DEPDIR)/android_android_tester-btdev.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='emulator/btdev.c' object='emulator/android_android_tester-btdev.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_android_tester_CFLAGS) $(CFLAGS) -c -o emulator/android_android_tester-btdev.obj `if test -f 'emulator/btdev.c'; then $(CYGPATH_W) 'emulator/btdev.c'; else $(CYGPATH_W) '$(srcdir)/emulator/btdev.c'; fi`
+
+emulator/android_android_tester-bthost.o: emulator/bthost.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_android_tester_CFLAGS) $(CFLAGS) -MT emulator/android_android_tester-bthost.o -MD -MP -MF emulator/$(DEPDIR)/android_android_tester-bthost.Tpo -c -o emulator/android_android_tester-bthost.o `test -f 'emulator/bthost.c' || echo '$(srcdir)/'`emulator/bthost.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) emulator/$(DEPDIR)/android_android_tester-bthost.Tpo emulator/$(DEPDIR)/android_android_tester-bthost.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='emulator/bthost.c' object='emulator/android_android_tester-bthost.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_android_tester_CFLAGS) $(CFLAGS) -c -o emulator/android_android_tester-bthost.o `test -f 'emulator/bthost.c' || echo '$(srcdir)/'`emulator/bthost.c
+
+emulator/android_android_tester-bthost.obj: emulator/bthost.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_android_tester_CFLAGS) $(CFLAGS) -MT emulator/android_android_tester-bthost.obj -MD -MP -MF emulator/$(DEPDIR)/android_android_tester-bthost.Tpo -c -o emulator/android_android_tester-bthost.obj `if test -f 'emulator/bthost.c'; then $(CYGPATH_W) 'emulator/bthost.c'; else $(CYGPATH_W) '$(srcdir)/emulator/bthost.c'; fi`
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) emulator/$(DEPDIR)/android_android_tester-bthost.Tpo emulator/$(DEPDIR)/android_android_tester-bthost.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='emulator/bthost.c' object='emulator/android_android_tester-bthost.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_android_tester_CFLAGS) $(CFLAGS) -c -o emulator/android_android_tester-bthost.obj `if test -f 'emulator/bthost.c'; then $(CYGPATH_W) 'emulator/bthost.c'; else $(CYGPATH_W) '$(srcdir)/emulator/bthost.c'; fi`
+
+emulator/android_android_tester-smp.o: emulator/smp.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_android_tester_CFLAGS) $(CFLAGS) -MT emulator/android_android_tester-smp.o -MD -MP -MF emulator/$(DEPDIR)/android_android_tester-smp.Tpo -c -o emulator/android_android_tester-smp.o `test -f 'emulator/smp.c' || echo '$(srcdir)/'`emulator/smp.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) emulator/$(DEPDIR)/android_android_tester-smp.Tpo emulator/$(DEPDIR)/android_android_tester-smp.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='emulator/smp.c' object='emulator/android_android_tester-smp.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_android_tester_CFLAGS) $(CFLAGS) -c -o emulator/android_android_tester-smp.o `test -f 'emulator/smp.c' || echo '$(srcdir)/'`emulator/smp.c
+
+emulator/android_android_tester-smp.obj: emulator/smp.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_android_tester_CFLAGS) $(CFLAGS) -MT emulator/android_android_tester-smp.obj -MD -MP -MF emulator/$(DEPDIR)/android_android_tester-smp.Tpo -c -o emulator/android_android_tester-smp.obj `if test -f 'emulator/smp.c'; then $(CYGPATH_W) 'emulator/smp.c'; else $(CYGPATH_W) '$(srcdir)/emulator/smp.c'; fi`
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) emulator/$(DEPDIR)/android_android_tester-smp.Tpo emulator/$(DEPDIR)/android_android_tester-smp.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='emulator/smp.c' object='emulator/android_android_tester-smp.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_android_tester_CFLAGS) $(CFLAGS) -c -o emulator/android_android_tester-smp.obj `if test -f 'emulator/smp.c'; then $(CYGPATH_W) 'emulator/smp.c'; else $(CYGPATH_W) '$(srcdir)/emulator/smp.c'; fi`
+
+src/shared/android_android_tester-crypto.o: src/shared/crypto.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_android_tester_CFLAGS) $(CFLAGS) -MT src/shared/android_android_tester-crypto.o -MD -MP -MF src/shared/$(DEPDIR)/android_android_tester-crypto.Tpo -c -o src/shared/android_android_tester-crypto.o `test -f 'src/shared/crypto.c' || echo '$(srcdir)/'`src/shared/crypto.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) src/shared/$(DEPDIR)/android_android_tester-crypto.Tpo src/shared/$(DEPDIR)/android_android_tester-crypto.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='src/shared/crypto.c' object='src/shared/android_android_tester-crypto.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_android_tester_CFLAGS) $(CFLAGS) -c -o src/shared/android_android_tester-crypto.o `test -f 'src/shared/crypto.c' || echo '$(srcdir)/'`src/shared/crypto.c
+
+src/shared/android_android_tester-crypto.obj: src/shared/crypto.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_android_tester_CFLAGS) $(CFLAGS) -MT src/shared/android_android_tester-crypto.obj -MD -MP -MF src/shared/$(DEPDIR)/android_android_tester-crypto.Tpo -c -o src/shared/android_android_tester-crypto.obj `if test -f 'src/shared/crypto.c'; then $(CYGPATH_W) 'src/shared/crypto.c'; else $(CYGPATH_W) '$(srcdir)/src/shared/crypto.c'; fi`
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) src/shared/$(DEPDIR)/android_android_tester-crypto.Tpo src/shared/$(DEPDIR)/android_android_tester-crypto.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='src/shared/crypto.c' object='src/shared/android_android_tester-crypto.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_android_tester_CFLAGS) $(CFLAGS) -c -o src/shared/android_android_tester-crypto.obj `if test -f 'src/shared/crypto.c'; then $(CYGPATH_W) 'src/shared/crypto.c'; else $(CYGPATH_W) '$(srcdir)/src/shared/crypto.c'; fi`
+
+src/shared/android_android_tester-io-glib.o: src/shared/io-glib.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_android_tester_CFLAGS) $(CFLAGS) -MT src/shared/android_android_tester-io-glib.o -MD -MP -MF src/shared/$(DEPDIR)/android_android_tester-io-glib.Tpo -c -o src/shared/android_android_tester-io-glib.o `test -f 'src/shared/io-glib.c' || echo '$(srcdir)/'`src/shared/io-glib.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) src/shared/$(DEPDIR)/android_android_tester-io-glib.Tpo src/shared/$(DEPDIR)/android_android_tester-io-glib.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='src/shared/io-glib.c' object='src/shared/android_android_tester-io-glib.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_android_tester_CFLAGS) $(CFLAGS) -c -o src/shared/android_android_tester-io-glib.o `test -f 'src/shared/io-glib.c' || echo '$(srcdir)/'`src/shared/io-glib.c
+
+src/shared/android_android_tester-io-glib.obj: src/shared/io-glib.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_android_tester_CFLAGS) $(CFLAGS) -MT src/shared/android_android_tester-io-glib.obj -MD -MP -MF src/shared/$(DEPDIR)/android_android_tester-io-glib.Tpo -c -o src/shared/android_android_tester-io-glib.obj `if test -f 'src/shared/io-glib.c'; then $(CYGPATH_W) 'src/shared/io-glib.c'; else $(CYGPATH_W) '$(srcdir)/src/shared/io-glib.c'; fi`
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) src/shared/$(DEPDIR)/android_android_tester-io-glib.Tpo src/shared/$(DEPDIR)/android_android_tester-io-glib.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='src/shared/io-glib.c' object='src/shared/android_android_tester-io-glib.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_android_tester_CFLAGS) $(CFLAGS) -c -o src/shared/android_android_tester-io-glib.obj `if test -f 'src/shared/io-glib.c'; then $(CYGPATH_W) 'src/shared/io-glib.c'; else $(CYGPATH_W) '$(srcdir)/src/shared/io-glib.c'; fi`
+
+src/shared/android_android_tester-queue.o: src/shared/queue.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_android_tester_CFLAGS) $(CFLAGS) -MT src/shared/android_android_tester-queue.o -MD -MP -MF src/shared/$(DEPDIR)/android_android_tester-queue.Tpo -c -o src/shared/android_android_tester-queue.o `test -f 'src/shared/queue.c' || echo '$(srcdir)/'`src/shared/queue.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) src/shared/$(DEPDIR)/android_android_tester-queue.Tpo src/shared/$(DEPDIR)/android_android_tester-queue.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='src/shared/queue.c' object='src/shared/android_android_tester-queue.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_android_tester_CFLAGS) $(CFLAGS) -c -o src/shared/android_android_tester-queue.o `test -f 'src/shared/queue.c' || echo '$(srcdir)/'`src/shared/queue.c
+
+src/shared/android_android_tester-queue.obj: src/shared/queue.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_android_tester_CFLAGS) $(CFLAGS) -MT src/shared/android_android_tester-queue.obj -MD -MP -MF src/shared/$(DEPDIR)/android_android_tester-queue.Tpo -c -o src/shared/android_android_tester-queue.obj `if test -f 'src/shared/queue.c'; then $(CYGPATH_W) 'src/shared/queue.c'; else $(CYGPATH_W) '$(srcdir)/src/shared/queue.c'; fi`
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) src/shared/$(DEPDIR)/android_android_tester-queue.Tpo src/shared/$(DEPDIR)/android_android_tester-queue.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='src/shared/queue.c' object='src/shared/android_android_tester-queue.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_android_tester_CFLAGS) $(CFLAGS) -c -o src/shared/android_android_tester-queue.obj `if test -f 'src/shared/queue.c'; then $(CYGPATH_W) 'src/shared/queue.c'; else $(CYGPATH_W) '$(srcdir)/src/shared/queue.c'; fi`
+
+src/shared/android_android_tester-util.o: src/shared/util.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_android_tester_CFLAGS) $(CFLAGS) -MT src/shared/android_android_tester-util.o -MD -MP -MF src/shared/$(DEPDIR)/android_android_tester-util.Tpo -c -o src/shared/android_android_tester-util.o `test -f 'src/shared/util.c' || echo '$(srcdir)/'`src/shared/util.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) src/shared/$(DEPDIR)/android_android_tester-util.Tpo src/shared/$(DEPDIR)/android_android_tester-util.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='src/shared/util.c' object='src/shared/android_android_tester-util.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_android_tester_CFLAGS) $(CFLAGS) -c -o src/shared/android_android_tester-util.o `test -f 'src/shared/util.c' || echo '$(srcdir)/'`src/shared/util.c
+
+src/shared/android_android_tester-util.obj: src/shared/util.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_android_tester_CFLAGS) $(CFLAGS) -MT src/shared/android_android_tester-util.obj -MD -MP -MF src/shared/$(DEPDIR)/android_android_tester-util.Tpo -c -o src/shared/android_android_tester-util.obj `if test -f 'src/shared/util.c'; then $(CYGPATH_W) 'src/shared/util.c'; else $(CYGPATH_W) '$(srcdir)/src/shared/util.c'; fi`
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) src/shared/$(DEPDIR)/android_android_tester-util.Tpo src/shared/$(DEPDIR)/android_android_tester-util.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='src/shared/util.c' object='src/shared/android_android_tester-util.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_android_tester_CFLAGS) $(CFLAGS) -c -o src/shared/android_android_tester-util.obj `if test -f 'src/shared/util.c'; then $(CYGPATH_W) 'src/shared/util.c'; else $(CYGPATH_W) '$(srcdir)/src/shared/util.c'; fi`
+
+src/shared/android_android_tester-mgmt.o: src/shared/mgmt.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_android_tester_CFLAGS) $(CFLAGS) -MT src/shared/android_android_tester-mgmt.o -MD -MP -MF src/shared/$(DEPDIR)/android_android_tester-mgmt.Tpo -c -o src/shared/android_android_tester-mgmt.o `test -f 'src/shared/mgmt.c' || echo '$(srcdir)/'`src/shared/mgmt.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) src/shared/$(DEPDIR)/android_android_tester-mgmt.Tpo src/shared/$(DEPDIR)/android_android_tester-mgmt.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='src/shared/mgmt.c' object='src/shared/android_android_tester-mgmt.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_android_tester_CFLAGS) $(CFLAGS) -c -o src/shared/android_android_tester-mgmt.o `test -f 'src/shared/mgmt.c' || echo '$(srcdir)/'`src/shared/mgmt.c
+
+src/shared/android_android_tester-mgmt.obj: src/shared/mgmt.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_android_tester_CFLAGS) $(CFLAGS) -MT src/shared/android_android_tester-mgmt.obj -MD -MP -MF src/shared/$(DEPDIR)/android_android_tester-mgmt.Tpo -c -o src/shared/android_android_tester-mgmt.obj `if test -f 'src/shared/mgmt.c'; then $(CYGPATH_W) 'src/shared/mgmt.c'; else $(CYGPATH_W) '$(srcdir)/src/shared/mgmt.c'; fi`
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) src/shared/$(DEPDIR)/android_android_tester-mgmt.Tpo src/shared/$(DEPDIR)/android_android_tester-mgmt.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='src/shared/mgmt.c' object='src/shared/android_android_tester-mgmt.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_android_tester_CFLAGS) $(CFLAGS) -c -o src/shared/android_android_tester-mgmt.obj `if test -f 'src/shared/mgmt.c'; then $(CYGPATH_W) 'src/shared/mgmt.c'; else $(CYGPATH_W) '$(srcdir)/src/shared/mgmt.c'; fi`
+
+src/shared/android_android_tester-hciemu.o: src/shared/hciemu.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_android_tester_CFLAGS) $(CFLAGS) -MT src/shared/android_android_tester-hciemu.o -MD -MP -MF src/shared/$(DEPDIR)/android_android_tester-hciemu.Tpo -c -o src/shared/android_android_tester-hciemu.o `test -f 'src/shared/hciemu.c' || echo '$(srcdir)/'`src/shared/hciemu.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) src/shared/$(DEPDIR)/android_android_tester-hciemu.Tpo src/shared/$(DEPDIR)/android_android_tester-hciemu.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='src/shared/hciemu.c' object='src/shared/android_android_tester-hciemu.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_android_tester_CFLAGS) $(CFLAGS) -c -o src/shared/android_android_tester-hciemu.o `test -f 'src/shared/hciemu.c' || echo '$(srcdir)/'`src/shared/hciemu.c
+
+src/shared/android_android_tester-hciemu.obj: src/shared/hciemu.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_android_tester_CFLAGS) $(CFLAGS) -MT src/shared/android_android_tester-hciemu.obj -MD -MP -MF src/shared/$(DEPDIR)/android_android_tester-hciemu.Tpo -c -o src/shared/android_android_tester-hciemu.obj `if test -f 'src/shared/hciemu.c'; then $(CYGPATH_W) 'src/shared/hciemu.c'; else $(CYGPATH_W) '$(srcdir)/src/shared/hciemu.c'; fi`
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) src/shared/$(DEPDIR)/android_android_tester-hciemu.Tpo src/shared/$(DEPDIR)/android_android_tester-hciemu.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='src/shared/hciemu.c' object='src/shared/android_android_tester-hciemu.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_android_tester_CFLAGS) $(CFLAGS) -c -o src/shared/android_android_tester-hciemu.obj `if test -f 'src/shared/hciemu.c'; then $(CYGPATH_W) 'src/shared/hciemu.c'; else $(CYGPATH_W) '$(srcdir)/src/shared/hciemu.c'; fi`
+
+src/shared/android_android_tester-tester.o: src/shared/tester.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_android_tester_CFLAGS) $(CFLAGS) -MT src/shared/android_android_tester-tester.o -MD -MP -MF src/shared/$(DEPDIR)/android_android_tester-tester.Tpo -c -o src/shared/android_android_tester-tester.o `test -f 'src/shared/tester.c' || echo '$(srcdir)/'`src/shared/tester.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) src/shared/$(DEPDIR)/android_android_tester-tester.Tpo src/shared/$(DEPDIR)/android_android_tester-tester.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='src/shared/tester.c' object='src/shared/android_android_tester-tester.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_android_tester_CFLAGS) $(CFLAGS) -c -o src/shared/android_android_tester-tester.o `test -f 'src/shared/tester.c' || echo '$(srcdir)/'`src/shared/tester.c
+
+src/shared/android_android_tester-tester.obj: src/shared/tester.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_android_tester_CFLAGS) $(CFLAGS) -MT src/shared/android_android_tester-tester.obj -MD -MP -MF src/shared/$(DEPDIR)/android_android_tester-tester.Tpo -c -o src/shared/android_android_tester-tester.obj `if test -f 'src/shared/tester.c'; then $(CYGPATH_W) 'src/shared/tester.c'; else $(CYGPATH_W) '$(srcdir)/src/shared/tester.c'; fi`
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) src/shared/$(DEPDIR)/android_android_tester-tester.Tpo src/shared/$(DEPDIR)/android_android_tester-tester.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='src/shared/tester.c' object='src/shared/android_android_tester-tester.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_android_tester_CFLAGS) $(CFLAGS) -c -o src/shared/android_android_tester-tester.obj `if test -f 'src/shared/tester.c'; then $(CYGPATH_W) 'src/shared/tester.c'; else $(CYGPATH_W) '$(srcdir)/src/shared/tester.c'; fi`
+
+src/shared/android_android_tester-timeout-glib.o: src/shared/timeout-glib.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_android_tester_CFLAGS) $(CFLAGS) -MT src/shared/android_android_tester-timeout-glib.o -MD -MP -MF src/shared/$(DEPDIR)/android_android_tester-timeout-glib.Tpo -c -o src/shared/android_android_tester-timeout-glib.o `test -f 'src/shared/timeout-glib.c' || echo '$(srcdir)/'`src/shared/timeout-glib.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) src/shared/$(DEPDIR)/android_android_tester-timeout-glib.Tpo src/shared/$(DEPDIR)/android_android_tester-timeout-glib.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='src/shared/timeout-glib.c' object='src/shared/android_android_tester-timeout-glib.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_android_tester_CFLAGS) $(CFLAGS) -c -o src/shared/android_android_tester-timeout-glib.o `test -f 'src/shared/timeout-glib.c' || echo '$(srcdir)/'`src/shared/timeout-glib.c
+
+src/shared/android_android_tester-timeout-glib.obj: src/shared/timeout-glib.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_android_tester_CFLAGS) $(CFLAGS) -MT src/shared/android_android_tester-timeout-glib.obj -MD -MP -MF src/shared/$(DEPDIR)/android_android_tester-timeout-glib.Tpo -c -o src/shared/android_android_tester-timeout-glib.obj `if test -f 'src/shared/timeout-glib.c'; then $(CYGPATH_W) 'src/shared/timeout-glib.c'; else $(CYGPATH_W) '$(srcdir)/src/shared/timeout-glib.c'; fi`
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) src/shared/$(DEPDIR)/android_android_tester-timeout-glib.Tpo src/shared/$(DEPDIR)/android_android_tester-timeout-glib.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='src/shared/timeout-glib.c' object='src/shared/android_android_tester-timeout-glib.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_android_tester_CFLAGS) $(CFLAGS) -c -o src/shared/android_android_tester-timeout-glib.obj `if test -f 'src/shared/timeout-glib.c'; then $(CYGPATH_W) 'src/shared/timeout-glib.c'; else $(CYGPATH_W) '$(srcdir)/src/shared/timeout-glib.c'; fi`
+
+android/hardware/android_android_tester-hardware.o: android/hardware/hardware.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_android_tester_CFLAGS) $(CFLAGS) -MT android/hardware/android_android_tester-hardware.o -MD -MP -MF android/hardware/$(DEPDIR)/android_android_tester-hardware.Tpo -c -o android/hardware/android_android_tester-hardware.o `test -f 'android/hardware/hardware.c' || echo '$(srcdir)/'`android/hardware/hardware.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) android/hardware/$(DEPDIR)/android_android_tester-hardware.Tpo android/hardware/$(DEPDIR)/android_android_tester-hardware.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='android/hardware/hardware.c' object='android/hardware/android_android_tester-hardware.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_android_tester_CFLAGS) $(CFLAGS) -c -o android/hardware/android_android_tester-hardware.o `test -f 'android/hardware/hardware.c' || echo '$(srcdir)/'`android/hardware/hardware.c
+
+android/hardware/android_android_tester-hardware.obj: android/hardware/hardware.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_android_tester_CFLAGS) $(CFLAGS) -MT android/hardware/android_android_tester-hardware.obj -MD -MP -MF android/hardware/$(DEPDIR)/android_android_tester-hardware.Tpo -c -o android/hardware/android_android_tester-hardware.obj `if test -f 'android/hardware/hardware.c'; then $(CYGPATH_W) 'android/hardware/hardware.c'; else $(CYGPATH_W) '$(srcdir)/android/hardware/hardware.c'; fi`
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) android/hardware/$(DEPDIR)/android_android_tester-hardware.Tpo android/hardware/$(DEPDIR)/android_android_tester-hardware.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='android/hardware/hardware.c' object='android/hardware/android_android_tester-hardware.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_android_tester_CFLAGS) $(CFLAGS) -c -o android/hardware/android_android_tester-hardware.obj `if test -f 'android/hardware/hardware.c'; then $(CYGPATH_W) 'android/hardware/hardware.c'; else $(CYGPATH_W) '$(srcdir)/android/hardware/hardware.c'; fi`
+
+android/android_android_tester-android-tester.o: android/android-tester.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_android_tester_CFLAGS) $(CFLAGS) -MT android/android_android_tester-android-tester.o -MD -MP -MF android/$(DEPDIR)/android_android_tester-android-tester.Tpo -c -o android/android_android_tester-android-tester.o `test -f 'android/android-tester.c' || echo '$(srcdir)/'`android/android-tester.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) android/$(DEPDIR)/android_android_tester-android-tester.Tpo android/$(DEPDIR)/android_android_tester-android-tester.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='android/android-tester.c' object='android/android_android_tester-android-tester.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_android_tester_CFLAGS) $(CFLAGS) -c -o android/android_android_tester-android-tester.o `test -f 'android/android-tester.c' || echo '$(srcdir)/'`android/android-tester.c
+
+android/android_android_tester-android-tester.obj: android/android-tester.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_android_tester_CFLAGS) $(CFLAGS) -MT android/android_android_tester-android-tester.obj -MD -MP -MF android/$(DEPDIR)/android_android_tester-android-tester.Tpo -c -o android/android_android_tester-android-tester.obj `if test -f 'android/android-tester.c'; then $(CYGPATH_W) 'android/android-tester.c'; else $(CYGPATH_W) '$(srcdir)/android/android-tester.c'; fi`
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) android/$(DEPDIR)/android_android_tester-android-tester.Tpo android/$(DEPDIR)/android_android_tester-android-tester.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='android/android-tester.c' object='android/android_android_tester-android-tester.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_android_tester_CFLAGS) $(CFLAGS) -c -o android/android_android_tester-android-tester.obj `if test -f 'android/android-tester.c'; then $(CYGPATH_W) 'android/android-tester.c'; else $(CYGPATH_W) '$(srcdir)/android/android-tester.c'; fi`
+
+android/client/android_haltest-haltest.o: android/client/haltest.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_haltest_CFLAGS) $(CFLAGS) -MT android/client/android_haltest-haltest.o -MD -MP -MF android/client/$(DEPDIR)/android_haltest-haltest.Tpo -c -o android/client/android_haltest-haltest.o `test -f 'android/client/haltest.c' || echo '$(srcdir)/'`android/client/haltest.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) android/client/$(DEPDIR)/android_haltest-haltest.Tpo android/client/$(DEPDIR)/android_haltest-haltest.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='android/client/haltest.c' object='android/client/android_haltest-haltest.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_haltest_CFLAGS) $(CFLAGS) -c -o android/client/android_haltest-haltest.o `test -f 'android/client/haltest.c' || echo '$(srcdir)/'`android/client/haltest.c
+
+android/client/android_haltest-haltest.obj: android/client/haltest.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_haltest_CFLAGS) $(CFLAGS) -MT android/client/android_haltest-haltest.obj -MD -MP -MF android/client/$(DEPDIR)/android_haltest-haltest.Tpo -c -o android/client/android_haltest-haltest.obj `if test -f 'android/client/haltest.c'; then $(CYGPATH_W) 'android/client/haltest.c'; else $(CYGPATH_W) '$(srcdir)/android/client/haltest.c'; fi`
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) android/client/$(DEPDIR)/android_haltest-haltest.Tpo android/client/$(DEPDIR)/android_haltest-haltest.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='android/client/haltest.c' object='android/client/android_haltest-haltest.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_haltest_CFLAGS) $(CFLAGS) -c -o android/client/android_haltest-haltest.obj `if test -f 'android/client/haltest.c'; then $(CYGPATH_W) 'android/client/haltest.c'; else $(CYGPATH_W) '$(srcdir)/android/client/haltest.c'; fi`
+
+android/client/android_haltest-pollhandler.o: android/client/pollhandler.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_haltest_CFLAGS) $(CFLAGS) -MT android/client/android_haltest-pollhandler.o -MD -MP -MF android/client/$(DEPDIR)/android_haltest-pollhandler.Tpo -c -o android/client/android_haltest-pollhandler.o `test -f 'android/client/pollhandler.c' || echo '$(srcdir)/'`android/client/pollhandler.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) android/client/$(DEPDIR)/android_haltest-pollhandler.Tpo android/client/$(DEPDIR)/android_haltest-pollhandler.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='android/client/pollhandler.c' object='android/client/android_haltest-pollhandler.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_haltest_CFLAGS) $(CFLAGS) -c -o android/client/android_haltest-pollhandler.o `test -f 'android/client/pollhandler.c' || echo '$(srcdir)/'`android/client/pollhandler.c
+
+android/client/android_haltest-pollhandler.obj: android/client/pollhandler.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_haltest_CFLAGS) $(CFLAGS) -MT android/client/android_haltest-pollhandler.obj -MD -MP -MF android/client/$(DEPDIR)/android_haltest-pollhandler.Tpo -c -o android/client/android_haltest-pollhandler.obj `if test -f 'android/client/pollhandler.c'; then $(CYGPATH_W) 'android/client/pollhandler.c'; else $(CYGPATH_W) '$(srcdir)/android/client/pollhandler.c'; fi`
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) android/client/$(DEPDIR)/android_haltest-pollhandler.Tpo android/client/$(DEPDIR)/android_haltest-pollhandler.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='android/client/pollhandler.c' object='android/client/android_haltest-pollhandler.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_haltest_CFLAGS) $(CFLAGS) -c -o android/client/android_haltest-pollhandler.obj `if test -f 'android/client/pollhandler.c'; then $(CYGPATH_W) 'android/client/pollhandler.c'; else $(CYGPATH_W) '$(srcdir)/android/client/pollhandler.c'; fi`
+
+android/client/android_haltest-terminal.o: android/client/terminal.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_haltest_CFLAGS) $(CFLAGS) -MT android/client/android_haltest-terminal.o -MD -MP -MF android/client/$(DEPDIR)/android_haltest-terminal.Tpo -c -o android/client/android_haltest-terminal.o `test -f 'android/client/terminal.c' || echo '$(srcdir)/'`android/client/terminal.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) android/client/$(DEPDIR)/android_haltest-terminal.Tpo android/client/$(DEPDIR)/android_haltest-terminal.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='android/client/terminal.c' object='android/client/android_haltest-terminal.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_haltest_CFLAGS) $(CFLAGS) -c -o android/client/android_haltest-terminal.o `test -f 'android/client/terminal.c' || echo '$(srcdir)/'`android/client/terminal.c
+
+android/client/android_haltest-terminal.obj: android/client/terminal.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_haltest_CFLAGS) $(CFLAGS) -MT android/client/android_haltest-terminal.obj -MD -MP -MF android/client/$(DEPDIR)/android_haltest-terminal.Tpo -c -o android/client/android_haltest-terminal.obj `if test -f 'android/client/terminal.c'; then $(CYGPATH_W) 'android/client/terminal.c'; else $(CYGPATH_W) '$(srcdir)/android/client/terminal.c'; fi`
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) android/client/$(DEPDIR)/android_haltest-terminal.Tpo android/client/$(DEPDIR)/android_haltest-terminal.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='android/client/terminal.c' object='android/client/android_haltest-terminal.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_haltest_CFLAGS) $(CFLAGS) -c -o android/client/android_haltest-terminal.obj `if test -f 'android/client/terminal.c'; then $(CYGPATH_W) 'android/client/terminal.c'; else $(CYGPATH_W) '$(srcdir)/android/client/terminal.c'; fi`
+
+android/client/android_haltest-history.o: android/client/history.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_haltest_CFLAGS) $(CFLAGS) -MT android/client/android_haltest-history.o -MD -MP -MF android/client/$(DEPDIR)/android_haltest-history.Tpo -c -o android/client/android_haltest-history.o `test -f 'android/client/history.c' || echo '$(srcdir)/'`android/client/history.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) android/client/$(DEPDIR)/android_haltest-history.Tpo android/client/$(DEPDIR)/android_haltest-history.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='android/client/history.c' object='android/client/android_haltest-history.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_haltest_CFLAGS) $(CFLAGS) -c -o android/client/android_haltest-history.o `test -f 'android/client/history.c' || echo '$(srcdir)/'`android/client/history.c
+
+android/client/android_haltest-history.obj: android/client/history.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_haltest_CFLAGS) $(CFLAGS) -MT android/client/android_haltest-history.obj -MD -MP -MF android/client/$(DEPDIR)/android_haltest-history.Tpo -c -o android/client/android_haltest-history.obj `if test -f 'android/client/history.c'; then $(CYGPATH_W) 'android/client/history.c'; else $(CYGPATH_W) '$(srcdir)/android/client/history.c'; fi`
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) android/client/$(DEPDIR)/android_haltest-history.Tpo android/client/$(DEPDIR)/android_haltest-history.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='android/client/history.c' object='android/client/android_haltest-history.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_haltest_CFLAGS) $(CFLAGS) -c -o android/client/android_haltest-history.obj `if test -f 'android/client/history.c'; then $(CYGPATH_W) 'android/client/history.c'; else $(CYGPATH_W) '$(srcdir)/android/client/history.c'; fi`
+
+android/client/android_haltest-tabcompletion.o: android/client/tabcompletion.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_haltest_CFLAGS) $(CFLAGS) -MT android/client/android_haltest-tabcompletion.o -MD -MP -MF android/client/$(DEPDIR)/android_haltest-tabcompletion.Tpo -c -o android/client/android_haltest-tabcompletion.o `test -f 'android/client/tabcompletion.c' || echo '$(srcdir)/'`android/client/tabcompletion.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) android/client/$(DEPDIR)/android_haltest-tabcompletion.Tpo android/client/$(DEPDIR)/android_haltest-tabcompletion.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='android/client/tabcompletion.c' object='android/client/android_haltest-tabcompletion.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_haltest_CFLAGS) $(CFLAGS) -c -o android/client/android_haltest-tabcompletion.o `test -f 'android/client/tabcompletion.c' || echo '$(srcdir)/'`android/client/tabcompletion.c
+
+android/client/android_haltest-tabcompletion.obj: android/client/tabcompletion.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_haltest_CFLAGS) $(CFLAGS) -MT android/client/android_haltest-tabcompletion.obj -MD -MP -MF android/client/$(DEPDIR)/android_haltest-tabcompletion.Tpo -c -o android/client/android_haltest-tabcompletion.obj `if test -f 'android/client/tabcompletion.c'; then $(CYGPATH_W) 'android/client/tabcompletion.c'; else $(CYGPATH_W) '$(srcdir)/android/client/tabcompletion.c'; fi`
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) android/client/$(DEPDIR)/android_haltest-tabcompletion.Tpo android/client/$(DEPDIR)/android_haltest-tabcompletion.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='android/client/tabcompletion.c' object='android/client/android_haltest-tabcompletion.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_haltest_CFLAGS) $(CFLAGS) -c -o android/client/android_haltest-tabcompletion.obj `if test -f 'android/client/tabcompletion.c'; then $(CYGPATH_W) 'android/client/tabcompletion.c'; else $(CYGPATH_W) '$(srcdir)/android/client/tabcompletion.c'; fi`
+
+android/client/android_haltest-if-av.o: android/client/if-av.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_haltest_CFLAGS) $(CFLAGS) -MT android/client/android_haltest-if-av.o -MD -MP -MF android/client/$(DEPDIR)/android_haltest-if-av.Tpo -c -o android/client/android_haltest-if-av.o `test -f 'android/client/if-av.c' || echo '$(srcdir)/'`android/client/if-av.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) android/client/$(DEPDIR)/android_haltest-if-av.Tpo android/client/$(DEPDIR)/android_haltest-if-av.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='android/client/if-av.c' object='android/client/android_haltest-if-av.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_haltest_CFLAGS) $(CFLAGS) -c -o android/client/android_haltest-if-av.o `test -f 'android/client/if-av.c' || echo '$(srcdir)/'`android/client/if-av.c
+
+android/client/android_haltest-if-av.obj: android/client/if-av.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_haltest_CFLAGS) $(CFLAGS) -MT android/client/android_haltest-if-av.obj -MD -MP -MF android/client/$(DEPDIR)/android_haltest-if-av.Tpo -c -o android/client/android_haltest-if-av.obj `if test -f 'android/client/if-av.c'; then $(CYGPATH_W) 'android/client/if-av.c'; else $(CYGPATH_W) '$(srcdir)/android/client/if-av.c'; fi`
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) android/client/$(DEPDIR)/android_haltest-if-av.Tpo android/client/$(DEPDIR)/android_haltest-if-av.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='android/client/if-av.c' object='android/client/android_haltest-if-av.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_haltest_CFLAGS) $(CFLAGS) -c -o android/client/android_haltest-if-av.obj `if test -f 'android/client/if-av.c'; then $(CYGPATH_W) 'android/client/if-av.c'; else $(CYGPATH_W) '$(srcdir)/android/client/if-av.c'; fi`
+
+android/client/android_haltest-if-rc.o: android/client/if-rc.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_haltest_CFLAGS) $(CFLAGS) -MT android/client/android_haltest-if-rc.o -MD -MP -MF android/client/$(DEPDIR)/android_haltest-if-rc.Tpo -c -o android/client/android_haltest-if-rc.o `test -f 'android/client/if-rc.c' || echo '$(srcdir)/'`android/client/if-rc.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) android/client/$(DEPDIR)/android_haltest-if-rc.Tpo android/client/$(DEPDIR)/android_haltest-if-rc.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='android/client/if-rc.c' object='android/client/android_haltest-if-rc.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_haltest_CFLAGS) $(CFLAGS) -c -o android/client/android_haltest-if-rc.o `test -f 'android/client/if-rc.c' || echo '$(srcdir)/'`android/client/if-rc.c
+
+android/client/android_haltest-if-rc.obj: android/client/if-rc.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_haltest_CFLAGS) $(CFLAGS) -MT android/client/android_haltest-if-rc.obj -MD -MP -MF android/client/$(DEPDIR)/android_haltest-if-rc.Tpo -c -o android/client/android_haltest-if-rc.obj `if test -f 'android/client/if-rc.c'; then $(CYGPATH_W) 'android/client/if-rc.c'; else $(CYGPATH_W) '$(srcdir)/android/client/if-rc.c'; fi`
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) android/client/$(DEPDIR)/android_haltest-if-rc.Tpo android/client/$(DEPDIR)/android_haltest-if-rc.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='android/client/if-rc.c' object='android/client/android_haltest-if-rc.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_haltest_CFLAGS) $(CFLAGS) -c -o android/client/android_haltest-if-rc.obj `if test -f 'android/client/if-rc.c'; then $(CYGPATH_W) 'android/client/if-rc.c'; else $(CYGPATH_W) '$(srcdir)/android/client/if-rc.c'; fi`
+
+android/client/android_haltest-if-bt.o: android/client/if-bt.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_haltest_CFLAGS) $(CFLAGS) -MT android/client/android_haltest-if-bt.o -MD -MP -MF android/client/$(DEPDIR)/android_haltest-if-bt.Tpo -c -o android/client/android_haltest-if-bt.o `test -f 'android/client/if-bt.c' || echo '$(srcdir)/'`android/client/if-bt.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) android/client/$(DEPDIR)/android_haltest-if-bt.Tpo android/client/$(DEPDIR)/android_haltest-if-bt.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='android/client/if-bt.c' object='android/client/android_haltest-if-bt.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_haltest_CFLAGS) $(CFLAGS) -c -o android/client/android_haltest-if-bt.o `test -f 'android/client/if-bt.c' || echo '$(srcdir)/'`android/client/if-bt.c
+
+android/client/android_haltest-if-bt.obj: android/client/if-bt.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_haltest_CFLAGS) $(CFLAGS) -MT android/client/android_haltest-if-bt.obj -MD -MP -MF android/client/$(DEPDIR)/android_haltest-if-bt.Tpo -c -o android/client/android_haltest-if-bt.obj `if test -f 'android/client/if-bt.c'; then $(CYGPATH_W) 'android/client/if-bt.c'; else $(CYGPATH_W) '$(srcdir)/android/client/if-bt.c'; fi`
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) android/client/$(DEPDIR)/android_haltest-if-bt.Tpo android/client/$(DEPDIR)/android_haltest-if-bt.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='android/client/if-bt.c' object='android/client/android_haltest-if-bt.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_haltest_CFLAGS) $(CFLAGS) -c -o android/client/android_haltest-if-bt.obj `if test -f 'android/client/if-bt.c'; then $(CYGPATH_W) 'android/client/if-bt.c'; else $(CYGPATH_W) '$(srcdir)/android/client/if-bt.c'; fi`
+
+android/client/android_haltest-if-gatt.o: android/client/if-gatt.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_haltest_CFLAGS) $(CFLAGS) -MT android/client/android_haltest-if-gatt.o -MD -MP -MF android/client/$(DEPDIR)/android_haltest-if-gatt.Tpo -c -o android/client/android_haltest-if-gatt.o `test -f 'android/client/if-gatt.c' || echo '$(srcdir)/'`android/client/if-gatt.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) android/client/$(DEPDIR)/android_haltest-if-gatt.Tpo android/client/$(DEPDIR)/android_haltest-if-gatt.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='android/client/if-gatt.c' object='android/client/android_haltest-if-gatt.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_haltest_CFLAGS) $(CFLAGS) -c -o android/client/android_haltest-if-gatt.o `test -f 'android/client/if-gatt.c' || echo '$(srcdir)/'`android/client/if-gatt.c
+
+android/client/android_haltest-if-gatt.obj: android/client/if-gatt.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_haltest_CFLAGS) $(CFLAGS) -MT android/client/android_haltest-if-gatt.obj -MD -MP -MF android/client/$(DEPDIR)/android_haltest-if-gatt.Tpo -c -o android/client/android_haltest-if-gatt.obj `if test -f 'android/client/if-gatt.c'; then $(CYGPATH_W) 'android/client/if-gatt.c'; else $(CYGPATH_W) '$(srcdir)/android/client/if-gatt.c'; fi`
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) android/client/$(DEPDIR)/android_haltest-if-gatt.Tpo android/client/$(DEPDIR)/android_haltest-if-gatt.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='android/client/if-gatt.c' object='android/client/android_haltest-if-gatt.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_haltest_CFLAGS) $(CFLAGS) -c -o android/client/android_haltest-if-gatt.obj `if test -f 'android/client/if-gatt.c'; then $(CYGPATH_W) 'android/client/if-gatt.c'; else $(CYGPATH_W) '$(srcdir)/android/client/if-gatt.c'; fi`
+
+android/client/android_haltest-if-hf.o: android/client/if-hf.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_haltest_CFLAGS) $(CFLAGS) -MT android/client/android_haltest-if-hf.o -MD -MP -MF android/client/$(DEPDIR)/android_haltest-if-hf.Tpo -c -o android/client/android_haltest-if-hf.o `test -f 'android/client/if-hf.c' || echo '$(srcdir)/'`android/client/if-hf.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) android/client/$(DEPDIR)/android_haltest-if-hf.Tpo android/client/$(DEPDIR)/android_haltest-if-hf.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='android/client/if-hf.c' object='android/client/android_haltest-if-hf.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_haltest_CFLAGS) $(CFLAGS) -c -o android/client/android_haltest-if-hf.o `test -f 'android/client/if-hf.c' || echo '$(srcdir)/'`android/client/if-hf.c
+
+android/client/android_haltest-if-hf.obj: android/client/if-hf.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_haltest_CFLAGS) $(CFLAGS) -MT android/client/android_haltest-if-hf.obj -MD -MP -MF android/client/$(DEPDIR)/android_haltest-if-hf.Tpo -c -o android/client/android_haltest-if-hf.obj `if test -f 'android/client/if-hf.c'; then $(CYGPATH_W) 'android/client/if-hf.c'; else $(CYGPATH_W) '$(srcdir)/android/client/if-hf.c'; fi`
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) android/client/$(DEPDIR)/android_haltest-if-hf.Tpo android/client/$(DEPDIR)/android_haltest-if-hf.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='android/client/if-hf.c' object='android/client/android_haltest-if-hf.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_haltest_CFLAGS) $(CFLAGS) -c -o android/client/android_haltest-if-hf.obj `if test -f 'android/client/if-hf.c'; then $(CYGPATH_W) 'android/client/if-hf.c'; else $(CYGPATH_W) '$(srcdir)/android/client/if-hf.c'; fi`
+
+android/client/android_haltest-if-hh.o: android/client/if-hh.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_haltest_CFLAGS) $(CFLAGS) -MT android/client/android_haltest-if-hh.o -MD -MP -MF android/client/$(DEPDIR)/android_haltest-if-hh.Tpo -c -o android/client/android_haltest-if-hh.o `test -f 'android/client/if-hh.c' || echo '$(srcdir)/'`android/client/if-hh.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) android/client/$(DEPDIR)/android_haltest-if-hh.Tpo android/client/$(DEPDIR)/android_haltest-if-hh.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='android/client/if-hh.c' object='android/client/android_haltest-if-hh.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_haltest_CFLAGS) $(CFLAGS) -c -o android/client/android_haltest-if-hh.o `test -f 'android/client/if-hh.c' || echo '$(srcdir)/'`android/client/if-hh.c
+
+android/client/android_haltest-if-hh.obj: android/client/if-hh.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_haltest_CFLAGS) $(CFLAGS) -MT android/client/android_haltest-if-hh.obj -MD -MP -MF android/client/$(DEPDIR)/android_haltest-if-hh.Tpo -c -o android/client/android_haltest-if-hh.obj `if test -f 'android/client/if-hh.c'; then $(CYGPATH_W) 'android/client/if-hh.c'; else $(CYGPATH_W) '$(srcdir)/android/client/if-hh.c'; fi`
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) android/client/$(DEPDIR)/android_haltest-if-hh.Tpo android/client/$(DEPDIR)/android_haltest-if-hh.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='android/client/if-hh.c' object='android/client/android_haltest-if-hh.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_haltest_CFLAGS) $(CFLAGS) -c -o android/client/android_haltest-if-hh.obj `if test -f 'android/client/if-hh.c'; then $(CYGPATH_W) 'android/client/if-hh.c'; else $(CYGPATH_W) '$(srcdir)/android/client/if-hh.c'; fi`
+
+android/client/android_haltest-if-pan.o: android/client/if-pan.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_haltest_CFLAGS) $(CFLAGS) -MT android/client/android_haltest-if-pan.o -MD -MP -MF android/client/$(DEPDIR)/android_haltest-if-pan.Tpo -c -o android/client/android_haltest-if-pan.o `test -f 'android/client/if-pan.c' || echo '$(srcdir)/'`android/client/if-pan.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) android/client/$(DEPDIR)/android_haltest-if-pan.Tpo android/client/$(DEPDIR)/android_haltest-if-pan.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='android/client/if-pan.c' object='android/client/android_haltest-if-pan.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_haltest_CFLAGS) $(CFLAGS) -c -o android/client/android_haltest-if-pan.o `test -f 'android/client/if-pan.c' || echo '$(srcdir)/'`android/client/if-pan.c
+
+android/client/android_haltest-if-pan.obj: android/client/if-pan.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_haltest_CFLAGS) $(CFLAGS) -MT android/client/android_haltest-if-pan.obj -MD -MP -MF android/client/$(DEPDIR)/android_haltest-if-pan.Tpo -c -o android/client/android_haltest-if-pan.obj `if test -f 'android/client/if-pan.c'; then $(CYGPATH_W) 'android/client/if-pan.c'; else $(CYGPATH_W) '$(srcdir)/android/client/if-pan.c'; fi`
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) android/client/$(DEPDIR)/android_haltest-if-pan.Tpo android/client/$(DEPDIR)/android_haltest-if-pan.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='android/client/if-pan.c' object='android/client/android_haltest-if-pan.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_haltest_CFLAGS) $(CFLAGS) -c -o android/client/android_haltest-if-pan.obj `if test -f 'android/client/if-pan.c'; then $(CYGPATH_W) 'android/client/if-pan.c'; else $(CYGPATH_W) '$(srcdir)/android/client/if-pan.c'; fi`
+
+android/client/android_haltest-if-sock.o: android/client/if-sock.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_haltest_CFLAGS) $(CFLAGS) -MT android/client/android_haltest-if-sock.o -MD -MP -MF android/client/$(DEPDIR)/android_haltest-if-sock.Tpo -c -o android/client/android_haltest-if-sock.o `test -f 'android/client/if-sock.c' || echo '$(srcdir)/'`android/client/if-sock.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) android/client/$(DEPDIR)/android_haltest-if-sock.Tpo android/client/$(DEPDIR)/android_haltest-if-sock.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='android/client/if-sock.c' object='android/client/android_haltest-if-sock.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_haltest_CFLAGS) $(CFLAGS) -c -o android/client/android_haltest-if-sock.o `test -f 'android/client/if-sock.c' || echo '$(srcdir)/'`android/client/if-sock.c
+
+android/client/android_haltest-if-sock.obj: android/client/if-sock.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_haltest_CFLAGS) $(CFLAGS) -MT android/client/android_haltest-if-sock.obj -MD -MP -MF android/client/$(DEPDIR)/android_haltest-if-sock.Tpo -c -o android/client/android_haltest-if-sock.obj `if test -f 'android/client/if-sock.c'; then $(CYGPATH_W) 'android/client/if-sock.c'; else $(CYGPATH_W) '$(srcdir)/android/client/if-sock.c'; fi`
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) android/client/$(DEPDIR)/android_haltest-if-sock.Tpo android/client/$(DEPDIR)/android_haltest-if-sock.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='android/client/if-sock.c' object='android/client/android_haltest-if-sock.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_haltest_CFLAGS) $(CFLAGS) -c -o android/client/android_haltest-if-sock.obj `if test -f 'android/client/if-sock.c'; then $(CYGPATH_W) 'android/client/if-sock.c'; else $(CYGPATH_W) '$(srcdir)/android/client/if-sock.c'; fi`
+
+android/client/android_haltest-if-audio.o: android/client/if-audio.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_haltest_CFLAGS) $(CFLAGS) -MT android/client/android_haltest-if-audio.o -MD -MP -MF android/client/$(DEPDIR)/android_haltest-if-audio.Tpo -c -o android/client/android_haltest-if-audio.o `test -f 'android/client/if-audio.c' || echo '$(srcdir)/'`android/client/if-audio.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) android/client/$(DEPDIR)/android_haltest-if-audio.Tpo android/client/$(DEPDIR)/android_haltest-if-audio.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='android/client/if-audio.c' object='android/client/android_haltest-if-audio.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_haltest_CFLAGS) $(CFLAGS) -c -o android/client/android_haltest-if-audio.o `test -f 'android/client/if-audio.c' || echo '$(srcdir)/'`android/client/if-audio.c
+
+android/client/android_haltest-if-audio.obj: android/client/if-audio.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_haltest_CFLAGS) $(CFLAGS) -MT android/client/android_haltest-if-audio.obj -MD -MP -MF android/client/$(DEPDIR)/android_haltest-if-audio.Tpo -c -o android/client/android_haltest-if-audio.obj `if test -f 'android/client/if-audio.c'; then $(CYGPATH_W) 'android/client/if-audio.c'; else $(CYGPATH_W) '$(srcdir)/android/client/if-audio.c'; fi`
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) android/client/$(DEPDIR)/android_haltest-if-audio.Tpo android/client/$(DEPDIR)/android_haltest-if-audio.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='android/client/if-audio.c' object='android/client/android_haltest-if-audio.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_haltest_CFLAGS) $(CFLAGS) -c -o android/client/android_haltest-if-audio.obj `if test -f 'android/client/if-audio.c'; then $(CYGPATH_W) 'android/client/if-audio.c'; else $(CYGPATH_W) '$(srcdir)/android/client/if-audio.c'; fi`
+
+android/hardware/android_haltest-hardware.o: android/hardware/hardware.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_haltest_CFLAGS) $(CFLAGS) -MT android/hardware/android_haltest-hardware.o -MD -MP -MF android/hardware/$(DEPDIR)/android_haltest-hardware.Tpo -c -o android/hardware/android_haltest-hardware.o `test -f 'android/hardware/hardware.c' || echo '$(srcdir)/'`android/hardware/hardware.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) android/hardware/$(DEPDIR)/android_haltest-hardware.Tpo android/hardware/$(DEPDIR)/android_haltest-hardware.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='android/hardware/hardware.c' object='android/hardware/android_haltest-hardware.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_haltest_CFLAGS) $(CFLAGS) -c -o android/hardware/android_haltest-hardware.o `test -f 'android/hardware/hardware.c' || echo '$(srcdir)/'`android/hardware/hardware.c
+
+android/hardware/android_haltest-hardware.obj: android/hardware/hardware.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_haltest_CFLAGS) $(CFLAGS) -MT android/hardware/android_haltest-hardware.obj -MD -MP -MF android/hardware/$(DEPDIR)/android_haltest-hardware.Tpo -c -o android/hardware/android_haltest-hardware.obj `if test -f 'android/hardware/hardware.c'; then $(CYGPATH_W) 'android/hardware/hardware.c'; else $(CYGPATH_W) '$(srcdir)/android/hardware/hardware.c'; fi`
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) android/hardware/$(DEPDIR)/android_haltest-hardware.Tpo android/hardware/$(DEPDIR)/android_haltest-hardware.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='android/hardware/hardware.c' object='android/hardware/android_haltest-hardware.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_haltest_CFLAGS) $(CFLAGS) -c -o android/hardware/android_haltest-hardware.obj `if test -f 'android/hardware/hardware.c'; then $(CYGPATH_W) 'android/hardware/hardware.c'; else $(CYGPATH_W) '$(srcdir)/android/hardware/hardware.c'; fi`
+
+android/android_haltest-hal-utils.o: android/hal-utils.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_haltest_CFLAGS) $(CFLAGS) -MT android/android_haltest-hal-utils.o -MD -MP -MF android/$(DEPDIR)/android_haltest-hal-utils.Tpo -c -o android/android_haltest-hal-utils.o `test -f 'android/hal-utils.c' || echo '$(srcdir)/'`android/hal-utils.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) android/$(DEPDIR)/android_haltest-hal-utils.Tpo android/$(DEPDIR)/android_haltest-hal-utils.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='android/hal-utils.c' object='android/android_haltest-hal-utils.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_haltest_CFLAGS) $(CFLAGS) -c -o android/android_haltest-hal-utils.o `test -f 'android/hal-utils.c' || echo '$(srcdir)/'`android/hal-utils.c
+
+android/android_haltest-hal-utils.obj: android/hal-utils.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_haltest_CFLAGS) $(CFLAGS) -MT android/android_haltest-hal-utils.obj -MD -MP -MF android/$(DEPDIR)/android_haltest-hal-utils.Tpo -c -o android/android_haltest-hal-utils.obj `if test -f 'android/hal-utils.c'; then $(CYGPATH_W) 'android/hal-utils.c'; else $(CYGPATH_W) '$(srcdir)/android/hal-utils.c'; fi`
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) android/$(DEPDIR)/android_haltest-hal-utils.Tpo android/$(DEPDIR)/android_haltest-hal-utils.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='android/hal-utils.c' object='android/android_haltest-hal-utils.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_haltest_CFLAGS) $(CFLAGS) -c -o android/android_haltest-hal-utils.obj `if test -f 'android/hal-utils.c'; then $(CYGPATH_W) 'android/hal-utils.c'; else $(CYGPATH_W) '$(srcdir)/android/hal-utils.c'; fi`
+
+emulator/android_ipc_tester-btdev.o: emulator/btdev.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_ipc_tester_CFLAGS) $(CFLAGS) -MT emulator/android_ipc_tester-btdev.o -MD -MP -MF emulator/$(DEPDIR)/android_ipc_tester-btdev.Tpo -c -o emulator/android_ipc_tester-btdev.o `test -f 'emulator/btdev.c' || echo '$(srcdir)/'`emulator/btdev.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) emulator/$(DEPDIR)/android_ipc_tester-btdev.Tpo emulator/$(DEPDIR)/android_ipc_tester-btdev.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='emulator/btdev.c' object='emulator/android_ipc_tester-btdev.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_ipc_tester_CFLAGS) $(CFLAGS) -c -o emulator/android_ipc_tester-btdev.o `test -f 'emulator/btdev.c' || echo '$(srcdir)/'`emulator/btdev.c
+
+emulator/android_ipc_tester-btdev.obj: emulator/btdev.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_ipc_tester_CFLAGS) $(CFLAGS) -MT emulator/android_ipc_tester-btdev.obj -MD -MP -MF emulator/$(DEPDIR)/android_ipc_tester-btdev.Tpo -c -o emulator/android_ipc_tester-btdev.obj `if test -f 'emulator/btdev.c'; then $(CYGPATH_W) 'emulator/btdev.c'; else $(CYGPATH_W) '$(srcdir)/emulator/btdev.c'; fi`
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) emulator/$(DEPDIR)/android_ipc_tester-btdev.Tpo emulator/$(DEPDIR)/android_ipc_tester-btdev.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='emulator/btdev.c' object='emulator/android_ipc_tester-btdev.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_ipc_tester_CFLAGS) $(CFLAGS) -c -o emulator/android_ipc_tester-btdev.obj `if test -f 'emulator/btdev.c'; then $(CYGPATH_W) 'emulator/btdev.c'; else $(CYGPATH_W) '$(srcdir)/emulator/btdev.c'; fi`
+
+emulator/android_ipc_tester-bthost.o: emulator/bthost.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_ipc_tester_CFLAGS) $(CFLAGS) -MT emulator/android_ipc_tester-bthost.o -MD -MP -MF emulator/$(DEPDIR)/android_ipc_tester-bthost.Tpo -c -o emulator/android_ipc_tester-bthost.o `test -f 'emulator/bthost.c' || echo '$(srcdir)/'`emulator/bthost.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) emulator/$(DEPDIR)/android_ipc_tester-bthost.Tpo emulator/$(DEPDIR)/android_ipc_tester-bthost.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='emulator/bthost.c' object='emulator/android_ipc_tester-bthost.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_ipc_tester_CFLAGS) $(CFLAGS) -c -o emulator/android_ipc_tester-bthost.o `test -f 'emulator/bthost.c' || echo '$(srcdir)/'`emulator/bthost.c
+
+emulator/android_ipc_tester-bthost.obj: emulator/bthost.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_ipc_tester_CFLAGS) $(CFLAGS) -MT emulator/android_ipc_tester-bthost.obj -MD -MP -MF emulator/$(DEPDIR)/android_ipc_tester-bthost.Tpo -c -o emulator/android_ipc_tester-bthost.obj `if test -f 'emulator/bthost.c'; then $(CYGPATH_W) 'emulator/bthost.c'; else $(CYGPATH_W) '$(srcdir)/emulator/bthost.c'; fi`
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) emulator/$(DEPDIR)/android_ipc_tester-bthost.Tpo emulator/$(DEPDIR)/android_ipc_tester-bthost.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='emulator/bthost.c' object='emulator/android_ipc_tester-bthost.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_ipc_tester_CFLAGS) $(CFLAGS) -c -o emulator/android_ipc_tester-bthost.obj `if test -f 'emulator/bthost.c'; then $(CYGPATH_W) 'emulator/bthost.c'; else $(CYGPATH_W) '$(srcdir)/emulator/bthost.c'; fi`
+
+emulator/android_ipc_tester-smp.o: emulator/smp.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_ipc_tester_CFLAGS) $(CFLAGS) -MT emulator/android_ipc_tester-smp.o -MD -MP -MF emulator/$(DEPDIR)/android_ipc_tester-smp.Tpo -c -o emulator/android_ipc_tester-smp.o `test -f 'emulator/smp.c' || echo '$(srcdir)/'`emulator/smp.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) emulator/$(DEPDIR)/android_ipc_tester-smp.Tpo emulator/$(DEPDIR)/android_ipc_tester-smp.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='emulator/smp.c' object='emulator/android_ipc_tester-smp.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_ipc_tester_CFLAGS) $(CFLAGS) -c -o emulator/android_ipc_tester-smp.o `test -f 'emulator/smp.c' || echo '$(srcdir)/'`emulator/smp.c
+
+emulator/android_ipc_tester-smp.obj: emulator/smp.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_ipc_tester_CFLAGS) $(CFLAGS) -MT emulator/android_ipc_tester-smp.obj -MD -MP -MF emulator/$(DEPDIR)/android_ipc_tester-smp.Tpo -c -o emulator/android_ipc_tester-smp.obj `if test -f 'emulator/smp.c'; then $(CYGPATH_W) 'emulator/smp.c'; else $(CYGPATH_W) '$(srcdir)/emulator/smp.c'; fi`
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) emulator/$(DEPDIR)/android_ipc_tester-smp.Tpo emulator/$(DEPDIR)/android_ipc_tester-smp.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='emulator/smp.c' object='emulator/android_ipc_tester-smp.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_ipc_tester_CFLAGS) $(CFLAGS) -c -o emulator/android_ipc_tester-smp.obj `if test -f 'emulator/smp.c'; then $(CYGPATH_W) 'emulator/smp.c'; else $(CYGPATH_W) '$(srcdir)/emulator/smp.c'; fi`
+
+src/shared/android_ipc_tester-crypto.o: src/shared/crypto.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_ipc_tester_CFLAGS) $(CFLAGS) -MT src/shared/android_ipc_tester-crypto.o -MD -MP -MF src/shared/$(DEPDIR)/android_ipc_tester-crypto.Tpo -c -o src/shared/android_ipc_tester-crypto.o `test -f 'src/shared/crypto.c' || echo '$(srcdir)/'`src/shared/crypto.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) src/shared/$(DEPDIR)/android_ipc_tester-crypto.Tpo src/shared/$(DEPDIR)/android_ipc_tester-crypto.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='src/shared/crypto.c' object='src/shared/android_ipc_tester-crypto.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_ipc_tester_CFLAGS) $(CFLAGS) -c -o src/shared/android_ipc_tester-crypto.o `test -f 'src/shared/crypto.c' || echo '$(srcdir)/'`src/shared/crypto.c
+
+src/shared/android_ipc_tester-crypto.obj: src/shared/crypto.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_ipc_tester_CFLAGS) $(CFLAGS) -MT src/shared/android_ipc_tester-crypto.obj -MD -MP -MF src/shared/$(DEPDIR)/android_ipc_tester-crypto.Tpo -c -o src/shared/android_ipc_tester-crypto.obj `if test -f 'src/shared/crypto.c'; then $(CYGPATH_W) 'src/shared/crypto.c'; else $(CYGPATH_W) '$(srcdir)/src/shared/crypto.c'; fi`
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) src/shared/$(DEPDIR)/android_ipc_tester-crypto.Tpo src/shared/$(DEPDIR)/android_ipc_tester-crypto.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='src/shared/crypto.c' object='src/shared/android_ipc_tester-crypto.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_ipc_tester_CFLAGS) $(CFLAGS) -c -o src/shared/android_ipc_tester-crypto.obj `if test -f 'src/shared/crypto.c'; then $(CYGPATH_W) 'src/shared/crypto.c'; else $(CYGPATH_W) '$(srcdir)/src/shared/crypto.c'; fi`
+
+src/shared/android_ipc_tester-io-glib.o: src/shared/io-glib.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_ipc_tester_CFLAGS) $(CFLAGS) -MT src/shared/android_ipc_tester-io-glib.o -MD -MP -MF src/shared/$(DEPDIR)/android_ipc_tester-io-glib.Tpo -c -o src/shared/android_ipc_tester-io-glib.o `test -f 'src/shared/io-glib.c' || echo '$(srcdir)/'`src/shared/io-glib.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) src/shared/$(DEPDIR)/android_ipc_tester-io-glib.Tpo src/shared/$(DEPDIR)/android_ipc_tester-io-glib.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='src/shared/io-glib.c' object='src/shared/android_ipc_tester-io-glib.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_ipc_tester_CFLAGS) $(CFLAGS) -c -o src/shared/android_ipc_tester-io-glib.o `test -f 'src/shared/io-glib.c' || echo '$(srcdir)/'`src/shared/io-glib.c
+
+src/shared/android_ipc_tester-io-glib.obj: src/shared/io-glib.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_ipc_tester_CFLAGS) $(CFLAGS) -MT src/shared/android_ipc_tester-io-glib.obj -MD -MP -MF src/shared/$(DEPDIR)/android_ipc_tester-io-glib.Tpo -c -o src/shared/android_ipc_tester-io-glib.obj `if test -f 'src/shared/io-glib.c'; then $(CYGPATH_W) 'src/shared/io-glib.c'; else $(CYGPATH_W) '$(srcdir)/src/shared/io-glib.c'; fi`
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) src/shared/$(DEPDIR)/android_ipc_tester-io-glib.Tpo src/shared/$(DEPDIR)/android_ipc_tester-io-glib.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='src/shared/io-glib.c' object='src/shared/android_ipc_tester-io-glib.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_ipc_tester_CFLAGS) $(CFLAGS) -c -o src/shared/android_ipc_tester-io-glib.obj `if test -f 'src/shared/io-glib.c'; then $(CYGPATH_W) 'src/shared/io-glib.c'; else $(CYGPATH_W) '$(srcdir)/src/shared/io-glib.c'; fi`
+
+src/shared/android_ipc_tester-queue.o: src/shared/queue.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_ipc_tester_CFLAGS) $(CFLAGS) -MT src/shared/android_ipc_tester-queue.o -MD -MP -MF src/shared/$(DEPDIR)/android_ipc_tester-queue.Tpo -c -o src/shared/android_ipc_tester-queue.o `test -f 'src/shared/queue.c' || echo '$(srcdir)/'`src/shared/queue.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) src/shared/$(DEPDIR)/android_ipc_tester-queue.Tpo src/shared/$(DEPDIR)/android_ipc_tester-queue.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='src/shared/queue.c' object='src/shared/android_ipc_tester-queue.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_ipc_tester_CFLAGS) $(CFLAGS) -c -o src/shared/android_ipc_tester-queue.o `test -f 'src/shared/queue.c' || echo '$(srcdir)/'`src/shared/queue.c
+
+src/shared/android_ipc_tester-queue.obj: src/shared/queue.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_ipc_tester_CFLAGS) $(CFLAGS) -MT src/shared/android_ipc_tester-queue.obj -MD -MP -MF src/shared/$(DEPDIR)/android_ipc_tester-queue.Tpo -c -o src/shared/android_ipc_tester-queue.obj `if test -f 'src/shared/queue.c'; then $(CYGPATH_W) 'src/shared/queue.c'; else $(CYGPATH_W) '$(srcdir)/src/shared/queue.c'; fi`
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) src/shared/$(DEPDIR)/android_ipc_tester-queue.Tpo src/shared/$(DEPDIR)/android_ipc_tester-queue.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='src/shared/queue.c' object='src/shared/android_ipc_tester-queue.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_ipc_tester_CFLAGS) $(CFLAGS) -c -o src/shared/android_ipc_tester-queue.obj `if test -f 'src/shared/queue.c'; then $(CYGPATH_W) 'src/shared/queue.c'; else $(CYGPATH_W) '$(srcdir)/src/shared/queue.c'; fi`
+
+src/shared/android_ipc_tester-util.o: src/shared/util.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_ipc_tester_CFLAGS) $(CFLAGS) -MT src/shared/android_ipc_tester-util.o -MD -MP -MF src/shared/$(DEPDIR)/android_ipc_tester-util.Tpo -c -o src/shared/android_ipc_tester-util.o `test -f 'src/shared/util.c' || echo '$(srcdir)/'`src/shared/util.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) src/shared/$(DEPDIR)/android_ipc_tester-util.Tpo src/shared/$(DEPDIR)/android_ipc_tester-util.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='src/shared/util.c' object='src/shared/android_ipc_tester-util.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_ipc_tester_CFLAGS) $(CFLAGS) -c -o src/shared/android_ipc_tester-util.o `test -f 'src/shared/util.c' || echo '$(srcdir)/'`src/shared/util.c
+
+src/shared/android_ipc_tester-util.obj: src/shared/util.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_ipc_tester_CFLAGS) $(CFLAGS) -MT src/shared/android_ipc_tester-util.obj -MD -MP -MF src/shared/$(DEPDIR)/android_ipc_tester-util.Tpo -c -o src/shared/android_ipc_tester-util.obj `if test -f 'src/shared/util.c'; then $(CYGPATH_W) 'src/shared/util.c'; else $(CYGPATH_W) '$(srcdir)/src/shared/util.c'; fi`
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) src/shared/$(DEPDIR)/android_ipc_tester-util.Tpo src/shared/$(DEPDIR)/android_ipc_tester-util.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='src/shared/util.c' object='src/shared/android_ipc_tester-util.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_ipc_tester_CFLAGS) $(CFLAGS) -c -o src/shared/android_ipc_tester-util.obj `if test -f 'src/shared/util.c'; then $(CYGPATH_W) 'src/shared/util.c'; else $(CYGPATH_W) '$(srcdir)/src/shared/util.c'; fi`
+
+src/shared/android_ipc_tester-mgmt.o: src/shared/mgmt.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_ipc_tester_CFLAGS) $(CFLAGS) -MT src/shared/android_ipc_tester-mgmt.o -MD -MP -MF src/shared/$(DEPDIR)/android_ipc_tester-mgmt.Tpo -c -o src/shared/android_ipc_tester-mgmt.o `test -f 'src/shared/mgmt.c' || echo '$(srcdir)/'`src/shared/mgmt.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) src/shared/$(DEPDIR)/android_ipc_tester-mgmt.Tpo src/shared/$(DEPDIR)/android_ipc_tester-mgmt.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='src/shared/mgmt.c' object='src/shared/android_ipc_tester-mgmt.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_ipc_tester_CFLAGS) $(CFLAGS) -c -o src/shared/android_ipc_tester-mgmt.o `test -f 'src/shared/mgmt.c' || echo '$(srcdir)/'`src/shared/mgmt.c
+
+src/shared/android_ipc_tester-mgmt.obj: src/shared/mgmt.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_ipc_tester_CFLAGS) $(CFLAGS) -MT src/shared/android_ipc_tester-mgmt.obj -MD -MP -MF src/shared/$(DEPDIR)/android_ipc_tester-mgmt.Tpo -c -o src/shared/android_ipc_tester-mgmt.obj `if test -f 'src/shared/mgmt.c'; then $(CYGPATH_W) 'src/shared/mgmt.c'; else $(CYGPATH_W) '$(srcdir)/src/shared/mgmt.c'; fi`
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) src/shared/$(DEPDIR)/android_ipc_tester-mgmt.Tpo src/shared/$(DEPDIR)/android_ipc_tester-mgmt.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='src/shared/mgmt.c' object='src/shared/android_ipc_tester-mgmt.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_ipc_tester_CFLAGS) $(CFLAGS) -c -o src/shared/android_ipc_tester-mgmt.obj `if test -f 'src/shared/mgmt.c'; then $(CYGPATH_W) 'src/shared/mgmt.c'; else $(CYGPATH_W) '$(srcdir)/src/shared/mgmt.c'; fi`
+
+src/shared/android_ipc_tester-hciemu.o: src/shared/hciemu.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_ipc_tester_CFLAGS) $(CFLAGS) -MT src/shared/android_ipc_tester-hciemu.o -MD -MP -MF src/shared/$(DEPDIR)/android_ipc_tester-hciemu.Tpo -c -o src/shared/android_ipc_tester-hciemu.o `test -f 'src/shared/hciemu.c' || echo '$(srcdir)/'`src/shared/hciemu.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) src/shared/$(DEPDIR)/android_ipc_tester-hciemu.Tpo src/shared/$(DEPDIR)/android_ipc_tester-hciemu.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='src/shared/hciemu.c' object='src/shared/android_ipc_tester-hciemu.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_ipc_tester_CFLAGS) $(CFLAGS) -c -o src/shared/android_ipc_tester-hciemu.o `test -f 'src/shared/hciemu.c' || echo '$(srcdir)/'`src/shared/hciemu.c
+
+src/shared/android_ipc_tester-hciemu.obj: src/shared/hciemu.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_ipc_tester_CFLAGS) $(CFLAGS) -MT src/shared/android_ipc_tester-hciemu.obj -MD -MP -MF src/shared/$(DEPDIR)/android_ipc_tester-hciemu.Tpo -c -o src/shared/android_ipc_tester-hciemu.obj `if test -f 'src/shared/hciemu.c'; then $(CYGPATH_W) 'src/shared/hciemu.c'; else $(CYGPATH_W) '$(srcdir)/src/shared/hciemu.c'; fi`
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) src/shared/$(DEPDIR)/android_ipc_tester-hciemu.Tpo src/shared/$(DEPDIR)/android_ipc_tester-hciemu.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='src/shared/hciemu.c' object='src/shared/android_ipc_tester-hciemu.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_ipc_tester_CFLAGS) $(CFLAGS) -c -o src/shared/android_ipc_tester-hciemu.obj `if test -f 'src/shared/hciemu.c'; then $(CYGPATH_W) 'src/shared/hciemu.c'; else $(CYGPATH_W) '$(srcdir)/src/shared/hciemu.c'; fi`
+
+src/shared/android_ipc_tester-tester.o: src/shared/tester.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_ipc_tester_CFLAGS) $(CFLAGS) -MT src/shared/android_ipc_tester-tester.o -MD -MP -MF src/shared/$(DEPDIR)/android_ipc_tester-tester.Tpo -c -o src/shared/android_ipc_tester-tester.o `test -f 'src/shared/tester.c' || echo '$(srcdir)/'`src/shared/tester.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) src/shared/$(DEPDIR)/android_ipc_tester-tester.Tpo src/shared/$(DEPDIR)/android_ipc_tester-tester.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='src/shared/tester.c' object='src/shared/android_ipc_tester-tester.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_ipc_tester_CFLAGS) $(CFLAGS) -c -o src/shared/android_ipc_tester-tester.o `test -f 'src/shared/tester.c' || echo '$(srcdir)/'`src/shared/tester.c
+
+src/shared/android_ipc_tester-tester.obj: src/shared/tester.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_ipc_tester_CFLAGS) $(CFLAGS) -MT src/shared/android_ipc_tester-tester.obj -MD -MP -MF src/shared/$(DEPDIR)/android_ipc_tester-tester.Tpo -c -o src/shared/android_ipc_tester-tester.obj `if test -f 'src/shared/tester.c'; then $(CYGPATH_W) 'src/shared/tester.c'; else $(CYGPATH_W) '$(srcdir)/src/shared/tester.c'; fi`
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) src/shared/$(DEPDIR)/android_ipc_tester-tester.Tpo src/shared/$(DEPDIR)/android_ipc_tester-tester.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='src/shared/tester.c' object='src/shared/android_ipc_tester-tester.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_ipc_tester_CFLAGS) $(CFLAGS) -c -o src/shared/android_ipc_tester-tester.obj `if test -f 'src/shared/tester.c'; then $(CYGPATH_W) 'src/shared/tester.c'; else $(CYGPATH_W) '$(srcdir)/src/shared/tester.c'; fi`
+
+src/shared/android_ipc_tester-timeout-glib.o: src/shared/timeout-glib.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_ipc_tester_CFLAGS) $(CFLAGS) -MT src/shared/android_ipc_tester-timeout-glib.o -MD -MP -MF src/shared/$(DEPDIR)/android_ipc_tester-timeout-glib.Tpo -c -o src/shared/android_ipc_tester-timeout-glib.o `test -f 'src/shared/timeout-glib.c' || echo '$(srcdir)/'`src/shared/timeout-glib.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) src/shared/$(DEPDIR)/android_ipc_tester-timeout-glib.Tpo src/shared/$(DEPDIR)/android_ipc_tester-timeout-glib.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='src/shared/timeout-glib.c' object='src/shared/android_ipc_tester-timeout-glib.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_ipc_tester_CFLAGS) $(CFLAGS) -c -o src/shared/android_ipc_tester-timeout-glib.o `test -f 'src/shared/timeout-glib.c' || echo '$(srcdir)/'`src/shared/timeout-glib.c
+
+src/shared/android_ipc_tester-timeout-glib.obj: src/shared/timeout-glib.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_ipc_tester_CFLAGS) $(CFLAGS) -MT src/shared/android_ipc_tester-timeout-glib.obj -MD -MP -MF src/shared/$(DEPDIR)/android_ipc_tester-timeout-glib.Tpo -c -o src/shared/android_ipc_tester-timeout-glib.obj `if test -f 'src/shared/timeout-glib.c'; then $(CYGPATH_W) 'src/shared/timeout-glib.c'; else $(CYGPATH_W) '$(srcdir)/src/shared/timeout-glib.c'; fi`
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) src/shared/$(DEPDIR)/android_ipc_tester-timeout-glib.Tpo src/shared/$(DEPDIR)/android_ipc_tester-timeout-glib.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='src/shared/timeout-glib.c' object='src/shared/android_ipc_tester-timeout-glib.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_ipc_tester_CFLAGS) $(CFLAGS) -c -o src/shared/android_ipc_tester-timeout-glib.obj `if test -f 'src/shared/timeout-glib.c'; then $(CYGPATH_W) 'src/shared/timeout-glib.c'; else $(CYGPATH_W) '$(srcdir)/src/shared/timeout-glib.c'; fi`
+
+android/android_ipc_tester-hal-utils.o: android/hal-utils.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_ipc_tester_CFLAGS) $(CFLAGS) -MT android/android_ipc_tester-hal-utils.o -MD -MP -MF android/$(DEPDIR)/android_ipc_tester-hal-utils.Tpo -c -o android/android_ipc_tester-hal-utils.o `test -f 'android/hal-utils.c' || echo '$(srcdir)/'`android/hal-utils.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) android/$(DEPDIR)/android_ipc_tester-hal-utils.Tpo android/$(DEPDIR)/android_ipc_tester-hal-utils.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='android/hal-utils.c' object='android/android_ipc_tester-hal-utils.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_ipc_tester_CFLAGS) $(CFLAGS) -c -o android/android_ipc_tester-hal-utils.o `test -f 'android/hal-utils.c' || echo '$(srcdir)/'`android/hal-utils.c
+
+android/android_ipc_tester-hal-utils.obj: android/hal-utils.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_ipc_tester_CFLAGS) $(CFLAGS) -MT android/android_ipc_tester-hal-utils.obj -MD -MP -MF android/$(DEPDIR)/android_ipc_tester-hal-utils.Tpo -c -o android/android_ipc_tester-hal-utils.obj `if test -f 'android/hal-utils.c'; then $(CYGPATH_W) 'android/hal-utils.c'; else $(CYGPATH_W) '$(srcdir)/android/hal-utils.c'; fi`
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) android/$(DEPDIR)/android_ipc_tester-hal-utils.Tpo android/$(DEPDIR)/android_ipc_tester-hal-utils.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='android/hal-utils.c' object='android/android_ipc_tester-hal-utils.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_ipc_tester_CFLAGS) $(CFLAGS) -c -o android/android_ipc_tester-hal-utils.obj `if test -f 'android/hal-utils.c'; then $(CYGPATH_W) 'android/hal-utils.c'; else $(CYGPATH_W) '$(srcdir)/android/hal-utils.c'; fi`
+
+android/android_ipc_tester-ipc-tester.o: android/ipc-tester.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_ipc_tester_CFLAGS) $(CFLAGS) -MT android/android_ipc_tester-ipc-tester.o -MD -MP -MF android/$(DEPDIR)/android_ipc_tester-ipc-tester.Tpo -c -o android/android_ipc_tester-ipc-tester.o `test -f 'android/ipc-tester.c' || echo '$(srcdir)/'`android/ipc-tester.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) android/$(DEPDIR)/android_ipc_tester-ipc-tester.Tpo android/$(DEPDIR)/android_ipc_tester-ipc-tester.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='android/ipc-tester.c' object='android/android_ipc_tester-ipc-tester.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_ipc_tester_CFLAGS) $(CFLAGS) -c -o android/android_ipc_tester-ipc-tester.o `test -f 'android/ipc-tester.c' || echo '$(srcdir)/'`android/ipc-tester.c
+
+android/android_ipc_tester-ipc-tester.obj: android/ipc-tester.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_ipc_tester_CFLAGS) $(CFLAGS) -MT android/android_ipc_tester-ipc-tester.obj -MD -MP -MF android/$(DEPDIR)/android_ipc_tester-ipc-tester.Tpo -c -o android/android_ipc_tester-ipc-tester.obj `if test -f 'android/ipc-tester.c'; then $(CYGPATH_W) 'android/ipc-tester.c'; else $(CYGPATH_W) '$(srcdir)/android/ipc-tester.c'; fi`
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) android/$(DEPDIR)/android_ipc_tester-ipc-tester.Tpo android/$(DEPDIR)/android_ipc_tester-ipc-tester.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='android/ipc-tester.c' object='android/android_ipc_tester-ipc-tester.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_ipc_tester_CFLAGS) $(CFLAGS) -c -o android/android_ipc_tester-ipc-tester.obj `if test -f 'android/ipc-tester.c'; then $(CYGPATH_W) 'android/ipc-tester.c'; else $(CYGPATH_W) '$(srcdir)/android/ipc-tester.c'; fi`
+
+btio/obexd-btio.o: btio/btio.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(obexd_src_obexd_CFLAGS) $(CFLAGS) -MT btio/obexd-btio.o -MD -MP -MF btio/$(DEPDIR)/obexd-btio.Tpo -c -o btio/obexd-btio.o `test -f 'btio/btio.c' || echo '$(srcdir)/'`btio/btio.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) btio/$(DEPDIR)/obexd-btio.Tpo btio/$(DEPDIR)/obexd-btio.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='btio/btio.c' object='btio/obexd-btio.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(obexd_src_obexd_CFLAGS) $(CFLAGS) -c -o btio/obexd-btio.o `test -f 'btio/btio.c' || echo '$(srcdir)/'`btio/btio.c
+
+btio/obexd-btio.obj: btio/btio.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(obexd_src_obexd_CFLAGS) $(CFLAGS) -MT btio/obexd-btio.obj -MD -MP -MF btio/$(DEPDIR)/obexd-btio.Tpo -c -o btio/obexd-btio.obj `if test -f 'btio/btio.c'; then $(CYGPATH_W) 'btio/btio.c'; else $(CYGPATH_W) '$(srcdir)/btio/btio.c'; fi`
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) btio/$(DEPDIR)/obexd-btio.Tpo btio/$(DEPDIR)/obexd-btio.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='btio/btio.c' object='btio/obexd-btio.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(obexd_src_obexd_CFLAGS) $(CFLAGS) -c -o btio/obexd-btio.obj `if test -f 'btio/btio.c'; then $(CYGPATH_W) 'btio/btio.c'; else $(CYGPATH_W) '$(srcdir)/btio/btio.c'; fi`
+
+gobex/obexd-gobex.o: gobex/gobex.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(obexd_src_obexd_CFLAGS) $(CFLAGS) -MT gobex/obexd-gobex.o -MD -MP -MF gobex/$(DEPDIR)/obexd-gobex.Tpo -c -o gobex/obexd-gobex.o `test -f 'gobex/gobex.c' || echo '$(srcdir)/'`gobex/gobex.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) gobex/$(DEPDIR)/obexd-gobex.Tpo gobex/$(DEPDIR)/obexd-gobex.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='gobex/gobex.c' object='gobex/obexd-gobex.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(obexd_src_obexd_CFLAGS) $(CFLAGS) -c -o gobex/obexd-gobex.o `test -f 'gobex/gobex.c' || echo '$(srcdir)/'`gobex/gobex.c
+
+gobex/obexd-gobex.obj: gobex/gobex.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(obexd_src_obexd_CFLAGS) $(CFLAGS) -MT gobex/obexd-gobex.obj -MD -MP -MF gobex/$(DEPDIR)/obexd-gobex.Tpo -c -o gobex/obexd-gobex.obj `if test -f 'gobex/gobex.c'; then $(CYGPATH_W) 'gobex/gobex.c'; else $(CYGPATH_W) '$(srcdir)/gobex/gobex.c'; fi`
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) gobex/$(DEPDIR)/obexd-gobex.Tpo gobex/$(DEPDIR)/obexd-gobex.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='gobex/gobex.c' object='gobex/obexd-gobex.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(obexd_src_obexd_CFLAGS) $(CFLAGS) -c -o gobex/obexd-gobex.obj `if test -f 'gobex/gobex.c'; then $(CYGPATH_W) 'gobex/gobex.c'; else $(CYGPATH_W) '$(srcdir)/gobex/gobex.c'; fi`
+
+gobex/obexd-gobex-defs.o: gobex/gobex-defs.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(obexd_src_obexd_CFLAGS) $(CFLAGS) -MT gobex/obexd-gobex-defs.o -MD -MP -MF gobex/$(DEPDIR)/obexd-gobex-defs.Tpo -c -o gobex/obexd-gobex-defs.o `test -f 'gobex/gobex-defs.c' || echo '$(srcdir)/'`gobex/gobex-defs.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) gobex/$(DEPDIR)/obexd-gobex-defs.Tpo gobex/$(DEPDIR)/obexd-gobex-defs.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='gobex/gobex-defs.c' object='gobex/obexd-gobex-defs.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(obexd_src_obexd_CFLAGS) $(CFLAGS) -c -o gobex/obexd-gobex-defs.o `test -f 'gobex/gobex-defs.c' || echo '$(srcdir)/'`gobex/gobex-defs.c
+
+gobex/obexd-gobex-defs.obj: gobex/gobex-defs.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(obexd_src_obexd_CFLAGS) $(CFLAGS) -MT gobex/obexd-gobex-defs.obj -MD -MP -MF gobex/$(DEPDIR)/obexd-gobex-defs.Tpo -c -o gobex/obexd-gobex-defs.obj `if test -f 'gobex/gobex-defs.c'; then $(CYGPATH_W) 'gobex/gobex-defs.c'; else $(CYGPATH_W) '$(srcdir)/gobex/gobex-defs.c'; fi`
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) gobex/$(DEPDIR)/obexd-gobex-defs.Tpo gobex/$(DEPDIR)/obexd-gobex-defs.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='gobex/gobex-defs.c' object='gobex/obexd-gobex-defs.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(obexd_src_obexd_CFLAGS) $(CFLAGS) -c -o gobex/obexd-gobex-defs.obj `if test -f 'gobex/gobex-defs.c'; then $(CYGPATH_W) 'gobex/gobex-defs.c'; else $(CYGPATH_W) '$(srcdir)/gobex/gobex-defs.c'; fi`
+
+gobex/obexd-gobex-packet.o: gobex/gobex-packet.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(obexd_src_obexd_CFLAGS) $(CFLAGS) -MT gobex/obexd-gobex-packet.o -MD -MP -MF gobex/$(DEPDIR)/obexd-gobex-packet.Tpo -c -o gobex/obexd-gobex-packet.o `test -f 'gobex/gobex-packet.c' || echo '$(srcdir)/'`gobex/gobex-packet.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) gobex/$(DEPDIR)/obexd-gobex-packet.Tpo gobex/$(DEPDIR)/obexd-gobex-packet.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='gobex/gobex-packet.c' object='gobex/obexd-gobex-packet.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(obexd_src_obexd_CFLAGS) $(CFLAGS) -c -o gobex/obexd-gobex-packet.o `test -f 'gobex/gobex-packet.c' || echo '$(srcdir)/'`gobex/gobex-packet.c
+
+gobex/obexd-gobex-packet.obj: gobex/gobex-packet.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(obexd_src_obexd_CFLAGS) $(CFLAGS) -MT gobex/obexd-gobex-packet.obj -MD -MP -MF gobex/$(DEPDIR)/obexd-gobex-packet.Tpo -c -o gobex/obexd-gobex-packet.obj `if test -f 'gobex/gobex-packet.c'; then $(CYGPATH_W) 'gobex/gobex-packet.c'; else $(CYGPATH_W) '$(srcdir)/gobex/gobex-packet.c'; fi`
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) gobex/$(DEPDIR)/obexd-gobex-packet.Tpo gobex/$(DEPDIR)/obexd-gobex-packet.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='gobex/gobex-packet.c' object='gobex/obexd-gobex-packet.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(obexd_src_obexd_CFLAGS) $(CFLAGS) -c -o gobex/obexd-gobex-packet.obj `if test -f 'gobex/gobex-packet.c'; then $(CYGPATH_W) 'gobex/gobex-packet.c'; else $(CYGPATH_W) '$(srcdir)/gobex/gobex-packet.c'; fi`
+
+gobex/obexd-gobex-header.o: gobex/gobex-header.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(obexd_src_obexd_CFLAGS) $(CFLAGS) -MT gobex/obexd-gobex-header.o -MD -MP -MF gobex/$(DEPDIR)/obexd-gobex-header.Tpo -c -o gobex/obexd-gobex-header.o `test -f 'gobex/gobex-header.c' || echo '$(srcdir)/'`gobex/gobex-header.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) gobex/$(DEPDIR)/obexd-gobex-header.Tpo gobex/$(DEPDIR)/obexd-gobex-header.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='gobex/gobex-header.c' object='gobex/obexd-gobex-header.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(obexd_src_obexd_CFLAGS) $(CFLAGS) -c -o gobex/obexd-gobex-header.o `test -f 'gobex/gobex-header.c' || echo '$(srcdir)/'`gobex/gobex-header.c
+
+gobex/obexd-gobex-header.obj: gobex/gobex-header.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(obexd_src_obexd_CFLAGS) $(CFLAGS) -MT gobex/obexd-gobex-header.obj -MD -MP -MF gobex/$(DEPDIR)/obexd-gobex-header.Tpo -c -o gobex/obexd-gobex-header.obj `if test -f 'gobex/gobex-header.c'; then $(CYGPATH_W) 'gobex/gobex-header.c'; else $(CYGPATH_W) '$(srcdir)/gobex/gobex-header.c'; fi`
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) gobex/$(DEPDIR)/obexd-gobex-header.Tpo gobex/$(DEPDIR)/obexd-gobex-header.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='gobex/gobex-header.c' object='gobex/obexd-gobex-header.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(obexd_src_obexd_CFLAGS) $(CFLAGS) -c -o gobex/obexd-gobex-header.obj `if test -f 'gobex/gobex-header.c'; then $(CYGPATH_W) 'gobex/gobex-header.c'; else $(CYGPATH_W) '$(srcdir)/gobex/gobex-header.c'; fi`
+
+gobex/obexd-gobex-transfer.o: gobex/gobex-transfer.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(obexd_src_obexd_CFLAGS) $(CFLAGS) -MT gobex/obexd-gobex-transfer.o -MD -MP -MF gobex/$(DEPDIR)/obexd-gobex-transfer.Tpo -c -o gobex/obexd-gobex-transfer.o `test -f 'gobex/gobex-transfer.c' || echo '$(srcdir)/'`gobex/gobex-transfer.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) gobex/$(DEPDIR)/obexd-gobex-transfer.Tpo gobex/$(DEPDIR)/obexd-gobex-transfer.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='gobex/gobex-transfer.c' object='gobex/obexd-gobex-transfer.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(obexd_src_obexd_CFLAGS) $(CFLAGS) -c -o gobex/obexd-gobex-transfer.o `test -f 'gobex/gobex-transfer.c' || echo '$(srcdir)/'`gobex/gobex-transfer.c
+
+gobex/obexd-gobex-transfer.obj: gobex/gobex-transfer.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(obexd_src_obexd_CFLAGS) $(CFLAGS) -MT gobex/obexd-gobex-transfer.obj -MD -MP -MF gobex/$(DEPDIR)/obexd-gobex-transfer.Tpo -c -o gobex/obexd-gobex-transfer.obj `if test -f 'gobex/gobex-transfer.c'; then $(CYGPATH_W) 'gobex/gobex-transfer.c'; else $(CYGPATH_W) '$(srcdir)/gobex/gobex-transfer.c'; fi`
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) gobex/$(DEPDIR)/obexd-gobex-transfer.Tpo gobex/$(DEPDIR)/obexd-gobex-transfer.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='gobex/gobex-transfer.c' object='gobex/obexd-gobex-transfer.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(obexd_src_obexd_CFLAGS) $(CFLAGS) -c -o gobex/obexd-gobex-transfer.obj `if test -f 'gobex/gobex-transfer.c'; then $(CYGPATH_W) 'gobex/gobex-transfer.c'; else $(CYGPATH_W) '$(srcdir)/gobex/gobex-transfer.c'; fi`
+
+gobex/obexd-gobex-apparam.o: gobex/gobex-apparam.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(obexd_src_obexd_CFLAGS) $(CFLAGS) -MT gobex/obexd-gobex-apparam.o -MD -MP -MF gobex/$(DEPDIR)/obexd-gobex-apparam.Tpo -c -o gobex/obexd-gobex-apparam.o `test -f 'gobex/gobex-apparam.c' || echo '$(srcdir)/'`gobex/gobex-apparam.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) gobex/$(DEPDIR)/obexd-gobex-apparam.Tpo gobex/$(DEPDIR)/obexd-gobex-apparam.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='gobex/gobex-apparam.c' object='gobex/obexd-gobex-apparam.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(obexd_src_obexd_CFLAGS) $(CFLAGS) -c -o gobex/obexd-gobex-apparam.o `test -f 'gobex/gobex-apparam.c' || echo '$(srcdir)/'`gobex/gobex-apparam.c
+
+gobex/obexd-gobex-apparam.obj: gobex/gobex-apparam.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(obexd_src_obexd_CFLAGS) $(CFLAGS) -MT gobex/obexd-gobex-apparam.obj -MD -MP -MF gobex/$(DEPDIR)/obexd-gobex-apparam.Tpo -c -o gobex/obexd-gobex-apparam.obj `if test -f 'gobex/gobex-apparam.c'; then $(CYGPATH_W) 'gobex/gobex-apparam.c'; else $(CYGPATH_W) '$(srcdir)/gobex/gobex-apparam.c'; fi`
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) gobex/$(DEPDIR)/obexd-gobex-apparam.Tpo gobex/$(DEPDIR)/obexd-gobex-apparam.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='gobex/gobex-apparam.c' object='gobex/obexd-gobex-apparam.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(obexd_src_obexd_CFLAGS) $(CFLAGS) -c -o gobex/obexd-gobex-apparam.obj `if test -f 'gobex/gobex-apparam.c'; then $(CYGPATH_W) 'gobex/gobex-apparam.c'; else $(CYGPATH_W) '$(srcdir)/gobex/gobex-apparam.c'; fi`
+
+obexd/plugins/obexd-filesystem.o: obexd/plugins/filesystem.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(obexd_src_obexd_CFLAGS) $(CFLAGS) -MT obexd/plugins/obexd-filesystem.o -MD -MP -MF obexd/plugins/$(DEPDIR)/obexd-filesystem.Tpo -c -o obexd/plugins/obexd-filesystem.o `test -f 'obexd/plugins/filesystem.c' || echo '$(srcdir)/'`obexd/plugins/filesystem.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) obexd/plugins/$(DEPDIR)/obexd-filesystem.Tpo obexd/plugins/$(DEPDIR)/obexd-filesystem.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='obexd/plugins/filesystem.c' object='obexd/plugins/obexd-filesystem.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(obexd_src_obexd_CFLAGS) $(CFLAGS) -c -o obexd/plugins/obexd-filesystem.o `test -f 'obexd/plugins/filesystem.c' || echo '$(srcdir)/'`obexd/plugins/filesystem.c
+
+obexd/plugins/obexd-filesystem.obj: obexd/plugins/filesystem.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(obexd_src_obexd_CFLAGS) $(CFLAGS) -MT obexd/plugins/obexd-filesystem.obj -MD -MP -MF obexd/plugins/$(DEPDIR)/obexd-filesystem.Tpo -c -o obexd/plugins/obexd-filesystem.obj `if test -f 'obexd/plugins/filesystem.c'; then $(CYGPATH_W) 'obexd/plugins/filesystem.c'; else $(CYGPATH_W) '$(srcdir)/obexd/plugins/filesystem.c'; fi`
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) obexd/plugins/$(DEPDIR)/obexd-filesystem.Tpo obexd/plugins/$(DEPDIR)/obexd-filesystem.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='obexd/plugins/filesystem.c' object='obexd/plugins/obexd-filesystem.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(obexd_src_obexd_CFLAGS) $(CFLAGS) -c -o obexd/plugins/obexd-filesystem.obj `if test -f 'obexd/plugins/filesystem.c'; then $(CYGPATH_W) 'obexd/plugins/filesystem.c'; else $(CYGPATH_W) '$(srcdir)/obexd/plugins/filesystem.c'; fi`
+
+obexd/plugins/obexd-bluetooth.o: obexd/plugins/bluetooth.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(obexd_src_obexd_CFLAGS) $(CFLAGS) -MT obexd/plugins/obexd-bluetooth.o -MD -MP -MF obexd/plugins/$(DEPDIR)/obexd-bluetooth.Tpo -c -o obexd/plugins/obexd-bluetooth.o `test -f 'obexd/plugins/bluetooth.c' || echo '$(srcdir)/'`obexd/plugins/bluetooth.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) obexd/plugins/$(DEPDIR)/obexd-bluetooth.Tpo obexd/plugins/$(DEPDIR)/obexd-bluetooth.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='obexd/plugins/bluetooth.c' object='obexd/plugins/obexd-bluetooth.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(obexd_src_obexd_CFLAGS) $(CFLAGS) -c -o obexd/plugins/obexd-bluetooth.o `test -f 'obexd/plugins/bluetooth.c' || echo '$(srcdir)/'`obexd/plugins/bluetooth.c
+
+obexd/plugins/obexd-bluetooth.obj: obexd/plugins/bluetooth.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(obexd_src_obexd_CFLAGS) $(CFLAGS) -MT obexd/plugins/obexd-bluetooth.obj -MD -MP -MF obexd/plugins/$(DEPDIR)/obexd-bluetooth.Tpo -c -o obexd/plugins/obexd-bluetooth.obj `if test -f 'obexd/plugins/bluetooth.c'; then $(CYGPATH_W) 'obexd/plugins/bluetooth.c'; else $(CYGPATH_W) '$(srcdir)/obexd/plugins/bluetooth.c'; fi`
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) obexd/plugins/$(DEPDIR)/obexd-bluetooth.Tpo obexd/plugins/$(DEPDIR)/obexd-bluetooth.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='obexd/plugins/bluetooth.c' object='obexd/plugins/obexd-bluetooth.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(obexd_src_obexd_CFLAGS) $(CFLAGS) -c -o obexd/plugins/obexd-bluetooth.obj `if test -f 'obexd/plugins/bluetooth.c'; then $(CYGPATH_W) 'obexd/plugins/bluetooth.c'; else $(CYGPATH_W) '$(srcdir)/obexd/plugins/bluetooth.c'; fi`
+
+obexd/plugins/obexd-pcsuite.o: obexd/plugins/pcsuite.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(obexd_src_obexd_CFLAGS) $(CFLAGS) -MT obexd/plugins/obexd-pcsuite.o -MD -MP -MF obexd/plugins/$(DEPDIR)/obexd-pcsuite.Tpo -c -o obexd/plugins/obexd-pcsuite.o `test -f 'obexd/plugins/pcsuite.c' || echo '$(srcdir)/'`obexd/plugins/pcsuite.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) obexd/plugins/$(DEPDIR)/obexd-pcsuite.Tpo obexd/plugins/$(DEPDIR)/obexd-pcsuite.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='obexd/plugins/pcsuite.c' object='obexd/plugins/obexd-pcsuite.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(obexd_src_obexd_CFLAGS) $(CFLAGS) -c -o obexd/plugins/obexd-pcsuite.o `test -f 'obexd/plugins/pcsuite.c' || echo '$(srcdir)/'`obexd/plugins/pcsuite.c
+
+obexd/plugins/obexd-pcsuite.obj: obexd/plugins/pcsuite.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(obexd_src_obexd_CFLAGS) $(CFLAGS) -MT obexd/plugins/obexd-pcsuite.obj -MD -MP -MF obexd/plugins/$(DEPDIR)/obexd-pcsuite.Tpo -c -o obexd/plugins/obexd-pcsuite.obj `if test -f 'obexd/plugins/pcsuite.c'; then $(CYGPATH_W) 'obexd/plugins/pcsuite.c'; else $(CYGPATH_W) '$(srcdir)/obexd/plugins/pcsuite.c'; fi`
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) obexd/plugins/$(DEPDIR)/obexd-pcsuite.Tpo obexd/plugins/$(DEPDIR)/obexd-pcsuite.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='obexd/plugins/pcsuite.c' object='obexd/plugins/obexd-pcsuite.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(obexd_src_obexd_CFLAGS) $(CFLAGS) -c -o obexd/plugins/obexd-pcsuite.obj `if test -f 'obexd/plugins/pcsuite.c'; then $(CYGPATH_W) 'obexd/plugins/pcsuite.c'; else $(CYGPATH_W) '$(srcdir)/obexd/plugins/pcsuite.c'; fi`
+
+obexd/plugins/obexd-opp.o: obexd/plugins/opp.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(obexd_src_obexd_CFLAGS) $(CFLAGS) -MT obexd/plugins/obexd-opp.o -MD -MP -MF obexd/plugins/$(DEPDIR)/obexd-opp.Tpo -c -o obexd/plugins/obexd-opp.o `test -f 'obexd/plugins/opp.c' || echo '$(srcdir)/'`obexd/plugins/opp.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) obexd/plugins/$(DEPDIR)/obexd-opp.Tpo obexd/plugins/$(DEPDIR)/obexd-opp.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='obexd/plugins/opp.c' object='obexd/plugins/obexd-opp.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(obexd_src_obexd_CFLAGS) $(CFLAGS) -c -o obexd/plugins/obexd-opp.o `test -f 'obexd/plugins/opp.c' || echo '$(srcdir)/'`obexd/plugins/opp.c
+
+obexd/plugins/obexd-opp.obj: obexd/plugins/opp.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(obexd_src_obexd_CFLAGS) $(CFLAGS) -MT obexd/plugins/obexd-opp.obj -MD -MP -MF obexd/plugins/$(DEPDIR)/obexd-opp.Tpo -c -o obexd/plugins/obexd-opp.obj `if test -f 'obexd/plugins/opp.c'; then $(CYGPATH_W) 'obexd/plugins/opp.c'; else $(CYGPATH_W) '$(srcdir)/obexd/plugins/opp.c'; fi`
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) obexd/plugins/$(DEPDIR)/obexd-opp.Tpo obexd/plugins/$(DEPDIR)/obexd-opp.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='obexd/plugins/opp.c' object='obexd/plugins/obexd-opp.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(obexd_src_obexd_CFLAGS) $(CFLAGS) -c -o obexd/plugins/obexd-opp.obj `if test -f 'obexd/plugins/opp.c'; then $(CYGPATH_W) 'obexd/plugins/opp.c'; else $(CYGPATH_W) '$(srcdir)/obexd/plugins/opp.c'; fi`
+
+obexd/plugins/obexd-ftp.o: obexd/plugins/ftp.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(obexd_src_obexd_CFLAGS) $(CFLAGS) -MT obexd/plugins/obexd-ftp.o -MD -MP -MF obexd/plugins/$(DEPDIR)/obexd-ftp.Tpo -c -o obexd/plugins/obexd-ftp.o `test -f 'obexd/plugins/ftp.c' || echo '$(srcdir)/'`obexd/plugins/ftp.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) obexd/plugins/$(DEPDIR)/obexd-ftp.Tpo obexd/plugins/$(DEPDIR)/obexd-ftp.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='obexd/plugins/ftp.c' object='obexd/plugins/obexd-ftp.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(obexd_src_obexd_CFLAGS) $(CFLAGS) -c -o obexd/plugins/obexd-ftp.o `test -f 'obexd/plugins/ftp.c' || echo '$(srcdir)/'`obexd/plugins/ftp.c
+
+obexd/plugins/obexd-ftp.obj: obexd/plugins/ftp.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(obexd_src_obexd_CFLAGS) $(CFLAGS) -MT obexd/plugins/obexd-ftp.obj -MD -MP -MF obexd/plugins/$(DEPDIR)/obexd-ftp.Tpo -c -o obexd/plugins/obexd-ftp.obj `if test -f 'obexd/plugins/ftp.c'; then $(CYGPATH_W) 'obexd/plugins/ftp.c'; else $(CYGPATH_W) '$(srcdir)/obexd/plugins/ftp.c'; fi`
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) obexd/plugins/$(DEPDIR)/obexd-ftp.Tpo obexd/plugins/$(DEPDIR)/obexd-ftp.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='obexd/plugins/ftp.c' object='obexd/plugins/obexd-ftp.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(obexd_src_obexd_CFLAGS) $(CFLAGS) -c -o obexd/plugins/obexd-ftp.obj `if test -f 'obexd/plugins/ftp.c'; then $(CYGPATH_W) 'obexd/plugins/ftp.c'; else $(CYGPATH_W) '$(srcdir)/obexd/plugins/ftp.c'; fi`
+
+obexd/plugins/obexd-irmc.o: obexd/plugins/irmc.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(obexd_src_obexd_CFLAGS) $(CFLAGS) -MT obexd/plugins/obexd-irmc.o -MD -MP -MF obexd/plugins/$(DEPDIR)/obexd-irmc.Tpo -c -o obexd/plugins/obexd-irmc.o `test -f 'obexd/plugins/irmc.c' || echo '$(srcdir)/'`obexd/plugins/irmc.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) obexd/plugins/$(DEPDIR)/obexd-irmc.Tpo obexd/plugins/$(DEPDIR)/obexd-irmc.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='obexd/plugins/irmc.c' object='obexd/plugins/obexd-irmc.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(obexd_src_obexd_CFLAGS) $(CFLAGS) -c -o obexd/plugins/obexd-irmc.o `test -f 'obexd/plugins/irmc.c' || echo '$(srcdir)/'`obexd/plugins/irmc.c
+
+obexd/plugins/obexd-irmc.obj: obexd/plugins/irmc.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(obexd_src_obexd_CFLAGS) $(CFLAGS) -MT obexd/plugins/obexd-irmc.obj -MD -MP -MF obexd/plugins/$(DEPDIR)/obexd-irmc.Tpo -c -o obexd/plugins/obexd-irmc.obj `if test -f 'obexd/plugins/irmc.c'; then $(CYGPATH_W) 'obexd/plugins/irmc.c'; else $(CYGPATH_W) '$(srcdir)/obexd/plugins/irmc.c'; fi`
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) obexd/plugins/$(DEPDIR)/obexd-irmc.Tpo obexd/plugins/$(DEPDIR)/obexd-irmc.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='obexd/plugins/irmc.c' object='obexd/plugins/obexd-irmc.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(obexd_src_obexd_CFLAGS) $(CFLAGS) -c -o obexd/plugins/obexd-irmc.obj `if test -f 'obexd/plugins/irmc.c'; then $(CYGPATH_W) 'obexd/plugins/irmc.c'; else $(CYGPATH_W) '$(srcdir)/obexd/plugins/irmc.c'; fi`
+
+obexd/plugins/obexd-pbap.o: obexd/plugins/pbap.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(obexd_src_obexd_CFLAGS) $(CFLAGS) -MT obexd/plugins/obexd-pbap.o -MD -MP -MF obexd/plugins/$(DEPDIR)/obexd-pbap.Tpo -c -o obexd/plugins/obexd-pbap.o `test -f 'obexd/plugins/pbap.c' || echo '$(srcdir)/'`obexd/plugins/pbap.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) obexd/plugins/$(DEPDIR)/obexd-pbap.Tpo obexd/plugins/$(DEPDIR)/obexd-pbap.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='obexd/plugins/pbap.c' object='obexd/plugins/obexd-pbap.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(obexd_src_obexd_CFLAGS) $(CFLAGS) -c -o obexd/plugins/obexd-pbap.o `test -f 'obexd/plugins/pbap.c' || echo '$(srcdir)/'`obexd/plugins/pbap.c
+
+obexd/plugins/obexd-pbap.obj: obexd/plugins/pbap.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(obexd_src_obexd_CFLAGS) $(CFLAGS) -MT obexd/plugins/obexd-pbap.obj -MD -MP -MF obexd/plugins/$(DEPDIR)/obexd-pbap.Tpo -c -o obexd/plugins/obexd-pbap.obj `if test -f 'obexd/plugins/pbap.c'; then $(CYGPATH_W) 'obexd/plugins/pbap.c'; else $(CYGPATH_W) '$(srcdir)/obexd/plugins/pbap.c'; fi`
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) obexd/plugins/$(DEPDIR)/obexd-pbap.Tpo obexd/plugins/$(DEPDIR)/obexd-pbap.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='obexd/plugins/pbap.c' object='obexd/plugins/obexd-pbap.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(obexd_src_obexd_CFLAGS) $(CFLAGS) -c -o obexd/plugins/obexd-pbap.obj `if test -f 'obexd/plugins/pbap.c'; then $(CYGPATH_W) 'obexd/plugins/pbap.c'; else $(CYGPATH_W) '$(srcdir)/obexd/plugins/pbap.c'; fi`
+
+obexd/plugins/obexd-vcard.o: obexd/plugins/vcard.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(obexd_src_obexd_CFLAGS) $(CFLAGS) -MT obexd/plugins/obexd-vcard.o -MD -MP -MF obexd/plugins/$(DEPDIR)/obexd-vcard.Tpo -c -o obexd/plugins/obexd-vcard.o `test -f 'obexd/plugins/vcard.c' || echo '$(srcdir)/'`obexd/plugins/vcard.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) obexd/plugins/$(DEPDIR)/obexd-vcard.Tpo obexd/plugins/$(DEPDIR)/obexd-vcard.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='obexd/plugins/vcard.c' object='obexd/plugins/obexd-vcard.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(obexd_src_obexd_CFLAGS) $(CFLAGS) -c -o obexd/plugins/obexd-vcard.o `test -f 'obexd/plugins/vcard.c' || echo '$(srcdir)/'`obexd/plugins/vcard.c
+
+obexd/plugins/obexd-vcard.obj: obexd/plugins/vcard.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(obexd_src_obexd_CFLAGS) $(CFLAGS) -MT obexd/plugins/obexd-vcard.obj -MD -MP -MF obexd/plugins/$(DEPDIR)/obexd-vcard.Tpo -c -o obexd/plugins/obexd-vcard.obj `if test -f 'obexd/plugins/vcard.c'; then $(CYGPATH_W) 'obexd/plugins/vcard.c'; else $(CYGPATH_W) '$(srcdir)/obexd/plugins/vcard.c'; fi`
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) obexd/plugins/$(DEPDIR)/obexd-vcard.Tpo obexd/plugins/$(DEPDIR)/obexd-vcard.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='obexd/plugins/vcard.c' object='obexd/plugins/obexd-vcard.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(obexd_src_obexd_CFLAGS) $(CFLAGS) -c -o obexd/plugins/obexd-vcard.obj `if test -f 'obexd/plugins/vcard.c'; then $(CYGPATH_W) 'obexd/plugins/vcard.c'; else $(CYGPATH_W) '$(srcdir)/obexd/plugins/vcard.c'; fi`
+
+obexd/plugins/obexd-phonebook-dummy.o: obexd/plugins/phonebook-dummy.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(obexd_src_obexd_CFLAGS) $(CFLAGS) -MT obexd/plugins/obexd-phonebook-dummy.o -MD -MP -MF obexd/plugins/$(DEPDIR)/obexd-phonebook-dummy.Tpo -c -o obexd/plugins/obexd-phonebook-dummy.o `test -f 'obexd/plugins/phonebook-dummy.c' || echo '$(srcdir)/'`obexd/plugins/phonebook-dummy.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) obexd/plugins/$(DEPDIR)/obexd-phonebook-dummy.Tpo obexd/plugins/$(DEPDIR)/obexd-phonebook-dummy.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='obexd/plugins/phonebook-dummy.c' object='obexd/plugins/obexd-phonebook-dummy.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(obexd_src_obexd_CFLAGS) $(CFLAGS) -c -o obexd/plugins/obexd-phonebook-dummy.o `test -f 'obexd/plugins/phonebook-dummy.c' || echo '$(srcdir)/'`obexd/plugins/phonebook-dummy.c
+
+obexd/plugins/obexd-phonebook-dummy.obj: obexd/plugins/phonebook-dummy.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(obexd_src_obexd_CFLAGS) $(CFLAGS) -MT obexd/plugins/obexd-phonebook-dummy.obj -MD -MP -MF obexd/plugins/$(DEPDIR)/obexd-phonebook-dummy.Tpo -c -o obexd/plugins/obexd-phonebook-dummy.obj `if test -f 'obexd/plugins/phonebook-dummy.c'; then $(CYGPATH_W) 'obexd/plugins/phonebook-dummy.c'; else $(CYGPATH_W) '$(srcdir)/obexd/plugins/phonebook-dummy.c'; fi`
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) obexd/plugins/$(DEPDIR)/obexd-phonebook-dummy.Tpo obexd/plugins/$(DEPDIR)/obexd-phonebook-dummy.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='obexd/plugins/phonebook-dummy.c' object='obexd/plugins/obexd-phonebook-dummy.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(obexd_src_obexd_CFLAGS) $(CFLAGS) -c -o obexd/plugins/obexd-phonebook-dummy.obj `if test -f 'obexd/plugins/phonebook-dummy.c'; then $(CYGPATH_W) 'obexd/plugins/phonebook-dummy.c'; else $(CYGPATH_W) '$(srcdir)/obexd/plugins/phonebook-dummy.c'; fi`
+
+obexd/plugins/obexd-mas.o: obexd/plugins/mas.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(obexd_src_obexd_CFLAGS) $(CFLAGS) -MT obexd/plugins/obexd-mas.o -MD -MP -MF obexd/plugins/$(DEPDIR)/obexd-mas.Tpo -c -o obexd/plugins/obexd-mas.o `test -f 'obexd/plugins/mas.c' || echo '$(srcdir)/'`obexd/plugins/mas.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) obexd/plugins/$(DEPDIR)/obexd-mas.Tpo obexd/plugins/$(DEPDIR)/obexd-mas.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='obexd/plugins/mas.c' object='obexd/plugins/obexd-mas.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(obexd_src_obexd_CFLAGS) $(CFLAGS) -c -o obexd/plugins/obexd-mas.o `test -f 'obexd/plugins/mas.c' || echo '$(srcdir)/'`obexd/plugins/mas.c
+
+obexd/plugins/obexd-mas.obj: obexd/plugins/mas.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(obexd_src_obexd_CFLAGS) $(CFLAGS) -MT obexd/plugins/obexd-mas.obj -MD -MP -MF obexd/plugins/$(DEPDIR)/obexd-mas.Tpo -c -o obexd/plugins/obexd-mas.obj `if test -f 'obexd/plugins/mas.c'; then $(CYGPATH_W) 'obexd/plugins/mas.c'; else $(CYGPATH_W) '$(srcdir)/obexd/plugins/mas.c'; fi`
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) obexd/plugins/$(DEPDIR)/obexd-mas.Tpo obexd/plugins/$(DEPDIR)/obexd-mas.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='obexd/plugins/mas.c' object='obexd/plugins/obexd-mas.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(obexd_src_obexd_CFLAGS) $(CFLAGS) -c -o obexd/plugins/obexd-mas.obj `if test -f 'obexd/plugins/mas.c'; then $(CYGPATH_W) 'obexd/plugins/mas.c'; else $(CYGPATH_W) '$(srcdir)/obexd/plugins/mas.c'; fi`
+
+obexd/plugins/obexd-messages-dummy.o: obexd/plugins/messages-dummy.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(obexd_src_obexd_CFLAGS) $(CFLAGS) -MT obexd/plugins/obexd-messages-dummy.o -MD -MP -MF obexd/plugins/$(DEPDIR)/obexd-messages-dummy.Tpo -c -o obexd/plugins/obexd-messages-dummy.o `test -f 'obexd/plugins/messages-dummy.c' || echo '$(srcdir)/'`obexd/plugins/messages-dummy.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) obexd/plugins/$(DEPDIR)/obexd-messages-dummy.Tpo obexd/plugins/$(DEPDIR)/obexd-messages-dummy.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='obexd/plugins/messages-dummy.c' object='obexd/plugins/obexd-messages-dummy.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(obexd_src_obexd_CFLAGS) $(CFLAGS) -c -o obexd/plugins/obexd-messages-dummy.o `test -f 'obexd/plugins/messages-dummy.c' || echo '$(srcdir)/'`obexd/plugins/messages-dummy.c
+
+obexd/plugins/obexd-messages-dummy.obj: obexd/plugins/messages-dummy.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(obexd_src_obexd_CFLAGS) $(CFLAGS) -MT obexd/plugins/obexd-messages-dummy.obj -MD -MP -MF obexd/plugins/$(DEPDIR)/obexd-messages-dummy.Tpo -c -o obexd/plugins/obexd-messages-dummy.obj `if test -f 'obexd/plugins/messages-dummy.c'; then $(CYGPATH_W) 'obexd/plugins/messages-dummy.c'; else $(CYGPATH_W) '$(srcdir)/obexd/plugins/messages-dummy.c'; fi`
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) obexd/plugins/$(DEPDIR)/obexd-messages-dummy.Tpo obexd/plugins/$(DEPDIR)/obexd-messages-dummy.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='obexd/plugins/messages-dummy.c' object='obexd/plugins/obexd-messages-dummy.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(obexd_src_obexd_CFLAGS) $(CFLAGS) -c -o obexd/plugins/obexd-messages-dummy.obj `if test -f 'obexd/plugins/messages-dummy.c'; then $(CYGPATH_W) 'obexd/plugins/messages-dummy.c'; else $(CYGPATH_W) '$(srcdir)/obexd/plugins/messages-dummy.c'; fi`
+
+obexd/client/obexd-mns.o: obexd/client/mns.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(obexd_src_obexd_CFLAGS) $(CFLAGS) -MT obexd/client/obexd-mns.o -MD -MP -MF obexd/client/$(DEPDIR)/obexd-mns.Tpo -c -o obexd/client/obexd-mns.o `test -f 'obexd/client/mns.c' || echo '$(srcdir)/'`obexd/client/mns.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) obexd/client/$(DEPDIR)/obexd-mns.Tpo obexd/client/$(DEPDIR)/obexd-mns.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='obexd/client/mns.c' object='obexd/client/obexd-mns.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(obexd_src_obexd_CFLAGS) $(CFLAGS) -c -o obexd/client/obexd-mns.o `test -f 'obexd/client/mns.c' || echo '$(srcdir)/'`obexd/client/mns.c
+
+obexd/client/obexd-mns.obj: obexd/client/mns.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(obexd_src_obexd_CFLAGS) $(CFLAGS) -MT obexd/client/obexd-mns.obj -MD -MP -MF obexd/client/$(DEPDIR)/obexd-mns.Tpo -c -o obexd/client/obexd-mns.obj `if test -f 'obexd/client/mns.c'; then $(CYGPATH_W) 'obexd/client/mns.c'; else $(CYGPATH_W) '$(srcdir)/obexd/client/mns.c'; fi`
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) obexd/client/$(DEPDIR)/obexd-mns.Tpo obexd/client/$(DEPDIR)/obexd-mns.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='obexd/client/mns.c' object='obexd/client/obexd-mns.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(obexd_src_obexd_CFLAGS) $(CFLAGS) -c -o obexd/client/obexd-mns.obj `if test -f 'obexd/client/mns.c'; then $(CYGPATH_W) 'obexd/client/mns.c'; else $(CYGPATH_W) '$(srcdir)/obexd/client/mns.c'; fi`
+
+obexd/src/obexd-main.o: obexd/src/main.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(obexd_src_obexd_CFLAGS) $(CFLAGS) -MT obexd/src/obexd-main.o -MD -MP -MF obexd/src/$(DEPDIR)/obexd-main.Tpo -c -o obexd/src/obexd-main.o `test -f 'obexd/src/main.c' || echo '$(srcdir)/'`obexd/src/main.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) obexd/src/$(DEPDIR)/obexd-main.Tpo obexd/src/$(DEPDIR)/obexd-main.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='obexd/src/main.c' object='obexd/src/obexd-main.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(obexd_src_obexd_CFLAGS) $(CFLAGS) -c -o obexd/src/obexd-main.o `test -f 'obexd/src/main.c' || echo '$(srcdir)/'`obexd/src/main.c
+
+obexd/src/obexd-main.obj: obexd/src/main.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(obexd_src_obexd_CFLAGS) $(CFLAGS) -MT obexd/src/obexd-main.obj -MD -MP -MF obexd/src/$(DEPDIR)/obexd-main.Tpo -c -o obexd/src/obexd-main.obj `if test -f 'obexd/src/main.c'; then $(CYGPATH_W) 'obexd/src/main.c'; else $(CYGPATH_W) '$(srcdir)/obexd/src/main.c'; fi`
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) obexd/src/$(DEPDIR)/obexd-main.Tpo obexd/src/$(DEPDIR)/obexd-main.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='obexd/src/main.c' object='obexd/src/obexd-main.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(obexd_src_obexd_CFLAGS) $(CFLAGS) -c -o obexd/src/obexd-main.obj `if test -f 'obexd/src/main.c'; then $(CYGPATH_W) 'obexd/src/main.c'; else $(CYGPATH_W) '$(srcdir)/obexd/src/main.c'; fi`
+
+obexd/src/obexd-plugin.o: obexd/src/plugin.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(obexd_src_obexd_CFLAGS) $(CFLAGS) -MT obexd/src/obexd-plugin.o -MD -MP -MF obexd/src/$(DEPDIR)/obexd-plugin.Tpo -c -o obexd/src/obexd-plugin.o `test -f 'obexd/src/plugin.c' || echo '$(srcdir)/'`obexd/src/plugin.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) obexd/src/$(DEPDIR)/obexd-plugin.Tpo obexd/src/$(DEPDIR)/obexd-plugin.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='obexd/src/plugin.c' object='obexd/src/obexd-plugin.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(obexd_src_obexd_CFLAGS) $(CFLAGS) -c -o obexd/src/obexd-plugin.o `test -f 'obexd/src/plugin.c' || echo '$(srcdir)/'`obexd/src/plugin.c
+
+obexd/src/obexd-plugin.obj: obexd/src/plugin.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(obexd_src_obexd_CFLAGS) $(CFLAGS) -MT obexd/src/obexd-plugin.obj -MD -MP -MF obexd/src/$(DEPDIR)/obexd-plugin.Tpo -c -o obexd/src/obexd-plugin.obj `if test -f 'obexd/src/plugin.c'; then $(CYGPATH_W) 'obexd/src/plugin.c'; else $(CYGPATH_W) '$(srcdir)/obexd/src/plugin.c'; fi`
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) obexd/src/$(DEPDIR)/obexd-plugin.Tpo obexd/src/$(DEPDIR)/obexd-plugin.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='obexd/src/plugin.c' object='obexd/src/obexd-plugin.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(obexd_src_obexd_CFLAGS) $(CFLAGS) -c -o obexd/src/obexd-plugin.obj `if test -f 'obexd/src/plugin.c'; then $(CYGPATH_W) 'obexd/src/plugin.c'; else $(CYGPATH_W) '$(srcdir)/obexd/src/plugin.c'; fi`
+
+obexd/src/obexd-log.o: obexd/src/log.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(obexd_src_obexd_CFLAGS) $(CFLAGS) -MT obexd/src/obexd-log.o -MD -MP -MF obexd/src/$(DEPDIR)/obexd-log.Tpo -c -o obexd/src/obexd-log.o `test -f 'obexd/src/log.c' || echo '$(srcdir)/'`obexd/src/log.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) obexd/src/$(DEPDIR)/obexd-log.Tpo obexd/src/$(DEPDIR)/obexd-log.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='obexd/src/log.c' object='obexd/src/obexd-log.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(obexd_src_obexd_CFLAGS) $(CFLAGS) -c -o obexd/src/obexd-log.o `test -f 'obexd/src/log.c' || echo '$(srcdir)/'`obexd/src/log.c
+
+obexd/src/obexd-log.obj: obexd/src/log.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(obexd_src_obexd_CFLAGS) $(CFLAGS) -MT obexd/src/obexd-log.obj -MD -MP -MF obexd/src/$(DEPDIR)/obexd-log.Tpo -c -o obexd/src/obexd-log.obj `if test -f 'obexd/src/log.c'; then $(CYGPATH_W) 'obexd/src/log.c'; else $(CYGPATH_W) '$(srcdir)/obexd/src/log.c'; fi`
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) obexd/src/$(DEPDIR)/obexd-log.Tpo obexd/src/$(DEPDIR)/obexd-log.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='obexd/src/log.c' object='obexd/src/obexd-log.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(obexd_src_obexd_CFLAGS) $(CFLAGS) -c -o obexd/src/obexd-log.obj `if test -f 'obexd/src/log.c'; then $(CYGPATH_W) 'obexd/src/log.c'; else $(CYGPATH_W) '$(srcdir)/obexd/src/log.c'; fi`
+
+obexd/src/obexd-manager.o: obexd/src/manager.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(obexd_src_obexd_CFLAGS) $(CFLAGS) -MT obexd/src/obexd-manager.o -MD -MP -MF obexd/src/$(DEPDIR)/obexd-manager.Tpo -c -o obexd/src/obexd-manager.o `test -f 'obexd/src/manager.c' || echo '$(srcdir)/'`obexd/src/manager.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) obexd/src/$(DEPDIR)/obexd-manager.Tpo obexd/src/$(DEPDIR)/obexd-manager.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='obexd/src/manager.c' object='obexd/src/obexd-manager.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(obexd_src_obexd_CFLAGS) $(CFLAGS) -c -o obexd/src/obexd-manager.o `test -f 'obexd/src/manager.c' || echo '$(srcdir)/'`obexd/src/manager.c
+
+obexd/src/obexd-manager.obj: obexd/src/manager.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(obexd_src_obexd_CFLAGS) $(CFLAGS) -MT obexd/src/obexd-manager.obj -MD -MP -MF obexd/src/$(DEPDIR)/obexd-manager.Tpo -c -o obexd/src/obexd-manager.obj `if test -f 'obexd/src/manager.c'; then $(CYGPATH_W) 'obexd/src/manager.c'; else $(CYGPATH_W) '$(srcdir)/obexd/src/manager.c'; fi`
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) obexd/src/$(DEPDIR)/obexd-manager.Tpo obexd/src/$(DEPDIR)/obexd-manager.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='obexd/src/manager.c' object='obexd/src/obexd-manager.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(obexd_src_obexd_CFLAGS) $(CFLAGS) -c -o obexd/src/obexd-manager.obj `if test -f 'obexd/src/manager.c'; then $(CYGPATH_W) 'obexd/src/manager.c'; else $(CYGPATH_W) '$(srcdir)/obexd/src/manager.c'; fi`
+
+obexd/src/obexd-obex.o: obexd/src/obex.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(obexd_src_obexd_CFLAGS) $(CFLAGS) -MT obexd/src/obexd-obex.o -MD -MP -MF obexd/src/$(DEPDIR)/obexd-obex.Tpo -c -o obexd/src/obexd-obex.o `test -f 'obexd/src/obex.c' || echo '$(srcdir)/'`obexd/src/obex.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) obexd/src/$(DEPDIR)/obexd-obex.Tpo obexd/src/$(DEPDIR)/obexd-obex.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='obexd/src/obex.c' object='obexd/src/obexd-obex.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(obexd_src_obexd_CFLAGS) $(CFLAGS) -c -o obexd/src/obexd-obex.o `test -f 'obexd/src/obex.c' || echo '$(srcdir)/'`obexd/src/obex.c
+
+obexd/src/obexd-obex.obj: obexd/src/obex.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(obexd_src_obexd_CFLAGS) $(CFLAGS) -MT obexd/src/obexd-obex.obj -MD -MP -MF obexd/src/$(DEPDIR)/obexd-obex.Tpo -c -o obexd/src/obexd-obex.obj `if test -f 'obexd/src/obex.c'; then $(CYGPATH_W) 'obexd/src/obex.c'; else $(CYGPATH_W) '$(srcdir)/obexd/src/obex.c'; fi`
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) obexd/src/$(DEPDIR)/obexd-obex.Tpo obexd/src/$(DEPDIR)/obexd-obex.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='obexd/src/obex.c' object='obexd/src/obexd-obex.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(obexd_src_obexd_CFLAGS) $(CFLAGS) -c -o obexd/src/obexd-obex.obj `if test -f 'obexd/src/obex.c'; then $(CYGPATH_W) 'obexd/src/obex.c'; else $(CYGPATH_W) '$(srcdir)/obexd/src/obex.c'; fi`
+
+obexd/src/obexd-mimetype.o: obexd/src/mimetype.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(obexd_src_obexd_CFLAGS) $(CFLAGS) -MT obexd/src/obexd-mimetype.o -MD -MP -MF obexd/src/$(DEPDIR)/obexd-mimetype.Tpo -c -o obexd/src/obexd-mimetype.o `test -f 'obexd/src/mimetype.c' || echo '$(srcdir)/'`obexd/src/mimetype.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) obexd/src/$(DEPDIR)/obexd-mimetype.Tpo obexd/src/$(DEPDIR)/obexd-mimetype.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='obexd/src/mimetype.c' object='obexd/src/obexd-mimetype.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(obexd_src_obexd_CFLAGS) $(CFLAGS) -c -o obexd/src/obexd-mimetype.o `test -f 'obexd/src/mimetype.c' || echo '$(srcdir)/'`obexd/src/mimetype.c
+
+obexd/src/obexd-mimetype.obj: obexd/src/mimetype.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(obexd_src_obexd_CFLAGS) $(CFLAGS) -MT obexd/src/obexd-mimetype.obj -MD -MP -MF obexd/src/$(DEPDIR)/obexd-mimetype.Tpo -c -o obexd/src/obexd-mimetype.obj `if test -f 'obexd/src/mimetype.c'; then $(CYGPATH_W) 'obexd/src/mimetype.c'; else $(CYGPATH_W) '$(srcdir)/obexd/src/mimetype.c'; fi`
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) obexd/src/$(DEPDIR)/obexd-mimetype.Tpo obexd/src/$(DEPDIR)/obexd-mimetype.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='obexd/src/mimetype.c' object='obexd/src/obexd-mimetype.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(obexd_src_obexd_CFLAGS) $(CFLAGS) -c -o obexd/src/obexd-mimetype.obj `if test -f 'obexd/src/mimetype.c'; then $(CYGPATH_W) 'obexd/src/mimetype.c'; else $(CYGPATH_W) '$(srcdir)/obexd/src/mimetype.c'; fi`
+
+obexd/src/obexd-service.o: obexd/src/service.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(obexd_src_obexd_CFLAGS) $(CFLAGS) -MT obexd/src/obexd-service.o -MD -MP -MF obexd/src/$(DEPDIR)/obexd-service.Tpo -c -o obexd/src/obexd-service.o `test -f 'obexd/src/service.c' || echo '$(srcdir)/'`obexd/src/service.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) obexd/src/$(DEPDIR)/obexd-service.Tpo obexd/src/$(DEPDIR)/obexd-service.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='obexd/src/service.c' object='obexd/src/obexd-service.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(obexd_src_obexd_CFLAGS) $(CFLAGS) -c -o obexd/src/obexd-service.o `test -f 'obexd/src/service.c' || echo '$(srcdir)/'`obexd/src/service.c
+
+obexd/src/obexd-service.obj: obexd/src/service.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(obexd_src_obexd_CFLAGS) $(CFLAGS) -MT obexd/src/obexd-service.obj -MD -MP -MF obexd/src/$(DEPDIR)/obexd-service.Tpo -c -o obexd/src/obexd-service.obj `if test -f 'obexd/src/service.c'; then $(CYGPATH_W) 'obexd/src/service.c'; else $(CYGPATH_W) '$(srcdir)/obexd/src/service.c'; fi`
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) obexd/src/$(DEPDIR)/obexd-service.Tpo obexd/src/$(DEPDIR)/obexd-service.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='obexd/src/service.c' object='obexd/src/obexd-service.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(obexd_src_obexd_CFLAGS) $(CFLAGS) -c -o obexd/src/obexd-service.obj `if test -f 'obexd/src/service.c'; then $(CYGPATH_W) 'obexd/src/service.c'; else $(CYGPATH_W) '$(srcdir)/obexd/src/service.c'; fi`
+
+obexd/src/obexd-transport.o: obexd/src/transport.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(obexd_src_obexd_CFLAGS) $(CFLAGS) -MT obexd/src/obexd-transport.o -MD -MP -MF obexd/src/$(DEPDIR)/obexd-transport.Tpo -c -o obexd/src/obexd-transport.o `test -f 'obexd/src/transport.c' || echo '$(srcdir)/'`obexd/src/transport.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) obexd/src/$(DEPDIR)/obexd-transport.Tpo obexd/src/$(DEPDIR)/obexd-transport.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='obexd/src/transport.c' object='obexd/src/obexd-transport.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(obexd_src_obexd_CFLAGS) $(CFLAGS) -c -o obexd/src/obexd-transport.o `test -f 'obexd/src/transport.c' || echo '$(srcdir)/'`obexd/src/transport.c
+
+obexd/src/obexd-transport.obj: obexd/src/transport.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(obexd_src_obexd_CFLAGS) $(CFLAGS) -MT obexd/src/obexd-transport.obj -MD -MP -MF obexd/src/$(DEPDIR)/obexd-transport.Tpo -c -o obexd/src/obexd-transport.obj `if test -f 'obexd/src/transport.c'; then $(CYGPATH_W) 'obexd/src/transport.c'; else $(CYGPATH_W) '$(srcdir)/obexd/src/transport.c'; fi`
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) obexd/src/$(DEPDIR)/obexd-transport.Tpo obexd/src/$(DEPDIR)/obexd-transport.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='obexd/src/transport.c' object='obexd/src/obexd-transport.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(obexd_src_obexd_CFLAGS) $(CFLAGS) -c -o obexd/src/obexd-transport.obj `if test -f 'obexd/src/transport.c'; then $(CYGPATH_W) 'obexd/src/transport.c'; else $(CYGPATH_W) '$(srcdir)/obexd/src/transport.c'; fi`
+
+obexd/src/obexd-server.o: obexd/src/server.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(obexd_src_obexd_CFLAGS) $(CFLAGS) -MT obexd/src/obexd-server.o -MD -MP -MF obexd/src/$(DEPDIR)/obexd-server.Tpo -c -o obexd/src/obexd-server.o `test -f 'obexd/src/server.c' || echo '$(srcdir)/'`obexd/src/server.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) obexd/src/$(DEPDIR)/obexd-server.Tpo obexd/src/$(DEPDIR)/obexd-server.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='obexd/src/server.c' object='obexd/src/obexd-server.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(obexd_src_obexd_CFLAGS) $(CFLAGS) -c -o obexd/src/obexd-server.o `test -f 'obexd/src/server.c' || echo '$(srcdir)/'`obexd/src/server.c
+
+obexd/src/obexd-server.obj: obexd/src/server.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(obexd_src_obexd_CFLAGS) $(CFLAGS) -MT obexd/src/obexd-server.obj -MD -MP -MF obexd/src/$(DEPDIR)/obexd-server.Tpo -c -o obexd/src/obexd-server.obj `if test -f 'obexd/src/server.c'; then $(CYGPATH_W) 'obexd/src/server.c'; else $(CYGPATH_W) '$(srcdir)/obexd/src/server.c'; fi`
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) obexd/src/$(DEPDIR)/obexd-server.Tpo obexd/src/$(DEPDIR)/obexd-server.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='obexd/src/server.c' object='obexd/src/obexd-server.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(obexd_src_obexd_CFLAGS) $(CFLAGS) -c -o obexd/src/obexd-server.obj `if test -f 'obexd/src/server.c'; then $(CYGPATH_W) 'obexd/src/server.c'; else $(CYGPATH_W) '$(srcdir)/obexd/src/server.c'; fi`
+
+obexd/client/obexd-manager.o: obexd/client/manager.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(obexd_src_obexd_CFLAGS) $(CFLAGS) -MT obexd/client/obexd-manager.o -MD -MP -MF obexd/client/$(DEPDIR)/obexd-manager.Tpo -c -o obexd/client/obexd-manager.o `test -f 'obexd/client/manager.c' || echo '$(srcdir)/'`obexd/client/manager.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) obexd/client/$(DEPDIR)/obexd-manager.Tpo obexd/client/$(DEPDIR)/obexd-manager.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='obexd/client/manager.c' object='obexd/client/obexd-manager.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(obexd_src_obexd_CFLAGS) $(CFLAGS) -c -o obexd/client/obexd-manager.o `test -f 'obexd/client/manager.c' || echo '$(srcdir)/'`obexd/client/manager.c
+
+obexd/client/obexd-manager.obj: obexd/client/manager.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(obexd_src_obexd_CFLAGS) $(CFLAGS) -MT obexd/client/obexd-manager.obj -MD -MP -MF obexd/client/$(DEPDIR)/obexd-manager.Tpo -c -o obexd/client/obexd-manager.obj `if test -f 'obexd/client/manager.c'; then $(CYGPATH_W) 'obexd/client/manager.c'; else $(CYGPATH_W) '$(srcdir)/obexd/client/manager.c'; fi`
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) obexd/client/$(DEPDIR)/obexd-manager.Tpo obexd/client/$(DEPDIR)/obexd-manager.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='obexd/client/manager.c' object='obexd/client/obexd-manager.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(obexd_src_obexd_CFLAGS) $(CFLAGS) -c -o obexd/client/obexd-manager.obj `if test -f 'obexd/client/manager.c'; then $(CYGPATH_W) 'obexd/client/manager.c'; else $(CYGPATH_W) '$(srcdir)/obexd/client/manager.c'; fi`
+
+obexd/client/obexd-session.o: obexd/client/session.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(obexd_src_obexd_CFLAGS) $(CFLAGS) -MT obexd/client/obexd-session.o -MD -MP -MF obexd/client/$(DEPDIR)/obexd-session.Tpo -c -o obexd/client/obexd-session.o `test -f 'obexd/client/session.c' || echo '$(srcdir)/'`obexd/client/session.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) obexd/client/$(DEPDIR)/obexd-session.Tpo obexd/client/$(DEPDIR)/obexd-session.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='obexd/client/session.c' object='obexd/client/obexd-session.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(obexd_src_obexd_CFLAGS) $(CFLAGS) -c -o obexd/client/obexd-session.o `test -f 'obexd/client/session.c' || echo '$(srcdir)/'`obexd/client/session.c
+
+obexd/client/obexd-session.obj: obexd/client/session.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(obexd_src_obexd_CFLAGS) $(CFLAGS) -MT obexd/client/obexd-session.obj -MD -MP -MF obexd/client/$(DEPDIR)/obexd-session.Tpo -c -o obexd/client/obexd-session.obj `if test -f 'obexd/client/session.c'; then $(CYGPATH_W) 'obexd/client/session.c'; else $(CYGPATH_W) '$(srcdir)/obexd/client/session.c'; fi`
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) obexd/client/$(DEPDIR)/obexd-session.Tpo obexd/client/$(DEPDIR)/obexd-session.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='obexd/client/session.c' object='obexd/client/obexd-session.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(obexd_src_obexd_CFLAGS) $(CFLAGS) -c -o obexd/client/obexd-session.obj `if test -f 'obexd/client/session.c'; then $(CYGPATH_W) 'obexd/client/session.c'; else $(CYGPATH_W) '$(srcdir)/obexd/client/session.c'; fi`
+
+obexd/client/obexd-bluetooth.o: obexd/client/bluetooth.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(obexd_src_obexd_CFLAGS) $(CFLAGS) -MT obexd/client/obexd-bluetooth.o -MD -MP -MF obexd/client/$(DEPDIR)/obexd-bluetooth.Tpo -c -o obexd/client/obexd-bluetooth.o `test -f 'obexd/client/bluetooth.c' || echo '$(srcdir)/'`obexd/client/bluetooth.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) obexd/client/$(DEPDIR)/obexd-bluetooth.Tpo obexd/client/$(DEPDIR)/obexd-bluetooth.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='obexd/client/bluetooth.c' object='obexd/client/obexd-bluetooth.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(obexd_src_obexd_CFLAGS) $(CFLAGS) -c -o obexd/client/obexd-bluetooth.o `test -f 'obexd/client/bluetooth.c' || echo '$(srcdir)/'`obexd/client/bluetooth.c
+
+obexd/client/obexd-bluetooth.obj: obexd/client/bluetooth.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(obexd_src_obexd_CFLAGS) $(CFLAGS) -MT obexd/client/obexd-bluetooth.obj -MD -MP -MF obexd/client/$(DEPDIR)/obexd-bluetooth.Tpo -c -o obexd/client/obexd-bluetooth.obj `if test -f 'obexd/client/bluetooth.c'; then $(CYGPATH_W) 'obexd/client/bluetooth.c'; else $(CYGPATH_W) '$(srcdir)/obexd/client/bluetooth.c'; fi`
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) obexd/client/$(DEPDIR)/obexd-bluetooth.Tpo obexd/client/$(DEPDIR)/obexd-bluetooth.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='obexd/client/bluetooth.c' object='obexd/client/obexd-bluetooth.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(obexd_src_obexd_CFLAGS) $(CFLAGS) -c -o obexd/client/obexd-bluetooth.obj `if test -f 'obexd/client/bluetooth.c'; then $(CYGPATH_W) 'obexd/client/bluetooth.c'; else $(CYGPATH_W) '$(srcdir)/obexd/client/bluetooth.c'; fi`
+
+obexd/client/obexd-sync.o: obexd/client/sync.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(obexd_src_obexd_CFLAGS) $(CFLAGS) -MT obexd/client/obexd-sync.o -MD -MP -MF obexd/client/$(DEPDIR)/obexd-sync.Tpo -c -o obexd/client/obexd-sync.o `test -f 'obexd/client/sync.c' || echo '$(srcdir)/'`obexd/client/sync.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) obexd/client/$(DEPDIR)/obexd-sync.Tpo obexd/client/$(DEPDIR)/obexd-sync.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='obexd/client/sync.c' object='obexd/client/obexd-sync.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(obexd_src_obexd_CFLAGS) $(CFLAGS) -c -o obexd/client/obexd-sync.o `test -f 'obexd/client/sync.c' || echo '$(srcdir)/'`obexd/client/sync.c
+
+obexd/client/obexd-sync.obj: obexd/client/sync.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(obexd_src_obexd_CFLAGS) $(CFLAGS) -MT obexd/client/obexd-sync.obj -MD -MP -MF obexd/client/$(DEPDIR)/obexd-sync.Tpo -c -o obexd/client/obexd-sync.obj `if test -f 'obexd/client/sync.c'; then $(CYGPATH_W) 'obexd/client/sync.c'; else $(CYGPATH_W) '$(srcdir)/obexd/client/sync.c'; fi`
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) obexd/client/$(DEPDIR)/obexd-sync.Tpo obexd/client/$(DEPDIR)/obexd-sync.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='obexd/client/sync.c' object='obexd/client/obexd-sync.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(obexd_src_obexd_CFLAGS) $(CFLAGS) -c -o obexd/client/obexd-sync.obj `if test -f 'obexd/client/sync.c'; then $(CYGPATH_W) 'obexd/client/sync.c'; else $(CYGPATH_W) '$(srcdir)/obexd/client/sync.c'; fi`
+
+obexd/client/obexd-pbap.o: obexd/client/pbap.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(obexd_src_obexd_CFLAGS) $(CFLAGS) -MT obexd/client/obexd-pbap.o -MD -MP -MF obexd/client/$(DEPDIR)/obexd-pbap.Tpo -c -o obexd/client/obexd-pbap.o `test -f 'obexd/client/pbap.c' || echo '$(srcdir)/'`obexd/client/pbap.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) obexd/client/$(DEPDIR)/obexd-pbap.Tpo obexd/client/$(DEPDIR)/obexd-pbap.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='obexd/client/pbap.c' object='obexd/client/obexd-pbap.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(obexd_src_obexd_CFLAGS) $(CFLAGS) -c -o obexd/client/obexd-pbap.o `test -f 'obexd/client/pbap.c' || echo '$(srcdir)/'`obexd/client/pbap.c
+
+obexd/client/obexd-pbap.obj: obexd/client/pbap.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(obexd_src_obexd_CFLAGS) $(CFLAGS) -MT obexd/client/obexd-pbap.obj -MD -MP -MF obexd/client/$(DEPDIR)/obexd-pbap.Tpo -c -o obexd/client/obexd-pbap.obj `if test -f 'obexd/client/pbap.c'; then $(CYGPATH_W) 'obexd/client/pbap.c'; else $(CYGPATH_W) '$(srcdir)/obexd/client/pbap.c'; fi`
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) obexd/client/$(DEPDIR)/obexd-pbap.Tpo obexd/client/$(DEPDIR)/obexd-pbap.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='obexd/client/pbap.c' object='obexd/client/obexd-pbap.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(obexd_src_obexd_CFLAGS) $(CFLAGS) -c -o obexd/client/obexd-pbap.obj `if test -f 'obexd/client/pbap.c'; then $(CYGPATH_W) 'obexd/client/pbap.c'; else $(CYGPATH_W) '$(srcdir)/obexd/client/pbap.c'; fi`
+
+obexd/client/obexd-ftp.o: obexd/client/ftp.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(obexd_src_obexd_CFLAGS) $(CFLAGS) -MT obexd/client/obexd-ftp.o -MD -MP -MF obexd/client/$(DEPDIR)/obexd-ftp.Tpo -c -o obexd/client/obexd-ftp.o `test -f 'obexd/client/ftp.c' || echo '$(srcdir)/'`obexd/client/ftp.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) obexd/client/$(DEPDIR)/obexd-ftp.Tpo obexd/client/$(DEPDIR)/obexd-ftp.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='obexd/client/ftp.c' object='obexd/client/obexd-ftp.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(obexd_src_obexd_CFLAGS) $(CFLAGS) -c -o obexd/client/obexd-ftp.o `test -f 'obexd/client/ftp.c' || echo '$(srcdir)/'`obexd/client/ftp.c
+
+obexd/client/obexd-ftp.obj: obexd/client/ftp.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(obexd_src_obexd_CFLAGS) $(CFLAGS) -MT obexd/client/obexd-ftp.obj -MD -MP -MF obexd/client/$(DEPDIR)/obexd-ftp.Tpo -c -o obexd/client/obexd-ftp.obj `if test -f 'obexd/client/ftp.c'; then $(CYGPATH_W) 'obexd/client/ftp.c'; else $(CYGPATH_W) '$(srcdir)/obexd/client/ftp.c'; fi`
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) obexd/client/$(DEPDIR)/obexd-ftp.Tpo obexd/client/$(DEPDIR)/obexd-ftp.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='obexd/client/ftp.c' object='obexd/client/obexd-ftp.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(obexd_src_obexd_CFLAGS) $(CFLAGS) -c -o obexd/client/obexd-ftp.obj `if test -f 'obexd/client/ftp.c'; then $(CYGPATH_W) 'obexd/client/ftp.c'; else $(CYGPATH_W) '$(srcdir)/obexd/client/ftp.c'; fi`
+
+obexd/client/obexd-opp.o: obexd/client/opp.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(obexd_src_obexd_CFLAGS) $(CFLAGS) -MT obexd/client/obexd-opp.o -MD -MP -MF obexd/client/$(DEPDIR)/obexd-opp.Tpo -c -o obexd/client/obexd-opp.o `test -f 'obexd/client/opp.c' || echo '$(srcdir)/'`obexd/client/opp.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) obexd/client/$(DEPDIR)/obexd-opp.Tpo obexd/client/$(DEPDIR)/obexd-opp.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='obexd/client/opp.c' object='obexd/client/obexd-opp.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(obexd_src_obexd_CFLAGS) $(CFLAGS) -c -o obexd/client/obexd-opp.o `test -f 'obexd/client/opp.c' || echo '$(srcdir)/'`obexd/client/opp.c
+
+obexd/client/obexd-opp.obj: obexd/client/opp.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(obexd_src_obexd_CFLAGS) $(CFLAGS) -MT obexd/client/obexd-opp.obj -MD -MP -MF obexd/client/$(DEPDIR)/obexd-opp.Tpo -c -o obexd/client/obexd-opp.obj `if test -f 'obexd/client/opp.c'; then $(CYGPATH_W) 'obexd/client/opp.c'; else $(CYGPATH_W) '$(srcdir)/obexd/client/opp.c'; fi`
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) obexd/client/$(DEPDIR)/obexd-opp.Tpo obexd/client/$(DEPDIR)/obexd-opp.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='obexd/client/opp.c' object='obexd/client/obexd-opp.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(obexd_src_obexd_CFLAGS) $(CFLAGS) -c -o obexd/client/obexd-opp.obj `if test -f 'obexd/client/opp.c'; then $(CYGPATH_W) 'obexd/client/opp.c'; else $(CYGPATH_W) '$(srcdir)/obexd/client/opp.c'; fi`
+
+obexd/client/obexd-map.o: obexd/client/map.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(obexd_src_obexd_CFLAGS) $(CFLAGS) -MT obexd/client/obexd-map.o -MD -MP -MF obexd/client/$(DEPDIR)/obexd-map.Tpo -c -o obexd/client/obexd-map.o `test -f 'obexd/client/map.c' || echo '$(srcdir)/'`obexd/client/map.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) obexd/client/$(DEPDIR)/obexd-map.Tpo obexd/client/$(DEPDIR)/obexd-map.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='obexd/client/map.c' object='obexd/client/obexd-map.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(obexd_src_obexd_CFLAGS) $(CFLAGS) -c -o obexd/client/obexd-map.o `test -f 'obexd/client/map.c' || echo '$(srcdir)/'`obexd/client/map.c
+
+obexd/client/obexd-map.obj: obexd/client/map.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(obexd_src_obexd_CFLAGS) $(CFLAGS) -MT obexd/client/obexd-map.obj -MD -MP -MF obexd/client/$(DEPDIR)/obexd-map.Tpo -c -o obexd/client/obexd-map.obj `if test -f 'obexd/client/map.c'; then $(CYGPATH_W) 'obexd/client/map.c'; else $(CYGPATH_W) '$(srcdir)/obexd/client/map.c'; fi`
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) obexd/client/$(DEPDIR)/obexd-map.Tpo obexd/client/$(DEPDIR)/obexd-map.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='obexd/client/map.c' object='obexd/client/obexd-map.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(obexd_src_obexd_CFLAGS) $(CFLAGS) -c -o obexd/client/obexd-map.obj `if test -f 'obexd/client/map.c'; then $(CYGPATH_W) 'obexd/client/map.c'; else $(CYGPATH_W) '$(srcdir)/obexd/client/map.c'; fi`
+
+obexd/client/obexd-map-event.o: obexd/client/map-event.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(obexd_src_obexd_CFLAGS) $(CFLAGS) -MT obexd/client/obexd-map-event.o -MD -MP -MF obexd/client/$(DEPDIR)/obexd-map-event.Tpo -c -o obexd/client/obexd-map-event.o `test -f 'obexd/client/map-event.c' || echo '$(srcdir)/'`obexd/client/map-event.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) obexd/client/$(DEPDIR)/obexd-map-event.Tpo obexd/client/$(DEPDIR)/obexd-map-event.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='obexd/client/map-event.c' object='obexd/client/obexd-map-event.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(obexd_src_obexd_CFLAGS) $(CFLAGS) -c -o obexd/client/obexd-map-event.o `test -f 'obexd/client/map-event.c' || echo '$(srcdir)/'`obexd/client/map-event.c
+
+obexd/client/obexd-map-event.obj: obexd/client/map-event.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(obexd_src_obexd_CFLAGS) $(CFLAGS) -MT obexd/client/obexd-map-event.obj -MD -MP -MF obexd/client/$(DEPDIR)/obexd-map-event.Tpo -c -o obexd/client/obexd-map-event.obj `if test -f 'obexd/client/map-event.c'; then $(CYGPATH_W) 'obexd/client/map-event.c'; else $(CYGPATH_W) '$(srcdir)/obexd/client/map-event.c'; fi`
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) obexd/client/$(DEPDIR)/obexd-map-event.Tpo obexd/client/$(DEPDIR)/obexd-map-event.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='obexd/client/map-event.c' object='obexd/client/obexd-map-event.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(obexd_src_obexd_CFLAGS) $(CFLAGS) -c -o obexd/client/obexd-map-event.obj `if test -f 'obexd/client/map-event.c'; then $(CYGPATH_W) 'obexd/client/map-event.c'; else $(CYGPATH_W) '$(srcdir)/obexd/client/map-event.c'; fi`
+
+obexd/client/obexd-transfer.o: obexd/client/transfer.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(obexd_src_obexd_CFLAGS) $(CFLAGS) -MT obexd/client/obexd-transfer.o -MD -MP -MF obexd/client/$(DEPDIR)/obexd-transfer.Tpo -c -o obexd/client/obexd-transfer.o `test -f 'obexd/client/transfer.c' || echo '$(srcdir)/'`obexd/client/transfer.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) obexd/client/$(DEPDIR)/obexd-transfer.Tpo obexd/client/$(DEPDIR)/obexd-transfer.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='obexd/client/transfer.c' object='obexd/client/obexd-transfer.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(obexd_src_obexd_CFLAGS) $(CFLAGS) -c -o obexd/client/obexd-transfer.o `test -f 'obexd/client/transfer.c' || echo '$(srcdir)/'`obexd/client/transfer.c
+
+obexd/client/obexd-transfer.obj: obexd/client/transfer.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(obexd_src_obexd_CFLAGS) $(CFLAGS) -MT obexd/client/obexd-transfer.obj -MD -MP -MF obexd/client/$(DEPDIR)/obexd-transfer.Tpo -c -o obexd/client/obexd-transfer.obj `if test -f 'obexd/client/transfer.c'; then $(CYGPATH_W) 'obexd/client/transfer.c'; else $(CYGPATH_W) '$(srcdir)/obexd/client/transfer.c'; fi`
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) obexd/client/$(DEPDIR)/obexd-transfer.Tpo obexd/client/$(DEPDIR)/obexd-transfer.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='obexd/client/transfer.c' object='obexd/client/obexd-transfer.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(obexd_src_obexd_CFLAGS) $(CFLAGS) -c -o obexd/client/obexd-transfer.obj `if test -f 'obexd/client/transfer.c'; then $(CYGPATH_W) 'obexd/client/transfer.c'; else $(CYGPATH_W) '$(srcdir)/obexd/client/transfer.c'; fi`
+
+obexd/client/obexd-transport.o: obexd/client/transport.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(obexd_src_obexd_CFLAGS) $(CFLAGS) -MT obexd/client/obexd-transport.o -MD -MP -MF obexd/client/$(DEPDIR)/obexd-transport.Tpo -c -o obexd/client/obexd-transport.o `test -f 'obexd/client/transport.c' || echo '$(srcdir)/'`obexd/client/transport.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) obexd/client/$(DEPDIR)/obexd-transport.Tpo obexd/client/$(DEPDIR)/obexd-transport.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='obexd/client/transport.c' object='obexd/client/obexd-transport.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(obexd_src_obexd_CFLAGS) $(CFLAGS) -c -o obexd/client/obexd-transport.o `test -f 'obexd/client/transport.c' || echo '$(srcdir)/'`obexd/client/transport.c
+
+obexd/client/obexd-transport.obj: obexd/client/transport.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(obexd_src_obexd_CFLAGS) $(CFLAGS) -MT obexd/client/obexd-transport.obj -MD -MP -MF obexd/client/$(DEPDIR)/obexd-transport.Tpo -c -o obexd/client/obexd-transport.obj `if test -f 'obexd/client/transport.c'; then $(CYGPATH_W) 'obexd/client/transport.c'; else $(CYGPATH_W) '$(srcdir)/obexd/client/transport.c'; fi`
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) obexd/client/$(DEPDIR)/obexd-transport.Tpo obexd/client/$(DEPDIR)/obexd-transport.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='obexd/client/transport.c' object='obexd/client/obexd-transport.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(obexd_src_obexd_CFLAGS) $(CFLAGS) -c -o obexd/client/obexd-transport.obj `if test -f 'obexd/client/transport.c'; then $(CYGPATH_W) 'obexd/client/transport.c'; else $(CYGPATH_W) '$(srcdir)/obexd/client/transport.c'; fi`
+
+obexd/client/obexd-dbus.o: obexd/client/dbus.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(obexd_src_obexd_CFLAGS) $(CFLAGS) -MT obexd/client/obexd-dbus.o -MD -MP -MF obexd/client/$(DEPDIR)/obexd-dbus.Tpo -c -o obexd/client/obexd-dbus.o `test -f 'obexd/client/dbus.c' || echo '$(srcdir)/'`obexd/client/dbus.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) obexd/client/$(DEPDIR)/obexd-dbus.Tpo obexd/client/$(DEPDIR)/obexd-dbus.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='obexd/client/dbus.c' object='obexd/client/obexd-dbus.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(obexd_src_obexd_CFLAGS) $(CFLAGS) -c -o obexd/client/obexd-dbus.o `test -f 'obexd/client/dbus.c' || echo '$(srcdir)/'`obexd/client/dbus.c
+
+obexd/client/obexd-dbus.obj: obexd/client/dbus.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(obexd_src_obexd_CFLAGS) $(CFLAGS) -MT obexd/client/obexd-dbus.obj -MD -MP -MF obexd/client/$(DEPDIR)/obexd-dbus.Tpo -c -o obexd/client/obexd-dbus.obj `if test -f 'obexd/client/dbus.c'; then $(CYGPATH_W) 'obexd/client/dbus.c'; else $(CYGPATH_W) '$(srcdir)/obexd/client/dbus.c'; fi`
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) obexd/client/$(DEPDIR)/obexd-dbus.Tpo obexd/client/$(DEPDIR)/obexd-dbus.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='obexd/client/dbus.c' object='obexd/client/obexd-dbus.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(obexd_src_obexd_CFLAGS) $(CFLAGS) -c -o obexd/client/obexd-dbus.obj `if test -f 'obexd/client/dbus.c'; then $(CYGPATH_W) 'obexd/client/dbus.c'; else $(CYGPATH_W) '$(srcdir)/obexd/client/dbus.c'; fi`
+
+obexd/client/obexd-driver.o: obexd/client/driver.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(obexd_src_obexd_CFLAGS) $(CFLAGS) -MT obexd/client/obexd-driver.o -MD -MP -MF obexd/client/$(DEPDIR)/obexd-driver.Tpo -c -o obexd/client/obexd-driver.o `test -f 'obexd/client/driver.c' || echo '$(srcdir)/'`obexd/client/driver.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) obexd/client/$(DEPDIR)/obexd-driver.Tpo obexd/client/$(DEPDIR)/obexd-driver.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='obexd/client/driver.c' object='obexd/client/obexd-driver.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(obexd_src_obexd_CFLAGS) $(CFLAGS) -c -o obexd/client/obexd-driver.o `test -f 'obexd/client/driver.c' || echo '$(srcdir)/'`obexd/client/driver.c
+
+obexd/client/obexd-driver.obj: obexd/client/driver.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(obexd_src_obexd_CFLAGS) $(CFLAGS) -MT obexd/client/obexd-driver.obj -MD -MP -MF obexd/client/$(DEPDIR)/obexd-driver.Tpo -c -o obexd/client/obexd-driver.obj `if test -f 'obexd/client/driver.c'; then $(CYGPATH_W) 'obexd/client/driver.c'; else $(CYGPATH_W) '$(srcdir)/obexd/client/driver.c'; fi`
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) obexd/client/$(DEPDIR)/obexd-driver.Tpo obexd/client/$(DEPDIR)/obexd-driver.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='obexd/client/driver.c' object='obexd/client/obexd-driver.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(obexd_src_obexd_CFLAGS) $(CFLAGS) -c -o obexd/client/obexd-driver.obj `if test -f 'obexd/client/driver.c'; then $(CYGPATH_W) 'obexd/client/driver.c'; else $(CYGPATH_W) '$(srcdir)/obexd/client/driver.c'; fi`
+
+plugins/bluetoothd-hostname.o: plugins/hostname.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT plugins/bluetoothd-hostname.o -MD -MP -MF plugins/$(DEPDIR)/bluetoothd-hostname.Tpo -c -o plugins/bluetoothd-hostname.o `test -f 'plugins/hostname.c' || echo '$(srcdir)/'`plugins/hostname.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) plugins/$(DEPDIR)/bluetoothd-hostname.Tpo plugins/$(DEPDIR)/bluetoothd-hostname.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='plugins/hostname.c' object='plugins/bluetoothd-hostname.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o plugins/bluetoothd-hostname.o `test -f 'plugins/hostname.c' || echo '$(srcdir)/'`plugins/hostname.c
+
+plugins/bluetoothd-hostname.obj: plugins/hostname.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT plugins/bluetoothd-hostname.obj -MD -MP -MF plugins/$(DEPDIR)/bluetoothd-hostname.Tpo -c -o plugins/bluetoothd-hostname.obj `if test -f 'plugins/hostname.c'; then $(CYGPATH_W) 'plugins/hostname.c'; else $(CYGPATH_W) '$(srcdir)/plugins/hostname.c'; fi`
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) plugins/$(DEPDIR)/bluetoothd-hostname.Tpo plugins/$(DEPDIR)/bluetoothd-hostname.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='plugins/hostname.c' object='plugins/bluetoothd-hostname.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o plugins/bluetoothd-hostname.obj `if test -f 'plugins/hostname.c'; then $(CYGPATH_W) 'plugins/hostname.c'; else $(CYGPATH_W) '$(srcdir)/plugins/hostname.c'; fi`
+
+plugins/bluetoothd-wiimote.o: plugins/wiimote.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT plugins/bluetoothd-wiimote.o -MD -MP -MF plugins/$(DEPDIR)/bluetoothd-wiimote.Tpo -c -o plugins/bluetoothd-wiimote.o `test -f 'plugins/wiimote.c' || echo '$(srcdir)/'`plugins/wiimote.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) plugins/$(DEPDIR)/bluetoothd-wiimote.Tpo plugins/$(DEPDIR)/bluetoothd-wiimote.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='plugins/wiimote.c' object='plugins/bluetoothd-wiimote.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o plugins/bluetoothd-wiimote.o `test -f 'plugins/wiimote.c' || echo '$(srcdir)/'`plugins/wiimote.c
+
+plugins/bluetoothd-wiimote.obj: plugins/wiimote.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT plugins/bluetoothd-wiimote.obj -MD -MP -MF plugins/$(DEPDIR)/bluetoothd-wiimote.Tpo -c -o plugins/bluetoothd-wiimote.obj `if test -f 'plugins/wiimote.c'; then $(CYGPATH_W) 'plugins/wiimote.c'; else $(CYGPATH_W) '$(srcdir)/plugins/wiimote.c'; fi`
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) plugins/$(DEPDIR)/bluetoothd-wiimote.Tpo plugins/$(DEPDIR)/bluetoothd-wiimote.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='plugins/wiimote.c' object='plugins/bluetoothd-wiimote.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o plugins/bluetoothd-wiimote.obj `if test -f 'plugins/wiimote.c'; then $(CYGPATH_W) 'plugins/wiimote.c'; else $(CYGPATH_W) '$(srcdir)/plugins/wiimote.c'; fi`
+
+plugins/bluetoothd-autopair.o: plugins/autopair.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT plugins/bluetoothd-autopair.o -MD -MP -MF plugins/$(DEPDIR)/bluetoothd-autopair.Tpo -c -o plugins/bluetoothd-autopair.o `test -f 'plugins/autopair.c' || echo '$(srcdir)/'`plugins/autopair.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) plugins/$(DEPDIR)/bluetoothd-autopair.Tpo plugins/$(DEPDIR)/bluetoothd-autopair.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='plugins/autopair.c' object='plugins/bluetoothd-autopair.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o plugins/bluetoothd-autopair.o `test -f 'plugins/autopair.c' || echo '$(srcdir)/'`plugins/autopair.c
+
+plugins/bluetoothd-autopair.obj: plugins/autopair.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT plugins/bluetoothd-autopair.obj -MD -MP -MF plugins/$(DEPDIR)/bluetoothd-autopair.Tpo -c -o plugins/bluetoothd-autopair.obj `if test -f 'plugins/autopair.c'; then $(CYGPATH_W) 'plugins/autopair.c'; else $(CYGPATH_W) '$(srcdir)/plugins/autopair.c'; fi`
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) plugins/$(DEPDIR)/bluetoothd-autopair.Tpo plugins/$(DEPDIR)/bluetoothd-autopair.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='plugins/autopair.c' object='plugins/bluetoothd-autopair.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o plugins/bluetoothd-autopair.obj `if test -f 'plugins/autopair.c'; then $(CYGPATH_W) 'plugins/autopair.c'; else $(CYGPATH_W) '$(srcdir)/plugins/autopair.c'; fi`
+
+plugins/bluetoothd-dropcam.o: plugins/dropcam.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT plugins/bluetoothd-dropcam.o -MD -MP -MF plugins/$(DEPDIR)/bluetoothd-dropcam.Tpo -c -o plugins/bluetoothd-dropcam.o `test -f 'plugins/dropcam.c' || echo '$(srcdir)/'`plugins/dropcam.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) plugins/$(DEPDIR)/bluetoothd-dropcam.Tpo plugins/$(DEPDIR)/bluetoothd-dropcam.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='plugins/dropcam.c' object='plugins/bluetoothd-dropcam.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o plugins/bluetoothd-dropcam.o `test -f 'plugins/dropcam.c' || echo '$(srcdir)/'`plugins/dropcam.c
+
+plugins/bluetoothd-dropcam.obj: plugins/dropcam.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT plugins/bluetoothd-dropcam.obj -MD -MP -MF plugins/$(DEPDIR)/bluetoothd-dropcam.Tpo -c -o plugins/bluetoothd-dropcam.obj `if test -f 'plugins/dropcam.c'; then $(CYGPATH_W) 'plugins/dropcam.c'; else $(CYGPATH_W) '$(srcdir)/plugins/dropcam.c'; fi`
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) plugins/$(DEPDIR)/bluetoothd-dropcam.Tpo plugins/$(DEPDIR)/bluetoothd-dropcam.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='plugins/dropcam.c' object='plugins/bluetoothd-dropcam.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o plugins/bluetoothd-dropcam.obj `if test -f 'plugins/dropcam.c'; then $(CYGPATH_W) 'plugins/dropcam.c'; else $(CYGPATH_W) '$(srcdir)/plugins/dropcam.c'; fi`
+
+plugins/bluetoothd-policy.o: plugins/policy.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT plugins/bluetoothd-policy.o -MD -MP -MF plugins/$(DEPDIR)/bluetoothd-policy.Tpo -c -o plugins/bluetoothd-policy.o `test -f 'plugins/policy.c' || echo '$(srcdir)/'`plugins/policy.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) plugins/$(DEPDIR)/bluetoothd-policy.Tpo plugins/$(DEPDIR)/bluetoothd-policy.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='plugins/policy.c' object='plugins/bluetoothd-policy.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o plugins/bluetoothd-policy.o `test -f 'plugins/policy.c' || echo '$(srcdir)/'`plugins/policy.c
+
+plugins/bluetoothd-policy.obj: plugins/policy.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT plugins/bluetoothd-policy.obj -MD -MP -MF plugins/$(DEPDIR)/bluetoothd-policy.Tpo -c -o plugins/bluetoothd-policy.obj `if test -f 'plugins/policy.c'; then $(CYGPATH_W) 'plugins/policy.c'; else $(CYGPATH_W) '$(srcdir)/plugins/policy.c'; fi`
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) plugins/$(DEPDIR)/bluetoothd-policy.Tpo plugins/$(DEPDIR)/bluetoothd-policy.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='plugins/policy.c' object='plugins/bluetoothd-policy.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o plugins/bluetoothd-policy.obj `if test -f 'plugins/policy.c'; then $(CYGPATH_W) 'plugins/policy.c'; else $(CYGPATH_W) '$(srcdir)/plugins/policy.c'; fi`
+
+plugins/bluetoothd-gatt-example.o: plugins/gatt-example.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT plugins/bluetoothd-gatt-example.o -MD -MP -MF plugins/$(DEPDIR)/bluetoothd-gatt-example.Tpo -c -o plugins/bluetoothd-gatt-example.o `test -f 'plugins/gatt-example.c' || echo '$(srcdir)/'`plugins/gatt-example.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) plugins/$(DEPDIR)/bluetoothd-gatt-example.Tpo plugins/$(DEPDIR)/bluetoothd-gatt-example.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='plugins/gatt-example.c' object='plugins/bluetoothd-gatt-example.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o plugins/bluetoothd-gatt-example.o `test -f 'plugins/gatt-example.c' || echo '$(srcdir)/'`plugins/gatt-example.c
+
+plugins/bluetoothd-gatt-example.obj: plugins/gatt-example.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT plugins/bluetoothd-gatt-example.obj -MD -MP -MF plugins/$(DEPDIR)/bluetoothd-gatt-example.Tpo -c -o plugins/bluetoothd-gatt-example.obj `if test -f 'plugins/gatt-example.c'; then $(CYGPATH_W) 'plugins/gatt-example.c'; else $(CYGPATH_W) '$(srcdir)/plugins/gatt-example.c'; fi`
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) plugins/$(DEPDIR)/bluetoothd-gatt-example.Tpo plugins/$(DEPDIR)/bluetoothd-gatt-example.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='plugins/gatt-example.c' object='plugins/bluetoothd-gatt-example.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o plugins/bluetoothd-gatt-example.obj `if test -f 'plugins/gatt-example.c'; then $(CYGPATH_W) 'plugins/gatt-example.c'; else $(CYGPATH_W) '$(srcdir)/plugins/gatt-example.c'; fi`
+
+plugins/bluetoothd-neard.o: plugins/neard.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT plugins/bluetoothd-neard.o -MD -MP -MF plugins/$(DEPDIR)/bluetoothd-neard.Tpo -c -o plugins/bluetoothd-neard.o `test -f 'plugins/neard.c' || echo '$(srcdir)/'`plugins/neard.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) plugins/$(DEPDIR)/bluetoothd-neard.Tpo plugins/$(DEPDIR)/bluetoothd-neard.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='plugins/neard.c' object='plugins/bluetoothd-neard.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o plugins/bluetoothd-neard.o `test -f 'plugins/neard.c' || echo '$(srcdir)/'`plugins/neard.c
+
+plugins/bluetoothd-neard.obj: plugins/neard.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT plugins/bluetoothd-neard.obj -MD -MP -MF plugins/$(DEPDIR)/bluetoothd-neard.Tpo -c -o plugins/bluetoothd-neard.obj `if test -f 'plugins/neard.c'; then $(CYGPATH_W) 'plugins/neard.c'; else $(CYGPATH_W) '$(srcdir)/plugins/neard.c'; fi`
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) plugins/$(DEPDIR)/bluetoothd-neard.Tpo plugins/$(DEPDIR)/bluetoothd-neard.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='plugins/neard.c' object='plugins/bluetoothd-neard.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o plugins/bluetoothd-neard.obj `if test -f 'plugins/neard.c'; then $(CYGPATH_W) 'plugins/neard.c'; else $(CYGPATH_W) '$(srcdir)/plugins/neard.c'; fi`
+
+profiles/sap/bluetoothd-main.o: profiles/sap/main.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT profiles/sap/bluetoothd-main.o -MD -MP -MF profiles/sap/$(DEPDIR)/bluetoothd-main.Tpo -c -o profiles/sap/bluetoothd-main.o `test -f 'profiles/sap/main.c' || echo '$(srcdir)/'`profiles/sap/main.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) profiles/sap/$(DEPDIR)/bluetoothd-main.Tpo profiles/sap/$(DEPDIR)/bluetoothd-main.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='profiles/sap/main.c' object='profiles/sap/bluetoothd-main.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o profiles/sap/bluetoothd-main.o `test -f 'profiles/sap/main.c' || echo '$(srcdir)/'`profiles/sap/main.c
+
+profiles/sap/bluetoothd-main.obj: profiles/sap/main.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT profiles/sap/bluetoothd-main.obj -MD -MP -MF profiles/sap/$(DEPDIR)/bluetoothd-main.Tpo -c -o profiles/sap/bluetoothd-main.obj `if test -f 'profiles/sap/main.c'; then $(CYGPATH_W) 'profiles/sap/main.c'; else $(CYGPATH_W) '$(srcdir)/profiles/sap/main.c'; fi`
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) profiles/sap/$(DEPDIR)/bluetoothd-main.Tpo profiles/sap/$(DEPDIR)/bluetoothd-main.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='profiles/sap/main.c' object='profiles/sap/bluetoothd-main.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o profiles/sap/bluetoothd-main.obj `if test -f 'profiles/sap/main.c'; then $(CYGPATH_W) 'profiles/sap/main.c'; else $(CYGPATH_W) '$(srcdir)/profiles/sap/main.c'; fi`
+
+profiles/sap/bluetoothd-manager.o: profiles/sap/manager.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT profiles/sap/bluetoothd-manager.o -MD -MP -MF profiles/sap/$(DEPDIR)/bluetoothd-manager.Tpo -c -o profiles/sap/bluetoothd-manager.o `test -f 'profiles/sap/manager.c' || echo '$(srcdir)/'`profiles/sap/manager.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) profiles/sap/$(DEPDIR)/bluetoothd-manager.Tpo profiles/sap/$(DEPDIR)/bluetoothd-manager.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='profiles/sap/manager.c' object='profiles/sap/bluetoothd-manager.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o profiles/sap/bluetoothd-manager.o `test -f 'profiles/sap/manager.c' || echo '$(srcdir)/'`profiles/sap/manager.c
+
+profiles/sap/bluetoothd-manager.obj: profiles/sap/manager.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT profiles/sap/bluetoothd-manager.obj -MD -MP -MF profiles/sap/$(DEPDIR)/bluetoothd-manager.Tpo -c -o profiles/sap/bluetoothd-manager.obj `if test -f 'profiles/sap/manager.c'; then $(CYGPATH_W) 'profiles/sap/manager.c'; else $(CYGPATH_W) '$(srcdir)/profiles/sap/manager.c'; fi`
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) profiles/sap/$(DEPDIR)/bluetoothd-manager.Tpo profiles/sap/$(DEPDIR)/bluetoothd-manager.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='profiles/sap/manager.c' object='profiles/sap/bluetoothd-manager.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o profiles/sap/bluetoothd-manager.obj `if test -f 'profiles/sap/manager.c'; then $(CYGPATH_W) 'profiles/sap/manager.c'; else $(CYGPATH_W) '$(srcdir)/profiles/sap/manager.c'; fi`
+
+profiles/sap/bluetoothd-server.o: profiles/sap/server.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT profiles/sap/bluetoothd-server.o -MD -MP -MF profiles/sap/$(DEPDIR)/bluetoothd-server.Tpo -c -o profiles/sap/bluetoothd-server.o `test -f 'profiles/sap/server.c' || echo '$(srcdir)/'`profiles/sap/server.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) profiles/sap/$(DEPDIR)/bluetoothd-server.Tpo profiles/sap/$(DEPDIR)/bluetoothd-server.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='profiles/sap/server.c' object='profiles/sap/bluetoothd-server.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o profiles/sap/bluetoothd-server.o `test -f 'profiles/sap/server.c' || echo '$(srcdir)/'`profiles/sap/server.c
+
+profiles/sap/bluetoothd-server.obj: profiles/sap/server.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT profiles/sap/bluetoothd-server.obj -MD -MP -MF profiles/sap/$(DEPDIR)/bluetoothd-server.Tpo -c -o profiles/sap/bluetoothd-server.obj `if test -f 'profiles/sap/server.c'; then $(CYGPATH_W) 'profiles/sap/server.c'; else $(CYGPATH_W) '$(srcdir)/profiles/sap/server.c'; fi`
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) profiles/sap/$(DEPDIR)/bluetoothd-server.Tpo profiles/sap/$(DEPDIR)/bluetoothd-server.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='profiles/sap/server.c' object='profiles/sap/bluetoothd-server.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o profiles/sap/bluetoothd-server.obj `if test -f 'profiles/sap/server.c'; then $(CYGPATH_W) 'profiles/sap/server.c'; else $(CYGPATH_W) '$(srcdir)/profiles/sap/server.c'; fi`
+
+profiles/sap/bluetoothd-sap-dummy.o: profiles/sap/sap-dummy.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT profiles/sap/bluetoothd-sap-dummy.o -MD -MP -MF profiles/sap/$(DEPDIR)/bluetoothd-sap-dummy.Tpo -c -o profiles/sap/bluetoothd-sap-dummy.o `test -f 'profiles/sap/sap-dummy.c' || echo '$(srcdir)/'`profiles/sap/sap-dummy.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) profiles/sap/$(DEPDIR)/bluetoothd-sap-dummy.Tpo profiles/sap/$(DEPDIR)/bluetoothd-sap-dummy.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='profiles/sap/sap-dummy.c' object='profiles/sap/bluetoothd-sap-dummy.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o profiles/sap/bluetoothd-sap-dummy.o `test -f 'profiles/sap/sap-dummy.c' || echo '$(srcdir)/'`profiles/sap/sap-dummy.c
+
+profiles/sap/bluetoothd-sap-dummy.obj: profiles/sap/sap-dummy.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT profiles/sap/bluetoothd-sap-dummy.obj -MD -MP -MF profiles/sap/$(DEPDIR)/bluetoothd-sap-dummy.Tpo -c -o profiles/sap/bluetoothd-sap-dummy.obj `if test -f 'profiles/sap/sap-dummy.c'; then $(CYGPATH_W) 'profiles/sap/sap-dummy.c'; else $(CYGPATH_W) '$(srcdir)/profiles/sap/sap-dummy.c'; fi`
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) profiles/sap/$(DEPDIR)/bluetoothd-sap-dummy.Tpo profiles/sap/$(DEPDIR)/bluetoothd-sap-dummy.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='profiles/sap/sap-dummy.c' object='profiles/sap/bluetoothd-sap-dummy.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o profiles/sap/bluetoothd-sap-dummy.obj `if test -f 'profiles/sap/sap-dummy.c'; then $(CYGPATH_W) 'profiles/sap/sap-dummy.c'; else $(CYGPATH_W) '$(srcdir)/profiles/sap/sap-dummy.c'; fi`
+
+profiles/audio/bluetoothd-source.o: profiles/audio/source.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT profiles/audio/bluetoothd-source.o -MD -MP -MF profiles/audio/$(DEPDIR)/bluetoothd-source.Tpo -c -o profiles/audio/bluetoothd-source.o `test -f 'profiles/audio/source.c' || echo '$(srcdir)/'`profiles/audio/source.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) profiles/audio/$(DEPDIR)/bluetoothd-source.Tpo profiles/audio/$(DEPDIR)/bluetoothd-source.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='profiles/audio/source.c' object='profiles/audio/bluetoothd-source.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o profiles/audio/bluetoothd-source.o `test -f 'profiles/audio/source.c' || echo '$(srcdir)/'`profiles/audio/source.c
+
+profiles/audio/bluetoothd-source.obj: profiles/audio/source.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT profiles/audio/bluetoothd-source.obj -MD -MP -MF profiles/audio/$(DEPDIR)/bluetoothd-source.Tpo -c -o profiles/audio/bluetoothd-source.obj `if test -f 'profiles/audio/source.c'; then $(CYGPATH_W) 'profiles/audio/source.c'; else $(CYGPATH_W) '$(srcdir)/profiles/audio/source.c'; fi`
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) profiles/audio/$(DEPDIR)/bluetoothd-source.Tpo profiles/audio/$(DEPDIR)/bluetoothd-source.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='profiles/audio/source.c' object='profiles/audio/bluetoothd-source.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o profiles/audio/bluetoothd-source.obj `if test -f 'profiles/audio/source.c'; then $(CYGPATH_W) 'profiles/audio/source.c'; else $(CYGPATH_W) '$(srcdir)/profiles/audio/source.c'; fi`
+
+profiles/audio/bluetoothd-sink.o: profiles/audio/sink.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT profiles/audio/bluetoothd-sink.o -MD -MP -MF profiles/audio/$(DEPDIR)/bluetoothd-sink.Tpo -c -o profiles/audio/bluetoothd-sink.o `test -f 'profiles/audio/sink.c' || echo '$(srcdir)/'`profiles/audio/sink.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) profiles/audio/$(DEPDIR)/bluetoothd-sink.Tpo profiles/audio/$(DEPDIR)/bluetoothd-sink.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='profiles/audio/sink.c' object='profiles/audio/bluetoothd-sink.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o profiles/audio/bluetoothd-sink.o `test -f 'profiles/audio/sink.c' || echo '$(srcdir)/'`profiles/audio/sink.c
+
+profiles/audio/bluetoothd-sink.obj: profiles/audio/sink.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT profiles/audio/bluetoothd-sink.obj -MD -MP -MF profiles/audio/$(DEPDIR)/bluetoothd-sink.Tpo -c -o profiles/audio/bluetoothd-sink.obj `if test -f 'profiles/audio/sink.c'; then $(CYGPATH_W) 'profiles/audio/sink.c'; else $(CYGPATH_W) '$(srcdir)/profiles/audio/sink.c'; fi`
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) profiles/audio/$(DEPDIR)/bluetoothd-sink.Tpo profiles/audio/$(DEPDIR)/bluetoothd-sink.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='profiles/audio/sink.c' object='profiles/audio/bluetoothd-sink.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o profiles/audio/bluetoothd-sink.obj `if test -f 'profiles/audio/sink.c'; then $(CYGPATH_W) 'profiles/audio/sink.c'; else $(CYGPATH_W) '$(srcdir)/profiles/audio/sink.c'; fi`
+
+profiles/audio/bluetoothd-a2dp.o: profiles/audio/a2dp.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT profiles/audio/bluetoothd-a2dp.o -MD -MP -MF profiles/audio/$(DEPDIR)/bluetoothd-a2dp.Tpo -c -o profiles/audio/bluetoothd-a2dp.o `test -f 'profiles/audio/a2dp.c' || echo '$(srcdir)/'`profiles/audio/a2dp.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) profiles/audio/$(DEPDIR)/bluetoothd-a2dp.Tpo profiles/audio/$(DEPDIR)/bluetoothd-a2dp.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='profiles/audio/a2dp.c' object='profiles/audio/bluetoothd-a2dp.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o profiles/audio/bluetoothd-a2dp.o `test -f 'profiles/audio/a2dp.c' || echo '$(srcdir)/'`profiles/audio/a2dp.c
+
+profiles/audio/bluetoothd-a2dp.obj: profiles/audio/a2dp.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT profiles/audio/bluetoothd-a2dp.obj -MD -MP -MF profiles/audio/$(DEPDIR)/bluetoothd-a2dp.Tpo -c -o profiles/audio/bluetoothd-a2dp.obj `if test -f 'profiles/audio/a2dp.c'; then $(CYGPATH_W) 'profiles/audio/a2dp.c'; else $(CYGPATH_W) '$(srcdir)/profiles/audio/a2dp.c'; fi`
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) profiles/audio/$(DEPDIR)/bluetoothd-a2dp.Tpo profiles/audio/$(DEPDIR)/bluetoothd-a2dp.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='profiles/audio/a2dp.c' object='profiles/audio/bluetoothd-a2dp.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o profiles/audio/bluetoothd-a2dp.obj `if test -f 'profiles/audio/a2dp.c'; then $(CYGPATH_W) 'profiles/audio/a2dp.c'; else $(CYGPATH_W) '$(srcdir)/profiles/audio/a2dp.c'; fi`
+
+profiles/audio/bluetoothd-avdtp.o: profiles/audio/avdtp.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT profiles/audio/bluetoothd-avdtp.o -MD -MP -MF profiles/audio/$(DEPDIR)/bluetoothd-avdtp.Tpo -c -o profiles/audio/bluetoothd-avdtp.o `test -f 'profiles/audio/avdtp.c' || echo '$(srcdir)/'`profiles/audio/avdtp.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) profiles/audio/$(DEPDIR)/bluetoothd-avdtp.Tpo profiles/audio/$(DEPDIR)/bluetoothd-avdtp.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='profiles/audio/avdtp.c' object='profiles/audio/bluetoothd-avdtp.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o profiles/audio/bluetoothd-avdtp.o `test -f 'profiles/audio/avdtp.c' || echo '$(srcdir)/'`profiles/audio/avdtp.c
+
+profiles/audio/bluetoothd-avdtp.obj: profiles/audio/avdtp.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT profiles/audio/bluetoothd-avdtp.obj -MD -MP -MF profiles/audio/$(DEPDIR)/bluetoothd-avdtp.Tpo -c -o profiles/audio/bluetoothd-avdtp.obj `if test -f 'profiles/audio/avdtp.c'; then $(CYGPATH_W) 'profiles/audio/avdtp.c'; else $(CYGPATH_W) '$(srcdir)/profiles/audio/avdtp.c'; fi`
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) profiles/audio/$(DEPDIR)/bluetoothd-avdtp.Tpo profiles/audio/$(DEPDIR)/bluetoothd-avdtp.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='profiles/audio/avdtp.c' object='profiles/audio/bluetoothd-avdtp.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o profiles/audio/bluetoothd-avdtp.obj `if test -f 'profiles/audio/avdtp.c'; then $(CYGPATH_W) 'profiles/audio/avdtp.c'; else $(CYGPATH_W) '$(srcdir)/profiles/audio/avdtp.c'; fi`
+
+profiles/audio/bluetoothd-media.o: profiles/audio/media.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT profiles/audio/bluetoothd-media.o -MD -MP -MF profiles/audio/$(DEPDIR)/bluetoothd-media.Tpo -c -o profiles/audio/bluetoothd-media.o `test -f 'profiles/audio/media.c' || echo '$(srcdir)/'`profiles/audio/media.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) profiles/audio/$(DEPDIR)/bluetoothd-media.Tpo profiles/audio/$(DEPDIR)/bluetoothd-media.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='profiles/audio/media.c' object='profiles/audio/bluetoothd-media.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o profiles/audio/bluetoothd-media.o `test -f 'profiles/audio/media.c' || echo '$(srcdir)/'`profiles/audio/media.c
+
+profiles/audio/bluetoothd-media.obj: profiles/audio/media.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT profiles/audio/bluetoothd-media.obj -MD -MP -MF profiles/audio/$(DEPDIR)/bluetoothd-media.Tpo -c -o profiles/audio/bluetoothd-media.obj `if test -f 'profiles/audio/media.c'; then $(CYGPATH_W) 'profiles/audio/media.c'; else $(CYGPATH_W) '$(srcdir)/profiles/audio/media.c'; fi`
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) profiles/audio/$(DEPDIR)/bluetoothd-media.Tpo profiles/audio/$(DEPDIR)/bluetoothd-media.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='profiles/audio/media.c' object='profiles/audio/bluetoothd-media.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o profiles/audio/bluetoothd-media.obj `if test -f 'profiles/audio/media.c'; then $(CYGPATH_W) 'profiles/audio/media.c'; else $(CYGPATH_W) '$(srcdir)/profiles/audio/media.c'; fi`
+
+profiles/audio/bluetoothd-transport.o: profiles/audio/transport.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT profiles/audio/bluetoothd-transport.o -MD -MP -MF profiles/audio/$(DEPDIR)/bluetoothd-transport.Tpo -c -o profiles/audio/bluetoothd-transport.o `test -f 'profiles/audio/transport.c' || echo '$(srcdir)/'`profiles/audio/transport.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) profiles/audio/$(DEPDIR)/bluetoothd-transport.Tpo profiles/audio/$(DEPDIR)/bluetoothd-transport.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='profiles/audio/transport.c' object='profiles/audio/bluetoothd-transport.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o profiles/audio/bluetoothd-transport.o `test -f 'profiles/audio/transport.c' || echo '$(srcdir)/'`profiles/audio/transport.c
+
+profiles/audio/bluetoothd-transport.obj: profiles/audio/transport.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT profiles/audio/bluetoothd-transport.obj -MD -MP -MF profiles/audio/$(DEPDIR)/bluetoothd-transport.Tpo -c -o profiles/audio/bluetoothd-transport.obj `if test -f 'profiles/audio/transport.c'; then $(CYGPATH_W) 'profiles/audio/transport.c'; else $(CYGPATH_W) '$(srcdir)/profiles/audio/transport.c'; fi`
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) profiles/audio/$(DEPDIR)/bluetoothd-transport.Tpo profiles/audio/$(DEPDIR)/bluetoothd-transport.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='profiles/audio/transport.c' object='profiles/audio/bluetoothd-transport.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o profiles/audio/bluetoothd-transport.obj `if test -f 'profiles/audio/transport.c'; then $(CYGPATH_W) 'profiles/audio/transport.c'; else $(CYGPATH_W) '$(srcdir)/profiles/audio/transport.c'; fi`
+
+profiles/audio/bluetoothd-control.o: profiles/audio/control.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT profiles/audio/bluetoothd-control.o -MD -MP -MF profiles/audio/$(DEPDIR)/bluetoothd-control.Tpo -c -o profiles/audio/bluetoothd-control.o `test -f 'profiles/audio/control.c' || echo '$(srcdir)/'`profiles/audio/control.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) profiles/audio/$(DEPDIR)/bluetoothd-control.Tpo profiles/audio/$(DEPDIR)/bluetoothd-control.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='profiles/audio/control.c' object='profiles/audio/bluetoothd-control.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o profiles/audio/bluetoothd-control.o `test -f 'profiles/audio/control.c' || echo '$(srcdir)/'`profiles/audio/control.c
+
+profiles/audio/bluetoothd-control.obj: profiles/audio/control.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT profiles/audio/bluetoothd-control.obj -MD -MP -MF profiles/audio/$(DEPDIR)/bluetoothd-control.Tpo -c -o profiles/audio/bluetoothd-control.obj `if test -f 'profiles/audio/control.c'; then $(CYGPATH_W) 'profiles/audio/control.c'; else $(CYGPATH_W) '$(srcdir)/profiles/audio/control.c'; fi`
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) profiles/audio/$(DEPDIR)/bluetoothd-control.Tpo profiles/audio/$(DEPDIR)/bluetoothd-control.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='profiles/audio/control.c' object='profiles/audio/bluetoothd-control.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o profiles/audio/bluetoothd-control.obj `if test -f 'profiles/audio/control.c'; then $(CYGPATH_W) 'profiles/audio/control.c'; else $(CYGPATH_W) '$(srcdir)/profiles/audio/control.c'; fi`
+
+profiles/audio/bluetoothd-avctp.o: profiles/audio/avctp.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT profiles/audio/bluetoothd-avctp.o -MD -MP -MF profiles/audio/$(DEPDIR)/bluetoothd-avctp.Tpo -c -o profiles/audio/bluetoothd-avctp.o `test -f 'profiles/audio/avctp.c' || echo '$(srcdir)/'`profiles/audio/avctp.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) profiles/audio/$(DEPDIR)/bluetoothd-avctp.Tpo profiles/audio/$(DEPDIR)/bluetoothd-avctp.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='profiles/audio/avctp.c' object='profiles/audio/bluetoothd-avctp.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o profiles/audio/bluetoothd-avctp.o `test -f 'profiles/audio/avctp.c' || echo '$(srcdir)/'`profiles/audio/avctp.c
+
+profiles/audio/bluetoothd-avctp.obj: profiles/audio/avctp.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT profiles/audio/bluetoothd-avctp.obj -MD -MP -MF profiles/audio/$(DEPDIR)/bluetoothd-avctp.Tpo -c -o profiles/audio/bluetoothd-avctp.obj `if test -f 'profiles/audio/avctp.c'; then $(CYGPATH_W) 'profiles/audio/avctp.c'; else $(CYGPATH_W) '$(srcdir)/profiles/audio/avctp.c'; fi`
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) profiles/audio/$(DEPDIR)/bluetoothd-avctp.Tpo profiles/audio/$(DEPDIR)/bluetoothd-avctp.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='profiles/audio/avctp.c' object='profiles/audio/bluetoothd-avctp.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o profiles/audio/bluetoothd-avctp.obj `if test -f 'profiles/audio/avctp.c'; then $(CYGPATH_W) 'profiles/audio/avctp.c'; else $(CYGPATH_W) '$(srcdir)/profiles/audio/avctp.c'; fi`
+
+profiles/audio/bluetoothd-avrcp.o: profiles/audio/avrcp.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT profiles/audio/bluetoothd-avrcp.o -MD -MP -MF profiles/audio/$(DEPDIR)/bluetoothd-avrcp.Tpo -c -o profiles/audio/bluetoothd-avrcp.o `test -f 'profiles/audio/avrcp.c' || echo '$(srcdir)/'`profiles/audio/avrcp.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) profiles/audio/$(DEPDIR)/bluetoothd-avrcp.Tpo profiles/audio/$(DEPDIR)/bluetoothd-avrcp.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='profiles/audio/avrcp.c' object='profiles/audio/bluetoothd-avrcp.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o profiles/audio/bluetoothd-avrcp.o `test -f 'profiles/audio/avrcp.c' || echo '$(srcdir)/'`profiles/audio/avrcp.c
+
+profiles/audio/bluetoothd-avrcp.obj: profiles/audio/avrcp.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT profiles/audio/bluetoothd-avrcp.obj -MD -MP -MF profiles/audio/$(DEPDIR)/bluetoothd-avrcp.Tpo -c -o profiles/audio/bluetoothd-avrcp.obj `if test -f 'profiles/audio/avrcp.c'; then $(CYGPATH_W) 'profiles/audio/avrcp.c'; else $(CYGPATH_W) '$(srcdir)/profiles/audio/avrcp.c'; fi`
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) profiles/audio/$(DEPDIR)/bluetoothd-avrcp.Tpo profiles/audio/$(DEPDIR)/bluetoothd-avrcp.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='profiles/audio/avrcp.c' object='profiles/audio/bluetoothd-avrcp.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o profiles/audio/bluetoothd-avrcp.obj `if test -f 'profiles/audio/avrcp.c'; then $(CYGPATH_W) 'profiles/audio/avrcp.c'; else $(CYGPATH_W) '$(srcdir)/profiles/audio/avrcp.c'; fi`
+
+profiles/audio/bluetoothd-player.o: profiles/audio/player.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT profiles/audio/bluetoothd-player.o -MD -MP -MF profiles/audio/$(DEPDIR)/bluetoothd-player.Tpo -c -o profiles/audio/bluetoothd-player.o `test -f 'profiles/audio/player.c' || echo '$(srcdir)/'`profiles/audio/player.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) profiles/audio/$(DEPDIR)/bluetoothd-player.Tpo profiles/audio/$(DEPDIR)/bluetoothd-player.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='profiles/audio/player.c' object='profiles/audio/bluetoothd-player.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o profiles/audio/bluetoothd-player.o `test -f 'profiles/audio/player.c' || echo '$(srcdir)/'`profiles/audio/player.c
+
+profiles/audio/bluetoothd-player.obj: profiles/audio/player.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT profiles/audio/bluetoothd-player.obj -MD -MP -MF profiles/audio/$(DEPDIR)/bluetoothd-player.Tpo -c -o profiles/audio/bluetoothd-player.obj `if test -f 'profiles/audio/player.c'; then $(CYGPATH_W) 'profiles/audio/player.c'; else $(CYGPATH_W) '$(srcdir)/profiles/audio/player.c'; fi`
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) profiles/audio/$(DEPDIR)/bluetoothd-player.Tpo profiles/audio/$(DEPDIR)/bluetoothd-player.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='profiles/audio/player.c' object='profiles/audio/bluetoothd-player.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o profiles/audio/bluetoothd-player.obj `if test -f 'profiles/audio/player.c'; then $(CYGPATH_W) 'profiles/audio/player.c'; else $(CYGPATH_W) '$(srcdir)/profiles/audio/player.c'; fi`
+
+profiles/network/bluetoothd-manager.o: profiles/network/manager.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT profiles/network/bluetoothd-manager.o -MD -MP -MF profiles/network/$(DEPDIR)/bluetoothd-manager.Tpo -c -o profiles/network/bluetoothd-manager.o `test -f 'profiles/network/manager.c' || echo '$(srcdir)/'`profiles/network/manager.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) profiles/network/$(DEPDIR)/bluetoothd-manager.Tpo profiles/network/$(DEPDIR)/bluetoothd-manager.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='profiles/network/manager.c' object='profiles/network/bluetoothd-manager.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o profiles/network/bluetoothd-manager.o `test -f 'profiles/network/manager.c' || echo '$(srcdir)/'`profiles/network/manager.c
+
+profiles/network/bluetoothd-manager.obj: profiles/network/manager.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT profiles/network/bluetoothd-manager.obj -MD -MP -MF profiles/network/$(DEPDIR)/bluetoothd-manager.Tpo -c -o profiles/network/bluetoothd-manager.obj `if test -f 'profiles/network/manager.c'; then $(CYGPATH_W) 'profiles/network/manager.c'; else $(CYGPATH_W) '$(srcdir)/profiles/network/manager.c'; fi`
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) profiles/network/$(DEPDIR)/bluetoothd-manager.Tpo profiles/network/$(DEPDIR)/bluetoothd-manager.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='profiles/network/manager.c' object='profiles/network/bluetoothd-manager.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o profiles/network/bluetoothd-manager.obj `if test -f 'profiles/network/manager.c'; then $(CYGPATH_W) 'profiles/network/manager.c'; else $(CYGPATH_W) '$(srcdir)/profiles/network/manager.c'; fi`
+
+profiles/network/bluetoothd-bnep.o: profiles/network/bnep.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT profiles/network/bluetoothd-bnep.o -MD -MP -MF profiles/network/$(DEPDIR)/bluetoothd-bnep.Tpo -c -o profiles/network/bluetoothd-bnep.o `test -f 'profiles/network/bnep.c' || echo '$(srcdir)/'`profiles/network/bnep.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) profiles/network/$(DEPDIR)/bluetoothd-bnep.Tpo profiles/network/$(DEPDIR)/bluetoothd-bnep.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='profiles/network/bnep.c' object='profiles/network/bluetoothd-bnep.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o profiles/network/bluetoothd-bnep.o `test -f 'profiles/network/bnep.c' || echo '$(srcdir)/'`profiles/network/bnep.c
+
+profiles/network/bluetoothd-bnep.obj: profiles/network/bnep.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT profiles/network/bluetoothd-bnep.obj -MD -MP -MF profiles/network/$(DEPDIR)/bluetoothd-bnep.Tpo -c -o profiles/network/bluetoothd-bnep.obj `if test -f 'profiles/network/bnep.c'; then $(CYGPATH_W) 'profiles/network/bnep.c'; else $(CYGPATH_W) '$(srcdir)/profiles/network/bnep.c'; fi`
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) profiles/network/$(DEPDIR)/bluetoothd-bnep.Tpo profiles/network/$(DEPDIR)/bluetoothd-bnep.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='profiles/network/bnep.c' object='profiles/network/bluetoothd-bnep.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o profiles/network/bluetoothd-bnep.obj `if test -f 'profiles/network/bnep.c'; then $(CYGPATH_W) 'profiles/network/bnep.c'; else $(CYGPATH_W) '$(srcdir)/profiles/network/bnep.c'; fi`
+
+profiles/network/bluetoothd-server.o: profiles/network/server.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT profiles/network/bluetoothd-server.o -MD -MP -MF profiles/network/$(DEPDIR)/bluetoothd-server.Tpo -c -o profiles/network/bluetoothd-server.o `test -f 'profiles/network/server.c' || echo '$(srcdir)/'`profiles/network/server.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) profiles/network/$(DEPDIR)/bluetoothd-server.Tpo profiles/network/$(DEPDIR)/bluetoothd-server.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='profiles/network/server.c' object='profiles/network/bluetoothd-server.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o profiles/network/bluetoothd-server.o `test -f 'profiles/network/server.c' || echo '$(srcdir)/'`profiles/network/server.c
+
+profiles/network/bluetoothd-server.obj: profiles/network/server.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT profiles/network/bluetoothd-server.obj -MD -MP -MF profiles/network/$(DEPDIR)/bluetoothd-server.Tpo -c -o profiles/network/bluetoothd-server.obj `if test -f 'profiles/network/server.c'; then $(CYGPATH_W) 'profiles/network/server.c'; else $(CYGPATH_W) '$(srcdir)/profiles/network/server.c'; fi`
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) profiles/network/$(DEPDIR)/bluetoothd-server.Tpo profiles/network/$(DEPDIR)/bluetoothd-server.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='profiles/network/server.c' object='profiles/network/bluetoothd-server.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o profiles/network/bluetoothd-server.obj `if test -f 'profiles/network/server.c'; then $(CYGPATH_W) 'profiles/network/server.c'; else $(CYGPATH_W) '$(srcdir)/profiles/network/server.c'; fi`
+
+profiles/network/bluetoothd-connection.o: profiles/network/connection.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT profiles/network/bluetoothd-connection.o -MD -MP -MF profiles/network/$(DEPDIR)/bluetoothd-connection.Tpo -c -o profiles/network/bluetoothd-connection.o `test -f 'profiles/network/connection.c' || echo '$(srcdir)/'`profiles/network/connection.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) profiles/network/$(DEPDIR)/bluetoothd-connection.Tpo profiles/network/$(DEPDIR)/bluetoothd-connection.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='profiles/network/connection.c' object='profiles/network/bluetoothd-connection.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o profiles/network/bluetoothd-connection.o `test -f 'profiles/network/connection.c' || echo '$(srcdir)/'`profiles/network/connection.c
+
+profiles/network/bluetoothd-connection.obj: profiles/network/connection.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT profiles/network/bluetoothd-connection.obj -MD -MP -MF profiles/network/$(DEPDIR)/bluetoothd-connection.Tpo -c -o profiles/network/bluetoothd-connection.obj `if test -f 'profiles/network/connection.c'; then $(CYGPATH_W) 'profiles/network/connection.c'; else $(CYGPATH_W) '$(srcdir)/profiles/network/connection.c'; fi`
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) profiles/network/$(DEPDIR)/bluetoothd-connection.Tpo profiles/network/$(DEPDIR)/bluetoothd-connection.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='profiles/network/connection.c' object='profiles/network/bluetoothd-connection.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o profiles/network/bluetoothd-connection.obj `if test -f 'profiles/network/connection.c'; then $(CYGPATH_W) 'profiles/network/connection.c'; else $(CYGPATH_W) '$(srcdir)/profiles/network/connection.c'; fi`
+
+profiles/input/bluetoothd-manager.o: profiles/input/manager.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT profiles/input/bluetoothd-manager.o -MD -MP -MF profiles/input/$(DEPDIR)/bluetoothd-manager.Tpo -c -o profiles/input/bluetoothd-manager.o `test -f 'profiles/input/manager.c' || echo '$(srcdir)/'`profiles/input/manager.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) profiles/input/$(DEPDIR)/bluetoothd-manager.Tpo profiles/input/$(DEPDIR)/bluetoothd-manager.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='profiles/input/manager.c' object='profiles/input/bluetoothd-manager.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o profiles/input/bluetoothd-manager.o `test -f 'profiles/input/manager.c' || echo '$(srcdir)/'`profiles/input/manager.c
+
+profiles/input/bluetoothd-manager.obj: profiles/input/manager.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT profiles/input/bluetoothd-manager.obj -MD -MP -MF profiles/input/$(DEPDIR)/bluetoothd-manager.Tpo -c -o profiles/input/bluetoothd-manager.obj `if test -f 'profiles/input/manager.c'; then $(CYGPATH_W) 'profiles/input/manager.c'; else $(CYGPATH_W) '$(srcdir)/profiles/input/manager.c'; fi`
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) profiles/input/$(DEPDIR)/bluetoothd-manager.Tpo profiles/input/$(DEPDIR)/bluetoothd-manager.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='profiles/input/manager.c' object='profiles/input/bluetoothd-manager.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o profiles/input/bluetoothd-manager.obj `if test -f 'profiles/input/manager.c'; then $(CYGPATH_W) 'profiles/input/manager.c'; else $(CYGPATH_W) '$(srcdir)/profiles/input/manager.c'; fi`
+
+profiles/input/bluetoothd-server.o: profiles/input/server.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT profiles/input/bluetoothd-server.o -MD -MP -MF profiles/input/$(DEPDIR)/bluetoothd-server.Tpo -c -o profiles/input/bluetoothd-server.o `test -f 'profiles/input/server.c' || echo '$(srcdir)/'`profiles/input/server.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) profiles/input/$(DEPDIR)/bluetoothd-server.Tpo profiles/input/$(DEPDIR)/bluetoothd-server.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='profiles/input/server.c' object='profiles/input/bluetoothd-server.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o profiles/input/bluetoothd-server.o `test -f 'profiles/input/server.c' || echo '$(srcdir)/'`profiles/input/server.c
+
+profiles/input/bluetoothd-server.obj: profiles/input/server.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT profiles/input/bluetoothd-server.obj -MD -MP -MF profiles/input/$(DEPDIR)/bluetoothd-server.Tpo -c -o profiles/input/bluetoothd-server.obj `if test -f 'profiles/input/server.c'; then $(CYGPATH_W) 'profiles/input/server.c'; else $(CYGPATH_W) '$(srcdir)/profiles/input/server.c'; fi`
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) profiles/input/$(DEPDIR)/bluetoothd-server.Tpo profiles/input/$(DEPDIR)/bluetoothd-server.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='profiles/input/server.c' object='profiles/input/bluetoothd-server.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o profiles/input/bluetoothd-server.obj `if test -f 'profiles/input/server.c'; then $(CYGPATH_W) 'profiles/input/server.c'; else $(CYGPATH_W) '$(srcdir)/profiles/input/server.c'; fi`
+
+profiles/input/bluetoothd-device.o: profiles/input/device.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT profiles/input/bluetoothd-device.o -MD -MP -MF profiles/input/$(DEPDIR)/bluetoothd-device.Tpo -c -o profiles/input/bluetoothd-device.o `test -f 'profiles/input/device.c' || echo '$(srcdir)/'`profiles/input/device.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) profiles/input/$(DEPDIR)/bluetoothd-device.Tpo profiles/input/$(DEPDIR)/bluetoothd-device.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='profiles/input/device.c' object='profiles/input/bluetoothd-device.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o profiles/input/bluetoothd-device.o `test -f 'profiles/input/device.c' || echo '$(srcdir)/'`profiles/input/device.c
+
+profiles/input/bluetoothd-device.obj: profiles/input/device.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT profiles/input/bluetoothd-device.obj -MD -MP -MF profiles/input/$(DEPDIR)/bluetoothd-device.Tpo -c -o profiles/input/bluetoothd-device.obj `if test -f 'profiles/input/device.c'; then $(CYGPATH_W) 'profiles/input/device.c'; else $(CYGPATH_W) '$(srcdir)/profiles/input/device.c'; fi`
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) profiles/input/$(DEPDIR)/bluetoothd-device.Tpo profiles/input/$(DEPDIR)/bluetoothd-device.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='profiles/input/device.c' object='profiles/input/bluetoothd-device.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o profiles/input/bluetoothd-device.obj `if test -f 'profiles/input/device.c'; then $(CYGPATH_W) 'profiles/input/device.c'; else $(CYGPATH_W) '$(srcdir)/profiles/input/device.c'; fi`
+
+profiles/input/bluetoothd-hog.o: profiles/input/hog.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT profiles/input/bluetoothd-hog.o -MD -MP -MF profiles/input/$(DEPDIR)/bluetoothd-hog.Tpo -c -o profiles/input/bluetoothd-hog.o `test -f 'profiles/input/hog.c' || echo '$(srcdir)/'`profiles/input/hog.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) profiles/input/$(DEPDIR)/bluetoothd-hog.Tpo profiles/input/$(DEPDIR)/bluetoothd-hog.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='profiles/input/hog.c' object='profiles/input/bluetoothd-hog.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o profiles/input/bluetoothd-hog.o `test -f 'profiles/input/hog.c' || echo '$(srcdir)/'`profiles/input/hog.c
+
+profiles/input/bluetoothd-hog.obj: profiles/input/hog.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT profiles/input/bluetoothd-hog.obj -MD -MP -MF profiles/input/$(DEPDIR)/bluetoothd-hog.Tpo -c -o profiles/input/bluetoothd-hog.obj `if test -f 'profiles/input/hog.c'; then $(CYGPATH_W) 'profiles/input/hog.c'; else $(CYGPATH_W) '$(srcdir)/profiles/input/hog.c'; fi`
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) profiles/input/$(DEPDIR)/bluetoothd-hog.Tpo profiles/input/$(DEPDIR)/bluetoothd-hog.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='profiles/input/hog.c' object='profiles/input/bluetoothd-hog.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o profiles/input/bluetoothd-hog.obj `if test -f 'profiles/input/hog.c'; then $(CYGPATH_W) 'profiles/input/hog.c'; else $(CYGPATH_W) '$(srcdir)/profiles/input/hog.c'; fi`
+
+profiles/input/bluetoothd-suspend-dummy.o: profiles/input/suspend-dummy.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT profiles/input/bluetoothd-suspend-dummy.o -MD -MP -MF profiles/input/$(DEPDIR)/bluetoothd-suspend-dummy.Tpo -c -o profiles/input/bluetoothd-suspend-dummy.o `test -f 'profiles/input/suspend-dummy.c' || echo '$(srcdir)/'`profiles/input/suspend-dummy.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) profiles/input/$(DEPDIR)/bluetoothd-suspend-dummy.Tpo profiles/input/$(DEPDIR)/bluetoothd-suspend-dummy.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='profiles/input/suspend-dummy.c' object='profiles/input/bluetoothd-suspend-dummy.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o profiles/input/bluetoothd-suspend-dummy.o `test -f 'profiles/input/suspend-dummy.c' || echo '$(srcdir)/'`profiles/input/suspend-dummy.c
+
+profiles/input/bluetoothd-suspend-dummy.obj: profiles/input/suspend-dummy.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT profiles/input/bluetoothd-suspend-dummy.obj -MD -MP -MF profiles/input/$(DEPDIR)/bluetoothd-suspend-dummy.Tpo -c -o profiles/input/bluetoothd-suspend-dummy.obj `if test -f 'profiles/input/suspend-dummy.c'; then $(CYGPATH_W) 'profiles/input/suspend-dummy.c'; else $(CYGPATH_W) '$(srcdir)/profiles/input/suspend-dummy.c'; fi`
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) profiles/input/$(DEPDIR)/bluetoothd-suspend-dummy.Tpo profiles/input/$(DEPDIR)/bluetoothd-suspend-dummy.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='profiles/input/suspend-dummy.c' object='profiles/input/bluetoothd-suspend-dummy.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o profiles/input/bluetoothd-suspend-dummy.obj `if test -f 'profiles/input/suspend-dummy.c'; then $(CYGPATH_W) 'profiles/input/suspend-dummy.c'; else $(CYGPATH_W) '$(srcdir)/profiles/input/suspend-dummy.c'; fi`
+
+profiles/health/bluetoothd-mcap.o: profiles/health/mcap.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT profiles/health/bluetoothd-mcap.o -MD -MP -MF profiles/health/$(DEPDIR)/bluetoothd-mcap.Tpo -c -o profiles/health/bluetoothd-mcap.o `test -f 'profiles/health/mcap.c' || echo '$(srcdir)/'`profiles/health/mcap.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) profiles/health/$(DEPDIR)/bluetoothd-mcap.Tpo profiles/health/$(DEPDIR)/bluetoothd-mcap.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='profiles/health/mcap.c' object='profiles/health/bluetoothd-mcap.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o profiles/health/bluetoothd-mcap.o `test -f 'profiles/health/mcap.c' || echo '$(srcdir)/'`profiles/health/mcap.c
+
+profiles/health/bluetoothd-mcap.obj: profiles/health/mcap.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT profiles/health/bluetoothd-mcap.obj -MD -MP -MF profiles/health/$(DEPDIR)/bluetoothd-mcap.Tpo -c -o profiles/health/bluetoothd-mcap.obj `if test -f 'profiles/health/mcap.c'; then $(CYGPATH_W) 'profiles/health/mcap.c'; else $(CYGPATH_W) '$(srcdir)/profiles/health/mcap.c'; fi`
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) profiles/health/$(DEPDIR)/bluetoothd-mcap.Tpo profiles/health/$(DEPDIR)/bluetoothd-mcap.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='profiles/health/mcap.c' object='profiles/health/bluetoothd-mcap.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o profiles/health/bluetoothd-mcap.obj `if test -f 'profiles/health/mcap.c'; then $(CYGPATH_W) 'profiles/health/mcap.c'; else $(CYGPATH_W) '$(srcdir)/profiles/health/mcap.c'; fi`
+
+profiles/health/bluetoothd-mcap_sync.o: profiles/health/mcap_sync.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT profiles/health/bluetoothd-mcap_sync.o -MD -MP -MF profiles/health/$(DEPDIR)/bluetoothd-mcap_sync.Tpo -c -o profiles/health/bluetoothd-mcap_sync.o `test -f 'profiles/health/mcap_sync.c' || echo '$(srcdir)/'`profiles/health/mcap_sync.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) profiles/health/$(DEPDIR)/bluetoothd-mcap_sync.Tpo profiles/health/$(DEPDIR)/bluetoothd-mcap_sync.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='profiles/health/mcap_sync.c' object='profiles/health/bluetoothd-mcap_sync.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o profiles/health/bluetoothd-mcap_sync.o `test -f 'profiles/health/mcap_sync.c' || echo '$(srcdir)/'`profiles/health/mcap_sync.c
+
+profiles/health/bluetoothd-mcap_sync.obj: profiles/health/mcap_sync.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT profiles/health/bluetoothd-mcap_sync.obj -MD -MP -MF profiles/health/$(DEPDIR)/bluetoothd-mcap_sync.Tpo -c -o profiles/health/bluetoothd-mcap_sync.obj `if test -f 'profiles/health/mcap_sync.c'; then $(CYGPATH_W) 'profiles/health/mcap_sync.c'; else $(CYGPATH_W) '$(srcdir)/profiles/health/mcap_sync.c'; fi`
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) profiles/health/$(DEPDIR)/bluetoothd-mcap_sync.Tpo profiles/health/$(DEPDIR)/bluetoothd-mcap_sync.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='profiles/health/mcap_sync.c' object='profiles/health/bluetoothd-mcap_sync.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o profiles/health/bluetoothd-mcap_sync.obj `if test -f 'profiles/health/mcap_sync.c'; then $(CYGPATH_W) 'profiles/health/mcap_sync.c'; else $(CYGPATH_W) '$(srcdir)/profiles/health/mcap_sync.c'; fi`
+
+profiles/health/bluetoothd-hdp_main.o: profiles/health/hdp_main.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT profiles/health/bluetoothd-hdp_main.o -MD -MP -MF profiles/health/$(DEPDIR)/bluetoothd-hdp_main.Tpo -c -o profiles/health/bluetoothd-hdp_main.o `test -f 'profiles/health/hdp_main.c' || echo '$(srcdir)/'`profiles/health/hdp_main.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) profiles/health/$(DEPDIR)/bluetoothd-hdp_main.Tpo profiles/health/$(DEPDIR)/bluetoothd-hdp_main.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='profiles/health/hdp_main.c' object='profiles/health/bluetoothd-hdp_main.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o profiles/health/bluetoothd-hdp_main.o `test -f 'profiles/health/hdp_main.c' || echo '$(srcdir)/'`profiles/health/hdp_main.c
+
+profiles/health/bluetoothd-hdp_main.obj: profiles/health/hdp_main.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT profiles/health/bluetoothd-hdp_main.obj -MD -MP -MF profiles/health/$(DEPDIR)/bluetoothd-hdp_main.Tpo -c -o profiles/health/bluetoothd-hdp_main.obj `if test -f 'profiles/health/hdp_main.c'; then $(CYGPATH_W) 'profiles/health/hdp_main.c'; else $(CYGPATH_W) '$(srcdir)/profiles/health/hdp_main.c'; fi`
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) profiles/health/$(DEPDIR)/bluetoothd-hdp_main.Tpo profiles/health/$(DEPDIR)/bluetoothd-hdp_main.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='profiles/health/hdp_main.c' object='profiles/health/bluetoothd-hdp_main.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o profiles/health/bluetoothd-hdp_main.obj `if test -f 'profiles/health/hdp_main.c'; then $(CYGPATH_W) 'profiles/health/hdp_main.c'; else $(CYGPATH_W) '$(srcdir)/profiles/health/hdp_main.c'; fi`
+
+profiles/health/bluetoothd-hdp_manager.o: profiles/health/hdp_manager.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT profiles/health/bluetoothd-hdp_manager.o -MD -MP -MF profiles/health/$(DEPDIR)/bluetoothd-hdp_manager.Tpo -c -o profiles/health/bluetoothd-hdp_manager.o `test -f 'profiles/health/hdp_manager.c' || echo '$(srcdir)/'`profiles/health/hdp_manager.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) profiles/health/$(DEPDIR)/bluetoothd-hdp_manager.Tpo profiles/health/$(DEPDIR)/bluetoothd-hdp_manager.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='profiles/health/hdp_manager.c' object='profiles/health/bluetoothd-hdp_manager.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o profiles/health/bluetoothd-hdp_manager.o `test -f 'profiles/health/hdp_manager.c' || echo '$(srcdir)/'`profiles/health/hdp_manager.c
+
+profiles/health/bluetoothd-hdp_manager.obj: profiles/health/hdp_manager.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT profiles/health/bluetoothd-hdp_manager.obj -MD -MP -MF profiles/health/$(DEPDIR)/bluetoothd-hdp_manager.Tpo -c -o profiles/health/bluetoothd-hdp_manager.obj `if test -f 'profiles/health/hdp_manager.c'; then $(CYGPATH_W) 'profiles/health/hdp_manager.c'; else $(CYGPATH_W) '$(srcdir)/profiles/health/hdp_manager.c'; fi`
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) profiles/health/$(DEPDIR)/bluetoothd-hdp_manager.Tpo profiles/health/$(DEPDIR)/bluetoothd-hdp_manager.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='profiles/health/hdp_manager.c' object='profiles/health/bluetoothd-hdp_manager.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o profiles/health/bluetoothd-hdp_manager.obj `if test -f 'profiles/health/hdp_manager.c'; then $(CYGPATH_W) 'profiles/health/hdp_manager.c'; else $(CYGPATH_W) '$(srcdir)/profiles/health/hdp_manager.c'; fi`
+
+profiles/health/bluetoothd-hdp.o: profiles/health/hdp.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT profiles/health/bluetoothd-hdp.o -MD -MP -MF profiles/health/$(DEPDIR)/bluetoothd-hdp.Tpo -c -o profiles/health/bluetoothd-hdp.o `test -f 'profiles/health/hdp.c' || echo '$(srcdir)/'`profiles/health/hdp.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) profiles/health/$(DEPDIR)/bluetoothd-hdp.Tpo profiles/health/$(DEPDIR)/bluetoothd-hdp.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='profiles/health/hdp.c' object='profiles/health/bluetoothd-hdp.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o profiles/health/bluetoothd-hdp.o `test -f 'profiles/health/hdp.c' || echo '$(srcdir)/'`profiles/health/hdp.c
+
+profiles/health/bluetoothd-hdp.obj: profiles/health/hdp.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT profiles/health/bluetoothd-hdp.obj -MD -MP -MF profiles/health/$(DEPDIR)/bluetoothd-hdp.Tpo -c -o profiles/health/bluetoothd-hdp.obj `if test -f 'profiles/health/hdp.c'; then $(CYGPATH_W) 'profiles/health/hdp.c'; else $(CYGPATH_W) '$(srcdir)/profiles/health/hdp.c'; fi`
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) profiles/health/$(DEPDIR)/bluetoothd-hdp.Tpo profiles/health/$(DEPDIR)/bluetoothd-hdp.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='profiles/health/hdp.c' object='profiles/health/bluetoothd-hdp.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o profiles/health/bluetoothd-hdp.obj `if test -f 'profiles/health/hdp.c'; then $(CYGPATH_W) 'profiles/health/hdp.c'; else $(CYGPATH_W) '$(srcdir)/profiles/health/hdp.c'; fi`
+
+profiles/health/bluetoothd-hdp_util.o: profiles/health/hdp_util.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT profiles/health/bluetoothd-hdp_util.o -MD -MP -MF profiles/health/$(DEPDIR)/bluetoothd-hdp_util.Tpo -c -o profiles/health/bluetoothd-hdp_util.o `test -f 'profiles/health/hdp_util.c' || echo '$(srcdir)/'`profiles/health/hdp_util.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) profiles/health/$(DEPDIR)/bluetoothd-hdp_util.Tpo profiles/health/$(DEPDIR)/bluetoothd-hdp_util.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='profiles/health/hdp_util.c' object='profiles/health/bluetoothd-hdp_util.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o profiles/health/bluetoothd-hdp_util.o `test -f 'profiles/health/hdp_util.c' || echo '$(srcdir)/'`profiles/health/hdp_util.c
+
+profiles/health/bluetoothd-hdp_util.obj: profiles/health/hdp_util.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT profiles/health/bluetoothd-hdp_util.obj -MD -MP -MF profiles/health/$(DEPDIR)/bluetoothd-hdp_util.Tpo -c -o profiles/health/bluetoothd-hdp_util.obj `if test -f 'profiles/health/hdp_util.c'; then $(CYGPATH_W) 'profiles/health/hdp_util.c'; else $(CYGPATH_W) '$(srcdir)/profiles/health/hdp_util.c'; fi`
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) profiles/health/$(DEPDIR)/bluetoothd-hdp_util.Tpo profiles/health/$(DEPDIR)/bluetoothd-hdp_util.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='profiles/health/hdp_util.c' object='profiles/health/bluetoothd-hdp_util.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o profiles/health/bluetoothd-hdp_util.obj `if test -f 'profiles/health/hdp_util.c'; then $(CYGPATH_W) 'profiles/health/hdp_util.c'; else $(CYGPATH_W) '$(srcdir)/profiles/health/hdp_util.c'; fi`
+
+profiles/gatt/bluetoothd-gas.o: profiles/gatt/gas.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT profiles/gatt/bluetoothd-gas.o -MD -MP -MF profiles/gatt/$(DEPDIR)/bluetoothd-gas.Tpo -c -o profiles/gatt/bluetoothd-gas.o `test -f 'profiles/gatt/gas.c' || echo '$(srcdir)/'`profiles/gatt/gas.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) profiles/gatt/$(DEPDIR)/bluetoothd-gas.Tpo profiles/gatt/$(DEPDIR)/bluetoothd-gas.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='profiles/gatt/gas.c' object='profiles/gatt/bluetoothd-gas.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o profiles/gatt/bluetoothd-gas.o `test -f 'profiles/gatt/gas.c' || echo '$(srcdir)/'`profiles/gatt/gas.c
+
+profiles/gatt/bluetoothd-gas.obj: profiles/gatt/gas.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT profiles/gatt/bluetoothd-gas.obj -MD -MP -MF profiles/gatt/$(DEPDIR)/bluetoothd-gas.Tpo -c -o profiles/gatt/bluetoothd-gas.obj `if test -f 'profiles/gatt/gas.c'; then $(CYGPATH_W) 'profiles/gatt/gas.c'; else $(CYGPATH_W) '$(srcdir)/profiles/gatt/gas.c'; fi`
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) profiles/gatt/$(DEPDIR)/bluetoothd-gas.Tpo profiles/gatt/$(DEPDIR)/bluetoothd-gas.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='profiles/gatt/gas.c' object='profiles/gatt/bluetoothd-gas.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o profiles/gatt/bluetoothd-gas.obj `if test -f 'profiles/gatt/gas.c'; then $(CYGPATH_W) 'profiles/gatt/gas.c'; else $(CYGPATH_W) '$(srcdir)/profiles/gatt/gas.c'; fi`
+
+profiles/scanparam/bluetoothd-scan.o: profiles/scanparam/scan.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT profiles/scanparam/bluetoothd-scan.o -MD -MP -MF profiles/scanparam/$(DEPDIR)/bluetoothd-scan.Tpo -c -o profiles/scanparam/bluetoothd-scan.o `test -f 'profiles/scanparam/scan.c' || echo '$(srcdir)/'`profiles/scanparam/scan.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) profiles/scanparam/$(DEPDIR)/bluetoothd-scan.Tpo profiles/scanparam/$(DEPDIR)/bluetoothd-scan.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='profiles/scanparam/scan.c' object='profiles/scanparam/bluetoothd-scan.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o profiles/scanparam/bluetoothd-scan.o `test -f 'profiles/scanparam/scan.c' || echo '$(srcdir)/'`profiles/scanparam/scan.c
+
+profiles/scanparam/bluetoothd-scan.obj: profiles/scanparam/scan.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT profiles/scanparam/bluetoothd-scan.obj -MD -MP -MF profiles/scanparam/$(DEPDIR)/bluetoothd-scan.Tpo -c -o profiles/scanparam/bluetoothd-scan.obj `if test -f 'profiles/scanparam/scan.c'; then $(CYGPATH_W) 'profiles/scanparam/scan.c'; else $(CYGPATH_W) '$(srcdir)/profiles/scanparam/scan.c'; fi`
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) profiles/scanparam/$(DEPDIR)/bluetoothd-scan.Tpo profiles/scanparam/$(DEPDIR)/bluetoothd-scan.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='profiles/scanparam/scan.c' object='profiles/scanparam/bluetoothd-scan.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o profiles/scanparam/bluetoothd-scan.obj `if test -f 'profiles/scanparam/scan.c'; then $(CYGPATH_W) 'profiles/scanparam/scan.c'; else $(CYGPATH_W) '$(srcdir)/profiles/scanparam/scan.c'; fi`
+
+profiles/deviceinfo/bluetoothd-deviceinfo.o: profiles/deviceinfo/deviceinfo.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT profiles/deviceinfo/bluetoothd-deviceinfo.o -MD -MP -MF profiles/deviceinfo/$(DEPDIR)/bluetoothd-deviceinfo.Tpo -c -o profiles/deviceinfo/bluetoothd-deviceinfo.o `test -f 'profiles/deviceinfo/deviceinfo.c' || echo '$(srcdir)/'`profiles/deviceinfo/deviceinfo.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) profiles/deviceinfo/$(DEPDIR)/bluetoothd-deviceinfo.Tpo profiles/deviceinfo/$(DEPDIR)/bluetoothd-deviceinfo.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='profiles/deviceinfo/deviceinfo.c' object='profiles/deviceinfo/bluetoothd-deviceinfo.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o profiles/deviceinfo/bluetoothd-deviceinfo.o `test -f 'profiles/deviceinfo/deviceinfo.c' || echo '$(srcdir)/'`profiles/deviceinfo/deviceinfo.c
+
+profiles/deviceinfo/bluetoothd-deviceinfo.obj: profiles/deviceinfo/deviceinfo.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT profiles/deviceinfo/bluetoothd-deviceinfo.obj -MD -MP -MF profiles/deviceinfo/$(DEPDIR)/bluetoothd-deviceinfo.Tpo -c -o profiles/deviceinfo/bluetoothd-deviceinfo.obj `if test -f 'profiles/deviceinfo/deviceinfo.c'; then $(CYGPATH_W) 'profiles/deviceinfo/deviceinfo.c'; else $(CYGPATH_W) '$(srcdir)/profiles/deviceinfo/deviceinfo.c'; fi`
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) profiles/deviceinfo/$(DEPDIR)/bluetoothd-deviceinfo.Tpo profiles/deviceinfo/$(DEPDIR)/bluetoothd-deviceinfo.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='profiles/deviceinfo/deviceinfo.c' object='profiles/deviceinfo/bluetoothd-deviceinfo.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o profiles/deviceinfo/bluetoothd-deviceinfo.obj `if test -f 'profiles/deviceinfo/deviceinfo.c'; then $(CYGPATH_W) 'profiles/deviceinfo/deviceinfo.c'; else $(CYGPATH_W) '$(srcdir)/profiles/deviceinfo/deviceinfo.c'; fi`
+
+profiles/alert/bluetoothd-server.o: profiles/alert/server.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT profiles/alert/bluetoothd-server.o -MD -MP -MF profiles/alert/$(DEPDIR)/bluetoothd-server.Tpo -c -o profiles/alert/bluetoothd-server.o `test -f 'profiles/alert/server.c' || echo '$(srcdir)/'`profiles/alert/server.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) profiles/alert/$(DEPDIR)/bluetoothd-server.Tpo profiles/alert/$(DEPDIR)/bluetoothd-server.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='profiles/alert/server.c' object='profiles/alert/bluetoothd-server.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o profiles/alert/bluetoothd-server.o `test -f 'profiles/alert/server.c' || echo '$(srcdir)/'`profiles/alert/server.c
+
+profiles/alert/bluetoothd-server.obj: profiles/alert/server.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT profiles/alert/bluetoothd-server.obj -MD -MP -MF profiles/alert/$(DEPDIR)/bluetoothd-server.Tpo -c -o profiles/alert/bluetoothd-server.obj `if test -f 'profiles/alert/server.c'; then $(CYGPATH_W) 'profiles/alert/server.c'; else $(CYGPATH_W) '$(srcdir)/profiles/alert/server.c'; fi`
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) profiles/alert/$(DEPDIR)/bluetoothd-server.Tpo profiles/alert/$(DEPDIR)/bluetoothd-server.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='profiles/alert/server.c' object='profiles/alert/bluetoothd-server.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o profiles/alert/bluetoothd-server.obj `if test -f 'profiles/alert/server.c'; then $(CYGPATH_W) 'profiles/alert/server.c'; else $(CYGPATH_W) '$(srcdir)/profiles/alert/server.c'; fi`
+
+profiles/time/bluetoothd-server.o: profiles/time/server.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT profiles/time/bluetoothd-server.o -MD -MP -MF profiles/time/$(DEPDIR)/bluetoothd-server.Tpo -c -o profiles/time/bluetoothd-server.o `test -f 'profiles/time/server.c' || echo '$(srcdir)/'`profiles/time/server.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) profiles/time/$(DEPDIR)/bluetoothd-server.Tpo profiles/time/$(DEPDIR)/bluetoothd-server.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='profiles/time/server.c' object='profiles/time/bluetoothd-server.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o profiles/time/bluetoothd-server.o `test -f 'profiles/time/server.c' || echo '$(srcdir)/'`profiles/time/server.c
+
+profiles/time/bluetoothd-server.obj: profiles/time/server.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT profiles/time/bluetoothd-server.obj -MD -MP -MF profiles/time/$(DEPDIR)/bluetoothd-server.Tpo -c -o profiles/time/bluetoothd-server.obj `if test -f 'profiles/time/server.c'; then $(CYGPATH_W) 'profiles/time/server.c'; else $(CYGPATH_W) '$(srcdir)/profiles/time/server.c'; fi`
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) profiles/time/$(DEPDIR)/bluetoothd-server.Tpo profiles/time/$(DEPDIR)/bluetoothd-server.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='profiles/time/server.c' object='profiles/time/bluetoothd-server.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o profiles/time/bluetoothd-server.obj `if test -f 'profiles/time/server.c'; then $(CYGPATH_W) 'profiles/time/server.c'; else $(CYGPATH_W) '$(srcdir)/profiles/time/server.c'; fi`
+
+profiles/proximity/bluetoothd-main.o: profiles/proximity/main.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT profiles/proximity/bluetoothd-main.o -MD -MP -MF profiles/proximity/$(DEPDIR)/bluetoothd-main.Tpo -c -o profiles/proximity/bluetoothd-main.o `test -f 'profiles/proximity/main.c' || echo '$(srcdir)/'`profiles/proximity/main.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) profiles/proximity/$(DEPDIR)/bluetoothd-main.Tpo profiles/proximity/$(DEPDIR)/bluetoothd-main.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='profiles/proximity/main.c' object='profiles/proximity/bluetoothd-main.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o profiles/proximity/bluetoothd-main.o `test -f 'profiles/proximity/main.c' || echo '$(srcdir)/'`profiles/proximity/main.c
+
+profiles/proximity/bluetoothd-main.obj: profiles/proximity/main.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT profiles/proximity/bluetoothd-main.obj -MD -MP -MF profiles/proximity/$(DEPDIR)/bluetoothd-main.Tpo -c -o profiles/proximity/bluetoothd-main.obj `if test -f 'profiles/proximity/main.c'; then $(CYGPATH_W) 'profiles/proximity/main.c'; else $(CYGPATH_W) '$(srcdir)/profiles/proximity/main.c'; fi`
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) profiles/proximity/$(DEPDIR)/bluetoothd-main.Tpo profiles/proximity/$(DEPDIR)/bluetoothd-main.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='profiles/proximity/main.c' object='profiles/proximity/bluetoothd-main.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o profiles/proximity/bluetoothd-main.obj `if test -f 'profiles/proximity/main.c'; then $(CYGPATH_W) 'profiles/proximity/main.c'; else $(CYGPATH_W) '$(srcdir)/profiles/proximity/main.c'; fi`
+
+profiles/proximity/bluetoothd-manager.o: profiles/proximity/manager.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT profiles/proximity/bluetoothd-manager.o -MD -MP -MF profiles/proximity/$(DEPDIR)/bluetoothd-manager.Tpo -c -o profiles/proximity/bluetoothd-manager.o `test -f 'profiles/proximity/manager.c' || echo '$(srcdir)/'`profiles/proximity/manager.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) profiles/proximity/$(DEPDIR)/bluetoothd-manager.Tpo profiles/proximity/$(DEPDIR)/bluetoothd-manager.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='profiles/proximity/manager.c' object='profiles/proximity/bluetoothd-manager.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o profiles/proximity/bluetoothd-manager.o `test -f 'profiles/proximity/manager.c' || echo '$(srcdir)/'`profiles/proximity/manager.c
+
+profiles/proximity/bluetoothd-manager.obj: profiles/proximity/manager.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT profiles/proximity/bluetoothd-manager.obj -MD -MP -MF profiles/proximity/$(DEPDIR)/bluetoothd-manager.Tpo -c -o profiles/proximity/bluetoothd-manager.obj `if test -f 'profiles/proximity/manager.c'; then $(CYGPATH_W) 'profiles/proximity/manager.c'; else $(CYGPATH_W) '$(srcdir)/profiles/proximity/manager.c'; fi`
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) profiles/proximity/$(DEPDIR)/bluetoothd-manager.Tpo profiles/proximity/$(DEPDIR)/bluetoothd-manager.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='profiles/proximity/manager.c' object='profiles/proximity/bluetoothd-manager.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o profiles/proximity/bluetoothd-manager.obj `if test -f 'profiles/proximity/manager.c'; then $(CYGPATH_W) 'profiles/proximity/manager.c'; else $(CYGPATH_W) '$(srcdir)/profiles/proximity/manager.c'; fi`
+
+profiles/proximity/bluetoothd-monitor.o: profiles/proximity/monitor.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT profiles/proximity/bluetoothd-monitor.o -MD -MP -MF profiles/proximity/$(DEPDIR)/bluetoothd-monitor.Tpo -c -o profiles/proximity/bluetoothd-monitor.o `test -f 'profiles/proximity/monitor.c' || echo '$(srcdir)/'`profiles/proximity/monitor.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) profiles/proximity/$(DEPDIR)/bluetoothd-monitor.Tpo profiles/proximity/$(DEPDIR)/bluetoothd-monitor.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='profiles/proximity/monitor.c' object='profiles/proximity/bluetoothd-monitor.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o profiles/proximity/bluetoothd-monitor.o `test -f 'profiles/proximity/monitor.c' || echo '$(srcdir)/'`profiles/proximity/monitor.c
+
+profiles/proximity/bluetoothd-monitor.obj: profiles/proximity/monitor.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT profiles/proximity/bluetoothd-monitor.obj -MD -MP -MF profiles/proximity/$(DEPDIR)/bluetoothd-monitor.Tpo -c -o profiles/proximity/bluetoothd-monitor.obj `if test -f 'profiles/proximity/monitor.c'; then $(CYGPATH_W) 'profiles/proximity/monitor.c'; else $(CYGPATH_W) '$(srcdir)/profiles/proximity/monitor.c'; fi`
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) profiles/proximity/$(DEPDIR)/bluetoothd-monitor.Tpo profiles/proximity/$(DEPDIR)/bluetoothd-monitor.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='profiles/proximity/monitor.c' object='profiles/proximity/bluetoothd-monitor.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o profiles/proximity/bluetoothd-monitor.obj `if test -f 'profiles/proximity/monitor.c'; then $(CYGPATH_W) 'profiles/proximity/monitor.c'; else $(CYGPATH_W) '$(srcdir)/profiles/proximity/monitor.c'; fi`
+
+profiles/proximity/bluetoothd-reporter.o: profiles/proximity/reporter.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT profiles/proximity/bluetoothd-reporter.o -MD -MP -MF profiles/proximity/$(DEPDIR)/bluetoothd-reporter.Tpo -c -o profiles/proximity/bluetoothd-reporter.o `test -f 'profiles/proximity/reporter.c' || echo '$(srcdir)/'`profiles/proximity/reporter.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) profiles/proximity/$(DEPDIR)/bluetoothd-reporter.Tpo profiles/proximity/$(DEPDIR)/bluetoothd-reporter.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='profiles/proximity/reporter.c' object='profiles/proximity/bluetoothd-reporter.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o profiles/proximity/bluetoothd-reporter.o `test -f 'profiles/proximity/reporter.c' || echo '$(srcdir)/'`profiles/proximity/reporter.c
+
+profiles/proximity/bluetoothd-reporter.obj: profiles/proximity/reporter.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT profiles/proximity/bluetoothd-reporter.obj -MD -MP -MF profiles/proximity/$(DEPDIR)/bluetoothd-reporter.Tpo -c -o profiles/proximity/bluetoothd-reporter.obj `if test -f 'profiles/proximity/reporter.c'; then $(CYGPATH_W) 'profiles/proximity/reporter.c'; else $(CYGPATH_W) '$(srcdir)/profiles/proximity/reporter.c'; fi`
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) profiles/proximity/$(DEPDIR)/bluetoothd-reporter.Tpo profiles/proximity/$(DEPDIR)/bluetoothd-reporter.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='profiles/proximity/reporter.c' object='profiles/proximity/bluetoothd-reporter.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o profiles/proximity/bluetoothd-reporter.obj `if test -f 'profiles/proximity/reporter.c'; then $(CYGPATH_W) 'profiles/proximity/reporter.c'; else $(CYGPATH_W) '$(srcdir)/profiles/proximity/reporter.c'; fi`
+
+profiles/proximity/bluetoothd-linkloss.o: profiles/proximity/linkloss.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT profiles/proximity/bluetoothd-linkloss.o -MD -MP -MF profiles/proximity/$(DEPDIR)/bluetoothd-linkloss.Tpo -c -o profiles/proximity/bluetoothd-linkloss.o `test -f 'profiles/proximity/linkloss.c' || echo '$(srcdir)/'`profiles/proximity/linkloss.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) profiles/proximity/$(DEPDIR)/bluetoothd-linkloss.Tpo profiles/proximity/$(DEPDIR)/bluetoothd-linkloss.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='profiles/proximity/linkloss.c' object='profiles/proximity/bluetoothd-linkloss.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o profiles/proximity/bluetoothd-linkloss.o `test -f 'profiles/proximity/linkloss.c' || echo '$(srcdir)/'`profiles/proximity/linkloss.c
+
+profiles/proximity/bluetoothd-linkloss.obj: profiles/proximity/linkloss.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT profiles/proximity/bluetoothd-linkloss.obj -MD -MP -MF profiles/proximity/$(DEPDIR)/bluetoothd-linkloss.Tpo -c -o profiles/proximity/bluetoothd-linkloss.obj `if test -f 'profiles/proximity/linkloss.c'; then $(CYGPATH_W) 'profiles/proximity/linkloss.c'; else $(CYGPATH_W) '$(srcdir)/profiles/proximity/linkloss.c'; fi`
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) profiles/proximity/$(DEPDIR)/bluetoothd-linkloss.Tpo profiles/proximity/$(DEPDIR)/bluetoothd-linkloss.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='profiles/proximity/linkloss.c' object='profiles/proximity/bluetoothd-linkloss.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o profiles/proximity/bluetoothd-linkloss.obj `if test -f 'profiles/proximity/linkloss.c'; then $(CYGPATH_W) 'profiles/proximity/linkloss.c'; else $(CYGPATH_W) '$(srcdir)/profiles/proximity/linkloss.c'; fi`
+
+profiles/proximity/bluetoothd-immalert.o: profiles/proximity/immalert.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT profiles/proximity/bluetoothd-immalert.o -MD -MP -MF profiles/proximity/$(DEPDIR)/bluetoothd-immalert.Tpo -c -o profiles/proximity/bluetoothd-immalert.o `test -f 'profiles/proximity/immalert.c' || echo '$(srcdir)/'`profiles/proximity/immalert.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) profiles/proximity/$(DEPDIR)/bluetoothd-immalert.Tpo profiles/proximity/$(DEPDIR)/bluetoothd-immalert.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='profiles/proximity/immalert.c' object='profiles/proximity/bluetoothd-immalert.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o profiles/proximity/bluetoothd-immalert.o `test -f 'profiles/proximity/immalert.c' || echo '$(srcdir)/'`profiles/proximity/immalert.c
+
+profiles/proximity/bluetoothd-immalert.obj: profiles/proximity/immalert.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT profiles/proximity/bluetoothd-immalert.obj -MD -MP -MF profiles/proximity/$(DEPDIR)/bluetoothd-immalert.Tpo -c -o profiles/proximity/bluetoothd-immalert.obj `if test -f 'profiles/proximity/immalert.c'; then $(CYGPATH_W) 'profiles/proximity/immalert.c'; else $(CYGPATH_W) '$(srcdir)/profiles/proximity/immalert.c'; fi`
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) profiles/proximity/$(DEPDIR)/bluetoothd-immalert.Tpo profiles/proximity/$(DEPDIR)/bluetoothd-immalert.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='profiles/proximity/immalert.c' object='profiles/proximity/bluetoothd-immalert.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o profiles/proximity/bluetoothd-immalert.obj `if test -f 'profiles/proximity/immalert.c'; then $(CYGPATH_W) 'profiles/proximity/immalert.c'; else $(CYGPATH_W) '$(srcdir)/profiles/proximity/immalert.c'; fi`
+
+profiles/thermometer/bluetoothd-thermometer.o: profiles/thermometer/thermometer.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT profiles/thermometer/bluetoothd-thermometer.o -MD -MP -MF profiles/thermometer/$(DEPDIR)/bluetoothd-thermometer.Tpo -c -o profiles/thermometer/bluetoothd-thermometer.o `test -f 'profiles/thermometer/thermometer.c' || echo '$(srcdir)/'`profiles/thermometer/thermometer.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) profiles/thermometer/$(DEPDIR)/bluetoothd-thermometer.Tpo profiles/thermometer/$(DEPDIR)/bluetoothd-thermometer.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='profiles/thermometer/thermometer.c' object='profiles/thermometer/bluetoothd-thermometer.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o profiles/thermometer/bluetoothd-thermometer.o `test -f 'profiles/thermometer/thermometer.c' || echo '$(srcdir)/'`profiles/thermometer/thermometer.c
+
+profiles/thermometer/bluetoothd-thermometer.obj: profiles/thermometer/thermometer.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT profiles/thermometer/bluetoothd-thermometer.obj -MD -MP -MF profiles/thermometer/$(DEPDIR)/bluetoothd-thermometer.Tpo -c -o profiles/thermometer/bluetoothd-thermometer.obj `if test -f 'profiles/thermometer/thermometer.c'; then $(CYGPATH_W) 'profiles/thermometer/thermometer.c'; else $(CYGPATH_W) '$(srcdir)/profiles/thermometer/thermometer.c'; fi`
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) profiles/thermometer/$(DEPDIR)/bluetoothd-thermometer.Tpo profiles/thermometer/$(DEPDIR)/bluetoothd-thermometer.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='profiles/thermometer/thermometer.c' object='profiles/thermometer/bluetoothd-thermometer.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o profiles/thermometer/bluetoothd-thermometer.obj `if test -f 'profiles/thermometer/thermometer.c'; then $(CYGPATH_W) 'profiles/thermometer/thermometer.c'; else $(CYGPATH_W) '$(srcdir)/profiles/thermometer/thermometer.c'; fi`
+
+profiles/heartrate/bluetoothd-heartrate.o: profiles/heartrate/heartrate.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT profiles/heartrate/bluetoothd-heartrate.o -MD -MP -MF profiles/heartrate/$(DEPDIR)/bluetoothd-heartrate.Tpo -c -o profiles/heartrate/bluetoothd-heartrate.o `test -f 'profiles/heartrate/heartrate.c' || echo '$(srcdir)/'`profiles/heartrate/heartrate.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) profiles/heartrate/$(DEPDIR)/bluetoothd-heartrate.Tpo profiles/heartrate/$(DEPDIR)/bluetoothd-heartrate.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='profiles/heartrate/heartrate.c' object='profiles/heartrate/bluetoothd-heartrate.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o profiles/heartrate/bluetoothd-heartrate.o `test -f 'profiles/heartrate/heartrate.c' || echo '$(srcdir)/'`profiles/heartrate/heartrate.c
+
+profiles/heartrate/bluetoothd-heartrate.obj: profiles/heartrate/heartrate.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT profiles/heartrate/bluetoothd-heartrate.obj -MD -MP -MF profiles/heartrate/$(DEPDIR)/bluetoothd-heartrate.Tpo -c -o profiles/heartrate/bluetoothd-heartrate.obj `if test -f 'profiles/heartrate/heartrate.c'; then $(CYGPATH_W) 'profiles/heartrate/heartrate.c'; else $(CYGPATH_W) '$(srcdir)/profiles/heartrate/heartrate.c'; fi`
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) profiles/heartrate/$(DEPDIR)/bluetoothd-heartrate.Tpo profiles/heartrate/$(DEPDIR)/bluetoothd-heartrate.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='profiles/heartrate/heartrate.c' object='profiles/heartrate/bluetoothd-heartrate.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o profiles/heartrate/bluetoothd-heartrate.obj `if test -f 'profiles/heartrate/heartrate.c'; then $(CYGPATH_W) 'profiles/heartrate/heartrate.c'; else $(CYGPATH_W) '$(srcdir)/profiles/heartrate/heartrate.c'; fi`
+
+profiles/cyclingspeed/bluetoothd-cyclingspeed.o: profiles/cyclingspeed/cyclingspeed.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT profiles/cyclingspeed/bluetoothd-cyclingspeed.o -MD -MP -MF profiles/cyclingspeed/$(DEPDIR)/bluetoothd-cyclingspeed.Tpo -c -o profiles/cyclingspeed/bluetoothd-cyclingspeed.o `test -f 'profiles/cyclingspeed/cyclingspeed.c' || echo '$(srcdir)/'`profiles/cyclingspeed/cyclingspeed.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) profiles/cyclingspeed/$(DEPDIR)/bluetoothd-cyclingspeed.Tpo profiles/cyclingspeed/$(DEPDIR)/bluetoothd-cyclingspeed.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='profiles/cyclingspeed/cyclingspeed.c' object='profiles/cyclingspeed/bluetoothd-cyclingspeed.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o profiles/cyclingspeed/bluetoothd-cyclingspeed.o `test -f 'profiles/cyclingspeed/cyclingspeed.c' || echo '$(srcdir)/'`profiles/cyclingspeed/cyclingspeed.c
+
+profiles/cyclingspeed/bluetoothd-cyclingspeed.obj: profiles/cyclingspeed/cyclingspeed.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT profiles/cyclingspeed/bluetoothd-cyclingspeed.obj -MD -MP -MF profiles/cyclingspeed/$(DEPDIR)/bluetoothd-cyclingspeed.Tpo -c -o profiles/cyclingspeed/bluetoothd-cyclingspeed.obj `if test -f 'profiles/cyclingspeed/cyclingspeed.c'; then $(CYGPATH_W) 'profiles/cyclingspeed/cyclingspeed.c'; else $(CYGPATH_W) '$(srcdir)/profiles/cyclingspeed/cyclingspeed.c'; fi`
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) profiles/cyclingspeed/$(DEPDIR)/bluetoothd-cyclingspeed.Tpo profiles/cyclingspeed/$(DEPDIR)/bluetoothd-cyclingspeed.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='profiles/cyclingspeed/cyclingspeed.c' object='profiles/cyclingspeed/bluetoothd-cyclingspeed.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o profiles/cyclingspeed/bluetoothd-cyclingspeed.obj `if test -f 'profiles/cyclingspeed/cyclingspeed.c'; then $(CYGPATH_W) 'profiles/cyclingspeed/cyclingspeed.c'; else $(CYGPATH_W) '$(srcdir)/profiles/cyclingspeed/cyclingspeed.c'; fi`
+
+attrib/bluetoothd-att.o: attrib/att.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT attrib/bluetoothd-att.o -MD -MP -MF attrib/$(DEPDIR)/bluetoothd-att.Tpo -c -o attrib/bluetoothd-att.o `test -f 'attrib/att.c' || echo '$(srcdir)/'`attrib/att.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) attrib/$(DEPDIR)/bluetoothd-att.Tpo attrib/$(DEPDIR)/bluetoothd-att.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='attrib/att.c' object='attrib/bluetoothd-att.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o attrib/bluetoothd-att.o `test -f 'attrib/att.c' || echo '$(srcdir)/'`attrib/att.c
+
+attrib/bluetoothd-att.obj: attrib/att.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT attrib/bluetoothd-att.obj -MD -MP -MF attrib/$(DEPDIR)/bluetoothd-att.Tpo -c -o attrib/bluetoothd-att.obj `if test -f 'attrib/att.c'; then $(CYGPATH_W) 'attrib/att.c'; else $(CYGPATH_W) '$(srcdir)/attrib/att.c'; fi`
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) attrib/$(DEPDIR)/bluetoothd-att.Tpo attrib/$(DEPDIR)/bluetoothd-att.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='attrib/att.c' object='attrib/bluetoothd-att.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o attrib/bluetoothd-att.obj `if test -f 'attrib/att.c'; then $(CYGPATH_W) 'attrib/att.c'; else $(CYGPATH_W) '$(srcdir)/attrib/att.c'; fi`
+
+attrib/bluetoothd-gatt.o: attrib/gatt.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT attrib/bluetoothd-gatt.o -MD -MP -MF attrib/$(DEPDIR)/bluetoothd-gatt.Tpo -c -o attrib/bluetoothd-gatt.o `test -f 'attrib/gatt.c' || echo '$(srcdir)/'`attrib/gatt.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) attrib/$(DEPDIR)/bluetoothd-gatt.Tpo attrib/$(DEPDIR)/bluetoothd-gatt.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='attrib/gatt.c' object='attrib/bluetoothd-gatt.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o attrib/bluetoothd-gatt.o `test -f 'attrib/gatt.c' || echo '$(srcdir)/'`attrib/gatt.c
+
+attrib/bluetoothd-gatt.obj: attrib/gatt.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT attrib/bluetoothd-gatt.obj -MD -MP -MF attrib/$(DEPDIR)/bluetoothd-gatt.Tpo -c -o attrib/bluetoothd-gatt.obj `if test -f 'attrib/gatt.c'; then $(CYGPATH_W) 'attrib/gatt.c'; else $(CYGPATH_W) '$(srcdir)/attrib/gatt.c'; fi`
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) attrib/$(DEPDIR)/bluetoothd-gatt.Tpo attrib/$(DEPDIR)/bluetoothd-gatt.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='attrib/gatt.c' object='attrib/bluetoothd-gatt.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o attrib/bluetoothd-gatt.obj `if test -f 'attrib/gatt.c'; then $(CYGPATH_W) 'attrib/gatt.c'; else $(CYGPATH_W) '$(srcdir)/attrib/gatt.c'; fi`
+
+attrib/bluetoothd-gattrib.o: attrib/gattrib.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT attrib/bluetoothd-gattrib.o -MD -MP -MF attrib/$(DEPDIR)/bluetoothd-gattrib.Tpo -c -o attrib/bluetoothd-gattrib.o `test -f 'attrib/gattrib.c' || echo '$(srcdir)/'`attrib/gattrib.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) attrib/$(DEPDIR)/bluetoothd-gattrib.Tpo attrib/$(DEPDIR)/bluetoothd-gattrib.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='attrib/gattrib.c' object='attrib/bluetoothd-gattrib.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o attrib/bluetoothd-gattrib.o `test -f 'attrib/gattrib.c' || echo '$(srcdir)/'`attrib/gattrib.c
+
+attrib/bluetoothd-gattrib.obj: attrib/gattrib.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT attrib/bluetoothd-gattrib.obj -MD -MP -MF attrib/$(DEPDIR)/bluetoothd-gattrib.Tpo -c -o attrib/bluetoothd-gattrib.obj `if test -f 'attrib/gattrib.c'; then $(CYGPATH_W) 'attrib/gattrib.c'; else $(CYGPATH_W) '$(srcdir)/attrib/gattrib.c'; fi`
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) attrib/$(DEPDIR)/bluetoothd-gattrib.Tpo attrib/$(DEPDIR)/bluetoothd-gattrib.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='attrib/gattrib.c' object='attrib/bluetoothd-gattrib.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o attrib/bluetoothd-gattrib.obj `if test -f 'attrib/gattrib.c'; then $(CYGPATH_W) 'attrib/gattrib.c'; else $(CYGPATH_W) '$(srcdir)/attrib/gattrib.c'; fi`
+
+attrib/bluetoothd-gatt-service.o: attrib/gatt-service.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT attrib/bluetoothd-gatt-service.o -MD -MP -MF attrib/$(DEPDIR)/bluetoothd-gatt-service.Tpo -c -o attrib/bluetoothd-gatt-service.o `test -f 'attrib/gatt-service.c' || echo '$(srcdir)/'`attrib/gatt-service.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) attrib/$(DEPDIR)/bluetoothd-gatt-service.Tpo attrib/$(DEPDIR)/bluetoothd-gatt-service.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='attrib/gatt-service.c' object='attrib/bluetoothd-gatt-service.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o attrib/bluetoothd-gatt-service.o `test -f 'attrib/gatt-service.c' || echo '$(srcdir)/'`attrib/gatt-service.c
+
+attrib/bluetoothd-gatt-service.obj: attrib/gatt-service.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT attrib/bluetoothd-gatt-service.obj -MD -MP -MF attrib/$(DEPDIR)/bluetoothd-gatt-service.Tpo -c -o attrib/bluetoothd-gatt-service.obj `if test -f 'attrib/gatt-service.c'; then $(CYGPATH_W) 'attrib/gatt-service.c'; else $(CYGPATH_W) '$(srcdir)/attrib/gatt-service.c'; fi`
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) attrib/$(DEPDIR)/bluetoothd-gatt-service.Tpo attrib/$(DEPDIR)/bluetoothd-gatt-service.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='attrib/gatt-service.c' object='attrib/bluetoothd-gatt-service.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o attrib/bluetoothd-gatt-service.obj `if test -f 'attrib/gatt-service.c'; then $(CYGPATH_W) 'attrib/gatt-service.c'; else $(CYGPATH_W) '$(srcdir)/attrib/gatt-service.c'; fi`
+
+btio/bluetoothd-btio.o: btio/btio.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT btio/bluetoothd-btio.o -MD -MP -MF btio/$(DEPDIR)/bluetoothd-btio.Tpo -c -o btio/bluetoothd-btio.o `test -f 'btio/btio.c' || echo '$(srcdir)/'`btio/btio.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) btio/$(DEPDIR)/bluetoothd-btio.Tpo btio/$(DEPDIR)/bluetoothd-btio.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='btio/btio.c' object='btio/bluetoothd-btio.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o btio/bluetoothd-btio.o `test -f 'btio/btio.c' || echo '$(srcdir)/'`btio/btio.c
+
+btio/bluetoothd-btio.obj: btio/btio.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT btio/bluetoothd-btio.obj -MD -MP -MF btio/$(DEPDIR)/bluetoothd-btio.Tpo -c -o btio/bluetoothd-btio.obj `if test -f 'btio/btio.c'; then $(CYGPATH_W) 'btio/btio.c'; else $(CYGPATH_W) '$(srcdir)/btio/btio.c'; fi`
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) btio/$(DEPDIR)/bluetoothd-btio.Tpo btio/$(DEPDIR)/bluetoothd-btio.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='btio/btio.c' object='btio/bluetoothd-btio.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o btio/bluetoothd-btio.obj `if test -f 'btio/btio.c'; then $(CYGPATH_W) 'btio/btio.c'; else $(CYGPATH_W) '$(srcdir)/btio/btio.c'; fi`
+
+src/bluetoothd-main.o: src/main.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT src/bluetoothd-main.o -MD -MP -MF src/$(DEPDIR)/bluetoothd-main.Tpo -c -o src/bluetoothd-main.o `test -f 'src/main.c' || echo '$(srcdir)/'`src/main.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) src/$(DEPDIR)/bluetoothd-main.Tpo src/$(DEPDIR)/bluetoothd-main.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='src/main.c' object='src/bluetoothd-main.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o src/bluetoothd-main.o `test -f 'src/main.c' || echo '$(srcdir)/'`src/main.c
+
+src/bluetoothd-main.obj: src/main.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT src/bluetoothd-main.obj -MD -MP -MF src/$(DEPDIR)/bluetoothd-main.Tpo -c -o src/bluetoothd-main.obj `if test -f 'src/main.c'; then $(CYGPATH_W) 'src/main.c'; else $(CYGPATH_W) '$(srcdir)/src/main.c'; fi`
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) src/$(DEPDIR)/bluetoothd-main.Tpo src/$(DEPDIR)/bluetoothd-main.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='src/main.c' object='src/bluetoothd-main.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o src/bluetoothd-main.obj `if test -f 'src/main.c'; then $(CYGPATH_W) 'src/main.c'; else $(CYGPATH_W) '$(srcdir)/src/main.c'; fi`
+
+src/bluetoothd-log.o: src/log.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT src/bluetoothd-log.o -MD -MP -MF src/$(DEPDIR)/bluetoothd-log.Tpo -c -o src/bluetoothd-log.o `test -f 'src/log.c' || echo '$(srcdir)/'`src/log.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) src/$(DEPDIR)/bluetoothd-log.Tpo src/$(DEPDIR)/bluetoothd-log.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='src/log.c' object='src/bluetoothd-log.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o src/bluetoothd-log.o `test -f 'src/log.c' || echo '$(srcdir)/'`src/log.c
+
+src/bluetoothd-log.obj: src/log.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT src/bluetoothd-log.obj -MD -MP -MF src/$(DEPDIR)/bluetoothd-log.Tpo -c -o src/bluetoothd-log.obj `if test -f 'src/log.c'; then $(CYGPATH_W) 'src/log.c'; else $(CYGPATH_W) '$(srcdir)/src/log.c'; fi`
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) src/$(DEPDIR)/bluetoothd-log.Tpo src/$(DEPDIR)/bluetoothd-log.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='src/log.c' object='src/bluetoothd-log.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o src/bluetoothd-log.obj `if test -f 'src/log.c'; then $(CYGPATH_W) 'src/log.c'; else $(CYGPATH_W) '$(srcdir)/src/log.c'; fi`
+
+src/bluetoothd-systemd.o: src/systemd.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT src/bluetoothd-systemd.o -MD -MP -MF src/$(DEPDIR)/bluetoothd-systemd.Tpo -c -o src/bluetoothd-systemd.o `test -f 'src/systemd.c' || echo '$(srcdir)/'`src/systemd.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) src/$(DEPDIR)/bluetoothd-systemd.Tpo src/$(DEPDIR)/bluetoothd-systemd.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='src/systemd.c' object='src/bluetoothd-systemd.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o src/bluetoothd-systemd.o `test -f 'src/systemd.c' || echo '$(srcdir)/'`src/systemd.c
+
+src/bluetoothd-systemd.obj: src/systemd.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT src/bluetoothd-systemd.obj -MD -MP -MF src/$(DEPDIR)/bluetoothd-systemd.Tpo -c -o src/bluetoothd-systemd.obj `if test -f 'src/systemd.c'; then $(CYGPATH_W) 'src/systemd.c'; else $(CYGPATH_W) '$(srcdir)/src/systemd.c'; fi`
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) src/$(DEPDIR)/bluetoothd-systemd.Tpo src/$(DEPDIR)/bluetoothd-systemd.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='src/systemd.c' object='src/bluetoothd-systemd.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o src/bluetoothd-systemd.obj `if test -f 'src/systemd.c'; then $(CYGPATH_W) 'src/systemd.c'; else $(CYGPATH_W) '$(srcdir)/src/systemd.c'; fi`
+
+src/bluetoothd-rfkill.o: src/rfkill.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT src/bluetoothd-rfkill.o -MD -MP -MF src/$(DEPDIR)/bluetoothd-rfkill.Tpo -c -o src/bluetoothd-rfkill.o `test -f 'src/rfkill.c' || echo '$(srcdir)/'`src/rfkill.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) src/$(DEPDIR)/bluetoothd-rfkill.Tpo src/$(DEPDIR)/bluetoothd-rfkill.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='src/rfkill.c' object='src/bluetoothd-rfkill.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o src/bluetoothd-rfkill.o `test -f 'src/rfkill.c' || echo '$(srcdir)/'`src/rfkill.c
+
+src/bluetoothd-rfkill.obj: src/rfkill.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT src/bluetoothd-rfkill.obj -MD -MP -MF src/$(DEPDIR)/bluetoothd-rfkill.Tpo -c -o src/bluetoothd-rfkill.obj `if test -f 'src/rfkill.c'; then $(CYGPATH_W) 'src/rfkill.c'; else $(CYGPATH_W) '$(srcdir)/src/rfkill.c'; fi`
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) src/$(DEPDIR)/bluetoothd-rfkill.Tpo src/$(DEPDIR)/bluetoothd-rfkill.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='src/rfkill.c' object='src/bluetoothd-rfkill.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o src/bluetoothd-rfkill.obj `if test -f 'src/rfkill.c'; then $(CYGPATH_W) 'src/rfkill.c'; else $(CYGPATH_W) '$(srcdir)/src/rfkill.c'; fi`
+
+src/bluetoothd-sdpd-server.o: src/sdpd-server.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT src/bluetoothd-sdpd-server.o -MD -MP -MF src/$(DEPDIR)/bluetoothd-sdpd-server.Tpo -c -o src/bluetoothd-sdpd-server.o `test -f 'src/sdpd-server.c' || echo '$(srcdir)/'`src/sdpd-server.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) src/$(DEPDIR)/bluetoothd-sdpd-server.Tpo src/$(DEPDIR)/bluetoothd-sdpd-server.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='src/sdpd-server.c' object='src/bluetoothd-sdpd-server.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o src/bluetoothd-sdpd-server.o `test -f 'src/sdpd-server.c' || echo '$(srcdir)/'`src/sdpd-server.c
+
+src/bluetoothd-sdpd-server.obj: src/sdpd-server.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT src/bluetoothd-sdpd-server.obj -MD -MP -MF src/$(DEPDIR)/bluetoothd-sdpd-server.Tpo -c -o src/bluetoothd-sdpd-server.obj `if test -f 'src/sdpd-server.c'; then $(CYGPATH_W) 'src/sdpd-server.c'; else $(CYGPATH_W) '$(srcdir)/src/sdpd-server.c'; fi`
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) src/$(DEPDIR)/bluetoothd-sdpd-server.Tpo src/$(DEPDIR)/bluetoothd-sdpd-server.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='src/sdpd-server.c' object='src/bluetoothd-sdpd-server.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o src/bluetoothd-sdpd-server.obj `if test -f 'src/sdpd-server.c'; then $(CYGPATH_W) 'src/sdpd-server.c'; else $(CYGPATH_W) '$(srcdir)/src/sdpd-server.c'; fi`
+
+src/bluetoothd-sdpd-request.o: src/sdpd-request.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT src/bluetoothd-sdpd-request.o -MD -MP -MF src/$(DEPDIR)/bluetoothd-sdpd-request.Tpo -c -o src/bluetoothd-sdpd-request.o `test -f 'src/sdpd-request.c' || echo '$(srcdir)/'`src/sdpd-request.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) src/$(DEPDIR)/bluetoothd-sdpd-request.Tpo src/$(DEPDIR)/bluetoothd-sdpd-request.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='src/sdpd-request.c' object='src/bluetoothd-sdpd-request.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o src/bluetoothd-sdpd-request.o `test -f 'src/sdpd-request.c' || echo '$(srcdir)/'`src/sdpd-request.c
+
+src/bluetoothd-sdpd-request.obj: src/sdpd-request.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT src/bluetoothd-sdpd-request.obj -MD -MP -MF src/$(DEPDIR)/bluetoothd-sdpd-request.Tpo -c -o src/bluetoothd-sdpd-request.obj `if test -f 'src/sdpd-request.c'; then $(CYGPATH_W) 'src/sdpd-request.c'; else $(CYGPATH_W) '$(srcdir)/src/sdpd-request.c'; fi`
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) src/$(DEPDIR)/bluetoothd-sdpd-request.Tpo src/$(DEPDIR)/bluetoothd-sdpd-request.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='src/sdpd-request.c' object='src/bluetoothd-sdpd-request.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o src/bluetoothd-sdpd-request.obj `if test -f 'src/sdpd-request.c'; then $(CYGPATH_W) 'src/sdpd-request.c'; else $(CYGPATH_W) '$(srcdir)/src/sdpd-request.c'; fi`
+
+src/bluetoothd-sdpd-service.o: src/sdpd-service.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT src/bluetoothd-sdpd-service.o -MD -MP -MF src/$(DEPDIR)/bluetoothd-sdpd-service.Tpo -c -o src/bluetoothd-sdpd-service.o `test -f 'src/sdpd-service.c' || echo '$(srcdir)/'`src/sdpd-service.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) src/$(DEPDIR)/bluetoothd-sdpd-service.Tpo src/$(DEPDIR)/bluetoothd-sdpd-service.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='src/sdpd-service.c' object='src/bluetoothd-sdpd-service.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o src/bluetoothd-sdpd-service.o `test -f 'src/sdpd-service.c' || echo '$(srcdir)/'`src/sdpd-service.c
+
+src/bluetoothd-sdpd-service.obj: src/sdpd-service.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT src/bluetoothd-sdpd-service.obj -MD -MP -MF src/$(DEPDIR)/bluetoothd-sdpd-service.Tpo -c -o src/bluetoothd-sdpd-service.obj `if test -f 'src/sdpd-service.c'; then $(CYGPATH_W) 'src/sdpd-service.c'; else $(CYGPATH_W) '$(srcdir)/src/sdpd-service.c'; fi`
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) src/$(DEPDIR)/bluetoothd-sdpd-service.Tpo src/$(DEPDIR)/bluetoothd-sdpd-service.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='src/sdpd-service.c' object='src/bluetoothd-sdpd-service.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o src/bluetoothd-sdpd-service.obj `if test -f 'src/sdpd-service.c'; then $(CYGPATH_W) 'src/sdpd-service.c'; else $(CYGPATH_W) '$(srcdir)/src/sdpd-service.c'; fi`
+
+src/bluetoothd-sdpd-database.o: src/sdpd-database.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT src/bluetoothd-sdpd-database.o -MD -MP -MF src/$(DEPDIR)/bluetoothd-sdpd-database.Tpo -c -o src/bluetoothd-sdpd-database.o `test -f 'src/sdpd-database.c' || echo '$(srcdir)/'`src/sdpd-database.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) src/$(DEPDIR)/bluetoothd-sdpd-database.Tpo src/$(DEPDIR)/bluetoothd-sdpd-database.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='src/sdpd-database.c' object='src/bluetoothd-sdpd-database.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o src/bluetoothd-sdpd-database.o `test -f 'src/sdpd-database.c' || echo '$(srcdir)/'`src/sdpd-database.c
+
+src/bluetoothd-sdpd-database.obj: src/sdpd-database.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT src/bluetoothd-sdpd-database.obj -MD -MP -MF src/$(DEPDIR)/bluetoothd-sdpd-database.Tpo -c -o src/bluetoothd-sdpd-database.obj `if test -f 'src/sdpd-database.c'; then $(CYGPATH_W) 'src/sdpd-database.c'; else $(CYGPATH_W) '$(srcdir)/src/sdpd-database.c'; fi`
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) src/$(DEPDIR)/bluetoothd-sdpd-database.Tpo src/$(DEPDIR)/bluetoothd-sdpd-database.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='src/sdpd-database.c' object='src/bluetoothd-sdpd-database.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o src/bluetoothd-sdpd-database.obj `if test -f 'src/sdpd-database.c'; then $(CYGPATH_W) 'src/sdpd-database.c'; else $(CYGPATH_W) '$(srcdir)/src/sdpd-database.c'; fi`
+
+src/bluetoothd-attrib-server.o: src/attrib-server.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT src/bluetoothd-attrib-server.o -MD -MP -MF src/$(DEPDIR)/bluetoothd-attrib-server.Tpo -c -o src/bluetoothd-attrib-server.o `test -f 'src/attrib-server.c' || echo '$(srcdir)/'`src/attrib-server.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) src/$(DEPDIR)/bluetoothd-attrib-server.Tpo src/$(DEPDIR)/bluetoothd-attrib-server.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='src/attrib-server.c' object='src/bluetoothd-attrib-server.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o src/bluetoothd-attrib-server.o `test -f 'src/attrib-server.c' || echo '$(srcdir)/'`src/attrib-server.c
+
+src/bluetoothd-attrib-server.obj: src/attrib-server.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT src/bluetoothd-attrib-server.obj -MD -MP -MF src/$(DEPDIR)/bluetoothd-attrib-server.Tpo -c -o src/bluetoothd-attrib-server.obj `if test -f 'src/attrib-server.c'; then $(CYGPATH_W) 'src/attrib-server.c'; else $(CYGPATH_W) '$(srcdir)/src/attrib-server.c'; fi`
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) src/$(DEPDIR)/bluetoothd-attrib-server.Tpo src/$(DEPDIR)/bluetoothd-attrib-server.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='src/attrib-server.c' object='src/bluetoothd-attrib-server.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o src/bluetoothd-attrib-server.obj `if test -f 'src/attrib-server.c'; then $(CYGPATH_W) 'src/attrib-server.c'; else $(CYGPATH_W) '$(srcdir)/src/attrib-server.c'; fi`
+
+src/bluetoothd-sdp-xml.o: src/sdp-xml.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT src/bluetoothd-sdp-xml.o -MD -MP -MF src/$(DEPDIR)/bluetoothd-sdp-xml.Tpo -c -o src/bluetoothd-sdp-xml.o `test -f 'src/sdp-xml.c' || echo '$(srcdir)/'`src/sdp-xml.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) src/$(DEPDIR)/bluetoothd-sdp-xml.Tpo src/$(DEPDIR)/bluetoothd-sdp-xml.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='src/sdp-xml.c' object='src/bluetoothd-sdp-xml.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o src/bluetoothd-sdp-xml.o `test -f 'src/sdp-xml.c' || echo '$(srcdir)/'`src/sdp-xml.c
+
+src/bluetoothd-sdp-xml.obj: src/sdp-xml.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT src/bluetoothd-sdp-xml.obj -MD -MP -MF src/$(DEPDIR)/bluetoothd-sdp-xml.Tpo -c -o src/bluetoothd-sdp-xml.obj `if test -f 'src/sdp-xml.c'; then $(CYGPATH_W) 'src/sdp-xml.c'; else $(CYGPATH_W) '$(srcdir)/src/sdp-xml.c'; fi`
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) src/$(DEPDIR)/bluetoothd-sdp-xml.Tpo src/$(DEPDIR)/bluetoothd-sdp-xml.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='src/sdp-xml.c' object='src/bluetoothd-sdp-xml.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o src/bluetoothd-sdp-xml.obj `if test -f 'src/sdp-xml.c'; then $(CYGPATH_W) 'src/sdp-xml.c'; else $(CYGPATH_W) '$(srcdir)/src/sdp-xml.c'; fi`
+
+src/bluetoothd-sdp-client.o: src/sdp-client.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT src/bluetoothd-sdp-client.o -MD -MP -MF src/$(DEPDIR)/bluetoothd-sdp-client.Tpo -c -o src/bluetoothd-sdp-client.o `test -f 'src/sdp-client.c' || echo '$(srcdir)/'`src/sdp-client.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) src/$(DEPDIR)/bluetoothd-sdp-client.Tpo src/$(DEPDIR)/bluetoothd-sdp-client.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='src/sdp-client.c' object='src/bluetoothd-sdp-client.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o src/bluetoothd-sdp-client.o `test -f 'src/sdp-client.c' || echo '$(srcdir)/'`src/sdp-client.c
+
+src/bluetoothd-sdp-client.obj: src/sdp-client.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT src/bluetoothd-sdp-client.obj -MD -MP -MF src/$(DEPDIR)/bluetoothd-sdp-client.Tpo -c -o src/bluetoothd-sdp-client.obj `if test -f 'src/sdp-client.c'; then $(CYGPATH_W) 'src/sdp-client.c'; else $(CYGPATH_W) '$(srcdir)/src/sdp-client.c'; fi`
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) src/$(DEPDIR)/bluetoothd-sdp-client.Tpo src/$(DEPDIR)/bluetoothd-sdp-client.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='src/sdp-client.c' object='src/bluetoothd-sdp-client.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o src/bluetoothd-sdp-client.obj `if test -f 'src/sdp-client.c'; then $(CYGPATH_W) 'src/sdp-client.c'; else $(CYGPATH_W) '$(srcdir)/src/sdp-client.c'; fi`
+
+src/bluetoothd-textfile.o: src/textfile.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT src/bluetoothd-textfile.o -MD -MP -MF src/$(DEPDIR)/bluetoothd-textfile.Tpo -c -o src/bluetoothd-textfile.o `test -f 'src/textfile.c' || echo '$(srcdir)/'`src/textfile.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) src/$(DEPDIR)/bluetoothd-textfile.Tpo src/$(DEPDIR)/bluetoothd-textfile.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='src/textfile.c' object='src/bluetoothd-textfile.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o src/bluetoothd-textfile.o `test -f 'src/textfile.c' || echo '$(srcdir)/'`src/textfile.c
+
+src/bluetoothd-textfile.obj: src/textfile.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT src/bluetoothd-textfile.obj -MD -MP -MF src/$(DEPDIR)/bluetoothd-textfile.Tpo -c -o src/bluetoothd-textfile.obj `if test -f 'src/textfile.c'; then $(CYGPATH_W) 'src/textfile.c'; else $(CYGPATH_W) '$(srcdir)/src/textfile.c'; fi`
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) src/$(DEPDIR)/bluetoothd-textfile.Tpo src/$(DEPDIR)/bluetoothd-textfile.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='src/textfile.c' object='src/bluetoothd-textfile.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o src/bluetoothd-textfile.obj `if test -f 'src/textfile.c'; then $(CYGPATH_W) 'src/textfile.c'; else $(CYGPATH_W) '$(srcdir)/src/textfile.c'; fi`
+
+src/bluetoothd-uuid-helper.o: src/uuid-helper.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT src/bluetoothd-uuid-helper.o -MD -MP -MF src/$(DEPDIR)/bluetoothd-uuid-helper.Tpo -c -o src/bluetoothd-uuid-helper.o `test -f 'src/uuid-helper.c' || echo '$(srcdir)/'`src/uuid-helper.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) src/$(DEPDIR)/bluetoothd-uuid-helper.Tpo src/$(DEPDIR)/bluetoothd-uuid-helper.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='src/uuid-helper.c' object='src/bluetoothd-uuid-helper.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o src/bluetoothd-uuid-helper.o `test -f 'src/uuid-helper.c' || echo '$(srcdir)/'`src/uuid-helper.c
+
+src/bluetoothd-uuid-helper.obj: src/uuid-helper.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT src/bluetoothd-uuid-helper.obj -MD -MP -MF src/$(DEPDIR)/bluetoothd-uuid-helper.Tpo -c -o src/bluetoothd-uuid-helper.obj `if test -f 'src/uuid-helper.c'; then $(CYGPATH_W) 'src/uuid-helper.c'; else $(CYGPATH_W) '$(srcdir)/src/uuid-helper.c'; fi`
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) src/$(DEPDIR)/bluetoothd-uuid-helper.Tpo src/$(DEPDIR)/bluetoothd-uuid-helper.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='src/uuid-helper.c' object='src/bluetoothd-uuid-helper.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o src/bluetoothd-uuid-helper.obj `if test -f 'src/uuid-helper.c'; then $(CYGPATH_W) 'src/uuid-helper.c'; else $(CYGPATH_W) '$(srcdir)/src/uuid-helper.c'; fi`
+
+src/bluetoothd-plugin.o: src/plugin.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT src/bluetoothd-plugin.o -MD -MP -MF src/$(DEPDIR)/bluetoothd-plugin.Tpo -c -o src/bluetoothd-plugin.o `test -f 'src/plugin.c' || echo '$(srcdir)/'`src/plugin.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) src/$(DEPDIR)/bluetoothd-plugin.Tpo src/$(DEPDIR)/bluetoothd-plugin.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='src/plugin.c' object='src/bluetoothd-plugin.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o src/bluetoothd-plugin.o `test -f 'src/plugin.c' || echo '$(srcdir)/'`src/plugin.c
+
+src/bluetoothd-plugin.obj: src/plugin.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT src/bluetoothd-plugin.obj -MD -MP -MF src/$(DEPDIR)/bluetoothd-plugin.Tpo -c -o src/bluetoothd-plugin.obj `if test -f 'src/plugin.c'; then $(CYGPATH_W) 'src/plugin.c'; else $(CYGPATH_W) '$(srcdir)/src/plugin.c'; fi`
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) src/$(DEPDIR)/bluetoothd-plugin.Tpo src/$(DEPDIR)/bluetoothd-plugin.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='src/plugin.c' object='src/bluetoothd-plugin.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o src/bluetoothd-plugin.obj `if test -f 'src/plugin.c'; then $(CYGPATH_W) 'src/plugin.c'; else $(CYGPATH_W) '$(srcdir)/src/plugin.c'; fi`
+
+src/bluetoothd-storage.o: src/storage.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT src/bluetoothd-storage.o -MD -MP -MF src/$(DEPDIR)/bluetoothd-storage.Tpo -c -o src/bluetoothd-storage.o `test -f 'src/storage.c' || echo '$(srcdir)/'`src/storage.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) src/$(DEPDIR)/bluetoothd-storage.Tpo src/$(DEPDIR)/bluetoothd-storage.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='src/storage.c' object='src/bluetoothd-storage.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o src/bluetoothd-storage.o `test -f 'src/storage.c' || echo '$(srcdir)/'`src/storage.c
+
+src/bluetoothd-storage.obj: src/storage.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT src/bluetoothd-storage.obj -MD -MP -MF src/$(DEPDIR)/bluetoothd-storage.Tpo -c -o src/bluetoothd-storage.obj `if test -f 'src/storage.c'; then $(CYGPATH_W) 'src/storage.c'; else $(CYGPATH_W) '$(srcdir)/src/storage.c'; fi`
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) src/$(DEPDIR)/bluetoothd-storage.Tpo src/$(DEPDIR)/bluetoothd-storage.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='src/storage.c' object='src/bluetoothd-storage.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o src/bluetoothd-storage.obj `if test -f 'src/storage.c'; then $(CYGPATH_W) 'src/storage.c'; else $(CYGPATH_W) '$(srcdir)/src/storage.c'; fi`
+
+src/bluetoothd-agent.o: src/agent.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT src/bluetoothd-agent.o -MD -MP -MF src/$(DEPDIR)/bluetoothd-agent.Tpo -c -o src/bluetoothd-agent.o `test -f 'src/agent.c' || echo '$(srcdir)/'`src/agent.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) src/$(DEPDIR)/bluetoothd-agent.Tpo src/$(DEPDIR)/bluetoothd-agent.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='src/agent.c' object='src/bluetoothd-agent.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o src/bluetoothd-agent.o `test -f 'src/agent.c' || echo '$(srcdir)/'`src/agent.c
+
+src/bluetoothd-agent.obj: src/agent.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT src/bluetoothd-agent.obj -MD -MP -MF src/$(DEPDIR)/bluetoothd-agent.Tpo -c -o src/bluetoothd-agent.obj `if test -f 'src/agent.c'; then $(CYGPATH_W) 'src/agent.c'; else $(CYGPATH_W) '$(srcdir)/src/agent.c'; fi`
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) src/$(DEPDIR)/bluetoothd-agent.Tpo src/$(DEPDIR)/bluetoothd-agent.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='src/agent.c' object='src/bluetoothd-agent.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o src/bluetoothd-agent.obj `if test -f 'src/agent.c'; then $(CYGPATH_W) 'src/agent.c'; else $(CYGPATH_W) '$(srcdir)/src/agent.c'; fi`
+
+src/bluetoothd-error.o: src/error.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT src/bluetoothd-error.o -MD -MP -MF src/$(DEPDIR)/bluetoothd-error.Tpo -c -o src/bluetoothd-error.o `test -f 'src/error.c' || echo '$(srcdir)/'`src/error.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) src/$(DEPDIR)/bluetoothd-error.Tpo src/$(DEPDIR)/bluetoothd-error.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='src/error.c' object='src/bluetoothd-error.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o src/bluetoothd-error.o `test -f 'src/error.c' || echo '$(srcdir)/'`src/error.c
+
+src/bluetoothd-error.obj: src/error.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT src/bluetoothd-error.obj -MD -MP -MF src/$(DEPDIR)/bluetoothd-error.Tpo -c -o src/bluetoothd-error.obj `if test -f 'src/error.c'; then $(CYGPATH_W) 'src/error.c'; else $(CYGPATH_W) '$(srcdir)/src/error.c'; fi`
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) src/$(DEPDIR)/bluetoothd-error.Tpo src/$(DEPDIR)/bluetoothd-error.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='src/error.c' object='src/bluetoothd-error.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o src/bluetoothd-error.obj `if test -f 'src/error.c'; then $(CYGPATH_W) 'src/error.c'; else $(CYGPATH_W) '$(srcdir)/src/error.c'; fi`
+
+src/bluetoothd-adapter.o: src/adapter.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT src/bluetoothd-adapter.o -MD -MP -MF src/$(DEPDIR)/bluetoothd-adapter.Tpo -c -o src/bluetoothd-adapter.o `test -f 'src/adapter.c' || echo '$(srcdir)/'`src/adapter.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) src/$(DEPDIR)/bluetoothd-adapter.Tpo src/$(DEPDIR)/bluetoothd-adapter.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='src/adapter.c' object='src/bluetoothd-adapter.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o src/bluetoothd-adapter.o `test -f 'src/adapter.c' || echo '$(srcdir)/'`src/adapter.c
+
+src/bluetoothd-adapter.obj: src/adapter.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT src/bluetoothd-adapter.obj -MD -MP -MF src/$(DEPDIR)/bluetoothd-adapter.Tpo -c -o src/bluetoothd-adapter.obj `if test -f 'src/adapter.c'; then $(CYGPATH_W) 'src/adapter.c'; else $(CYGPATH_W) '$(srcdir)/src/adapter.c'; fi`
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) src/$(DEPDIR)/bluetoothd-adapter.Tpo src/$(DEPDIR)/bluetoothd-adapter.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='src/adapter.c' object='src/bluetoothd-adapter.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o src/bluetoothd-adapter.obj `if test -f 'src/adapter.c'; then $(CYGPATH_W) 'src/adapter.c'; else $(CYGPATH_W) '$(srcdir)/src/adapter.c'; fi`
+
+src/bluetoothd-profile.o: src/profile.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT src/bluetoothd-profile.o -MD -MP -MF src/$(DEPDIR)/bluetoothd-profile.Tpo -c -o src/bluetoothd-profile.o `test -f 'src/profile.c' || echo '$(srcdir)/'`src/profile.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) src/$(DEPDIR)/bluetoothd-profile.Tpo src/$(DEPDIR)/bluetoothd-profile.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='src/profile.c' object='src/bluetoothd-profile.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o src/bluetoothd-profile.o `test -f 'src/profile.c' || echo '$(srcdir)/'`src/profile.c
+
+src/bluetoothd-profile.obj: src/profile.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT src/bluetoothd-profile.obj -MD -MP -MF src/$(DEPDIR)/bluetoothd-profile.Tpo -c -o src/bluetoothd-profile.obj `if test -f 'src/profile.c'; then $(CYGPATH_W) 'src/profile.c'; else $(CYGPATH_W) '$(srcdir)/src/profile.c'; fi`
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) src/$(DEPDIR)/bluetoothd-profile.Tpo src/$(DEPDIR)/bluetoothd-profile.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='src/profile.c' object='src/bluetoothd-profile.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o src/bluetoothd-profile.obj `if test -f 'src/profile.c'; then $(CYGPATH_W) 'src/profile.c'; else $(CYGPATH_W) '$(srcdir)/src/profile.c'; fi`
+
+src/bluetoothd-service.o: src/service.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT src/bluetoothd-service.o -MD -MP -MF src/$(DEPDIR)/bluetoothd-service.Tpo -c -o src/bluetoothd-service.o `test -f 'src/service.c' || echo '$(srcdir)/'`src/service.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) src/$(DEPDIR)/bluetoothd-service.Tpo src/$(DEPDIR)/bluetoothd-service.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='src/service.c' object='src/bluetoothd-service.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o src/bluetoothd-service.o `test -f 'src/service.c' || echo '$(srcdir)/'`src/service.c
+
+src/bluetoothd-service.obj: src/service.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT src/bluetoothd-service.obj -MD -MP -MF src/$(DEPDIR)/bluetoothd-service.Tpo -c -o src/bluetoothd-service.obj `if test -f 'src/service.c'; then $(CYGPATH_W) 'src/service.c'; else $(CYGPATH_W) '$(srcdir)/src/service.c'; fi`
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) src/$(DEPDIR)/bluetoothd-service.Tpo src/$(DEPDIR)/bluetoothd-service.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='src/service.c' object='src/bluetoothd-service.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o src/bluetoothd-service.obj `if test -f 'src/service.c'; then $(CYGPATH_W) 'src/service.c'; else $(CYGPATH_W) '$(srcdir)/src/service.c'; fi`
+
+src/bluetoothd-gatt-dbus.o: src/gatt-dbus.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT src/bluetoothd-gatt-dbus.o -MD -MP -MF src/$(DEPDIR)/bluetoothd-gatt-dbus.Tpo -c -o src/bluetoothd-gatt-dbus.o `test -f 'src/gatt-dbus.c' || echo '$(srcdir)/'`src/gatt-dbus.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) src/$(DEPDIR)/bluetoothd-gatt-dbus.Tpo src/$(DEPDIR)/bluetoothd-gatt-dbus.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='src/gatt-dbus.c' object='src/bluetoothd-gatt-dbus.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o src/bluetoothd-gatt-dbus.o `test -f 'src/gatt-dbus.c' || echo '$(srcdir)/'`src/gatt-dbus.c
+
+src/bluetoothd-gatt-dbus.obj: src/gatt-dbus.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT src/bluetoothd-gatt-dbus.obj -MD -MP -MF src/$(DEPDIR)/bluetoothd-gatt-dbus.Tpo -c -o src/bluetoothd-gatt-dbus.obj `if test -f 'src/gatt-dbus.c'; then $(CYGPATH_W) 'src/gatt-dbus.c'; else $(CYGPATH_W) '$(srcdir)/src/gatt-dbus.c'; fi`
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) src/$(DEPDIR)/bluetoothd-gatt-dbus.Tpo src/$(DEPDIR)/bluetoothd-gatt-dbus.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='src/gatt-dbus.c' object='src/bluetoothd-gatt-dbus.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o src/bluetoothd-gatt-dbus.obj `if test -f 'src/gatt-dbus.c'; then $(CYGPATH_W) 'src/gatt-dbus.c'; else $(CYGPATH_W) '$(srcdir)/src/gatt-dbus.c'; fi`
+
+src/bluetoothd-gatt.o: src/gatt.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT src/bluetoothd-gatt.o -MD -MP -MF src/$(DEPDIR)/bluetoothd-gatt.Tpo -c -o src/bluetoothd-gatt.o `test -f 'src/gatt.c' || echo '$(srcdir)/'`src/gatt.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) src/$(DEPDIR)/bluetoothd-gatt.Tpo src/$(DEPDIR)/bluetoothd-gatt.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='src/gatt.c' object='src/bluetoothd-gatt.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o src/bluetoothd-gatt.o `test -f 'src/gatt.c' || echo '$(srcdir)/'`src/gatt.c
+
+src/bluetoothd-gatt.obj: src/gatt.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT src/bluetoothd-gatt.obj -MD -MP -MF src/$(DEPDIR)/bluetoothd-gatt.Tpo -c -o src/bluetoothd-gatt.obj `if test -f 'src/gatt.c'; then $(CYGPATH_W) 'src/gatt.c'; else $(CYGPATH_W) '$(srcdir)/src/gatt.c'; fi`
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) src/$(DEPDIR)/bluetoothd-gatt.Tpo src/$(DEPDIR)/bluetoothd-gatt.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='src/gatt.c' object='src/bluetoothd-gatt.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o src/bluetoothd-gatt.obj `if test -f 'src/gatt.c'; then $(CYGPATH_W) 'src/gatt.c'; else $(CYGPATH_W) '$(srcdir)/src/gatt.c'; fi`
+
+src/bluetoothd-device.o: src/device.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT src/bluetoothd-device.o -MD -MP -MF src/$(DEPDIR)/bluetoothd-device.Tpo -c -o src/bluetoothd-device.o `test -f 'src/device.c' || echo '$(srcdir)/'`src/device.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) src/$(DEPDIR)/bluetoothd-device.Tpo src/$(DEPDIR)/bluetoothd-device.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='src/device.c' object='src/bluetoothd-device.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o src/bluetoothd-device.o `test -f 'src/device.c' || echo '$(srcdir)/'`src/device.c
+
+src/bluetoothd-device.obj: src/device.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT src/bluetoothd-device.obj -MD -MP -MF src/$(DEPDIR)/bluetoothd-device.Tpo -c -o src/bluetoothd-device.obj `if test -f 'src/device.c'; then $(CYGPATH_W) 'src/device.c'; else $(CYGPATH_W) '$(srcdir)/src/device.c'; fi`
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) src/$(DEPDIR)/bluetoothd-device.Tpo src/$(DEPDIR)/bluetoothd-device.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='src/device.c' object='src/bluetoothd-device.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o src/bluetoothd-device.obj `if test -f 'src/device.c'; then $(CYGPATH_W) 'src/device.c'; else $(CYGPATH_W) '$(srcdir)/src/device.c'; fi`
+
+src/bluetoothd-dbus-common.o: src/dbus-common.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT src/bluetoothd-dbus-common.o -MD -MP -MF src/$(DEPDIR)/bluetoothd-dbus-common.Tpo -c -o src/bluetoothd-dbus-common.o `test -f 'src/dbus-common.c' || echo '$(srcdir)/'`src/dbus-common.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) src/$(DEPDIR)/bluetoothd-dbus-common.Tpo src/$(DEPDIR)/bluetoothd-dbus-common.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='src/dbus-common.c' object='src/bluetoothd-dbus-common.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o src/bluetoothd-dbus-common.o `test -f 'src/dbus-common.c' || echo '$(srcdir)/'`src/dbus-common.c
+
+src/bluetoothd-dbus-common.obj: src/dbus-common.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT src/bluetoothd-dbus-common.obj -MD -MP -MF src/$(DEPDIR)/bluetoothd-dbus-common.Tpo -c -o src/bluetoothd-dbus-common.obj `if test -f 'src/dbus-common.c'; then $(CYGPATH_W) 'src/dbus-common.c'; else $(CYGPATH_W) '$(srcdir)/src/dbus-common.c'; fi`
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) src/$(DEPDIR)/bluetoothd-dbus-common.Tpo src/$(DEPDIR)/bluetoothd-dbus-common.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='src/dbus-common.c' object='src/bluetoothd-dbus-common.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o src/bluetoothd-dbus-common.obj `if test -f 'src/dbus-common.c'; then $(CYGPATH_W) 'src/dbus-common.c'; else $(CYGPATH_W) '$(srcdir)/src/dbus-common.c'; fi`
+
+src/bluetoothd-eir.o: src/eir.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT src/bluetoothd-eir.o -MD -MP -MF src/$(DEPDIR)/bluetoothd-eir.Tpo -c -o src/bluetoothd-eir.o `test -f 'src/eir.c' || echo '$(srcdir)/'`src/eir.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) src/$(DEPDIR)/bluetoothd-eir.Tpo src/$(DEPDIR)/bluetoothd-eir.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='src/eir.c' object='src/bluetoothd-eir.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o src/bluetoothd-eir.o `test -f 'src/eir.c' || echo '$(srcdir)/'`src/eir.c
+
+src/bluetoothd-eir.obj: src/eir.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT src/bluetoothd-eir.obj -MD -MP -MF src/$(DEPDIR)/bluetoothd-eir.Tpo -c -o src/bluetoothd-eir.obj `if test -f 'src/eir.c'; then $(CYGPATH_W) 'src/eir.c'; else $(CYGPATH_W) '$(srcdir)/src/eir.c'; fi`
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) src/$(DEPDIR)/bluetoothd-eir.Tpo src/$(DEPDIR)/bluetoothd-eir.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='src/eir.c' object='src/bluetoothd-eir.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o src/bluetoothd-eir.obj `if test -f 'src/eir.c'; then $(CYGPATH_W) 'src/eir.c'; else $(CYGPATH_W) '$(srcdir)/src/eir.c'; fi`
+
+src/shared/bluetoothd-io-glib.o: src/shared/io-glib.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT src/shared/bluetoothd-io-glib.o -MD -MP -MF src/shared/$(DEPDIR)/bluetoothd-io-glib.Tpo -c -o src/shared/bluetoothd-io-glib.o `test -f 'src/shared/io-glib.c' || echo '$(srcdir)/'`src/shared/io-glib.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) src/shared/$(DEPDIR)/bluetoothd-io-glib.Tpo src/shared/$(DEPDIR)/bluetoothd-io-glib.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='src/shared/io-glib.c' object='src/shared/bluetoothd-io-glib.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o src/shared/bluetoothd-io-glib.o `test -f 'src/shared/io-glib.c' || echo '$(srcdir)/'`src/shared/io-glib.c
+
+src/shared/bluetoothd-io-glib.obj: src/shared/io-glib.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT src/shared/bluetoothd-io-glib.obj -MD -MP -MF src/shared/$(DEPDIR)/bluetoothd-io-glib.Tpo -c -o src/shared/bluetoothd-io-glib.obj `if test -f 'src/shared/io-glib.c'; then $(CYGPATH_W) 'src/shared/io-glib.c'; else $(CYGPATH_W) '$(srcdir)/src/shared/io-glib.c'; fi`
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) src/shared/$(DEPDIR)/bluetoothd-io-glib.Tpo src/shared/$(DEPDIR)/bluetoothd-io-glib.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='src/shared/io-glib.c' object='src/shared/bluetoothd-io-glib.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o src/shared/bluetoothd-io-glib.obj `if test -f 'src/shared/io-glib.c'; then $(CYGPATH_W) 'src/shared/io-glib.c'; else $(CYGPATH_W) '$(srcdir)/src/shared/io-glib.c'; fi`
+
+src/shared/bluetoothd-timeout-glib.o: src/shared/timeout-glib.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT src/shared/bluetoothd-timeout-glib.o -MD -MP -MF src/shared/$(DEPDIR)/bluetoothd-timeout-glib.Tpo -c -o src/shared/bluetoothd-timeout-glib.o `test -f 'src/shared/timeout-glib.c' || echo '$(srcdir)/'`src/shared/timeout-glib.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) src/shared/$(DEPDIR)/bluetoothd-timeout-glib.Tpo src/shared/$(DEPDIR)/bluetoothd-timeout-glib.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='src/shared/timeout-glib.c' object='src/shared/bluetoothd-timeout-glib.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o src/shared/bluetoothd-timeout-glib.o `test -f 'src/shared/timeout-glib.c' || echo '$(srcdir)/'`src/shared/timeout-glib.c
+
+src/shared/bluetoothd-timeout-glib.obj: src/shared/timeout-glib.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT src/shared/bluetoothd-timeout-glib.obj -MD -MP -MF src/shared/$(DEPDIR)/bluetoothd-timeout-glib.Tpo -c -o src/shared/bluetoothd-timeout-glib.obj `if test -f 'src/shared/timeout-glib.c'; then $(CYGPATH_W) 'src/shared/timeout-glib.c'; else $(CYGPATH_W) '$(srcdir)/src/shared/timeout-glib.c'; fi`
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) src/shared/$(DEPDIR)/bluetoothd-timeout-glib.Tpo src/shared/$(DEPDIR)/bluetoothd-timeout-glib.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='src/shared/timeout-glib.c' object='src/shared/bluetoothd-timeout-glib.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o src/shared/bluetoothd-timeout-glib.obj `if test -f 'src/shared/timeout-glib.c'; then $(CYGPATH_W) 'src/shared/timeout-glib.c'; else $(CYGPATH_W) '$(srcdir)/src/shared/timeout-glib.c'; fi`
+
+src/shared/bluetoothd-queue.o: src/shared/queue.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT src/shared/bluetoothd-queue.o -MD -MP -MF src/shared/$(DEPDIR)/bluetoothd-queue.Tpo -c -o src/shared/bluetoothd-queue.o `test -f 'src/shared/queue.c' || echo '$(srcdir)/'`src/shared/queue.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) src/shared/$(DEPDIR)/bluetoothd-queue.Tpo src/shared/$(DEPDIR)/bluetoothd-queue.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='src/shared/queue.c' object='src/shared/bluetoothd-queue.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o src/shared/bluetoothd-queue.o `test -f 'src/shared/queue.c' || echo '$(srcdir)/'`src/shared/queue.c
+
+src/shared/bluetoothd-queue.obj: src/shared/queue.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT src/shared/bluetoothd-queue.obj -MD -MP -MF src/shared/$(DEPDIR)/bluetoothd-queue.Tpo -c -o src/shared/bluetoothd-queue.obj `if test -f 'src/shared/queue.c'; then $(CYGPATH_W) 'src/shared/queue.c'; else $(CYGPATH_W) '$(srcdir)/src/shared/queue.c'; fi`
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) src/shared/$(DEPDIR)/bluetoothd-queue.Tpo src/shared/$(DEPDIR)/bluetoothd-queue.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='src/shared/queue.c' object='src/shared/bluetoothd-queue.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o src/shared/bluetoothd-queue.obj `if test -f 'src/shared/queue.c'; then $(CYGPATH_W) 'src/shared/queue.c'; else $(CYGPATH_W) '$(srcdir)/src/shared/queue.c'; fi`
+
+src/shared/bluetoothd-util.o: src/shared/util.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT src/shared/bluetoothd-util.o -MD -MP -MF src/shared/$(DEPDIR)/bluetoothd-util.Tpo -c -o src/shared/bluetoothd-util.o `test -f 'src/shared/util.c' || echo '$(srcdir)/'`src/shared/util.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) src/shared/$(DEPDIR)/bluetoothd-util.Tpo src/shared/$(DEPDIR)/bluetoothd-util.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='src/shared/util.c' object='src/shared/bluetoothd-util.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o src/shared/bluetoothd-util.o `test -f 'src/shared/util.c' || echo '$(srcdir)/'`src/shared/util.c
+
+src/shared/bluetoothd-util.obj: src/shared/util.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT src/shared/bluetoothd-util.obj -MD -MP -MF src/shared/$(DEPDIR)/bluetoothd-util.Tpo -c -o src/shared/bluetoothd-util.obj `if test -f 'src/shared/util.c'; then $(CYGPATH_W) 'src/shared/util.c'; else $(CYGPATH_W) '$(srcdir)/src/shared/util.c'; fi`
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) src/shared/$(DEPDIR)/bluetoothd-util.Tpo src/shared/$(DEPDIR)/bluetoothd-util.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='src/shared/util.c' object='src/shared/bluetoothd-util.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o src/shared/bluetoothd-util.obj `if test -f 'src/shared/util.c'; then $(CYGPATH_W) 'src/shared/util.c'; else $(CYGPATH_W) '$(srcdir)/src/shared/util.c'; fi`
+
+src/shared/bluetoothd-mgmt.o: src/shared/mgmt.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT src/shared/bluetoothd-mgmt.o -MD -MP -MF src/shared/$(DEPDIR)/bluetoothd-mgmt.Tpo -c -o src/shared/bluetoothd-mgmt.o `test -f 'src/shared/mgmt.c' || echo '$(srcdir)/'`src/shared/mgmt.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) src/shared/$(DEPDIR)/bluetoothd-mgmt.Tpo src/shared/$(DEPDIR)/bluetoothd-mgmt.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='src/shared/mgmt.c' object='src/shared/bluetoothd-mgmt.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o src/shared/bluetoothd-mgmt.o `test -f 'src/shared/mgmt.c' || echo '$(srcdir)/'`src/shared/mgmt.c
+
+src/shared/bluetoothd-mgmt.obj: src/shared/mgmt.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT src/shared/bluetoothd-mgmt.obj -MD -MP -MF src/shared/$(DEPDIR)/bluetoothd-mgmt.Tpo -c -o src/shared/bluetoothd-mgmt.obj `if test -f 'src/shared/mgmt.c'; then $(CYGPATH_W) 'src/shared/mgmt.c'; else $(CYGPATH_W) '$(srcdir)/src/shared/mgmt.c'; fi`
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) src/shared/$(DEPDIR)/bluetoothd-mgmt.Tpo src/shared/$(DEPDIR)/bluetoothd-mgmt.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='src/shared/mgmt.c' object='src/shared/bluetoothd-mgmt.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o src/shared/bluetoothd-mgmt.obj `if test -f 'src/shared/mgmt.c'; then $(CYGPATH_W) 'src/shared/mgmt.c'; else $(CYGPATH_W) '$(srcdir)/src/shared/mgmt.c'; fi`
+
+mostlyclean-libtool:
+	-rm -f *.lo
+
+clean-libtool:
+	-rm -rf .libs _libs
+	-rm -rf android/.libs android/_libs
+	-rm -rf attrib/.libs attrib/_libs
+	-rm -rf client/.libs client/_libs
+	-rm -rf emulator/.libs emulator/_libs
+	-rm -rf gdbus/.libs gdbus/_libs
+	-rm -rf lib/.libs lib/_libs
+	-rm -rf monitor/.libs monitor/_libs
+	-rm -rf obexd/src/.libs obexd/src/_libs
+	-rm -rf plugins/.libs plugins/_libs
+	-rm -rf profiles/cups/.libs profiles/cups/_libs
+	-rm -rf profiles/iap/.libs profiles/iap/_libs
+	-rm -rf src/.libs src/_libs
+	-rm -rf tools/.libs tools/_libs
+	-rm -rf unit/.libs unit/_libs
+
+distclean-libtool:
+	-rm -f libtool config.lt
+install-man1: $(dist_man_MANS) $(man_MANS)
+	@$(NORMAL_INSTALL)
+	@list1=''; \
+	list2='$(dist_man_MANS) $(man_MANS)'; \
+	test -n "$(man1dir)" \
+	  && test -n "`echo $$list1$$list2`" \
+	  || exit 0; \
+	echo " $(MKDIR_P) '$(DESTDIR)$(man1dir)'"; \
+	$(MKDIR_P) "$(DESTDIR)$(man1dir)" || exit 1; \
+	{ for i in $$list1; do echo "$$i"; done;  \
+	if test -n "$$list2"; then \
+	  for i in $$list2; do echo "$$i"; done \
+	    | sed -n '/\.1[a-z]*$$/p'; \
+	fi; \
+	} | while read p; do \
+	  if test -f $$p; then d=; else d="$(srcdir)/"; fi; \
+	  echo "$$d$$p"; echo "$$p"; \
+	done | \
+	sed -e 'n;s,.*/,,;p;h;s,.*\.,,;s,^[^1][0-9a-z]*$$,1,;x' \
+	      -e 's,\.[0-9a-z]*$$,,;$(transform);G;s,\n,.,' | \
+	sed 'N;N;s,\n, ,g' | { \
+	list=; while read file base inst; do \
+	  if test "$$base" = "$$inst"; then list="$$list $$file"; else \
+	    echo " $(INSTALL_DATA) '$$file' '$(DESTDIR)$(man1dir)/$$inst'"; \
+	    $(INSTALL_DATA) "$$file" "$(DESTDIR)$(man1dir)/$$inst" || exit $$?; \
+	  fi; \
+	done; \
+	for i in $$list; do echo "$$i"; done | $(am__base_list) | \
+	while read files; do \
+	  test -z "$$files" || { \
+	    echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(man1dir)'"; \
+	    $(INSTALL_DATA) $$files "$(DESTDIR)$(man1dir)" || exit $$?; }; \
+	done; }
+
+uninstall-man1:
+	@$(NORMAL_UNINSTALL)
+	@list=''; test -n "$(man1dir)" || exit 0; \
+	files=`{ for i in $$list; do echo "$$i"; done; \
+	l2='$(dist_man_MANS) $(man_MANS)'; for i in $$l2; do echo "$$i"; done | \
+	  sed -n '/\.1[a-z]*$$/p'; \
+	} | sed -e 's,.*/,,;h;s,.*\.,,;s,^[^1][0-9a-z]*$$,1,;x' \
+	      -e 's,\.[0-9a-z]*$$,,;$(transform);G;s,\n,.,'`; \
+	dir='$(DESTDIR)$(man1dir)'; $(am__uninstall_files_from_dir)
+install-man8: $(dist_man_MANS) $(man_MANS)
+	@$(NORMAL_INSTALL)
+	@list1=''; \
+	list2='$(dist_man_MANS) $(man_MANS)'; \
+	test -n "$(man8dir)" \
+	  && test -n "`echo $$list1$$list2`" \
+	  || exit 0; \
+	echo " $(MKDIR_P) '$(DESTDIR)$(man8dir)'"; \
+	$(MKDIR_P) "$(DESTDIR)$(man8dir)" || exit 1; \
+	{ for i in $$list1; do echo "$$i"; done;  \
+	if test -n "$$list2"; then \
+	  for i in $$list2; do echo "$$i"; done \
+	    | sed -n '/\.8[a-z]*$$/p'; \
+	fi; \
+	} | while read p; do \
+	  if test -f $$p; then d=; else d="$(srcdir)/"; fi; \
+	  echo "$$d$$p"; echo "$$p"; \
+	done | \
+	sed -e 'n;s,.*/,,;p;h;s,.*\.,,;s,^[^8][0-9a-z]*$$,8,;x' \
+	      -e 's,\.[0-9a-z]*$$,,;$(transform);G;s,\n,.,' | \
+	sed 'N;N;s,\n, ,g' | { \
+	list=; while read file base inst; do \
+	  if test "$$base" = "$$inst"; then list="$$list $$file"; else \
+	    echo " $(INSTALL_DATA) '$$file' '$(DESTDIR)$(man8dir)/$$inst'"; \
+	    $(INSTALL_DATA) "$$file" "$(DESTDIR)$(man8dir)/$$inst" || exit $$?; \
+	  fi; \
+	done; \
+	for i in $$list; do echo "$$i"; done | $(am__base_list) | \
+	while read files; do \
+	  test -z "$$files" || { \
+	    echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(man8dir)'"; \
+	    $(INSTALL_DATA) $$files "$(DESTDIR)$(man8dir)" || exit $$?; }; \
+	done; }
+
+uninstall-man8:
+	@$(NORMAL_UNINSTALL)
+	@list=''; test -n "$(man8dir)" || exit 0; \
+	files=`{ for i in $$list; do echo "$$i"; done; \
+	l2='$(dist_man_MANS) $(man_MANS)'; for i in $$l2; do echo "$$i"; done | \
+	  sed -n '/\.8[a-z]*$$/p'; \
+	} | sed -e 's,.*/,,;h;s,.*\.,,;s,^[^8][0-9a-z]*$$,8,;x' \
+	      -e 's,\.[0-9a-z]*$$,,;$(transform);G;s,\n,.,'`; \
+	dir='$(DESTDIR)$(man8dir)'; $(am__uninstall_files_from_dir)
+install-confDATA: $(conf_DATA)
+	@$(NORMAL_INSTALL)
+	@list='$(conf_DATA)'; test -n "$(confdir)" || list=; \
+	if test -n "$$list"; then \
+	  echo " $(MKDIR_P) '$(DESTDIR)$(confdir)'"; \
+	  $(MKDIR_P) "$(DESTDIR)$(confdir)" || exit 1; \
+	fi; \
+	for p in $$list; do \
+	  if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \
+	  echo "$$d$$p"; \
+	done | $(am__base_list) | \
+	while read files; do \
+	  echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(confdir)'"; \
+	  $(INSTALL_DATA) $$files "$(DESTDIR)$(confdir)" || exit $$?; \
+	done
+
+uninstall-confDATA:
+	@$(NORMAL_UNINSTALL)
+	@list='$(conf_DATA)'; test -n "$(confdir)" || list=; \
+	files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \
+	dir='$(DESTDIR)$(confdir)'; $(am__uninstall_files_from_dir)
+install-dbusDATA: $(dbus_DATA)
+	@$(NORMAL_INSTALL)
+	@list='$(dbus_DATA)'; test -n "$(dbusdir)" || list=; \
+	if test -n "$$list"; then \
+	  echo " $(MKDIR_P) '$(DESTDIR)$(dbusdir)'"; \
+	  $(MKDIR_P) "$(DESTDIR)$(dbusdir)" || exit 1; \
+	fi; \
+	for p in $$list; do \
+	  if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \
+	  echo "$$d$$p"; \
+	done | $(am__base_list) | \
+	while read files; do \
+	  echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(dbusdir)'"; \
+	  $(INSTALL_DATA) $$files "$(DESTDIR)$(dbusdir)" || exit $$?; \
+	done
+
+uninstall-dbusDATA:
+	@$(NORMAL_UNINSTALL)
+	@list='$(dbus_DATA)'; test -n "$(dbusdir)" || list=; \
+	files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \
+	dir='$(DESTDIR)$(dbusdir)'; $(am__uninstall_files_from_dir)
+install-dbussessionbusDATA: $(dbussessionbus_DATA)
+	@$(NORMAL_INSTALL)
+	@list='$(dbussessionbus_DATA)'; test -n "$(dbussessionbusdir)" || list=; \
+	if test -n "$$list"; then \
+	  echo " $(MKDIR_P) '$(DESTDIR)$(dbussessionbusdir)'"; \
+	  $(MKDIR_P) "$(DESTDIR)$(dbussessionbusdir)" || exit 1; \
+	fi; \
+	for p in $$list; do \
+	  if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \
+	  echo "$$d$$p"; \
+	done | $(am__base_list) | \
+	while read files; do \
+	  echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(dbussessionbusdir)'"; \
+	  $(INSTALL_DATA) $$files "$(DESTDIR)$(dbussessionbusdir)" || exit $$?; \
+	done
+
+uninstall-dbussessionbusDATA:
+	@$(NORMAL_UNINSTALL)
+	@list='$(dbussessionbus_DATA)'; test -n "$(dbussessionbusdir)" || list=; \
+	files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \
+	dir='$(DESTDIR)$(dbussessionbusdir)'; $(am__uninstall_files_from_dir)
+install-dbussystembusDATA: $(dbussystembus_DATA)
+	@$(NORMAL_INSTALL)
+	@list='$(dbussystembus_DATA)'; test -n "$(dbussystembusdir)" || list=; \
+	if test -n "$$list"; then \
+	  echo " $(MKDIR_P) '$(DESTDIR)$(dbussystembusdir)'"; \
+	  $(MKDIR_P) "$(DESTDIR)$(dbussystembusdir)" || exit 1; \
+	fi; \
+	for p in $$list; do \
+	  if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \
+	  echo "$$d$$p"; \
+	done | $(am__base_list) | \
+	while read files; do \
+	  echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(dbussystembusdir)'"; \
+	  $(INSTALL_DATA) $$files "$(DESTDIR)$(dbussystembusdir)" || exit $$?; \
+	done
+
+uninstall-dbussystembusDATA:
+	@$(NORMAL_UNINSTALL)
+	@list='$(dbussystembus_DATA)'; test -n "$(dbussystembusdir)" || list=; \
+	files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \
+	dir='$(DESTDIR)$(dbussystembusdir)'; $(am__uninstall_files_from_dir)
+install-pkgconfigDATA: $(pkgconfig_DATA)
+	@$(NORMAL_INSTALL)
+	@list='$(pkgconfig_DATA)'; test -n "$(pkgconfigdir)" || list=; \
+	if test -n "$$list"; then \
+	  echo " $(MKDIR_P) '$(DESTDIR)$(pkgconfigdir)'"; \
+	  $(MKDIR_P) "$(DESTDIR)$(pkgconfigdir)" || exit 1; \
+	fi; \
+	for p in $$list; do \
+	  if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \
+	  echo "$$d$$p"; \
+	done | $(am__base_list) | \
+	while read files; do \
+	  echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(pkgconfigdir)'"; \
+	  $(INSTALL_DATA) $$files "$(DESTDIR)$(pkgconfigdir)" || exit $$?; \
+	done
+
+uninstall-pkgconfigDATA:
+	@$(NORMAL_UNINSTALL)
+	@list='$(pkgconfig_DATA)'; test -n "$(pkgconfigdir)" || list=; \
+	files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \
+	dir='$(DESTDIR)$(pkgconfigdir)'; $(am__uninstall_files_from_dir)
+install-rulesDATA: $(rules_DATA)
+	@$(NORMAL_INSTALL)
+	@list='$(rules_DATA)'; test -n "$(rulesdir)" || list=; \
+	if test -n "$$list"; then \
+	  echo " $(MKDIR_P) '$(DESTDIR)$(rulesdir)'"; \
+	  $(MKDIR_P) "$(DESTDIR)$(rulesdir)" || exit 1; \
+	fi; \
+	for p in $$list; do \
+	  if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \
+	  echo "$$d$$p"; \
+	done | $(am__base_list) | \
+	while read files; do \
+	  echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(rulesdir)'"; \
+	  $(INSTALL_DATA) $$files "$(DESTDIR)$(rulesdir)" || exit $$?; \
+	done
+
+uninstall-rulesDATA:
+	@$(NORMAL_UNINSTALL)
+	@list='$(rules_DATA)'; test -n "$(rulesdir)" || list=; \
+	files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \
+	dir='$(DESTDIR)$(rulesdir)'; $(am__uninstall_files_from_dir)
+install-stateDATA: $(state_DATA)
+	@$(NORMAL_INSTALL)
+	@list='$(state_DATA)'; test -n "$(statedir)" || list=; \
+	if test -n "$$list"; then \
+	  echo " $(MKDIR_P) '$(DESTDIR)$(statedir)'"; \
+	  $(MKDIR_P) "$(DESTDIR)$(statedir)" || exit 1; \
+	fi; \
+	for p in $$list; do \
+	  if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \
+	  echo "$$d$$p"; \
+	done | $(am__base_list) | \
+	while read files; do \
+	  echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(statedir)'"; \
+	  $(INSTALL_DATA) $$files "$(DESTDIR)$(statedir)" || exit $$?; \
+	done
+
+uninstall-stateDATA:
+	@$(NORMAL_UNINSTALL)
+	@list='$(state_DATA)'; test -n "$(statedir)" || list=; \
+	files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \
+	dir='$(DESTDIR)$(statedir)'; $(am__uninstall_files_from_dir)
+install-systemdsystemunitDATA: $(systemdsystemunit_DATA)
+	@$(NORMAL_INSTALL)
+	@list='$(systemdsystemunit_DATA)'; test -n "$(systemdsystemunitdir)" || list=; \
+	if test -n "$$list"; then \
+	  echo " $(MKDIR_P) '$(DESTDIR)$(systemdsystemunitdir)'"; \
+	  $(MKDIR_P) "$(DESTDIR)$(systemdsystemunitdir)" || exit 1; \
+	fi; \
+	for p in $$list; do \
+	  if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \
+	  echo "$$d$$p"; \
+	done | $(am__base_list) | \
+	while read files; do \
+	  echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(systemdsystemunitdir)'"; \
+	  $(INSTALL_DATA) $$files "$(DESTDIR)$(systemdsystemunitdir)" || exit $$?; \
+	done
+
+uninstall-systemdsystemunitDATA:
+	@$(NORMAL_UNINSTALL)
+	@list='$(systemdsystemunit_DATA)'; test -n "$(systemdsystemunitdir)" || list=; \
+	files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \
+	dir='$(DESTDIR)$(systemdsystemunitdir)'; $(am__uninstall_files_from_dir)
+install-systemduserunitDATA: $(systemduserunit_DATA)
+	@$(NORMAL_INSTALL)
+	@list='$(systemduserunit_DATA)'; test -n "$(systemduserunitdir)" || list=; \
+	if test -n "$$list"; then \
+	  echo " $(MKDIR_P) '$(DESTDIR)$(systemduserunitdir)'"; \
+	  $(MKDIR_P) "$(DESTDIR)$(systemduserunitdir)" || exit 1; \
+	fi; \
+	for p in $$list; do \
+	  if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \
+	  echo "$$d$$p"; \
+	done | $(am__base_list) | \
+	while read files; do \
+	  echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(systemduserunitdir)'"; \
+	  $(INSTALL_DATA) $$files "$(DESTDIR)$(systemduserunitdir)" || exit $$?; \
+	done
+
+uninstall-systemduserunitDATA:
+	@$(NORMAL_UNINSTALL)
+	@list='$(systemduserunit_DATA)'; test -n "$(systemduserunitdir)" || list=; \
+	files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \
+	dir='$(DESTDIR)$(systemduserunitdir)'; $(am__uninstall_files_from_dir)
+install-includeHEADERS: $(include_HEADERS)
+	@$(NORMAL_INSTALL)
+	@list='$(include_HEADERS)'; test -n "$(includedir)" || list=; \
+	if test -n "$$list"; then \
+	  echo " $(MKDIR_P) '$(DESTDIR)$(includedir)'"; \
+	  $(MKDIR_P) "$(DESTDIR)$(includedir)" || exit 1; \
+	fi; \
+	for p in $$list; do \
+	  if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \
+	  echo "$$d$$p"; \
+	done | $(am__base_list) | \
+	while read files; do \
+	  echo " $(INSTALL_HEADER) $$files '$(DESTDIR)$(includedir)'"; \
+	  $(INSTALL_HEADER) $$files "$(DESTDIR)$(includedir)" || exit $$?; \
+	done
+
+uninstall-includeHEADERS:
+	@$(NORMAL_UNINSTALL)
+	@list='$(include_HEADERS)'; test -n "$(includedir)" || list=; \
+	files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \
+	dir='$(DESTDIR)$(includedir)'; $(am__uninstall_files_from_dir)
+
+ID: $(am__tagged_files)
+	$(am__define_uniq_tagged_files); mkid -fID $$unique
+tags: tags-am
+TAGS: tags
+
+tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+	set x; \
+	here=`pwd`; \
+	$(am__define_uniq_tagged_files); \
+	shift; \
+	if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \
+	  test -n "$$unique" || unique=$$empty_fix; \
+	  if test $$# -gt 0; then \
+	    $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+	      "$$@" $$unique; \
+	  else \
+	    $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+	      $$unique; \
+	  fi; \
+	fi
+ctags: ctags-am
+
+CTAGS: ctags
+ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+	$(am__define_uniq_tagged_files); \
+	test -z "$(CTAGS_ARGS)$$unique" \
+	  || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
+	     $$unique
+
+GTAGS:
+	here=`$(am__cd) $(top_builddir) && pwd` \
+	  && $(am__cd) $(top_srcdir) \
+	  && gtags -i $(GTAGS_ARGS) "$$here"
+cscope: cscope.files
+	test ! -s cscope.files \
+	  || $(CSCOPE) -b -q $(AM_CSCOPEFLAGS) $(CSCOPEFLAGS) -i cscope.files $(CSCOPE_ARGS)
+clean-cscope:
+	-rm -f cscope.files
+cscope.files: clean-cscope cscopelist
+cscopelist: cscopelist-am
+
+cscopelist-am: $(am__tagged_files)
+	list='$(am__tagged_files)'; \
+	case "$(srcdir)" in \
+	  [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \
+	  *) sdir=$(subdir)/$(srcdir) ;; \
+	esac; \
+	for i in $$list; do \
+	  if test -f "$$i"; then \
+	    echo "$(subdir)/$$i"; \
+	  else \
+	    echo "$$sdir/$$i"; \
+	  fi; \
+	done >> $(top_builddir)/cscope.files
+
+distclean-tags:
+	-rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
+	-rm -f cscope.out cscope.in.out cscope.po.out cscope.files
+
+# Recover from deleted '.trs' file; this should ensure that
+# "rm -f foo.log; make foo.trs" re-run 'foo.test', and re-create
+# both 'foo.log' and 'foo.trs'.  Break the recipe in two subshells
+# to avoid problems with "make -n".
+.log.trs:
+	rm -f $< $@
+	$(MAKE) $(AM_MAKEFLAGS) $<
+
+# Leading 'am--fnord' is there to ensure the list of targets does not
+# expand to empty, as could happen e.g. with make check TESTS=''.
+am--fnord $(TEST_LOGS) $(TEST_LOGS:.log=.trs): $(am__force_recheck)
+am--force-recheck:
+	@:
+
+$(TEST_SUITE_LOG): $(TEST_LOGS)
+	@$(am__set_TESTS_bases); \
+	am__f_ok () { test -f "$$1" && test -r "$$1"; }; \
+	redo_bases=`for i in $$bases; do \
+	              am__f_ok $$i.trs && am__f_ok $$i.log || echo $$i; \
+	            done`; \
+	if test -n "$$redo_bases"; then \
+	  redo_logs=`for i in $$redo_bases; do echo $$i.log; done`; \
+	  redo_results=`for i in $$redo_bases; do echo $$i.trs; done`; \
+	  if $(am__make_dryrun); then :; else \
+	    rm -f $$redo_logs && rm -f $$redo_results || exit 1; \
+	  fi; \
+	fi; \
+	if test -n "$$am__remaking_logs"; then \
+	  echo "fatal: making $(TEST_SUITE_LOG): possible infinite" \
+	       "recursion detected" >&2; \
+	else \
+	  am__remaking_logs=yes $(MAKE) $(AM_MAKEFLAGS) $$redo_logs; \
+	fi; \
+	if $(am__make_dryrun); then :; else \
+	  st=0;  \
+	  errmsg="fatal: making $(TEST_SUITE_LOG): failed to create"; \
+	  for i in $$redo_bases; do \
+	    test -f $$i.trs && test -r $$i.trs \
+	      || { echo "$$errmsg $$i.trs" >&2; st=1; }; \
+	    test -f $$i.log && test -r $$i.log \
+	      || { echo "$$errmsg $$i.log" >&2; st=1; }; \
+	  done; \
+	  test $$st -eq 0 || exit 1; \
+	fi
+	@$(am__sh_e_setup); $(am__tty_colors); $(am__set_TESTS_bases); \
+	ws='[ 	]'; \
+	results=`for b in $$bases; do echo $$b.trs; done`; \
+	test -n "$$results" || results=/dev/null; \
+	all=`  grep "^$$ws*:test-result:"           $$results | wc -l`; \
+	pass=` grep "^$$ws*:test-result:$$ws*PASS"  $$results | wc -l`; \
+	fail=` grep "^$$ws*:test-result:$$ws*FAIL"  $$results | wc -l`; \
+	skip=` grep "^$$ws*:test-result:$$ws*SKIP"  $$results | wc -l`; \
+	xfail=`grep "^$$ws*:test-result:$$ws*XFAIL" $$results | wc -l`; \
+	xpass=`grep "^$$ws*:test-result:$$ws*XPASS" $$results | wc -l`; \
+	error=`grep "^$$ws*:test-result:$$ws*ERROR" $$results | wc -l`; \
+	if test `expr $$fail + $$xpass + $$error` -eq 0; then \
+	  success=true; \
+	else \
+	  success=false; \
+	fi; \
+	br='==================='; br=$$br$$br$$br$$br; \
+	result_count () \
+	{ \
+	    if test x"$$1" = x"--maybe-color"; then \
+	      maybe_colorize=yes; \
+	    elif test x"$$1" = x"--no-color"; then \
+	      maybe_colorize=no; \
+	    else \
+	      echo "$@: invalid 'result_count' usage" >&2; exit 4; \
+	    fi; \
+	    shift; \
+	    desc=$$1 count=$$2; \
+	    if test $$maybe_colorize = yes && test $$count -gt 0; then \
+	      color_start=$$3 color_end=$$std; \
+	    else \
+	      color_start= color_end=; \
+	    fi; \
+	    echo "$${color_start}# $$desc $$count$${color_end}"; \
+	}; \
+	create_testsuite_report () \
+	{ \
+	  result_count $$1 "TOTAL:" $$all   "$$brg"; \
+	  result_count $$1 "PASS: " $$pass  "$$grn"; \
+	  result_count $$1 "SKIP: " $$skip  "$$blu"; \
+	  result_count $$1 "XFAIL:" $$xfail "$$lgn"; \
+	  result_count $$1 "FAIL: " $$fail  "$$red"; \
+	  result_count $$1 "XPASS:" $$xpass "$$red"; \
+	  result_count $$1 "ERROR:" $$error "$$mgn"; \
+	}; \
+	{								\
+	  echo "$(PACKAGE_STRING): $(subdir)/$(TEST_SUITE_LOG)" |	\
+	    $(am__rst_title);						\
+	  create_testsuite_report --no-color;				\
+	  echo;								\
+	  echo ".. contents:: :depth: 2";				\
+	  echo;								\
+	  for b in $$bases; do echo $$b; done				\
+	    | $(am__create_global_log);					\
+	} >$(TEST_SUITE_LOG).tmp || exit 1;				\
+	mv $(TEST_SUITE_LOG).tmp $(TEST_SUITE_LOG);			\
+	if $$success; then						\
+	  col="$$grn";							\
+	 else								\
+	  col="$$red";							\
+	  test x"$$VERBOSE" = x || cat $(TEST_SUITE_LOG);		\
+	fi;								\
+	echo "$${col}$$br$${std}"; 					\
+	echo "$${col}Testsuite summary for $(PACKAGE_STRING)$${std}";	\
+	echo "$${col}$$br$${std}"; 					\
+	create_testsuite_report --maybe-color;				\
+	echo "$$col$$br$$std";						\
+	if $$success; then :; else					\
+	  echo "$${col}See $(subdir)/$(TEST_SUITE_LOG)$${std}";		\
+	  if test -n "$(PACKAGE_BUGREPORT)"; then			\
+	    echo "$${col}Please report to $(PACKAGE_BUGREPORT)$${std}";	\
+	  fi;								\
+	  echo "$$col$$br$$std";					\
+	fi;								\
+	$$success || exit 1
+
+check-TESTS:
+	@list='$(RECHECK_LOGS)';           test -z "$$list" || rm -f $$list
+	@list='$(RECHECK_LOGS:.log=.trs)'; test -z "$$list" || rm -f $$list
+	@test -z "$(TEST_SUITE_LOG)" || rm -f $(TEST_SUITE_LOG)
+	@set +e; $(am__set_TESTS_bases); \
+	log_list=`for i in $$bases; do echo $$i.log; done`; \
+	trs_list=`for i in $$bases; do echo $$i.trs; done`; \
+	log_list=`echo $$log_list`; trs_list=`echo $$trs_list`; \
+	$(MAKE) $(AM_MAKEFLAGS) $(TEST_SUITE_LOG) TEST_LOGS="$$log_list"; \
+	exit $$?;
+recheck: all 
+	@test -z "$(TEST_SUITE_LOG)" || rm -f $(TEST_SUITE_LOG)
+	@set +e; $(am__set_TESTS_bases); \
+	bases=`for i in $$bases; do echo $$i; done \
+	         | $(am__list_recheck_tests)` || exit 1; \
+	log_list=`for i in $$bases; do echo $$i.log; done`; \
+	log_list=`echo $$log_list`; \
+	$(MAKE) $(AM_MAKEFLAGS) $(TEST_SUITE_LOG) \
+	        am__force_recheck=am--force-recheck \
+	        TEST_LOGS="$$log_list"; \
+	exit $$?
+android/test-ipc.log: android/test-ipc$(EXEEXT)
+	@p='android/test-ipc$(EXEEXT)'; \
+	b='android/test-ipc'; \
+	$(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \
+	--log-file $$b.log --trs-file $$b.trs \
+	$(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \
+	"$$tst" $(AM_TESTS_FD_REDIRECT)
+unit/test-eir.log: unit/test-eir$(EXEEXT)
+	@p='unit/test-eir$(EXEEXT)'; \
+	b='unit/test-eir'; \
+	$(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \
+	--log-file $$b.log --trs-file $$b.trs \
+	$(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \
+	"$$tst" $(AM_TESTS_FD_REDIRECT)
+unit/test-uuid.log: unit/test-uuid$(EXEEXT)
+	@p='unit/test-uuid$(EXEEXT)'; \
+	b='unit/test-uuid'; \
+	$(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \
+	--log-file $$b.log --trs-file $$b.trs \
+	$(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \
+	"$$tst" $(AM_TESTS_FD_REDIRECT)
+unit/test-textfile.log: unit/test-textfile$(EXEEXT)
+	@p='unit/test-textfile$(EXEEXT)'; \
+	b='unit/test-textfile'; \
+	$(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \
+	--log-file $$b.log --trs-file $$b.trs \
+	$(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \
+	"$$tst" $(AM_TESTS_FD_REDIRECT)
+unit/test-crc.log: unit/test-crc$(EXEEXT)
+	@p='unit/test-crc$(EXEEXT)'; \
+	b='unit/test-crc'; \
+	$(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \
+	--log-file $$b.log --trs-file $$b.trs \
+	$(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \
+	"$$tst" $(AM_TESTS_FD_REDIRECT)
+unit/test-ringbuf.log: unit/test-ringbuf$(EXEEXT)
+	@p='unit/test-ringbuf$(EXEEXT)'; \
+	b='unit/test-ringbuf'; \
+	$(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \
+	--log-file $$b.log --trs-file $$b.trs \
+	$(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \
+	"$$tst" $(AM_TESTS_FD_REDIRECT)
+unit/test-queue.log: unit/test-queue$(EXEEXT)
+	@p='unit/test-queue$(EXEEXT)'; \
+	b='unit/test-queue'; \
+	$(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \
+	--log-file $$b.log --trs-file $$b.trs \
+	$(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \
+	"$$tst" $(AM_TESTS_FD_REDIRECT)
+unit/test-mgmt.log: unit/test-mgmt$(EXEEXT)
+	@p='unit/test-mgmt$(EXEEXT)'; \
+	b='unit/test-mgmt'; \
+	$(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \
+	--log-file $$b.log --trs-file $$b.trs \
+	$(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \
+	"$$tst" $(AM_TESTS_FD_REDIRECT)
+unit/test-sdp.log: unit/test-sdp$(EXEEXT)
+	@p='unit/test-sdp$(EXEEXT)'; \
+	b='unit/test-sdp'; \
+	$(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \
+	--log-file $$b.log --trs-file $$b.trs \
+	$(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \
+	"$$tst" $(AM_TESTS_FD_REDIRECT)
+unit/test-avdtp.log: unit/test-avdtp$(EXEEXT)
+	@p='unit/test-avdtp$(EXEEXT)'; \
+	b='unit/test-avdtp'; \
+	$(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \
+	--log-file $$b.log --trs-file $$b.trs \
+	$(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \
+	"$$tst" $(AM_TESTS_FD_REDIRECT)
+unit/test-avctp.log: unit/test-avctp$(EXEEXT)
+	@p='unit/test-avctp$(EXEEXT)'; \
+	b='unit/test-avctp'; \
+	$(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \
+	--log-file $$b.log --trs-file $$b.trs \
+	$(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \
+	"$$tst" $(AM_TESTS_FD_REDIRECT)
+unit/test-avrcp.log: unit/test-avrcp$(EXEEXT)
+	@p='unit/test-avrcp$(EXEEXT)'; \
+	b='unit/test-avrcp'; \
+	$(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \
+	--log-file $$b.log --trs-file $$b.trs \
+	$(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \
+	"$$tst" $(AM_TESTS_FD_REDIRECT)
+unit/test-hfp.log: unit/test-hfp$(EXEEXT)
+	@p='unit/test-hfp$(EXEEXT)'; \
+	b='unit/test-hfp'; \
+	$(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \
+	--log-file $$b.log --trs-file $$b.trs \
+	$(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \
+	"$$tst" $(AM_TESTS_FD_REDIRECT)
+unit/test-gdbus-client.log: unit/test-gdbus-client$(EXEEXT)
+	@p='unit/test-gdbus-client$(EXEEXT)'; \
+	b='unit/test-gdbus-client'; \
+	$(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \
+	--log-file $$b.log --trs-file $$b.trs \
+	$(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \
+	"$$tst" $(AM_TESTS_FD_REDIRECT)
+unit/test-gobex-header.log: unit/test-gobex-header$(EXEEXT)
+	@p='unit/test-gobex-header$(EXEEXT)'; \
+	b='unit/test-gobex-header'; \
+	$(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \
+	--log-file $$b.log --trs-file $$b.trs \
+	$(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \
+	"$$tst" $(AM_TESTS_FD_REDIRECT)
+unit/test-gobex-packet.log: unit/test-gobex-packet$(EXEEXT)
+	@p='unit/test-gobex-packet$(EXEEXT)'; \
+	b='unit/test-gobex-packet'; \
+	$(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \
+	--log-file $$b.log --trs-file $$b.trs \
+	$(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \
+	"$$tst" $(AM_TESTS_FD_REDIRECT)
+unit/test-gobex.log: unit/test-gobex$(EXEEXT)
+	@p='unit/test-gobex$(EXEEXT)'; \
+	b='unit/test-gobex'; \
+	$(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \
+	--log-file $$b.log --trs-file $$b.trs \
+	$(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \
+	"$$tst" $(AM_TESTS_FD_REDIRECT)
+unit/test-gobex-transfer.log: unit/test-gobex-transfer$(EXEEXT)
+	@p='unit/test-gobex-transfer$(EXEEXT)'; \
+	b='unit/test-gobex-transfer'; \
+	$(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \
+	--log-file $$b.log --trs-file $$b.trs \
+	$(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \
+	"$$tst" $(AM_TESTS_FD_REDIRECT)
+unit/test-gobex-apparam.log: unit/test-gobex-apparam$(EXEEXT)
+	@p='unit/test-gobex-apparam$(EXEEXT)'; \
+	b='unit/test-gobex-apparam'; \
+	$(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \
+	--log-file $$b.log --trs-file $$b.trs \
+	$(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \
+	"$$tst" $(AM_TESTS_FD_REDIRECT)
+unit/test-lib.log: unit/test-lib$(EXEEXT)
+	@p='unit/test-lib$(EXEEXT)'; \
+	b='unit/test-lib'; \
+	$(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \
+	--log-file $$b.log --trs-file $$b.trs \
+	$(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \
+	"$$tst" $(AM_TESTS_FD_REDIRECT)
+.test.log:
+	@p='$<'; \
+	$(am__set_b); \
+	$(am__check_pre) $(TEST_LOG_DRIVER) --test-name "$$f" \
+	--log-file $$b.log --trs-file $$b.trs \
+	$(am__common_driver_flags) $(AM_TEST_LOG_DRIVER_FLAGS) $(TEST_LOG_DRIVER_FLAGS) -- $(TEST_LOG_COMPILE) \
+	"$$tst" $(AM_TESTS_FD_REDIRECT)
+@am__EXEEXT_TRUE@.test$(EXEEXT).log:
+@am__EXEEXT_TRUE@	@p='$<'; \
+@am__EXEEXT_TRUE@	$(am__set_b); \
+@am__EXEEXT_TRUE@	$(am__check_pre) $(TEST_LOG_DRIVER) --test-name "$$f" \
+@am__EXEEXT_TRUE@	--log-file $$b.log --trs-file $$b.trs \
+@am__EXEEXT_TRUE@	$(am__common_driver_flags) $(AM_TEST_LOG_DRIVER_FLAGS) $(TEST_LOG_DRIVER_FLAGS) -- $(TEST_LOG_COMPILE) \
+@am__EXEEXT_TRUE@	"$$tst" $(AM_TESTS_FD_REDIRECT)
+
+distdir: $(DISTFILES)
+	$(am__remove_distdir)
+	test -d "$(distdir)" || mkdir "$(distdir)"
+	@srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+	topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+	list='$(DISTFILES)'; \
+	  dist_files=`for file in $$list; do echo $$file; done | \
+	  sed -e "s|^$$srcdirstrip/||;t" \
+	      -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
+	case $$dist_files in \
+	  */*) $(MKDIR_P) `echo "$$dist_files" | \
+			   sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
+			   sort -u` ;; \
+	esac; \
+	for file in $$dist_files; do \
+	  if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+	  if test -d $$d/$$file; then \
+	    dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
+	    if test -d "$(distdir)/$$file"; then \
+	      find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+	    fi; \
+	    if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+	      cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \
+	      find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+	    fi; \
+	    cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \
+	  else \
+	    test -f "$(distdir)/$$file" \
+	    || cp -p $$d/$$file "$(distdir)/$$file" \
+	    || exit 1; \
+	  fi; \
+	done
+	-test -n "$(am__skip_mode_fix)" \
+	|| find "$(distdir)" -type d ! -perm -755 \
+		-exec chmod u+rwx,go+rx {} \; -o \
+	  ! -type d ! -perm -444 -links 1 -exec chmod a+r {} \; -o \
+	  ! -type d ! -perm -400 -exec chmod a+r {} \; -o \
+	  ! -type d ! -perm -444 -exec $(install_sh) -c -m a+r {} {} \; \
+	|| chmod -R a+r "$(distdir)"
+dist-gzip: distdir
+	tardir=$(distdir) && $(am__tar) | GZIP=$(GZIP_ENV) gzip -c >$(distdir).tar.gz
+	$(am__post_remove_distdir)
+
+dist-bzip2: distdir
+	tardir=$(distdir) && $(am__tar) | BZIP2=$${BZIP2--9} bzip2 -c >$(distdir).tar.bz2
+	$(am__post_remove_distdir)
+
+dist-lzip: distdir
+	tardir=$(distdir) && $(am__tar) | lzip -c $${LZIP_OPT--9} >$(distdir).tar.lz
+	$(am__post_remove_distdir)
+dist-xz: distdir
+	tardir=$(distdir) && $(am__tar) | XZ_OPT=$${XZ_OPT--e} xz -c >$(distdir).tar.xz
+	$(am__post_remove_distdir)
+
+dist-tarZ: distdir
+	tardir=$(distdir) && $(am__tar) | compress -c >$(distdir).tar.Z
+	$(am__post_remove_distdir)
+
+dist-shar: distdir
+	shar $(distdir) | GZIP=$(GZIP_ENV) gzip -c >$(distdir).shar.gz
+	$(am__post_remove_distdir)
+
+dist-zip: distdir
+	-rm -f $(distdir).zip
+	zip -rq $(distdir).zip $(distdir)
+	$(am__post_remove_distdir)
+
+dist dist-all:
+	$(MAKE) $(AM_MAKEFLAGS) $(DIST_TARGETS) am__post_remove_distdir='@:'
+	$(am__post_remove_distdir)
+
+# This target untars the dist file and tries a VPATH configuration.  Then
+# it guarantees that the distribution is self-contained by making another
+# tarfile.
+distcheck: dist
+	case '$(DIST_ARCHIVES)' in \
+	*.tar.gz*) \
+	  GZIP=$(GZIP_ENV) gzip -dc $(distdir).tar.gz | $(am__untar) ;;\
+	*.tar.bz2*) \
+	  bzip2 -dc $(distdir).tar.bz2 | $(am__untar) ;;\
+	*.tar.lz*) \
+	  lzip -dc $(distdir).tar.lz | $(am__untar) ;;\
+	*.tar.xz*) \
+	  xz -dc $(distdir).tar.xz | $(am__untar) ;;\
+	*.tar.Z*) \
+	  uncompress -c $(distdir).tar.Z | $(am__untar) ;;\
+	*.shar.gz*) \
+	  GZIP=$(GZIP_ENV) gzip -dc $(distdir).shar.gz | unshar ;;\
+	*.zip*) \
+	  unzip $(distdir).zip ;;\
+	esac
+	chmod -R a-w $(distdir)
+	chmod u+w $(distdir)
+	mkdir $(distdir)/_build $(distdir)/_inst
+	chmod a-w $(distdir)
+	test -d $(distdir)/_build || exit 0; \
+	dc_install_base=`$(am__cd) $(distdir)/_inst && pwd | sed -e 's,^[^:\\/]:[\\/],/,'` \
+	  && dc_destdir="$${TMPDIR-/tmp}/am-dc-$$$$/" \
+	  && am__cwd=`pwd` \
+	  && $(am__cd) $(distdir)/_build \
+	  && ../configure --srcdir=.. --prefix="$$dc_install_base" \
+	    $(AM_DISTCHECK_CONFIGURE_FLAGS) \
+	    $(DISTCHECK_CONFIGURE_FLAGS) \
+	  && $(MAKE) $(AM_MAKEFLAGS) \
+	  && $(MAKE) $(AM_MAKEFLAGS) dvi \
+	  && $(MAKE) $(AM_MAKEFLAGS) check \
+	  && $(MAKE) $(AM_MAKEFLAGS) install \
+	  && $(MAKE) $(AM_MAKEFLAGS) installcheck \
+	  && $(MAKE) $(AM_MAKEFLAGS) uninstall \
+	  && $(MAKE) $(AM_MAKEFLAGS) distuninstallcheck_dir="$$dc_install_base" \
+	        distuninstallcheck \
+	  && chmod -R a-w "$$dc_install_base" \
+	  && ({ \
+	       (cd ../.. && umask 077 && mkdir "$$dc_destdir") \
+	       && $(MAKE) $(AM_MAKEFLAGS) DESTDIR="$$dc_destdir" install \
+	       && $(MAKE) $(AM_MAKEFLAGS) DESTDIR="$$dc_destdir" uninstall \
+	       && $(MAKE) $(AM_MAKEFLAGS) DESTDIR="$$dc_destdir" \
+	            distuninstallcheck_dir="$$dc_destdir" distuninstallcheck; \
+	      } || { rm -rf "$$dc_destdir"; exit 1; }) \
+	  && rm -rf "$$dc_destdir" \
+	  && $(MAKE) $(AM_MAKEFLAGS) dist \
+	  && rm -rf $(DIST_ARCHIVES) \
+	  && $(MAKE) $(AM_MAKEFLAGS) distcleancheck \
+	  && cd "$$am__cwd" \
+	  || exit 1
+	$(am__post_remove_distdir)
+	@(echo "$(distdir) archives ready for distribution: "; \
+	  list='$(DIST_ARCHIVES)'; for i in $$list; do echo $$i; done) | \
+	  sed -e 1h -e 1s/./=/g -e 1p -e 1x -e '$$p' -e '$$x'
+distuninstallcheck:
+	@test -n '$(distuninstallcheck_dir)' || { \
+	  echo 'ERROR: trying to run $@ with an empty' \
+	       '$$(distuninstallcheck_dir)' >&2; \
+	  exit 1; \
+	}; \
+	$(am__cd) '$(distuninstallcheck_dir)' || { \
+	  echo 'ERROR: cannot chdir into $(distuninstallcheck_dir)' >&2; \
+	  exit 1; \
+	}; \
+	test `$(am__distuninstallcheck_listfiles) | wc -l` -eq 0 \
+	   || { echo "ERROR: files left after uninstall:" ; \
+	        if test -n "$(DESTDIR)"; then \
+	          echo "  (check DESTDIR support)"; \
+	        fi ; \
+	        $(distuninstallcheck_listfiles) ; \
+	        exit 1; } >&2
+distcleancheck: distclean
+	@if test '$(srcdir)' = . ; then \
+	  echo "ERROR: distcleancheck can only run from a VPATH build" ; \
+	  exit 1 ; \
+	fi
+	@test `$(distcleancheck_listfiles) | wc -l` -eq 0 \
+	  || { echo "ERROR: files left in build directory after distclean:" ; \
+	       $(distcleancheck_listfiles) ; \
+	       exit 1; } >&2
+check-am: all-am
+	$(MAKE) $(AM_MAKEFLAGS) check-TESTS
+check: $(BUILT_SOURCES)
+	$(MAKE) $(AM_MAKEFLAGS) check-am
+all-am: Makefile $(LIBRARIES) $(LTLIBRARIES) $(PROGRAMS) $(SCRIPTS) \
+		$(MANS) $(DATA) $(HEADERS) config.h
+install-binPROGRAMS: install-libLTLIBRARIES
+
+installdirs:
+	for dir in "$(DESTDIR)$(libdir)" "$(DESTDIR)$(plugindir)" "$(DESTDIR)$(bindir)" "$(DESTDIR)$(cupsdir)" "$(DESTDIR)$(libexecdir)" "$(DESTDIR)$(udevdir)" "$(DESTDIR)$(testdir)" "$(DESTDIR)$(man1dir)" "$(DESTDIR)$(man8dir)" "$(DESTDIR)$(confdir)" "$(DESTDIR)$(dbusdir)" "$(DESTDIR)$(dbussessionbusdir)" "$(DESTDIR)$(dbussystembusdir)" "$(DESTDIR)$(pkgconfigdir)" "$(DESTDIR)$(rulesdir)" "$(DESTDIR)$(statedir)" "$(DESTDIR)$(systemdsystemunitdir)" "$(DESTDIR)$(systemduserunitdir)" "$(DESTDIR)$(includedir)"; do \
+	  test -z "$$dir" || $(MKDIR_P) "$$dir"; \
+	done
+install: $(BUILT_SOURCES)
+	$(MAKE) $(AM_MAKEFLAGS) install-am
+install-exec: install-exec-am
+install-data: install-data-am
+uninstall: uninstall-am
+
+install-am: all-am
+	@$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-am
+install-strip:
+	if test -z '$(STRIP)'; then \
+	  $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+	    install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+	      install; \
+	else \
+	  $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+	    install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+	    "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \
+	fi
+mostlyclean-generic:
+	-test -z "$(TEST_LOGS)" || rm -f $(TEST_LOGS)
+	-test -z "$(TEST_LOGS:.log=.trs)" || rm -f $(TEST_LOGS:.log=.trs)
+	-test -z "$(TEST_SUITE_LOG)" || rm -f $(TEST_SUITE_LOG)
+
+clean-generic:
+	-test -z "$(CLEANFILES)" || rm -f $(CLEANFILES)
+
+distclean-generic:
+	-test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+	-test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
+	-rm -f android/$(DEPDIR)/$(am__dirstamp)
+	-rm -f android/$(am__dirstamp)
+	-rm -f android/client/$(DEPDIR)/$(am__dirstamp)
+	-rm -f android/client/$(am__dirstamp)
+	-rm -f android/hardware/$(DEPDIR)/$(am__dirstamp)
+	-rm -f android/hardware/$(am__dirstamp)
+	-rm -f attrib/$(DEPDIR)/$(am__dirstamp)
+	-rm -f attrib/$(am__dirstamp)
+	-rm -f btio/$(DEPDIR)/$(am__dirstamp)
+	-rm -f btio/$(am__dirstamp)
+	-rm -f client/$(DEPDIR)/$(am__dirstamp)
+	-rm -f client/$(am__dirstamp)
+	-rm -f emulator/$(DEPDIR)/$(am__dirstamp)
+	-rm -f emulator/$(am__dirstamp)
+	-rm -f gdbus/$(DEPDIR)/$(am__dirstamp)
+	-rm -f gdbus/$(am__dirstamp)
+	-rm -f gobex/$(DEPDIR)/$(am__dirstamp)
+	-rm -f gobex/$(am__dirstamp)
+	-rm -f lib/$(DEPDIR)/$(am__dirstamp)
+	-rm -f lib/$(am__dirstamp)
+	-rm -f monitor/$(DEPDIR)/$(am__dirstamp)
+	-rm -f monitor/$(am__dirstamp)
+	-rm -f obexd/client/$(DEPDIR)/$(am__dirstamp)
+	-rm -f obexd/client/$(am__dirstamp)
+	-rm -f obexd/plugins/$(DEPDIR)/$(am__dirstamp)
+	-rm -f obexd/plugins/$(am__dirstamp)
+	-rm -f obexd/src/$(DEPDIR)/$(am__dirstamp)
+	-rm -f obexd/src/$(am__dirstamp)
+	-rm -f plugins/$(DEPDIR)/$(am__dirstamp)
+	-rm -f plugins/$(am__dirstamp)
+	-rm -f profiles/alert/$(DEPDIR)/$(am__dirstamp)
+	-rm -f profiles/alert/$(am__dirstamp)
+	-rm -f profiles/audio/$(DEPDIR)/$(am__dirstamp)
+	-rm -f profiles/audio/$(am__dirstamp)
+	-rm -f profiles/cups/$(DEPDIR)/$(am__dirstamp)
+	-rm -f profiles/cups/$(am__dirstamp)
+	-rm -f profiles/cyclingspeed/$(DEPDIR)/$(am__dirstamp)
+	-rm -f profiles/cyclingspeed/$(am__dirstamp)
+	-rm -f profiles/deviceinfo/$(DEPDIR)/$(am__dirstamp)
+	-rm -f profiles/deviceinfo/$(am__dirstamp)
+	-rm -f profiles/gatt/$(DEPDIR)/$(am__dirstamp)
+	-rm -f profiles/gatt/$(am__dirstamp)
+	-rm -f profiles/health/$(DEPDIR)/$(am__dirstamp)
+	-rm -f profiles/health/$(am__dirstamp)
+	-rm -f profiles/heartrate/$(DEPDIR)/$(am__dirstamp)
+	-rm -f profiles/heartrate/$(am__dirstamp)
+	-rm -f profiles/iap/$(DEPDIR)/$(am__dirstamp)
+	-rm -f profiles/iap/$(am__dirstamp)
+	-rm -f profiles/input/$(DEPDIR)/$(am__dirstamp)
+	-rm -f profiles/input/$(am__dirstamp)
+	-rm -f profiles/network/$(DEPDIR)/$(am__dirstamp)
+	-rm -f profiles/network/$(am__dirstamp)
+	-rm -f profiles/proximity/$(DEPDIR)/$(am__dirstamp)
+	-rm -f profiles/proximity/$(am__dirstamp)
+	-rm -f profiles/sap/$(DEPDIR)/$(am__dirstamp)
+	-rm -f profiles/sap/$(am__dirstamp)
+	-rm -f profiles/scanparam/$(DEPDIR)/$(am__dirstamp)
+	-rm -f profiles/scanparam/$(am__dirstamp)
+	-rm -f profiles/thermometer/$(DEPDIR)/$(am__dirstamp)
+	-rm -f profiles/thermometer/$(am__dirstamp)
+	-rm -f profiles/time/$(DEPDIR)/$(am__dirstamp)
+	-rm -f profiles/time/$(am__dirstamp)
+	-rm -f src/$(DEPDIR)/$(am__dirstamp)
+	-rm -f src/$(am__dirstamp)
+	-rm -f src/shared/$(DEPDIR)/$(am__dirstamp)
+	-rm -f src/shared/$(am__dirstamp)
+	-rm -f tools/$(DEPDIR)/$(am__dirstamp)
+	-rm -f tools/$(am__dirstamp)
+	-rm -f tools/parser/$(DEPDIR)/$(am__dirstamp)
+	-rm -f tools/parser/$(am__dirstamp)
+	-rm -f unit/$(DEPDIR)/$(am__dirstamp)
+	-rm -f unit/$(am__dirstamp)
+	-test -z "$(DISTCLEANFILES)" || rm -f $(DISTCLEANFILES)
+
+maintainer-clean-generic:
+	@echo "This command is intended for maintainers to use"
+	@echo "it deletes files that may require special tools to rebuild."
+	-test -z "$(BUILT_SOURCES)" || rm -f $(BUILT_SOURCES)
+	-test -z "$(MAINTAINERCLEANFILES)" || rm -f $(MAINTAINERCLEANFILES)
+clean: clean-am
+
+clean-am: clean-binPROGRAMS clean-cupsPROGRAMS clean-generic \
+	clean-libLTLIBRARIES clean-libexecPROGRAMS clean-libtool \
+	clean-local clean-noinstLIBRARIES clean-noinstLTLIBRARIES \
+	clean-noinstPROGRAMS clean-pluginLTLIBRARIES \
+	clean-udevPROGRAMS mostlyclean-am
+
+distclean: distclean-am
+	-rm -f $(am__CONFIG_DISTCLEAN_FILES)
+	-rm -rf android/$(DEPDIR) android/client/$(DEPDIR) android/hardware/$(DEPDIR) attrib/$(DEPDIR) btio/$(DEPDIR) client/$(DEPDIR) emulator/$(DEPDIR) gdbus/$(DEPDIR) gobex/$(DEPDIR) lib/$(DEPDIR) monitor/$(DEPDIR) obexd/client/$(DEPDIR) obexd/plugins/$(DEPDIR) obexd/src/$(DEPDIR) plugins/$(DEPDIR) profiles/alert/$(DEPDIR) profiles/audio/$(DEPDIR) profiles/cups/$(DEPDIR) profiles/cyclingspeed/$(DEPDIR) profiles/deviceinfo/$(DEPDIR) profiles/gatt/$(DEPDIR) profiles/health/$(DEPDIR) profiles/heartrate/$(DEPDIR) profiles/iap/$(DEPDIR) profiles/input/$(DEPDIR) profiles/network/$(DEPDIR) profiles/proximity/$(DEPDIR) profiles/sap/$(DEPDIR) profiles/scanparam/$(DEPDIR) profiles/thermometer/$(DEPDIR) profiles/time/$(DEPDIR) src/$(DEPDIR) src/shared/$(DEPDIR) tools/$(DEPDIR) tools/parser/$(DEPDIR) unit/$(DEPDIR)
+	-rm -f Makefile
+distclean-am: clean-am distclean-compile distclean-generic \
+	distclean-hdr distclean-libtool distclean-tags
+
+dvi: dvi-am
+
+dvi-am:
+
+html: html-am
+
+html-am:
+
+info: info-am
+
+info-am:
+
+install-data-am: install-confDATA install-cupsPROGRAMS \
+	install-dbusDATA install-dbussessionbusDATA \
+	install-dbussystembusDATA install-includeHEADERS install-man \
+	install-pkgconfigDATA install-pluginLTLIBRARIES \
+	install-rulesDATA install-stateDATA \
+	install-systemdsystemunitDATA install-systemduserunitDATA \
+	install-testSCRIPTS install-udevPROGRAMS
+
+install-dvi: install-dvi-am
+
+install-dvi-am:
+
+install-exec-am: install-binPROGRAMS install-libLTLIBRARIES \
+	install-libexecPROGRAMS
+
+install-html: install-html-am
+
+install-html-am:
+
+install-info: install-info-am
+
+install-info-am:
+
+install-man: install-man1 install-man8
+
+install-pdf: install-pdf-am
+
+install-pdf-am:
+
+install-ps: install-ps-am
+
+install-ps-am:
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-am
+	-rm -f $(am__CONFIG_DISTCLEAN_FILES)
+	-rm -rf $(top_srcdir)/autom4te.cache
+	-rm -rf android/$(DEPDIR) android/client/$(DEPDIR) android/hardware/$(DEPDIR) attrib/$(DEPDIR) btio/$(DEPDIR) client/$(DEPDIR) emulator/$(DEPDIR) gdbus/$(DEPDIR) gobex/$(DEPDIR) lib/$(DEPDIR) monitor/$(DEPDIR) obexd/client/$(DEPDIR) obexd/plugins/$(DEPDIR) obexd/src/$(DEPDIR) plugins/$(DEPDIR) profiles/alert/$(DEPDIR) profiles/audio/$(DEPDIR) profiles/cups/$(DEPDIR) profiles/cyclingspeed/$(DEPDIR) profiles/deviceinfo/$(DEPDIR) profiles/gatt/$(DEPDIR) profiles/health/$(DEPDIR) profiles/heartrate/$(DEPDIR) profiles/iap/$(DEPDIR) profiles/input/$(DEPDIR) profiles/network/$(DEPDIR) profiles/proximity/$(DEPDIR) profiles/sap/$(DEPDIR) profiles/scanparam/$(DEPDIR) profiles/thermometer/$(DEPDIR) profiles/time/$(DEPDIR) src/$(DEPDIR) src/shared/$(DEPDIR) tools/$(DEPDIR) tools/parser/$(DEPDIR) unit/$(DEPDIR)
+	-rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-am
+
+mostlyclean-am: mostlyclean-compile mostlyclean-generic \
+	mostlyclean-libtool
+
+pdf: pdf-am
+
+pdf-am:
+
+ps: ps-am
+
+ps-am:
+
+uninstall-am: uninstall-binPROGRAMS uninstall-confDATA \
+	uninstall-cupsPROGRAMS uninstall-dbusDATA \
+	uninstall-dbussessionbusDATA uninstall-dbussystembusDATA \
+	uninstall-includeHEADERS uninstall-libLTLIBRARIES \
+	uninstall-libexecPROGRAMS uninstall-man \
+	uninstall-pkgconfigDATA uninstall-pluginLTLIBRARIES \
+	uninstall-rulesDATA uninstall-stateDATA \
+	uninstall-systemdsystemunitDATA uninstall-systemduserunitDATA \
+	uninstall-testSCRIPTS uninstall-udevPROGRAMS
+
+uninstall-man: uninstall-man1 uninstall-man8
+
+.MAKE: all check check-am install install-am install-strip
+
+.PHONY: CTAGS GTAGS TAGS all all-am am--refresh check check-TESTS \
+	check-am clean clean-binPROGRAMS clean-cscope \
+	clean-cupsPROGRAMS clean-generic clean-libLTLIBRARIES \
+	clean-libexecPROGRAMS clean-libtool clean-local \
+	clean-noinstLIBRARIES clean-noinstLTLIBRARIES \
+	clean-noinstPROGRAMS clean-pluginLTLIBRARIES \
+	clean-udevPROGRAMS cscope cscopelist-am ctags ctags-am dist \
+	dist-all dist-bzip2 dist-gzip dist-lzip dist-shar dist-tarZ \
+	dist-xz dist-zip distcheck distclean distclean-compile \
+	distclean-generic distclean-hdr distclean-libtool \
+	distclean-tags distcleancheck distdir distuninstallcheck dvi \
+	dvi-am html html-am info info-am install install-am \
+	install-binPROGRAMS install-confDATA install-cupsPROGRAMS \
+	install-data install-data-am install-dbusDATA \
+	install-dbussessionbusDATA install-dbussystembusDATA \
+	install-dvi install-dvi-am install-exec install-exec-am \
+	install-html install-html-am install-includeHEADERS \
+	install-info install-info-am install-libLTLIBRARIES \
+	install-libexecPROGRAMS install-man install-man1 install-man8 \
+	install-pdf install-pdf-am install-pkgconfigDATA \
+	install-pluginLTLIBRARIES install-ps install-ps-am \
+	install-rulesDATA install-stateDATA install-strip \
+	install-systemdsystemunitDATA install-systemduserunitDATA \
+	install-testSCRIPTS install-udevPROGRAMS installcheck \
+	installcheck-am installdirs maintainer-clean \
+	maintainer-clean-generic mostlyclean mostlyclean-compile \
+	mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \
+	recheck tags tags-am uninstall uninstall-am \
+	uninstall-binPROGRAMS uninstall-confDATA \
+	uninstall-cupsPROGRAMS uninstall-dbusDATA \
+	uninstall-dbussessionbusDATA uninstall-dbussystembusDATA \
+	uninstall-includeHEADERS uninstall-libLTLIBRARIES \
+	uninstall-libexecPROGRAMS uninstall-man uninstall-man1 \
+	uninstall-man8 uninstall-pkgconfigDATA \
+	uninstall-pluginLTLIBRARIES uninstall-rulesDATA \
+	uninstall-stateDATA uninstall-systemdsystemunitDATA \
+	uninstall-systemduserunitDATA uninstall-testSCRIPTS \
+	uninstall-udevPROGRAMS
+
+
+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) > $@
+
+%.service: %.service.in Makefile
+	$(SED_PROCESS)
+
+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)"/$< $@
+
+clean-local:
+	$(RM) -r lib/bluetooth
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/bluez/Makefile.obexd b/bluez/Makefile.obexd
new file mode 100644
index 0000000..3760867
--- /dev/null
+++ b/bluez/Makefile.obexd
@@ -0,0 +1,112 @@
+
+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  \
+				-I$(srcdir)/obexd/src -I$(srcdir)/btio \
+				-I$(srcdir)/gobex -I$(srcdir)/gdbus
+
+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/Makefile.plugins b/bluez/Makefile.plugins
new file mode 100644
index 0000000..415d023
--- /dev/null
+++ b/bluez/Makefile.plugins
@@ -0,0 +1,123 @@
+
+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 += dropcam
+builtin_sources += plugins/dropcam.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
+
+builtin_modules += hog
+builtin_sources += profiles/input/hog.c profiles/input/uhid_copy.h \
+			profiles/input/suspend.h profiles/input/suspend-dummy.c
+
+if EXPERIMENTAL
+builtin_modules += health
+builtin_sources += profiles/health/mcap_lib.h profiles/health/mcap_internal.h \
+			profiles/health/mcap.h profiles/health/mcap.c \
+			profiles/health/mcap_sync.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 += gatt
+builtin_sources += profiles/gatt/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/Makefile.tools b/bluez/Makefile.tools
new file mode 100644
index 0000000..a5491ac
--- /dev/null
+++ b/bluez/Makefile.tools
@@ -0,0 +1,414 @@
+
+if CLIENT
+bin_PROGRAMS += client/bluetoothctl
+
+client_bluetoothctl_SOURCES = client/main.c \
+					client/display.h client/display.c \
+					client/agent.h client/agent.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/mainloop.h monitor/mainloop.c \
+				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/uuid.h monitor/uuid.c \
+				monitor/hwdb.h monitor/hwdb.c \
+				monitor/keys.h monitor/keys.c \
+				monitor/analyze.h monitor/analyze.c \
+				src/shared/util.h src/shared/util.c \
+				src/shared/queue.h src/shared/queue.c \
+				src/shared/crypto.h src/shared/crypto.c \
+				src/shared/btsnoop.h src/shared/btsnoop.c
+monitor_btmon_LDADD = lib/libbluetooth-internal.la @UDEV_LIBS@
+endif
+
+if EXPERIMENTAL
+noinst_PROGRAMS += emulator/btvirt emulator/b1ee emulator/hfp tools/3dsp \
+					tools/mgmt-tester tools/gap-tester \
+					tools/l2cap-tester tools/sco-tester \
+					tools/smp-tester tools/hci-tester \
+					tools/rfcomm-tester
+
+emulator_btvirt_SOURCES = emulator/main.c monitor/bt.h \
+				monitor/mainloop.h monitor/mainloop.c \
+				src/shared/timeout.h \
+				src/shared/timeout-mainloop.c \
+				src/shared/util.h src/shared/util.c \
+				src/shared/crypto.h src/shared/crypto.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/amp.h emulator/amp.c \
+				emulator/le.h emulator/le.c
+emulator_btvirt_LDADD = lib/libbluetooth-internal.la
+
+emulator_b1ee_SOURCES = emulator/b1ee.c monitor/mainloop.h monitor/mainloop.c
+
+emulator_hfp_SOURCES = emulator/hfp.c \
+				monitor/mainloop.h monitor/mainloop.c \
+				src/shared/io.h src/shared/io-mainloop.c \
+				src/shared/util.h src/shared/util.c \
+				src/shared/queue.h src/shared/queue.c \
+				src/shared/ringbuf.h src/shared/ringbuf.c \
+				src/shared/hfp.h src/shared/hfp.c
+
+tools_3dsp_SOURCES = tools/3dsp.c monitor/bt.h \
+				monitor/mainloop.h monitor/mainloop.c \
+				src/shared/io.h src/shared/io-mainloop.c \
+				src/shared/timeout.h \
+				src/shared/timeout-mainloop.c \
+				src/shared/hci.h src/shared/hci.c \
+				src/shared/util.h src/shared/util.c \
+				src/shared/queue.h src/shared/queue.c
+
+tools_mgmt_tester_SOURCES = tools/mgmt-tester.c monitor/bt.h \
+				emulator/btdev.h emulator/btdev.c \
+				emulator/bthost.h emulator/bthost.c \
+				emulator/smp.c \
+				src/shared/crypto.h src/shared/crypto.c \
+				src/shared/io.h src/shared/io-glib.c \
+				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/hciemu.h src/shared/hciemu.c \
+				src/shared/tester.h src/shared/tester.c \
+				src/shared/timeout.h src/shared/timeout-glib.c
+tools_mgmt_tester_LDADD = lib/libbluetooth-internal.la @GLIB_LIBS@
+
+tools_l2cap_tester_SOURCES = tools/l2cap-tester.c monitor/bt.h \
+				emulator/btdev.h emulator/btdev.c \
+				emulator/bthost.h emulator/bthost.c \
+				emulator/smp.c \
+				src/shared/crypto.h src/shared/crypto.c \
+				src/shared/io.h src/shared/io-glib.c \
+				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/hciemu.h src/shared/hciemu.c \
+				src/shared/tester.h src/shared/tester.c \
+				src/shared/timeout.h src/shared/timeout-glib.c
+tools_l2cap_tester_LDADD = lib/libbluetooth-internal.la @GLIB_LIBS@
+
+tools_rfcomm_tester_SOURCES = tools/rfcomm-tester.c monitor/bt.h \
+				emulator/btdev.h emulator/btdev.c \
+				emulator/bthost.h emulator/bthost.c \
+				emulator/smp.c \
+				src/shared/crypto.h src/shared/crypto.c \
+				src/shared/io.h src/shared/io-glib.c \
+				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/hciemu.h src/shared/hciemu.c \
+				src/shared/tester.h src/shared/tester.c \
+				src/shared/timeout.h src/shared/timeout-glib.c
+tools_rfcomm_tester_LDADD = lib/libbluetooth-internal.la @GLIB_LIBS@
+
+tools_smp_tester_SOURCES = tools/smp-tester.c monitor/bt.h \
+				emulator/btdev.h emulator/btdev.c \
+				emulator/bthost.h emulator/bthost.c \
+				emulator/smp.c \
+				src/shared/crypto.h src/shared/crypto.c \
+				src/shared/io.h src/shared/io-glib.c \
+				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/hciemu.h src/shared/hciemu.c \
+				src/shared/tester.h src/shared/tester.c \
+				src/shared/timeout.h src/shared/timeout-glib.c
+tools_smp_tester_LDADD = lib/libbluetooth-internal.la @GLIB_LIBS@
+
+tools_gap_tester_SOURCES = tools/gap-tester.c monitor/bt.h \
+				emulator/btdev.h emulator/btdev.c \
+				emulator/bthost.h emulator/bthost.c \
+				emulator/smp.c \
+				src/shared/crypto.h src/shared/crypto.c \
+				src/shared/util.h src/shared/util.c \
+				src/shared/queue.h src/shared/queue.c \
+				src/shared/hciemu.h src/shared/hciemu.c \
+				src/shared/tester.h src/shared/tester.c \
+				src/shared/timeout.h src/shared/timeout-glib.c
+tools_gap_tester_LDADD =  lib/libbluetooth-internal.la \
+				gdbus/libgdbus-internal.la \
+				@GLIB_LIBS@ @DBUS_LIBS@
+
+tools_sco_tester_SOURCES = tools/sco-tester.c monitor/bt.h \
+				emulator/btdev.h emulator/btdev.c \
+				emulator/bthost.h emulator/bthost.c \
+				emulator/smp.c \
+				src/shared/crypto.h src/shared/crypto.c \
+				src/shared/io.h src/shared/io-glib.c \
+				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/hciemu.h src/shared/hciemu.c \
+				src/shared/tester.h src/shared/tester.c \
+				src/shared/timeout.h src/shared/timeout-glib.c
+tools_sco_tester_LDADD = lib/libbluetooth-internal.la @GLIB_LIBS@
+
+tools_hci_tester_SOURCES = tools/hci-tester.c monitor/bt.h \
+				src/shared/io.h src/shared/io-glib.c \
+				src/shared/hci.h src/shared/hci.c \
+				src/shared/util.h src/shared/util.c \
+				src/shared/queue.h src/shared/queue.c \
+				src/shared/tester.h src/shared/tester.c
+tools_hci_tester_LDADD = @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/bluemoon
+
+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_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 @GLIB_LIBS@ @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_bluemoon_SOURCES = tools/bluemoon.c monitor/bt.h \
+				monitor/mainloop.h monitor/mainloop.c \
+				src/shared/io.h src/shared/io-mainloop.c \
+				src/shared/hci.h src/shared/hci.c \
+				src/shared/util.h src/shared/util.c \
+				src/shared/queue.h src/shared/queue.c
+
+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
+noinst_PROGRAMS += tools/bdaddr tools/avinfo tools/avtest \
+			tools/scotest tools/amptest tools/hwdb \
+			tools/hcieventmask tools/hcisecfilter \
+			tools/btmgmt tools/btinfo tools/btattach \
+			tools/btsnoop tools/btproxy tools/btiotest \
+			tools/mpris-player tools/cltest tools/seq2bseq \
+			tools/ibeacon
+
+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_btmgmt_SOURCES = tools/btmgmt.c src/uuid-helper.c \
+				monitor/mainloop.h monitor/mainloop.c \
+				src/shared/io.h src/shared/io-mainloop.c \
+				src/shared/queue.h src/shared/queue.c \
+				src/shared/util.h src/shared/util.c \
+				src/shared/mgmt.h src/shared/mgmt.c
+tools_btmgmt_LDADD = lib/libbluetooth-internal.la
+
+tools_btinfo_SOURCES = tools/btinfo.c monitor/bt.h \
+				monitor/mainloop.h monitor/mainloop.c \
+				src/shared/io.h src/shared/io-mainloop.c \
+				src/shared/timeout.h \
+				src/shared/timeout-mainloop.c \
+				src/shared/hci.h src/shared/hci.c \
+				src/shared/util.h src/shared/util.c \
+				src/shared/queue.h src/shared/queue.c
+
+tools_btsnoop_SOURCES = tools/btsnoop.c \
+				src/shared/pcap.h src/shared/pcap.c \
+				src/shared/btsnoop.h src/shared/btsnoop.c
+
+tools_btproxy_SOURCES = tools/btproxy.c monitor/bt.h \
+				monitor/mainloop.h monitor/mainloop.c \
+				src/shared/util.h src/shared/util.c
+
+tools_btiotest_SOURCES = tools/btiotest.c btio/btio.h btio/btio.c
+tools_btiotest_LDADD = lib/libbluetooth-internal.la @GLIB_LIBS@
+
+tools_mpris_player_SOURCES = tools/mpris-player.c
+tools_mpris_player_LDADD = gdbus/libgdbus-internal.la @GLIB_LIBS@ @DBUS_LIBS@
+
+tools_cltest_SOURCES = tools/cltest.c monitor/mainloop.h monitor/mainloop.c
+tools_cltest_LDADD = lib/libbluetooth-internal.la
+
+tools_seq2bseq_SOURCES = tools/seq2bseq.c
+
+tools_ibeacon_SOURCES = tools/ibeacon.c monitor/bt.h \
+				monitor/mainloop.h monitor/mainloop.c \
+				src/shared/io.h src/shared/io-mainloop.c \
+				src/shared/timeout.h \
+				src/shared/timeout-mainloop.c \
+				src/shared/hci.h src/shared/hci.c \
+				src/shared/util.h src/shared/util.c \
+				src/shared/queue.h src/shared/queue.c
+
+EXTRA_DIST += tools/bdaddr.1
+endif
+
+if READLINE
+noinst_PROGRAMS += attrib/gatttool \
+			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 @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
+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-service 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
diff --git a/bluez/NEWS b/bluez/NEWS
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/bluez/NEWS
diff --git a/bluez/README b/bluez/README
new file mode 100644
index 0000000..c991ab0
--- /dev/null
+++ b/bluez/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/TODO b/bluez/TODO
new file mode 100644
index 0000000..eb2177e
--- /dev/null
+++ b/bluez/TODO
@@ -0,0 +1,271 @@
+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
+
+- Add support for Authentication for client sessions in obexd
+
+  Priority: high
+  Complexity: C2
+
+- 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
+
+- Rename glib-helper file to a more convenient name. The idea is try to keep
+  only sdp helpers functions. bt_* prefix shall be also changed.
+
+  Priority: Low
+  Complexity: C1
+
+- 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
+
+Low Energy
+==========
+
+- Advertising management. Adapter interface needs to be changed to manage
+  connection modes, adapter type and advertising policy. See Volume 3,
+  Part C, section 9.3. If Attribute Server is enabled the LE capable
+  adapter shall to start advertising. Further investigation is necessary
+  to define which connectable mode needs to be supported: Non-connectable,
+  directed connectable and undirected connectable. Basically, two connectable
+  scenarios shall be addressed:
+  1. GATT client is disconnected, but intends to become a Peripheral to
+     receive indications/notifications.
+  2. GATT server intends to accept connections.
+
+  Priority: Medium
+  Complexity: C2
+
+- Define Auto Connection Establishment Procedure. Some profiles such as
+  Proximity requires an active link to identify path lost situation. It is
+  necessary to define how to manage connections, it seems that White List
+  is appropriated to address auto connections, however is not clear if the
+  this procedure shall be a profile specific detail or if the remote device
+  object can expose a property "WhiteList", maybe "Trusted" property can be
+  also used for this purpose. Another alternative is to define a method to
+  allow application to request/register the wanted scanning/connection
+  parameters. Before start this task, a RFC/PATCH shall be sent to the ML.
+  See Volume 3, Part C, section 9.3.5 for more information.
+
+  Priority: Medium
+  Complexity: C2
+
+- Implement a tool(or extend hciconfig) to setup the advertising parameters
+  and data. Extend hciconfig passing extra arguments when enabling the
+  advertises is not the right approach, it will be almost impossible to
+  address all arguments needed in an acceptable way. For testing, we need
+  a tool to change easily the AD Flags, the UUIDs and other data that can be
+  exported through the advertising data field. Suggestions: 1) extend hciconfig
+  passing a config file when enabling advertises; 2) write a ncurses based tool
+
+  Priority: Medium
+  Complexity: C2
+
+- Add new property in the DeviceFound signal to report the device type:
+  BR/EDR, single mode or dual-mode.
+
+  Priority: Medium
+  Complexity: C1
+
+- Privacy: When privacy is enabled in the adapter, LE scanning/connection
+  should use a private address. StartDiscovery method shall be changed and
+  new adapter property shall be added.
+
+  Priority: Medium
+  Complexity: C1
+
+- 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
+
+- Reconnection address: Reconnection address is a non resolvable private
+  address that the central writes in the peripheral. BlueZ will support
+  multiple profiles, it is not clear how it needs to be implemented.
+  Further discussion is necessary.
+
+  Priority: Low
+  Complexity: C2
+
+- 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
+========
+
+- 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
+
+- ATT/GATT parsing to hcidump. Partially implemented, missing to fix
+  multiple advertises in the same event and RSSI.
+
+  Priority: Medium
+  Complexity: C2
+
+- 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
+
+- Fix hard-coded PSM for GATT services over basic rate.
+
+  Priority: Low
+  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
+
+- Refactoring of gatt.c functions. Currently, the callbacks of the services
+  and characteristics discovery functions return the ATT PDU and the caller
+  needs to call again the same function to fetch the remaining data when
+  necessary. Investigate if all results can be returned in the callback
+  result to avoid repeated code. Before change the code, please analyze
+  if this change will not break the GATT/ATT qualification tests. Maybe
+  an interactive fetch/query is necessary to pass the tests.
+
+  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
+
+- Add sdp discovery support to gatttool with BR (--sdp, default is 0x1f)
+
+  Priority: Low
+  Complexity: 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
+
+- Define attribute server API. External applications needs to register,
+  change attributes and to be notified about changes. Example: Proximity,
+  Time and Alert Profiles. "Local Service hierarchy" in the attribute-api
+  needs to be proposed and a RFC shall be sent to the ML.
+
+  Priority: Low
+  Complexity: C2
+  Owner: Anderson Lizardo <anderson.lizardo@openbossa.org>
+
+Management Interface
+====================
+
+- Whitelist support (initially only for LE)
+
+  Priority: Medium
+  Complexity: C2
+  Owner: Andre Guedes <andre.guedes@openbossa.org>
diff --git a/bluez/acinclude.m4 b/bluez/acinclude.m4
new file mode 100644
index 0000000..2065852
--- /dev/null
+++ b/bluez/acinclude.m4
@@ -0,0 +1,56 @@
+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 -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
+	])
+	AC_SUBST([MISC_CFLAGS], $misc_cflags)
+	AC_SUBST([MISC_LDFLAGS], $misc_ldflags)
+])
diff --git a/bluez/aclocal.m4 b/bluez/aclocal.m4
new file mode 100644
index 0000000..0483402
--- /dev/null
+++ b/bluez/aclocal.m4
@@ -0,0 +1,9881 @@
+# generated automatically by aclocal 1.13.3 -*- Autoconf -*-
+
+# Copyright (C) 1996-2013 Free Software Foundation, Inc.
+
+# This file is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+m4_ifndef([AC_CONFIG_MACRO_DIRS], [m4_defun([_AM_CONFIG_MACRO_DIRS], [])m4_defun([AC_CONFIG_MACRO_DIRS], [_AM_CONFIG_MACRO_DIRS($@)])])
+m4_ifndef([AC_AUTOCONF_VERSION],
+  [m4_copy([m4_PACKAGE_VERSION], [AC_AUTOCONF_VERSION])])dnl
+m4_if(m4_defn([AC_AUTOCONF_VERSION]), [2.69],,
+[m4_warning([this file was generated for autoconf 2.69.
+You have another version of autoconf.  It may work, but is not guaranteed to.
+If you have problems, you may need to regenerate the build system entirely.
+To do so, use the procedure documented by the package, typically 'autoreconf'.])])
+
+# libtool.m4 - Configure libtool for the host system. -*-Autoconf-*-
+#
+#   Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2003, 2004, 2005,
+#                 2006, 2007, 2008, 2009, 2010, 2011 Free Software
+#                 Foundation, Inc.
+#   Written by Gordon Matzigkeit, 1996
+#
+# This file is free software; the Free Software Foundation gives
+# unlimited permission to copy and/or distribute it, with or without
+# modifications, as long as this notice is preserved.
+
+m4_define([_LT_COPYING], [dnl
+#   Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2003, 2004, 2005,
+#                 2006, 2007, 2008, 2009, 2010, 2011 Free Software
+#                 Foundation, Inc.
+#   Written by Gordon Matzigkeit, 1996
+#
+#   This file is part of GNU Libtool.
+#
+# GNU Libtool is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License as
+# published by the Free Software Foundation; either version 2 of
+# the License, or (at your option) any later version.
+#
+# As a special exception to the GNU General Public License,
+# if you distribute this file as part of a program or library that
+# is built using GNU Libtool, you may include this file under the
+# same distribution terms that you use for the rest of that program.
+#
+# GNU Libtool is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with GNU Libtool; see the file COPYING.  If not, a copy
+# can be downloaded from http://www.gnu.org/licenses/gpl.html, or
+# obtained by writing to the Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+])
+
+# serial 57 LT_INIT
+
+
+# LT_PREREQ(VERSION)
+# ------------------
+# Complain and exit if this libtool version is less that VERSION.
+m4_defun([LT_PREREQ],
+[m4_if(m4_version_compare(m4_defn([LT_PACKAGE_VERSION]), [$1]), -1,
+       [m4_default([$3],
+		   [m4_fatal([Libtool version $1 or higher is required],
+		             63)])],
+       [$2])])
+
+
+# _LT_CHECK_BUILDDIR
+# ------------------
+# Complain if the absolute build directory name contains unusual characters
+m4_defun([_LT_CHECK_BUILDDIR],
+[case `pwd` in
+  *\ * | *\	*)
+    AC_MSG_WARN([Libtool does not cope well with whitespace in `pwd`]) ;;
+esac
+])
+
+
+# LT_INIT([OPTIONS])
+# ------------------
+AC_DEFUN([LT_INIT],
+[AC_PREREQ([2.58])dnl We use AC_INCLUDES_DEFAULT
+AC_REQUIRE([AC_CONFIG_AUX_DIR_DEFAULT])dnl
+AC_BEFORE([$0], [LT_LANG])dnl
+AC_BEFORE([$0], [LT_OUTPUT])dnl
+AC_BEFORE([$0], [LTDL_INIT])dnl
+m4_require([_LT_CHECK_BUILDDIR])dnl
+
+dnl Autoconf doesn't catch unexpanded LT_ macros by default:
+m4_pattern_forbid([^_?LT_[A-Z_]+$])dnl
+m4_pattern_allow([^(_LT_EOF|LT_DLGLOBAL|LT_DLLAZY_OR_NOW|LT_MULTI_MODULE)$])dnl
+dnl aclocal doesn't pull ltoptions.m4, ltsugar.m4, or ltversion.m4
+dnl unless we require an AC_DEFUNed macro:
+AC_REQUIRE([LTOPTIONS_VERSION])dnl
+AC_REQUIRE([LTSUGAR_VERSION])dnl
+AC_REQUIRE([LTVERSION_VERSION])dnl
+AC_REQUIRE([LTOBSOLETE_VERSION])dnl
+m4_require([_LT_PROG_LTMAIN])dnl
+
+_LT_SHELL_INIT([SHELL=${CONFIG_SHELL-/bin/sh}])
+
+dnl Parse OPTIONS
+_LT_SET_OPTIONS([$0], [$1])
+
+# This can be used to rebuild libtool when needed
+LIBTOOL_DEPS="$ltmain"
+
+# Always use our own libtool.
+LIBTOOL='$(SHELL) $(top_builddir)/libtool'
+AC_SUBST(LIBTOOL)dnl
+
+_LT_SETUP
+
+# Only expand once:
+m4_define([LT_INIT])
+])# LT_INIT
+
+# Old names:
+AU_ALIAS([AC_PROG_LIBTOOL], [LT_INIT])
+AU_ALIAS([AM_PROG_LIBTOOL], [LT_INIT])
+dnl aclocal-1.4 backwards compatibility:
+dnl AC_DEFUN([AC_PROG_LIBTOOL], [])
+dnl AC_DEFUN([AM_PROG_LIBTOOL], [])
+
+
+# _LT_CC_BASENAME(CC)
+# -------------------
+# Calculate cc_basename.  Skip known compiler wrappers and cross-prefix.
+m4_defun([_LT_CC_BASENAME],
+[for cc_temp in $1""; do
+  case $cc_temp in
+    compile | *[[\\/]]compile | ccache | *[[\\/]]ccache ) ;;
+    distcc | *[[\\/]]distcc | purify | *[[\\/]]purify ) ;;
+    \-*) ;;
+    *) break;;
+  esac
+done
+cc_basename=`$ECHO "$cc_temp" | $SED "s%.*/%%; s%^$host_alias-%%"`
+])
+
+
+# _LT_FILEUTILS_DEFAULTS
+# ----------------------
+# It is okay to use these file commands and assume they have been set
+# sensibly after `m4_require([_LT_FILEUTILS_DEFAULTS])'.
+m4_defun([_LT_FILEUTILS_DEFAULTS],
+[: ${CP="cp -f"}
+: ${MV="mv -f"}
+: ${RM="rm -f"}
+])# _LT_FILEUTILS_DEFAULTS
+
+
+# _LT_SETUP
+# ---------
+m4_defun([_LT_SETUP],
+[AC_REQUIRE([AC_CANONICAL_HOST])dnl
+AC_REQUIRE([AC_CANONICAL_BUILD])dnl
+AC_REQUIRE([_LT_PREPARE_SED_QUOTE_VARS])dnl
+AC_REQUIRE([_LT_PROG_ECHO_BACKSLASH])dnl
+
+_LT_DECL([], [PATH_SEPARATOR], [1], [The PATH separator for the build system])dnl
+dnl
+_LT_DECL([], [host_alias], [0], [The host system])dnl
+_LT_DECL([], [host], [0])dnl
+_LT_DECL([], [host_os], [0])dnl
+dnl
+_LT_DECL([], [build_alias], [0], [The build system])dnl
+_LT_DECL([], [build], [0])dnl
+_LT_DECL([], [build_os], [0])dnl
+dnl
+AC_REQUIRE([AC_PROG_CC])dnl
+AC_REQUIRE([LT_PATH_LD])dnl
+AC_REQUIRE([LT_PATH_NM])dnl
+dnl
+AC_REQUIRE([AC_PROG_LN_S])dnl
+test -z "$LN_S" && LN_S="ln -s"
+_LT_DECL([], [LN_S], [1], [Whether we need soft or hard links])dnl
+dnl
+AC_REQUIRE([LT_CMD_MAX_LEN])dnl
+_LT_DECL([objext], [ac_objext], [0], [Object file suffix (normally "o")])dnl
+_LT_DECL([], [exeext], [0], [Executable file suffix (normally "")])dnl
+dnl
+m4_require([_LT_FILEUTILS_DEFAULTS])dnl
+m4_require([_LT_CHECK_SHELL_FEATURES])dnl
+m4_require([_LT_PATH_CONVERSION_FUNCTIONS])dnl
+m4_require([_LT_CMD_RELOAD])dnl
+m4_require([_LT_CHECK_MAGIC_METHOD])dnl
+m4_require([_LT_CHECK_SHAREDLIB_FROM_LINKLIB])dnl
+m4_require([_LT_CMD_OLD_ARCHIVE])dnl
+m4_require([_LT_CMD_GLOBAL_SYMBOLS])dnl
+m4_require([_LT_WITH_SYSROOT])dnl
+
+_LT_CONFIG_LIBTOOL_INIT([
+# See if we are running on zsh, and set the options which allow our
+# commands through without removal of \ escapes INIT.
+if test -n "\${ZSH_VERSION+set}" ; then
+   setopt NO_GLOB_SUBST
+fi
+])
+if test -n "${ZSH_VERSION+set}" ; then
+   setopt NO_GLOB_SUBST
+fi
+
+_LT_CHECK_OBJDIR
+
+m4_require([_LT_TAG_COMPILER])dnl
+
+case $host_os in
+aix3*)
+  # AIX sometimes has problems with the GCC collect2 program.  For some
+  # reason, if we set the COLLECT_NAMES environment variable, the problems
+  # vanish in a puff of smoke.
+  if test "X${COLLECT_NAMES+set}" != Xset; then
+    COLLECT_NAMES=
+    export COLLECT_NAMES
+  fi
+  ;;
+esac
+
+# Global variables:
+ofile=libtool
+can_build_shared=yes
+
+# All known linkers require a `.a' archive for static linking (except MSVC,
+# which needs '.lib').
+libext=a
+
+with_gnu_ld="$lt_cv_prog_gnu_ld"
+
+old_CC="$CC"
+old_CFLAGS="$CFLAGS"
+
+# Set sane defaults for various variables
+test -z "$CC" && CC=cc
+test -z "$LTCC" && LTCC=$CC
+test -z "$LTCFLAGS" && LTCFLAGS=$CFLAGS
+test -z "$LD" && LD=ld
+test -z "$ac_objext" && ac_objext=o
+
+_LT_CC_BASENAME([$compiler])
+
+# Only perform the check for file, if the check method requires it
+test -z "$MAGIC_CMD" && MAGIC_CMD=file
+case $deplibs_check_method in
+file_magic*)
+  if test "$file_magic_cmd" = '$MAGIC_CMD'; then
+    _LT_PATH_MAGIC
+  fi
+  ;;
+esac
+
+# Use C for the default configuration in the libtool script
+LT_SUPPORTED_TAG([CC])
+_LT_LANG_C_CONFIG
+_LT_LANG_DEFAULT_CONFIG
+_LT_CONFIG_COMMANDS
+])# _LT_SETUP
+
+
+# _LT_PREPARE_SED_QUOTE_VARS
+# --------------------------
+# Define a few sed substitution that help us do robust quoting.
+m4_defun([_LT_PREPARE_SED_QUOTE_VARS],
+[# Backslashify metacharacters that are still active within
+# double-quoted strings.
+sed_quote_subst='s/\([["`$\\]]\)/\\\1/g'
+
+# Same as above, but do not quote variable references.
+double_quote_subst='s/\([["`\\]]\)/\\\1/g'
+
+# Sed substitution to delay expansion of an escaped shell variable in a
+# double_quote_subst'ed string.
+delay_variable_subst='s/\\\\\\\\\\\$/\\\\\\$/g'
+
+# Sed substitution to delay expansion of an escaped single quote.
+delay_single_quote_subst='s/'\''/'\'\\\\\\\'\''/g'
+
+# Sed substitution to avoid accidental globbing in evaled expressions
+no_glob_subst='s/\*/\\\*/g'
+])
+
+# _LT_PROG_LTMAIN
+# ---------------
+# Note that this code is called both from `configure', and `config.status'
+# now that we use AC_CONFIG_COMMANDS to generate libtool.  Notably,
+# `config.status' has no value for ac_aux_dir unless we are using Automake,
+# so we pass a copy along to make sure it has a sensible value anyway.
+m4_defun([_LT_PROG_LTMAIN],
+[m4_ifdef([AC_REQUIRE_AUX_FILE], [AC_REQUIRE_AUX_FILE([ltmain.sh])])dnl
+_LT_CONFIG_LIBTOOL_INIT([ac_aux_dir='$ac_aux_dir'])
+ltmain="$ac_aux_dir/ltmain.sh"
+])# _LT_PROG_LTMAIN
+
+
+
+# So that we can recreate a full libtool script including additional
+# tags, we accumulate the chunks of code to send to AC_CONFIG_COMMANDS
+# in macros and then make a single call at the end using the `libtool'
+# label.
+
+
+# _LT_CONFIG_LIBTOOL_INIT([INIT-COMMANDS])
+# ----------------------------------------
+# Register INIT-COMMANDS to be passed to AC_CONFIG_COMMANDS later.
+m4_define([_LT_CONFIG_LIBTOOL_INIT],
+[m4_ifval([$1],
+          [m4_append([_LT_OUTPUT_LIBTOOL_INIT],
+                     [$1
+])])])
+
+# Initialize.
+m4_define([_LT_OUTPUT_LIBTOOL_INIT])
+
+
+# _LT_CONFIG_LIBTOOL([COMMANDS])
+# ------------------------------
+# Register COMMANDS to be passed to AC_CONFIG_COMMANDS later.
+m4_define([_LT_CONFIG_LIBTOOL],
+[m4_ifval([$1],
+          [m4_append([_LT_OUTPUT_LIBTOOL_COMMANDS],
+                     [$1
+])])])
+
+# Initialize.
+m4_define([_LT_OUTPUT_LIBTOOL_COMMANDS])
+
+
+# _LT_CONFIG_SAVE_COMMANDS([COMMANDS], [INIT_COMMANDS])
+# -----------------------------------------------------
+m4_defun([_LT_CONFIG_SAVE_COMMANDS],
+[_LT_CONFIG_LIBTOOL([$1])
+_LT_CONFIG_LIBTOOL_INIT([$2])
+])
+
+
+# _LT_FORMAT_COMMENT([COMMENT])
+# -----------------------------
+# Add leading comment marks to the start of each line, and a trailing
+# full-stop to the whole comment if one is not present already.
+m4_define([_LT_FORMAT_COMMENT],
+[m4_ifval([$1], [
+m4_bpatsubst([m4_bpatsubst([$1], [^ *], [# ])],
+              [['`$\]], [\\\&])]m4_bmatch([$1], [[!?.]$], [], [.])
+)])
+
+
+
+
+
+# _LT_DECL([CONFIGNAME], VARNAME, VALUE, [DESCRIPTION], [IS-TAGGED?])
+# -------------------------------------------------------------------
+# CONFIGNAME is the name given to the value in the libtool script.
+# VARNAME is the (base) name used in the configure script.
+# VALUE may be 0, 1 or 2 for a computed quote escaped value based on
+# VARNAME.  Any other value will be used directly.
+m4_define([_LT_DECL],
+[lt_if_append_uniq([lt_decl_varnames], [$2], [, ],
+    [lt_dict_add_subkey([lt_decl_dict], [$2], [libtool_name],
+	[m4_ifval([$1], [$1], [$2])])
+    lt_dict_add_subkey([lt_decl_dict], [$2], [value], [$3])
+    m4_ifval([$4],
+	[lt_dict_add_subkey([lt_decl_dict], [$2], [description], [$4])])
+    lt_dict_add_subkey([lt_decl_dict], [$2],
+	[tagged?], [m4_ifval([$5], [yes], [no])])])
+])
+
+
+# _LT_TAGDECL([CONFIGNAME], VARNAME, VALUE, [DESCRIPTION])
+# --------------------------------------------------------
+m4_define([_LT_TAGDECL], [_LT_DECL([$1], [$2], [$3], [$4], [yes])])
+
+
+# lt_decl_tag_varnames([SEPARATOR], [VARNAME1...])
+# ------------------------------------------------
+m4_define([lt_decl_tag_varnames],
+[_lt_decl_filter([tagged?], [yes], $@)])
+
+
+# _lt_decl_filter(SUBKEY, VALUE, [SEPARATOR], [VARNAME1..])
+# ---------------------------------------------------------
+m4_define([_lt_decl_filter],
+[m4_case([$#],
+  [0], [m4_fatal([$0: too few arguments: $#])],
+  [1], [m4_fatal([$0: too few arguments: $#: $1])],
+  [2], [lt_dict_filter([lt_decl_dict], [$1], [$2], [], lt_decl_varnames)],
+  [3], [lt_dict_filter([lt_decl_dict], [$1], [$2], [$3], lt_decl_varnames)],
+  [lt_dict_filter([lt_decl_dict], $@)])[]dnl
+])
+
+
+# lt_decl_quote_varnames([SEPARATOR], [VARNAME1...])
+# --------------------------------------------------
+m4_define([lt_decl_quote_varnames],
+[_lt_decl_filter([value], [1], $@)])
+
+
+# lt_decl_dquote_varnames([SEPARATOR], [VARNAME1...])
+# ---------------------------------------------------
+m4_define([lt_decl_dquote_varnames],
+[_lt_decl_filter([value], [2], $@)])
+
+
+# lt_decl_varnames_tagged([SEPARATOR], [VARNAME1...])
+# ---------------------------------------------------
+m4_define([lt_decl_varnames_tagged],
+[m4_assert([$# <= 2])dnl
+_$0(m4_quote(m4_default([$1], [[, ]])),
+    m4_ifval([$2], [[$2]], [m4_dquote(lt_decl_tag_varnames)]),
+    m4_split(m4_normalize(m4_quote(_LT_TAGS)), [ ]))])
+m4_define([_lt_decl_varnames_tagged],
+[m4_ifval([$3], [lt_combine([$1], [$2], [_], $3)])])
+
+
+# lt_decl_all_varnames([SEPARATOR], [VARNAME1...])
+# ------------------------------------------------
+m4_define([lt_decl_all_varnames],
+[_$0(m4_quote(m4_default([$1], [[, ]])),
+     m4_if([$2], [],
+	   m4_quote(lt_decl_varnames),
+	m4_quote(m4_shift($@))))[]dnl
+])
+m4_define([_lt_decl_all_varnames],
+[lt_join($@, lt_decl_varnames_tagged([$1],
+			lt_decl_tag_varnames([[, ]], m4_shift($@))))dnl
+])
+
+
+# _LT_CONFIG_STATUS_DECLARE([VARNAME])
+# ------------------------------------
+# Quote a variable value, and forward it to `config.status' so that its
+# declaration there will have the same value as in `configure'.  VARNAME
+# must have a single quote delimited value for this to work.
+m4_define([_LT_CONFIG_STATUS_DECLARE],
+[$1='`$ECHO "$][$1" | $SED "$delay_single_quote_subst"`'])
+
+
+# _LT_CONFIG_STATUS_DECLARATIONS
+# ------------------------------
+# We delimit libtool config variables with single quotes, so when
+# we write them to config.status, we have to be sure to quote all
+# embedded single quotes properly.  In configure, this macro expands
+# each variable declared with _LT_DECL (and _LT_TAGDECL) into:
+#
+#    <var>='`$ECHO "$<var>" | $SED "$delay_single_quote_subst"`'
+m4_defun([_LT_CONFIG_STATUS_DECLARATIONS],
+[m4_foreach([_lt_var], m4_quote(lt_decl_all_varnames),
+    [m4_n([_LT_CONFIG_STATUS_DECLARE(_lt_var)])])])
+
+
+# _LT_LIBTOOL_TAGS
+# ----------------
+# Output comment and list of tags supported by the script
+m4_defun([_LT_LIBTOOL_TAGS],
+[_LT_FORMAT_COMMENT([The names of the tagged configurations supported by this script])dnl
+available_tags="_LT_TAGS"dnl
+])
+
+
+# _LT_LIBTOOL_DECLARE(VARNAME, [TAG])
+# -----------------------------------
+# Extract the dictionary values for VARNAME (optionally with TAG) and
+# expand to a commented shell variable setting:
+#
+#    # Some comment about what VAR is for.
+#    visible_name=$lt_internal_name
+m4_define([_LT_LIBTOOL_DECLARE],
+[_LT_FORMAT_COMMENT(m4_quote(lt_dict_fetch([lt_decl_dict], [$1],
+					   [description])))[]dnl
+m4_pushdef([_libtool_name],
+    m4_quote(lt_dict_fetch([lt_decl_dict], [$1], [libtool_name])))[]dnl
+m4_case(m4_quote(lt_dict_fetch([lt_decl_dict], [$1], [value])),
+    [0], [_libtool_name=[$]$1],
+    [1], [_libtool_name=$lt_[]$1],
+    [2], [_libtool_name=$lt_[]$1],
+    [_libtool_name=lt_dict_fetch([lt_decl_dict], [$1], [value])])[]dnl
+m4_ifval([$2], [_$2])[]m4_popdef([_libtool_name])[]dnl
+])
+
+
+# _LT_LIBTOOL_CONFIG_VARS
+# -----------------------
+# Produce commented declarations of non-tagged libtool config variables
+# suitable for insertion in the LIBTOOL CONFIG section of the `libtool'
+# script.  Tagged libtool config variables (even for the LIBTOOL CONFIG
+# section) are produced by _LT_LIBTOOL_TAG_VARS.
+m4_defun([_LT_LIBTOOL_CONFIG_VARS],
+[m4_foreach([_lt_var],
+    m4_quote(_lt_decl_filter([tagged?], [no], [], lt_decl_varnames)),
+    [m4_n([_LT_LIBTOOL_DECLARE(_lt_var)])])])
+
+
+# _LT_LIBTOOL_TAG_VARS(TAG)
+# -------------------------
+m4_define([_LT_LIBTOOL_TAG_VARS],
+[m4_foreach([_lt_var], m4_quote(lt_decl_tag_varnames),
+    [m4_n([_LT_LIBTOOL_DECLARE(_lt_var, [$1])])])])
+
+
+# _LT_TAGVAR(VARNAME, [TAGNAME])
+# ------------------------------
+m4_define([_LT_TAGVAR], [m4_ifval([$2], [$1_$2], [$1])])
+
+
+# _LT_CONFIG_COMMANDS
+# -------------------
+# Send accumulated output to $CONFIG_STATUS.  Thanks to the lists of
+# variables for single and double quote escaping we saved from calls
+# to _LT_DECL, we can put quote escaped variables declarations
+# into `config.status', and then the shell code to quote escape them in
+# for loops in `config.status'.  Finally, any additional code accumulated
+# from calls to _LT_CONFIG_LIBTOOL_INIT is expanded.
+m4_defun([_LT_CONFIG_COMMANDS],
+[AC_PROVIDE_IFELSE([LT_OUTPUT],
+	dnl If the libtool generation code has been placed in $CONFIG_LT,
+	dnl instead of duplicating it all over again into config.status,
+	dnl then we will have config.status run $CONFIG_LT later, so it
+	dnl needs to know what name is stored there:
+        [AC_CONFIG_COMMANDS([libtool],
+            [$SHELL $CONFIG_LT || AS_EXIT(1)], [CONFIG_LT='$CONFIG_LT'])],
+    dnl If the libtool generation code is destined for config.status,
+    dnl expand the accumulated commands and init code now:
+    [AC_CONFIG_COMMANDS([libtool],
+        [_LT_OUTPUT_LIBTOOL_COMMANDS], [_LT_OUTPUT_LIBTOOL_COMMANDS_INIT])])
+])#_LT_CONFIG_COMMANDS
+
+
+# Initialize.
+m4_define([_LT_OUTPUT_LIBTOOL_COMMANDS_INIT],
+[
+
+# The HP-UX ksh and POSIX shell print the target directory to stdout
+# if CDPATH is set.
+(unset CDPATH) >/dev/null 2>&1 && unset CDPATH
+
+sed_quote_subst='$sed_quote_subst'
+double_quote_subst='$double_quote_subst'
+delay_variable_subst='$delay_variable_subst'
+_LT_CONFIG_STATUS_DECLARATIONS
+LTCC='$LTCC'
+LTCFLAGS='$LTCFLAGS'
+compiler='$compiler_DEFAULT'
+
+# A function that is used when there is no print builtin or printf.
+func_fallback_echo ()
+{
+  eval 'cat <<_LTECHO_EOF
+\$[]1
+_LTECHO_EOF'
+}
+
+# Quote evaled strings.
+for var in lt_decl_all_varnames([[ \
+]], lt_decl_quote_varnames); do
+    case \`eval \\\\\$ECHO \\\\""\\\\\$\$var"\\\\"\` in
+    *[[\\\\\\\`\\"\\\$]]*)
+      eval "lt_\$var=\\\\\\"\\\`\\\$ECHO \\"\\\$\$var\\" | \\\$SED \\"\\\$sed_quote_subst\\"\\\`\\\\\\""
+      ;;
+    *)
+      eval "lt_\$var=\\\\\\"\\\$\$var\\\\\\""
+      ;;
+    esac
+done
+
+# Double-quote double-evaled strings.
+for var in lt_decl_all_varnames([[ \
+]], lt_decl_dquote_varnames); do
+    case \`eval \\\\\$ECHO \\\\""\\\\\$\$var"\\\\"\` in
+    *[[\\\\\\\`\\"\\\$]]*)
+      eval "lt_\$var=\\\\\\"\\\`\\\$ECHO \\"\\\$\$var\\" | \\\$SED -e \\"\\\$double_quote_subst\\" -e \\"\\\$sed_quote_subst\\" -e \\"\\\$delay_variable_subst\\"\\\`\\\\\\""
+      ;;
+    *)
+      eval "lt_\$var=\\\\\\"\\\$\$var\\\\\\""
+      ;;
+    esac
+done
+
+_LT_OUTPUT_LIBTOOL_INIT
+])
+
+# _LT_GENERATED_FILE_INIT(FILE, [COMMENT])
+# ------------------------------------
+# Generate a child script FILE with all initialization necessary to
+# reuse the environment learned by the parent script, and make the
+# file executable.  If COMMENT is supplied, it is inserted after the
+# `#!' sequence but before initialization text begins.  After this
+# macro, additional text can be appended to FILE to form the body of
+# the child script.  The macro ends with non-zero status if the
+# file could not be fully written (such as if the disk is full).
+m4_ifdef([AS_INIT_GENERATED],
+[m4_defun([_LT_GENERATED_FILE_INIT],[AS_INIT_GENERATED($@)])],
+[m4_defun([_LT_GENERATED_FILE_INIT],
+[m4_require([AS_PREPARE])]dnl
+[m4_pushdef([AS_MESSAGE_LOG_FD])]dnl
+[lt_write_fail=0
+cat >$1 <<_ASEOF || lt_write_fail=1
+#! $SHELL
+# Generated by $as_me.
+$2
+SHELL=\${CONFIG_SHELL-$SHELL}
+export SHELL
+_ASEOF
+cat >>$1 <<\_ASEOF || lt_write_fail=1
+AS_SHELL_SANITIZE
+_AS_PREPARE
+exec AS_MESSAGE_FD>&1
+_ASEOF
+test $lt_write_fail = 0 && chmod +x $1[]dnl
+m4_popdef([AS_MESSAGE_LOG_FD])])])# _LT_GENERATED_FILE_INIT
+
+# LT_OUTPUT
+# ---------
+# This macro allows early generation of the libtool script (before
+# AC_OUTPUT is called), incase it is used in configure for compilation
+# tests.
+AC_DEFUN([LT_OUTPUT],
+[: ${CONFIG_LT=./config.lt}
+AC_MSG_NOTICE([creating $CONFIG_LT])
+_LT_GENERATED_FILE_INIT(["$CONFIG_LT"],
+[# Run this file to recreate a libtool stub with the current configuration.])
+
+cat >>"$CONFIG_LT" <<\_LTEOF
+lt_cl_silent=false
+exec AS_MESSAGE_LOG_FD>>config.log
+{
+  echo
+  AS_BOX([Running $as_me.])
+} >&AS_MESSAGE_LOG_FD
+
+lt_cl_help="\
+\`$as_me' creates a local libtool stub from the current configuration,
+for use in further configure time tests before the real libtool is
+generated.
+
+Usage: $[0] [[OPTIONS]]
+
+  -h, --help      print this help, then exit
+  -V, --version   print version number, then exit
+  -q, --quiet     do not print progress messages
+  -d, --debug     don't remove temporary files
+
+Report bugs to <bug-libtool@gnu.org>."
+
+lt_cl_version="\
+m4_ifset([AC_PACKAGE_NAME], [AC_PACKAGE_NAME ])config.lt[]dnl
+m4_ifset([AC_PACKAGE_VERSION], [ AC_PACKAGE_VERSION])
+configured by $[0], generated by m4_PACKAGE_STRING.
+
+Copyright (C) 2011 Free Software Foundation, Inc.
+This config.lt script is free software; the Free Software Foundation
+gives unlimited permision to copy, distribute and modify it."
+
+while test $[#] != 0
+do
+  case $[1] in
+    --version | --v* | -V )
+      echo "$lt_cl_version"; exit 0 ;;
+    --help | --h* | -h )
+      echo "$lt_cl_help"; exit 0 ;;
+    --debug | --d* | -d )
+      debug=: ;;
+    --quiet | --q* | --silent | --s* | -q )
+      lt_cl_silent=: ;;
+
+    -*) AC_MSG_ERROR([unrecognized option: $[1]
+Try \`$[0] --help' for more information.]) ;;
+
+    *) AC_MSG_ERROR([unrecognized argument: $[1]
+Try \`$[0] --help' for more information.]) ;;
+  esac
+  shift
+done
+
+if $lt_cl_silent; then
+  exec AS_MESSAGE_FD>/dev/null
+fi
+_LTEOF
+
+cat >>"$CONFIG_LT" <<_LTEOF
+_LT_OUTPUT_LIBTOOL_COMMANDS_INIT
+_LTEOF
+
+cat >>"$CONFIG_LT" <<\_LTEOF
+AC_MSG_NOTICE([creating $ofile])
+_LT_OUTPUT_LIBTOOL_COMMANDS
+AS_EXIT(0)
+_LTEOF
+chmod +x "$CONFIG_LT"
+
+# configure is writing to config.log, but config.lt does its own redirection,
+# appending to config.log, which fails on DOS, as config.log is still kept
+# open by configure.  Here we exec the FD to /dev/null, effectively closing
+# config.log, so it can be properly (re)opened and appended to by config.lt.
+lt_cl_success=:
+test "$silent" = yes &&
+  lt_config_lt_args="$lt_config_lt_args --quiet"
+exec AS_MESSAGE_LOG_FD>/dev/null
+$SHELL "$CONFIG_LT" $lt_config_lt_args || lt_cl_success=false
+exec AS_MESSAGE_LOG_FD>>config.log
+$lt_cl_success || AS_EXIT(1)
+])# LT_OUTPUT
+
+
+# _LT_CONFIG(TAG)
+# ---------------
+# If TAG is the built-in tag, create an initial libtool script with a
+# default configuration from the untagged config vars.  Otherwise add code
+# to config.status for appending the configuration named by TAG from the
+# matching tagged config vars.
+m4_defun([_LT_CONFIG],
+[m4_require([_LT_FILEUTILS_DEFAULTS])dnl
+_LT_CONFIG_SAVE_COMMANDS([
+  m4_define([_LT_TAG], m4_if([$1], [], [C], [$1]))dnl
+  m4_if(_LT_TAG, [C], [
+    # See if we are running on zsh, and set the options which allow our
+    # commands through without removal of \ escapes.
+    if test -n "${ZSH_VERSION+set}" ; then
+      setopt NO_GLOB_SUBST
+    fi
+
+    cfgfile="${ofile}T"
+    trap "$RM \"$cfgfile\"; exit 1" 1 2 15
+    $RM "$cfgfile"
+
+    cat <<_LT_EOF >> "$cfgfile"
+#! $SHELL
+
+# `$ECHO "$ofile" | sed 's%^.*/%%'` - Provide generalized library-building support services.
+# Generated automatically by $as_me ($PACKAGE$TIMESTAMP) $VERSION
+# Libtool was configured on host `(hostname || uname -n) 2>/dev/null | sed 1q`:
+# NOTE: Changes made to this file will be lost: look at ltmain.sh.
+#
+_LT_COPYING
+_LT_LIBTOOL_TAGS
+
+# ### BEGIN LIBTOOL CONFIG
+_LT_LIBTOOL_CONFIG_VARS
+_LT_LIBTOOL_TAG_VARS
+# ### END LIBTOOL CONFIG
+
+_LT_EOF
+
+  case $host_os in
+  aix3*)
+    cat <<\_LT_EOF >> "$cfgfile"
+# AIX sometimes has problems with the GCC collect2 program.  For some
+# reason, if we set the COLLECT_NAMES environment variable, the problems
+# vanish in a puff of smoke.
+if test "X${COLLECT_NAMES+set}" != Xset; then
+  COLLECT_NAMES=
+  export COLLECT_NAMES
+fi
+_LT_EOF
+    ;;
+  esac
+
+  _LT_PROG_LTMAIN
+
+  # We use sed instead of cat because bash on DJGPP gets confused if
+  # if finds mixed CR/LF and LF-only lines.  Since sed operates in
+  # text mode, it properly converts lines to CR/LF.  This bash problem
+  # is reportedly fixed, but why not run on old versions too?
+  sed '$q' "$ltmain" >> "$cfgfile" \
+     || (rm -f "$cfgfile"; exit 1)
+
+  _LT_PROG_REPLACE_SHELLFNS
+
+   mv -f "$cfgfile" "$ofile" ||
+    (rm -f "$ofile" && cp "$cfgfile" "$ofile" && rm -f "$cfgfile")
+  chmod +x "$ofile"
+],
+[cat <<_LT_EOF >> "$ofile"
+
+dnl Unfortunately we have to use $1 here, since _LT_TAG is not expanded
+dnl in a comment (ie after a #).
+# ### BEGIN LIBTOOL TAG CONFIG: $1
+_LT_LIBTOOL_TAG_VARS(_LT_TAG)
+# ### END LIBTOOL TAG CONFIG: $1
+_LT_EOF
+])dnl /m4_if
+],
+[m4_if([$1], [], [
+    PACKAGE='$PACKAGE'
+    VERSION='$VERSION'
+    TIMESTAMP='$TIMESTAMP'
+    RM='$RM'
+    ofile='$ofile'], [])
+])dnl /_LT_CONFIG_SAVE_COMMANDS
+])# _LT_CONFIG
+
+
+# LT_SUPPORTED_TAG(TAG)
+# ---------------------
+# Trace this macro to discover what tags are supported by the libtool
+# --tag option, using:
+#    autoconf --trace 'LT_SUPPORTED_TAG:$1'
+AC_DEFUN([LT_SUPPORTED_TAG], [])
+
+
+# C support is built-in for now
+m4_define([_LT_LANG_C_enabled], [])
+m4_define([_LT_TAGS], [])
+
+
+# LT_LANG(LANG)
+# -------------
+# Enable libtool support for the given language if not already enabled.
+AC_DEFUN([LT_LANG],
+[AC_BEFORE([$0], [LT_OUTPUT])dnl
+m4_case([$1],
+  [C],			[_LT_LANG(C)],
+  [C++],		[_LT_LANG(CXX)],
+  [Go],			[_LT_LANG(GO)],
+  [Java],		[_LT_LANG(GCJ)],
+  [Fortran 77],		[_LT_LANG(F77)],
+  [Fortran],		[_LT_LANG(FC)],
+  [Windows Resource],	[_LT_LANG(RC)],
+  [m4_ifdef([_LT_LANG_]$1[_CONFIG],
+    [_LT_LANG($1)],
+    [m4_fatal([$0: unsupported language: "$1"])])])dnl
+])# LT_LANG
+
+
+# _LT_LANG(LANGNAME)
+# ------------------
+m4_defun([_LT_LANG],
+[m4_ifdef([_LT_LANG_]$1[_enabled], [],
+  [LT_SUPPORTED_TAG([$1])dnl
+  m4_append([_LT_TAGS], [$1 ])dnl
+  m4_define([_LT_LANG_]$1[_enabled], [])dnl
+  _LT_LANG_$1_CONFIG($1)])dnl
+])# _LT_LANG
+
+
+m4_ifndef([AC_PROG_GO], [
+# NOTE: This macro has been submitted for inclusion into   #
+#  GNU Autoconf as AC_PROG_GO.  When it is available in    #
+#  a released version of Autoconf we should remove this    #
+#  macro and use it instead.                               #
+m4_defun([AC_PROG_GO],
+[AC_LANG_PUSH(Go)dnl
+AC_ARG_VAR([GOC],     [Go compiler command])dnl
+AC_ARG_VAR([GOFLAGS], [Go compiler flags])dnl
+_AC_ARG_VAR_LDFLAGS()dnl
+AC_CHECK_TOOL(GOC, gccgo)
+if test -z "$GOC"; then
+  if test -n "$ac_tool_prefix"; then
+    AC_CHECK_PROG(GOC, [${ac_tool_prefix}gccgo], [${ac_tool_prefix}gccgo])
+  fi
+fi
+if test -z "$GOC"; then
+  AC_CHECK_PROG(GOC, gccgo, gccgo, false)
+fi
+])#m4_defun
+])#m4_ifndef
+
+
+# _LT_LANG_DEFAULT_CONFIG
+# -----------------------
+m4_defun([_LT_LANG_DEFAULT_CONFIG],
+[AC_PROVIDE_IFELSE([AC_PROG_CXX],
+  [LT_LANG(CXX)],
+  [m4_define([AC_PROG_CXX], defn([AC_PROG_CXX])[LT_LANG(CXX)])])
+
+AC_PROVIDE_IFELSE([AC_PROG_F77],
+  [LT_LANG(F77)],
+  [m4_define([AC_PROG_F77], defn([AC_PROG_F77])[LT_LANG(F77)])])
+
+AC_PROVIDE_IFELSE([AC_PROG_FC],
+  [LT_LANG(FC)],
+  [m4_define([AC_PROG_FC], defn([AC_PROG_FC])[LT_LANG(FC)])])
+
+dnl The call to [A][M_PROG_GCJ] is quoted like that to stop aclocal
+dnl pulling things in needlessly.
+AC_PROVIDE_IFELSE([AC_PROG_GCJ],
+  [LT_LANG(GCJ)],
+  [AC_PROVIDE_IFELSE([A][M_PROG_GCJ],
+    [LT_LANG(GCJ)],
+    [AC_PROVIDE_IFELSE([LT_PROG_GCJ],
+      [LT_LANG(GCJ)],
+      [m4_ifdef([AC_PROG_GCJ],
+	[m4_define([AC_PROG_GCJ], defn([AC_PROG_GCJ])[LT_LANG(GCJ)])])
+       m4_ifdef([A][M_PROG_GCJ],
+	[m4_define([A][M_PROG_GCJ], defn([A][M_PROG_GCJ])[LT_LANG(GCJ)])])
+       m4_ifdef([LT_PROG_GCJ],
+	[m4_define([LT_PROG_GCJ], defn([LT_PROG_GCJ])[LT_LANG(GCJ)])])])])])
+
+AC_PROVIDE_IFELSE([AC_PROG_GO],
+  [LT_LANG(GO)],
+  [m4_define([AC_PROG_GO], defn([AC_PROG_GO])[LT_LANG(GO)])])
+
+AC_PROVIDE_IFELSE([LT_PROG_RC],
+  [LT_LANG(RC)],
+  [m4_define([LT_PROG_RC], defn([LT_PROG_RC])[LT_LANG(RC)])])
+])# _LT_LANG_DEFAULT_CONFIG
+
+# Obsolete macros:
+AU_DEFUN([AC_LIBTOOL_CXX], [LT_LANG(C++)])
+AU_DEFUN([AC_LIBTOOL_F77], [LT_LANG(Fortran 77)])
+AU_DEFUN([AC_LIBTOOL_FC], [LT_LANG(Fortran)])
+AU_DEFUN([AC_LIBTOOL_GCJ], [LT_LANG(Java)])
+AU_DEFUN([AC_LIBTOOL_RC], [LT_LANG(Windows Resource)])
+dnl aclocal-1.4 backwards compatibility:
+dnl AC_DEFUN([AC_LIBTOOL_CXX], [])
+dnl AC_DEFUN([AC_LIBTOOL_F77], [])
+dnl AC_DEFUN([AC_LIBTOOL_FC], [])
+dnl AC_DEFUN([AC_LIBTOOL_GCJ], [])
+dnl AC_DEFUN([AC_LIBTOOL_RC], [])
+
+
+# _LT_TAG_COMPILER
+# ----------------
+m4_defun([_LT_TAG_COMPILER],
+[AC_REQUIRE([AC_PROG_CC])dnl
+
+_LT_DECL([LTCC], [CC], [1], [A C compiler])dnl
+_LT_DECL([LTCFLAGS], [CFLAGS], [1], [LTCC compiler flags])dnl
+_LT_TAGDECL([CC], [compiler], [1], [A language specific compiler])dnl
+_LT_TAGDECL([with_gcc], [GCC], [0], [Is the compiler the GNU compiler?])dnl
+
+# If no C compiler was specified, use CC.
+LTCC=${LTCC-"$CC"}
+
+# If no C compiler flags were specified, use CFLAGS.
+LTCFLAGS=${LTCFLAGS-"$CFLAGS"}
+
+# Allow CC to be a program name with arguments.
+compiler=$CC
+])# _LT_TAG_COMPILER
+
+
+# _LT_COMPILER_BOILERPLATE
+# ------------------------
+# Check for compiler boilerplate output or warnings with
+# the simple compiler test code.
+m4_defun([_LT_COMPILER_BOILERPLATE],
+[m4_require([_LT_DECL_SED])dnl
+ac_outfile=conftest.$ac_objext
+echo "$lt_simple_compile_test_code" >conftest.$ac_ext
+eval "$ac_compile" 2>&1 >/dev/null | $SED '/^$/d; /^ *+/d' >conftest.err
+_lt_compiler_boilerplate=`cat conftest.err`
+$RM conftest*
+])# _LT_COMPILER_BOILERPLATE
+
+
+# _LT_LINKER_BOILERPLATE
+# ----------------------
+# Check for linker boilerplate output or warnings with
+# the simple link test code.
+m4_defun([_LT_LINKER_BOILERPLATE],
+[m4_require([_LT_DECL_SED])dnl
+ac_outfile=conftest.$ac_objext
+echo "$lt_simple_link_test_code" >conftest.$ac_ext
+eval "$ac_link" 2>&1 >/dev/null | $SED '/^$/d; /^ *+/d' >conftest.err
+_lt_linker_boilerplate=`cat conftest.err`
+$RM -r conftest*
+])# _LT_LINKER_BOILERPLATE
+
+# _LT_REQUIRED_DARWIN_CHECKS
+# -------------------------
+m4_defun_once([_LT_REQUIRED_DARWIN_CHECKS],[
+  case $host_os in
+    rhapsody* | darwin*)
+    AC_CHECK_TOOL([DSYMUTIL], [dsymutil], [:])
+    AC_CHECK_TOOL([NMEDIT], [nmedit], [:])
+    AC_CHECK_TOOL([LIPO], [lipo], [:])
+    AC_CHECK_TOOL([OTOOL], [otool], [:])
+    AC_CHECK_TOOL([OTOOL64], [otool64], [:])
+    _LT_DECL([], [DSYMUTIL], [1],
+      [Tool to manipulate archived DWARF debug symbol files on Mac OS X])
+    _LT_DECL([], [NMEDIT], [1],
+      [Tool to change global to local symbols on Mac OS X])
+    _LT_DECL([], [LIPO], [1],
+      [Tool to manipulate fat objects and archives on Mac OS X])
+    _LT_DECL([], [OTOOL], [1],
+      [ldd/readelf like tool for Mach-O binaries on Mac OS X])
+    _LT_DECL([], [OTOOL64], [1],
+      [ldd/readelf like tool for 64 bit Mach-O binaries on Mac OS X 10.4])
+
+    AC_CACHE_CHECK([for -single_module linker flag],[lt_cv_apple_cc_single_mod],
+      [lt_cv_apple_cc_single_mod=no
+      if test -z "${LT_MULTI_MODULE}"; then
+	# By default we will add the -single_module flag. You can override
+	# by either setting the environment variable LT_MULTI_MODULE
+	# non-empty at configure time, or by adding -multi_module to the
+	# link flags.
+	rm -rf libconftest.dylib*
+	echo "int foo(void){return 1;}" > conftest.c
+	echo "$LTCC $LTCFLAGS $LDFLAGS -o libconftest.dylib \
+-dynamiclib -Wl,-single_module conftest.c" >&AS_MESSAGE_LOG_FD
+	$LTCC $LTCFLAGS $LDFLAGS -o libconftest.dylib \
+	  -dynamiclib -Wl,-single_module conftest.c 2>conftest.err
+        _lt_result=$?
+	# If there is a non-empty error log, and "single_module"
+	# appears in it, assume the flag caused a linker warning
+        if test -s conftest.err && $GREP single_module conftest.err; then
+	  cat conftest.err >&AS_MESSAGE_LOG_FD
+	# Otherwise, if the output was created with a 0 exit code from
+	# the compiler, it worked.
+	elif test -f libconftest.dylib && test $_lt_result -eq 0; then
+	  lt_cv_apple_cc_single_mod=yes
+	else
+	  cat conftest.err >&AS_MESSAGE_LOG_FD
+	fi
+	rm -rf libconftest.dylib*
+	rm -f conftest.*
+      fi])
+
+    AC_CACHE_CHECK([for -exported_symbols_list linker flag],
+      [lt_cv_ld_exported_symbols_list],
+      [lt_cv_ld_exported_symbols_list=no
+      save_LDFLAGS=$LDFLAGS
+      echo "_main" > conftest.sym
+      LDFLAGS="$LDFLAGS -Wl,-exported_symbols_list,conftest.sym"
+      AC_LINK_IFELSE([AC_LANG_PROGRAM([],[])],
+	[lt_cv_ld_exported_symbols_list=yes],
+	[lt_cv_ld_exported_symbols_list=no])
+	LDFLAGS="$save_LDFLAGS"
+    ])
+
+    AC_CACHE_CHECK([for -force_load linker flag],[lt_cv_ld_force_load],
+      [lt_cv_ld_force_load=no
+      cat > conftest.c << _LT_EOF
+int forced_loaded() { return 2;}
+_LT_EOF
+      echo "$LTCC $LTCFLAGS -c -o conftest.o conftest.c" >&AS_MESSAGE_LOG_FD
+      $LTCC $LTCFLAGS -c -o conftest.o conftest.c 2>&AS_MESSAGE_LOG_FD
+      echo "$AR cru libconftest.a conftest.o" >&AS_MESSAGE_LOG_FD
+      $AR cru libconftest.a conftest.o 2>&AS_MESSAGE_LOG_FD
+      echo "$RANLIB libconftest.a" >&AS_MESSAGE_LOG_FD
+      $RANLIB libconftest.a 2>&AS_MESSAGE_LOG_FD
+      cat > conftest.c << _LT_EOF
+int main() { return 0;}
+_LT_EOF
+      echo "$LTCC $LTCFLAGS $LDFLAGS -o conftest conftest.c -Wl,-force_load,./libconftest.a" >&AS_MESSAGE_LOG_FD
+      $LTCC $LTCFLAGS $LDFLAGS -o conftest conftest.c -Wl,-force_load,./libconftest.a 2>conftest.err
+      _lt_result=$?
+      if test -s conftest.err && $GREP force_load conftest.err; then
+	cat conftest.err >&AS_MESSAGE_LOG_FD
+      elif test -f conftest && test $_lt_result -eq 0 && $GREP forced_load conftest >/dev/null 2>&1 ; then
+	lt_cv_ld_force_load=yes
+      else
+	cat conftest.err >&AS_MESSAGE_LOG_FD
+      fi
+        rm -f conftest.err libconftest.a conftest conftest.c
+        rm -rf conftest.dSYM
+    ])
+    case $host_os in
+    rhapsody* | darwin1.[[012]])
+      _lt_dar_allow_undefined='${wl}-undefined ${wl}suppress' ;;
+    darwin1.*)
+      _lt_dar_allow_undefined='${wl}-flat_namespace ${wl}-undefined ${wl}suppress' ;;
+    darwin*) # darwin 5.x on
+      # if running on 10.5 or later, the deployment target defaults
+      # to the OS version, if on x86, and 10.4, the deployment
+      # target defaults to 10.4. Don't you love it?
+      case ${MACOSX_DEPLOYMENT_TARGET-10.0},$host in
+	10.0,*86*-darwin8*|10.0,*-darwin[[91]]*)
+	  _lt_dar_allow_undefined='${wl}-undefined ${wl}dynamic_lookup' ;;
+	10.[[012]]*)
+	  _lt_dar_allow_undefined='${wl}-flat_namespace ${wl}-undefined ${wl}suppress' ;;
+	10.*)
+	  _lt_dar_allow_undefined='${wl}-undefined ${wl}dynamic_lookup' ;;
+      esac
+    ;;
+  esac
+    if test "$lt_cv_apple_cc_single_mod" = "yes"; then
+      _lt_dar_single_mod='$single_module'
+    fi
+    if test "$lt_cv_ld_exported_symbols_list" = "yes"; then
+      _lt_dar_export_syms=' ${wl}-exported_symbols_list,$output_objdir/${libname}-symbols.expsym'
+    else
+      _lt_dar_export_syms='~$NMEDIT -s $output_objdir/${libname}-symbols.expsym ${lib}'
+    fi
+    if test "$DSYMUTIL" != ":" && test "$lt_cv_ld_force_load" = "no"; then
+      _lt_dsymutil='~$DSYMUTIL $lib || :'
+    else
+      _lt_dsymutil=
+    fi
+    ;;
+  esac
+])
+
+
+# _LT_DARWIN_LINKER_FEATURES([TAG])
+# ---------------------------------
+# Checks for linker and compiler features on darwin
+m4_defun([_LT_DARWIN_LINKER_FEATURES],
+[
+  m4_require([_LT_REQUIRED_DARWIN_CHECKS])
+  _LT_TAGVAR(archive_cmds_need_lc, $1)=no
+  _LT_TAGVAR(hardcode_direct, $1)=no
+  _LT_TAGVAR(hardcode_automatic, $1)=yes
+  _LT_TAGVAR(hardcode_shlibpath_var, $1)=unsupported
+  if test "$lt_cv_ld_force_load" = "yes"; then
+    _LT_TAGVAR(whole_archive_flag_spec, $1)='`for conv in $convenience\"\"; do test  -n \"$conv\" && new_convenience=\"$new_convenience ${wl}-force_load,$conv\"; done; func_echo_all \"$new_convenience\"`'
+    m4_case([$1], [F77], [_LT_TAGVAR(compiler_needs_object, $1)=yes],
+                  [FC],  [_LT_TAGVAR(compiler_needs_object, $1)=yes])
+  else
+    _LT_TAGVAR(whole_archive_flag_spec, $1)=''
+  fi
+  _LT_TAGVAR(link_all_deplibs, $1)=yes
+  _LT_TAGVAR(allow_undefined_flag, $1)="$_lt_dar_allow_undefined"
+  case $cc_basename in
+     ifort*) _lt_dar_can_shared=yes ;;
+     *) _lt_dar_can_shared=$GCC ;;
+  esac
+  if test "$_lt_dar_can_shared" = "yes"; then
+    output_verbose_link_cmd=func_echo_all
+    _LT_TAGVAR(archive_cmds, $1)="\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring $_lt_dar_single_mod${_lt_dsymutil}"
+    _LT_TAGVAR(module_cmds, $1)="\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags${_lt_dsymutil}"
+    _LT_TAGVAR(archive_expsym_cmds, $1)="sed 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring ${_lt_dar_single_mod}${_lt_dar_export_syms}${_lt_dsymutil}"
+    _LT_TAGVAR(module_expsym_cmds, $1)="sed -e 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags${_lt_dar_export_syms}${_lt_dsymutil}"
+    m4_if([$1], [CXX],
+[   if test "$lt_cv_apple_cc_single_mod" != "yes"; then
+      _LT_TAGVAR(archive_cmds, $1)="\$CC -r -keep_private_externs -nostdlib -o \${lib}-master.o \$libobjs~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \${lib}-master.o \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring${_lt_dsymutil}"
+      _LT_TAGVAR(archive_expsym_cmds, $1)="sed 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC -r -keep_private_externs -nostdlib -o \${lib}-master.o \$libobjs~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \${lib}-master.o \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring${_lt_dar_export_syms}${_lt_dsymutil}"
+    fi
+],[])
+  else
+  _LT_TAGVAR(ld_shlibs, $1)=no
+  fi
+])
+
+# _LT_SYS_MODULE_PATH_AIX([TAGNAME])
+# ----------------------------------
+# Links a minimal program and checks the executable
+# for the system default hardcoded library path. In most cases,
+# this is /usr/lib:/lib, but when the MPI compilers are used
+# the location of the communication and MPI libs are included too.
+# If we don't find anything, use the default library path according
+# to the aix ld manual.
+# Store the results from the different compilers for each TAGNAME.
+# Allow to override them for all tags through lt_cv_aix_libpath.
+m4_defun([_LT_SYS_MODULE_PATH_AIX],
+[m4_require([_LT_DECL_SED])dnl
+if test "${lt_cv_aix_libpath+set}" = set; then
+  aix_libpath=$lt_cv_aix_libpath
+else
+  AC_CACHE_VAL([_LT_TAGVAR([lt_cv_aix_libpath_], [$1])],
+  [AC_LINK_IFELSE([AC_LANG_PROGRAM],[
+  lt_aix_libpath_sed='[
+      /Import File Strings/,/^$/ {
+	  /^0/ {
+	      s/^0  *\([^ ]*\) *$/\1/
+	      p
+	  }
+      }]'
+  _LT_TAGVAR([lt_cv_aix_libpath_], [$1])=`dump -H conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"`
+  # Check for a 64-bit object if we didn't find anything.
+  if test -z "$_LT_TAGVAR([lt_cv_aix_libpath_], [$1])"; then
+    _LT_TAGVAR([lt_cv_aix_libpath_], [$1])=`dump -HX64 conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"`
+  fi],[])
+  if test -z "$_LT_TAGVAR([lt_cv_aix_libpath_], [$1])"; then
+    _LT_TAGVAR([lt_cv_aix_libpath_], [$1])="/usr/lib:/lib"
+  fi
+  ])
+  aix_libpath=$_LT_TAGVAR([lt_cv_aix_libpath_], [$1])
+fi
+])# _LT_SYS_MODULE_PATH_AIX
+
+
+# _LT_SHELL_INIT(ARG)
+# -------------------
+m4_define([_LT_SHELL_INIT],
+[m4_divert_text([M4SH-INIT], [$1
+])])# _LT_SHELL_INIT
+
+
+
+# _LT_PROG_ECHO_BACKSLASH
+# -----------------------
+# Find how we can fake an echo command that does not interpret backslash.
+# In particular, with Autoconf 2.60 or later we add some code to the start
+# of the generated configure script which will find a shell with a builtin
+# printf (which we can use as an echo command).
+m4_defun([_LT_PROG_ECHO_BACKSLASH],
+[ECHO='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\'
+ECHO=$ECHO$ECHO$ECHO$ECHO$ECHO
+ECHO=$ECHO$ECHO$ECHO$ECHO$ECHO$ECHO
+
+AC_MSG_CHECKING([how to print strings])
+# Test print first, because it will be a builtin if present.
+if test "X`( print -r -- -n ) 2>/dev/null`" = X-n && \
+   test "X`print -r -- $ECHO 2>/dev/null`" = "X$ECHO"; then
+  ECHO='print -r --'
+elif test "X`printf %s $ECHO 2>/dev/null`" = "X$ECHO"; then
+  ECHO='printf %s\n'
+else
+  # Use this function as a fallback that always works.
+  func_fallback_echo ()
+  {
+    eval 'cat <<_LTECHO_EOF
+$[]1
+_LTECHO_EOF'
+  }
+  ECHO='func_fallback_echo'
+fi
+
+# func_echo_all arg...
+# Invoke $ECHO with all args, space-separated.
+func_echo_all ()
+{
+    $ECHO "$*" 
+}
+
+case "$ECHO" in
+  printf*) AC_MSG_RESULT([printf]) ;;
+  print*) AC_MSG_RESULT([print -r]) ;;
+  *) AC_MSG_RESULT([cat]) ;;
+esac
+
+m4_ifdef([_AS_DETECT_SUGGESTED],
+[_AS_DETECT_SUGGESTED([
+  test -n "${ZSH_VERSION+set}${BASH_VERSION+set}" || (
+    ECHO='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\'
+    ECHO=$ECHO$ECHO$ECHO$ECHO$ECHO
+    ECHO=$ECHO$ECHO$ECHO$ECHO$ECHO$ECHO
+    PATH=/empty FPATH=/empty; export PATH FPATH
+    test "X`printf %s $ECHO`" = "X$ECHO" \
+      || test "X`print -r -- $ECHO`" = "X$ECHO" )])])
+
+_LT_DECL([], [SHELL], [1], [Shell to use when invoking shell scripts])
+_LT_DECL([], [ECHO], [1], [An echo program that protects backslashes])
+])# _LT_PROG_ECHO_BACKSLASH
+
+
+# _LT_WITH_SYSROOT
+# ----------------
+AC_DEFUN([_LT_WITH_SYSROOT],
+[AC_MSG_CHECKING([for sysroot])
+AC_ARG_WITH([sysroot],
+[  --with-sysroot[=DIR] Search for dependent libraries within DIR
+                        (or the compiler's sysroot if not specified).],
+[], [with_sysroot=no])
+
+dnl lt_sysroot will always be passed unquoted.  We quote it here
+dnl in case the user passed a directory name.
+lt_sysroot=
+case ${with_sysroot} in #(
+ yes)
+   if test "$GCC" = yes; then
+     lt_sysroot=`$CC --print-sysroot 2>/dev/null`
+   fi
+   ;; #(
+ /*)
+   lt_sysroot=`echo "$with_sysroot" | sed -e "$sed_quote_subst"`
+   ;; #(
+ no|'')
+   ;; #(
+ *)
+   AC_MSG_RESULT([${with_sysroot}])
+   AC_MSG_ERROR([The sysroot must be an absolute path.])
+   ;;
+esac
+
+ AC_MSG_RESULT([${lt_sysroot:-no}])
+_LT_DECL([], [lt_sysroot], [0], [The root where to search for ]dnl
+[dependent libraries, and in which our libraries should be installed.])])
+
+# _LT_ENABLE_LOCK
+# ---------------
+m4_defun([_LT_ENABLE_LOCK],
+[AC_ARG_ENABLE([libtool-lock],
+  [AS_HELP_STRING([--disable-libtool-lock],
+    [avoid locking (might break parallel builds)])])
+test "x$enable_libtool_lock" != xno && enable_libtool_lock=yes
+
+# Some flags need to be propagated to the compiler or linker for good
+# libtool support.
+case $host in
+ia64-*-hpux*)
+  # Find out which ABI we are using.
+  echo 'int i;' > conftest.$ac_ext
+  if AC_TRY_EVAL(ac_compile); then
+    case `/usr/bin/file conftest.$ac_objext` in
+      *ELF-32*)
+	HPUX_IA64_MODE="32"
+	;;
+      *ELF-64*)
+	HPUX_IA64_MODE="64"
+	;;
+    esac
+  fi
+  rm -rf conftest*
+  ;;
+*-*-irix6*)
+  # Find out which ABI we are using.
+  echo '[#]line '$LINENO' "configure"' > conftest.$ac_ext
+  if AC_TRY_EVAL(ac_compile); then
+    if test "$lt_cv_prog_gnu_ld" = yes; then
+      case `/usr/bin/file conftest.$ac_objext` in
+	*32-bit*)
+	  LD="${LD-ld} -melf32bsmip"
+	  ;;
+	*N32*)
+	  LD="${LD-ld} -melf32bmipn32"
+	  ;;
+	*64-bit*)
+	  LD="${LD-ld} -melf64bmip"
+	;;
+      esac
+    else
+      case `/usr/bin/file conftest.$ac_objext` in
+	*32-bit*)
+	  LD="${LD-ld} -32"
+	  ;;
+	*N32*)
+	  LD="${LD-ld} -n32"
+	  ;;
+	*64-bit*)
+	  LD="${LD-ld} -64"
+	  ;;
+      esac
+    fi
+  fi
+  rm -rf conftest*
+  ;;
+
+x86_64-*kfreebsd*-gnu|x86_64-*linux*|ppc*-*linux*|powerpc*-*linux*| \
+s390*-*linux*|s390*-*tpf*|sparc*-*linux*)
+  # Find out which ABI we are using.
+  echo 'int i;' > conftest.$ac_ext
+  if AC_TRY_EVAL(ac_compile); then
+    case `/usr/bin/file conftest.o` in
+      *32-bit*)
+	case $host in
+	  x86_64-*kfreebsd*-gnu)
+	    LD="${LD-ld} -m elf_i386_fbsd"
+	    ;;
+	  x86_64-*linux*)
+	    case `/usr/bin/file conftest.o` in
+	      *x86-64*)
+		LD="${LD-ld} -m elf32_x86_64"
+		;;
+	      *)
+		LD="${LD-ld} -m elf_i386"
+		;;
+	    esac
+	    ;;
+	  ppc64-*linux*|powerpc64-*linux*)
+	    LD="${LD-ld} -m elf32ppclinux"
+	    ;;
+	  s390x-*linux*)
+	    LD="${LD-ld} -m elf_s390"
+	    ;;
+	  sparc64-*linux*)
+	    LD="${LD-ld} -m elf32_sparc"
+	    ;;
+	esac
+	;;
+      *64-bit*)
+	case $host in
+	  x86_64-*kfreebsd*-gnu)
+	    LD="${LD-ld} -m elf_x86_64_fbsd"
+	    ;;
+	  x86_64-*linux*)
+	    LD="${LD-ld} -m elf_x86_64"
+	    ;;
+	  ppc*-*linux*|powerpc*-*linux*)
+	    LD="${LD-ld} -m elf64ppc"
+	    ;;
+	  s390*-*linux*|s390*-*tpf*)
+	    LD="${LD-ld} -m elf64_s390"
+	    ;;
+	  sparc*-*linux*)
+	    LD="${LD-ld} -m elf64_sparc"
+	    ;;
+	esac
+	;;
+    esac
+  fi
+  rm -rf conftest*
+  ;;
+
+*-*-sco3.2v5*)
+  # On SCO OpenServer 5, we need -belf to get full-featured binaries.
+  SAVE_CFLAGS="$CFLAGS"
+  CFLAGS="$CFLAGS -belf"
+  AC_CACHE_CHECK([whether the C compiler needs -belf], lt_cv_cc_needs_belf,
+    [AC_LANG_PUSH(C)
+     AC_LINK_IFELSE([AC_LANG_PROGRAM([[]],[[]])],[lt_cv_cc_needs_belf=yes],[lt_cv_cc_needs_belf=no])
+     AC_LANG_POP])
+  if test x"$lt_cv_cc_needs_belf" != x"yes"; then
+    # this is probably gcc 2.8.0, egcs 1.0 or newer; no need for -belf
+    CFLAGS="$SAVE_CFLAGS"
+  fi
+  ;;
+*-*solaris*)
+  # Find out which ABI we are using.
+  echo 'int i;' > conftest.$ac_ext
+  if AC_TRY_EVAL(ac_compile); then
+    case `/usr/bin/file conftest.o` in
+    *64-bit*)
+      case $lt_cv_prog_gnu_ld in
+      yes*)
+        case $host in
+        i?86-*-solaris*)
+          LD="${LD-ld} -m elf_x86_64"
+          ;;
+        sparc*-*-solaris*)
+          LD="${LD-ld} -m elf64_sparc"
+          ;;
+        esac
+        # GNU ld 2.21 introduced _sol2 emulations.  Use them if available.
+        if ${LD-ld} -V | grep _sol2 >/dev/null 2>&1; then
+          LD="${LD-ld}_sol2"
+        fi
+        ;;
+      *)
+	if ${LD-ld} -64 -r -o conftest2.o conftest.o >/dev/null 2>&1; then
+	  LD="${LD-ld} -64"
+	fi
+	;;
+      esac
+      ;;
+    esac
+  fi
+  rm -rf conftest*
+  ;;
+esac
+
+need_locks="$enable_libtool_lock"
+])# _LT_ENABLE_LOCK
+
+
+# _LT_PROG_AR
+# -----------
+m4_defun([_LT_PROG_AR],
+[AC_CHECK_TOOLS(AR, [ar], false)
+: ${AR=ar}
+: ${AR_FLAGS=cru}
+_LT_DECL([], [AR], [1], [The archiver])
+_LT_DECL([], [AR_FLAGS], [1], [Flags to create an archive])
+
+AC_CACHE_CHECK([for archiver @FILE support], [lt_cv_ar_at_file],
+  [lt_cv_ar_at_file=no
+   AC_COMPILE_IFELSE([AC_LANG_PROGRAM],
+     [echo conftest.$ac_objext > conftest.lst
+      lt_ar_try='$AR $AR_FLAGS libconftest.a @conftest.lst >&AS_MESSAGE_LOG_FD'
+      AC_TRY_EVAL([lt_ar_try])
+      if test "$ac_status" -eq 0; then
+	# Ensure the archiver fails upon bogus file names.
+	rm -f conftest.$ac_objext libconftest.a
+	AC_TRY_EVAL([lt_ar_try])
+	if test "$ac_status" -ne 0; then
+          lt_cv_ar_at_file=@
+        fi
+      fi
+      rm -f conftest.* libconftest.a
+     ])
+  ])
+
+if test "x$lt_cv_ar_at_file" = xno; then
+  archiver_list_spec=
+else
+  archiver_list_spec=$lt_cv_ar_at_file
+fi
+_LT_DECL([], [archiver_list_spec], [1],
+  [How to feed a file listing to the archiver])
+])# _LT_PROG_AR
+
+
+# _LT_CMD_OLD_ARCHIVE
+# -------------------
+m4_defun([_LT_CMD_OLD_ARCHIVE],
+[_LT_PROG_AR
+
+AC_CHECK_TOOL(STRIP, strip, :)
+test -z "$STRIP" && STRIP=:
+_LT_DECL([], [STRIP], [1], [A symbol stripping program])
+
+AC_CHECK_TOOL(RANLIB, ranlib, :)
+test -z "$RANLIB" && RANLIB=:
+_LT_DECL([], [RANLIB], [1],
+    [Commands used to install an old-style archive])
+
+# Determine commands to create old-style static archives.
+old_archive_cmds='$AR $AR_FLAGS $oldlib$oldobjs'
+old_postinstall_cmds='chmod 644 $oldlib'
+old_postuninstall_cmds=
+
+if test -n "$RANLIB"; then
+  case $host_os in
+  openbsd*)
+    old_postinstall_cmds="$old_postinstall_cmds~\$RANLIB -t \$tool_oldlib"
+    ;;
+  *)
+    old_postinstall_cmds="$old_postinstall_cmds~\$RANLIB \$tool_oldlib"
+    ;;
+  esac
+  old_archive_cmds="$old_archive_cmds~\$RANLIB \$tool_oldlib"
+fi
+
+case $host_os in
+  darwin*)
+    lock_old_archive_extraction=yes ;;
+  *)
+    lock_old_archive_extraction=no ;;
+esac
+_LT_DECL([], [old_postinstall_cmds], [2])
+_LT_DECL([], [old_postuninstall_cmds], [2])
+_LT_TAGDECL([], [old_archive_cmds], [2],
+    [Commands used to build an old-style archive])
+_LT_DECL([], [lock_old_archive_extraction], [0],
+    [Whether to use a lock for old archive extraction])
+])# _LT_CMD_OLD_ARCHIVE
+
+
+# _LT_COMPILER_OPTION(MESSAGE, VARIABLE-NAME, FLAGS,
+#		[OUTPUT-FILE], [ACTION-SUCCESS], [ACTION-FAILURE])
+# ----------------------------------------------------------------
+# Check whether the given compiler option works
+AC_DEFUN([_LT_COMPILER_OPTION],
+[m4_require([_LT_FILEUTILS_DEFAULTS])dnl
+m4_require([_LT_DECL_SED])dnl
+AC_CACHE_CHECK([$1], [$2],
+  [$2=no
+   m4_if([$4], , [ac_outfile=conftest.$ac_objext], [ac_outfile=$4])
+   echo "$lt_simple_compile_test_code" > conftest.$ac_ext
+   lt_compiler_flag="$3"
+   # Insert the option either (1) after the last *FLAGS variable, or
+   # (2) before a word containing "conftest.", or (3) at the end.
+   # Note that $ac_compile itself does not contain backslashes and begins
+   # with a dollar sign (not a hyphen), so the echo should work correctly.
+   # The option is referenced via a variable to avoid confusing sed.
+   lt_compile=`echo "$ac_compile" | $SED \
+   -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \
+   -e 's: [[^ ]]*conftest\.: $lt_compiler_flag&:; t' \
+   -e 's:$: $lt_compiler_flag:'`
+   (eval echo "\"\$as_me:$LINENO: $lt_compile\"" >&AS_MESSAGE_LOG_FD)
+   (eval "$lt_compile" 2>conftest.err)
+   ac_status=$?
+   cat conftest.err >&AS_MESSAGE_LOG_FD
+   echo "$as_me:$LINENO: \$? = $ac_status" >&AS_MESSAGE_LOG_FD
+   if (exit $ac_status) && test -s "$ac_outfile"; then
+     # The compiler can only warn and ignore the option if not recognized
+     # So say no if there are warnings other than the usual output.
+     $ECHO "$_lt_compiler_boilerplate" | $SED '/^$/d' >conftest.exp
+     $SED '/^$/d; /^ *+/d' conftest.err >conftest.er2
+     if test ! -s conftest.er2 || diff conftest.exp conftest.er2 >/dev/null; then
+       $2=yes
+     fi
+   fi
+   $RM conftest*
+])
+
+if test x"[$]$2" = xyes; then
+    m4_if([$5], , :, [$5])
+else
+    m4_if([$6], , :, [$6])
+fi
+])# _LT_COMPILER_OPTION
+
+# Old name:
+AU_ALIAS([AC_LIBTOOL_COMPILER_OPTION], [_LT_COMPILER_OPTION])
+dnl aclocal-1.4 backwards compatibility:
+dnl AC_DEFUN([AC_LIBTOOL_COMPILER_OPTION], [])
+
+
+# _LT_LINKER_OPTION(MESSAGE, VARIABLE-NAME, FLAGS,
+#                  [ACTION-SUCCESS], [ACTION-FAILURE])
+# ----------------------------------------------------
+# Check whether the given linker option works
+AC_DEFUN([_LT_LINKER_OPTION],
+[m4_require([_LT_FILEUTILS_DEFAULTS])dnl
+m4_require([_LT_DECL_SED])dnl
+AC_CACHE_CHECK([$1], [$2],
+  [$2=no
+   save_LDFLAGS="$LDFLAGS"
+   LDFLAGS="$LDFLAGS $3"
+   echo "$lt_simple_link_test_code" > conftest.$ac_ext
+   if (eval $ac_link 2>conftest.err) && test -s conftest$ac_exeext; then
+     # The linker can only warn and ignore the option if not recognized
+     # So say no if there are warnings
+     if test -s conftest.err; then
+       # Append any errors to the config.log.
+       cat conftest.err 1>&AS_MESSAGE_LOG_FD
+       $ECHO "$_lt_linker_boilerplate" | $SED '/^$/d' > conftest.exp
+       $SED '/^$/d; /^ *+/d' conftest.err >conftest.er2
+       if diff conftest.exp conftest.er2 >/dev/null; then
+         $2=yes
+       fi
+     else
+       $2=yes
+     fi
+   fi
+   $RM -r conftest*
+   LDFLAGS="$save_LDFLAGS"
+])
+
+if test x"[$]$2" = xyes; then
+    m4_if([$4], , :, [$4])
+else
+    m4_if([$5], , :, [$5])
+fi
+])# _LT_LINKER_OPTION
+
+# Old name:
+AU_ALIAS([AC_LIBTOOL_LINKER_OPTION], [_LT_LINKER_OPTION])
+dnl aclocal-1.4 backwards compatibility:
+dnl AC_DEFUN([AC_LIBTOOL_LINKER_OPTION], [])
+
+
+# LT_CMD_MAX_LEN
+#---------------
+AC_DEFUN([LT_CMD_MAX_LEN],
+[AC_REQUIRE([AC_CANONICAL_HOST])dnl
+# find the maximum length of command line arguments
+AC_MSG_CHECKING([the maximum length of command line arguments])
+AC_CACHE_VAL([lt_cv_sys_max_cmd_len], [dnl
+  i=0
+  teststring="ABCD"
+
+  case $build_os in
+  msdosdjgpp*)
+    # On DJGPP, this test can blow up pretty badly due to problems in libc
+    # (any single argument exceeding 2000 bytes causes a buffer overrun
+    # during glob expansion).  Even if it were fixed, the result of this
+    # check would be larger than it should be.
+    lt_cv_sys_max_cmd_len=12288;    # 12K is about right
+    ;;
+
+  gnu*)
+    # Under GNU Hurd, this test is not required because there is
+    # no limit to the length of command line arguments.
+    # Libtool will interpret -1 as no limit whatsoever
+    lt_cv_sys_max_cmd_len=-1;
+    ;;
+
+  cygwin* | mingw* | cegcc*)
+    # On Win9x/ME, this test blows up -- it succeeds, but takes
+    # about 5 minutes as the teststring grows exponentially.
+    # Worse, since 9x/ME are not pre-emptively multitasking,
+    # you end up with a "frozen" computer, even though with patience
+    # the test eventually succeeds (with a max line length of 256k).
+    # Instead, let's just punt: use the minimum linelength reported by
+    # all of the supported platforms: 8192 (on NT/2K/XP).
+    lt_cv_sys_max_cmd_len=8192;
+    ;;
+
+  mint*)
+    # On MiNT this can take a long time and run out of memory.
+    lt_cv_sys_max_cmd_len=8192;
+    ;;
+
+  amigaos*)
+    # On AmigaOS with pdksh, this test takes hours, literally.
+    # So we just punt and use a minimum line length of 8192.
+    lt_cv_sys_max_cmd_len=8192;
+    ;;
+
+  netbsd* | freebsd* | openbsd* | darwin* | dragonfly*)
+    # This has been around since 386BSD, at least.  Likely further.
+    if test -x /sbin/sysctl; then
+      lt_cv_sys_max_cmd_len=`/sbin/sysctl -n kern.argmax`
+    elif test -x /usr/sbin/sysctl; then
+      lt_cv_sys_max_cmd_len=`/usr/sbin/sysctl -n kern.argmax`
+    else
+      lt_cv_sys_max_cmd_len=65536	# usable default for all BSDs
+    fi
+    # And add a safety zone
+    lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \/ 4`
+    lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \* 3`
+    ;;
+
+  interix*)
+    # We know the value 262144 and hardcode it with a safety zone (like BSD)
+    lt_cv_sys_max_cmd_len=196608
+    ;;
+
+  os2*)
+    # The test takes a long time on OS/2.
+    lt_cv_sys_max_cmd_len=8192
+    ;;
+
+  osf*)
+    # Dr. Hans Ekkehard Plesser reports seeing a kernel panic running configure
+    # due to this test when exec_disable_arg_limit is 1 on Tru64. It is not
+    # nice to cause kernel panics so lets avoid the loop below.
+    # First set a reasonable default.
+    lt_cv_sys_max_cmd_len=16384
+    #
+    if test -x /sbin/sysconfig; then
+      case `/sbin/sysconfig -q proc exec_disable_arg_limit` in
+        *1*) lt_cv_sys_max_cmd_len=-1 ;;
+      esac
+    fi
+    ;;
+  sco3.2v5*)
+    lt_cv_sys_max_cmd_len=102400
+    ;;
+  sysv5* | sco5v6* | sysv4.2uw2*)
+    kargmax=`grep ARG_MAX /etc/conf/cf.d/stune 2>/dev/null`
+    if test -n "$kargmax"; then
+      lt_cv_sys_max_cmd_len=`echo $kargmax | sed 's/.*[[	 ]]//'`
+    else
+      lt_cv_sys_max_cmd_len=32768
+    fi
+    ;;
+  *)
+    lt_cv_sys_max_cmd_len=`(getconf ARG_MAX) 2> /dev/null`
+    if test -n "$lt_cv_sys_max_cmd_len" && \
+	test undefined != "$lt_cv_sys_max_cmd_len"; then
+      lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \/ 4`
+      lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \* 3`
+    else
+      # Make teststring a little bigger before we do anything with it.
+      # a 1K string should be a reasonable start.
+      for i in 1 2 3 4 5 6 7 8 ; do
+        teststring=$teststring$teststring
+      done
+      SHELL=${SHELL-${CONFIG_SHELL-/bin/sh}}
+      # If test is not a shell built-in, we'll probably end up computing a
+      # maximum length that is only half of the actual maximum length, but
+      # we can't tell.
+      while { test "X"`env echo "$teststring$teststring" 2>/dev/null` \
+	         = "X$teststring$teststring"; } >/dev/null 2>&1 &&
+	      test $i != 17 # 1/2 MB should be enough
+      do
+        i=`expr $i + 1`
+        teststring=$teststring$teststring
+      done
+      # Only check the string length outside the loop.
+      lt_cv_sys_max_cmd_len=`expr "X$teststring" : ".*" 2>&1`
+      teststring=
+      # Add a significant safety factor because C++ compilers can tack on
+      # massive amounts of additional arguments before passing them to the
+      # linker.  It appears as though 1/2 is a usable value.
+      lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \/ 2`
+    fi
+    ;;
+  esac
+])
+if test -n $lt_cv_sys_max_cmd_len ; then
+  AC_MSG_RESULT($lt_cv_sys_max_cmd_len)
+else
+  AC_MSG_RESULT(none)
+fi
+max_cmd_len=$lt_cv_sys_max_cmd_len
+_LT_DECL([], [max_cmd_len], [0],
+    [What is the maximum length of a command?])
+])# LT_CMD_MAX_LEN
+
+# Old name:
+AU_ALIAS([AC_LIBTOOL_SYS_MAX_CMD_LEN], [LT_CMD_MAX_LEN])
+dnl aclocal-1.4 backwards compatibility:
+dnl AC_DEFUN([AC_LIBTOOL_SYS_MAX_CMD_LEN], [])
+
+
+# _LT_HEADER_DLFCN
+# ----------------
+m4_defun([_LT_HEADER_DLFCN],
+[AC_CHECK_HEADERS([dlfcn.h], [], [], [AC_INCLUDES_DEFAULT])dnl
+])# _LT_HEADER_DLFCN
+
+
+# _LT_TRY_DLOPEN_SELF (ACTION-IF-TRUE, ACTION-IF-TRUE-W-USCORE,
+#                      ACTION-IF-FALSE, ACTION-IF-CROSS-COMPILING)
+# ----------------------------------------------------------------
+m4_defun([_LT_TRY_DLOPEN_SELF],
+[m4_require([_LT_HEADER_DLFCN])dnl
+if test "$cross_compiling" = yes; then :
+  [$4]
+else
+  lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2
+  lt_status=$lt_dlunknown
+  cat > conftest.$ac_ext <<_LT_EOF
+[#line $LINENO "configure"
+#include "confdefs.h"
+
+#if HAVE_DLFCN_H
+#include <dlfcn.h>
+#endif
+
+#include <stdio.h>
+
+#ifdef RTLD_GLOBAL
+#  define LT_DLGLOBAL		RTLD_GLOBAL
+#else
+#  ifdef DL_GLOBAL
+#    define LT_DLGLOBAL		DL_GLOBAL
+#  else
+#    define LT_DLGLOBAL		0
+#  endif
+#endif
+
+/* We may have to define LT_DLLAZY_OR_NOW in the command line if we
+   find out it does not work in some platform. */
+#ifndef LT_DLLAZY_OR_NOW
+#  ifdef RTLD_LAZY
+#    define LT_DLLAZY_OR_NOW		RTLD_LAZY
+#  else
+#    ifdef DL_LAZY
+#      define LT_DLLAZY_OR_NOW		DL_LAZY
+#    else
+#      ifdef RTLD_NOW
+#        define LT_DLLAZY_OR_NOW	RTLD_NOW
+#      else
+#        ifdef DL_NOW
+#          define LT_DLLAZY_OR_NOW	DL_NOW
+#        else
+#          define LT_DLLAZY_OR_NOW	0
+#        endif
+#      endif
+#    endif
+#  endif
+#endif
+
+/* When -fvisbility=hidden is used, assume the code has been annotated
+   correspondingly for the symbols needed.  */
+#if defined(__GNUC__) && (((__GNUC__ == 3) && (__GNUC_MINOR__ >= 3)) || (__GNUC__ > 3))
+int fnord () __attribute__((visibility("default")));
+#endif
+
+int fnord () { return 42; }
+int main ()
+{
+  void *self = dlopen (0, LT_DLGLOBAL|LT_DLLAZY_OR_NOW);
+  int status = $lt_dlunknown;
+
+  if (self)
+    {
+      if (dlsym (self,"fnord"))       status = $lt_dlno_uscore;
+      else
+        {
+	  if (dlsym( self,"_fnord"))  status = $lt_dlneed_uscore;
+          else puts (dlerror ());
+	}
+      /* dlclose (self); */
+    }
+  else
+    puts (dlerror ());
+
+  return status;
+}]
+_LT_EOF
+  if AC_TRY_EVAL(ac_link) && test -s conftest${ac_exeext} 2>/dev/null; then
+    (./conftest; exit; ) >&AS_MESSAGE_LOG_FD 2>/dev/null
+    lt_status=$?
+    case x$lt_status in
+      x$lt_dlno_uscore) $1 ;;
+      x$lt_dlneed_uscore) $2 ;;
+      x$lt_dlunknown|x*) $3 ;;
+    esac
+  else :
+    # compilation failed
+    $3
+  fi
+fi
+rm -fr conftest*
+])# _LT_TRY_DLOPEN_SELF
+
+
+# LT_SYS_DLOPEN_SELF
+# ------------------
+AC_DEFUN([LT_SYS_DLOPEN_SELF],
+[m4_require([_LT_HEADER_DLFCN])dnl
+if test "x$enable_dlopen" != xyes; then
+  enable_dlopen=unknown
+  enable_dlopen_self=unknown
+  enable_dlopen_self_static=unknown
+else
+  lt_cv_dlopen=no
+  lt_cv_dlopen_libs=
+
+  case $host_os in
+  beos*)
+    lt_cv_dlopen="load_add_on"
+    lt_cv_dlopen_libs=
+    lt_cv_dlopen_self=yes
+    ;;
+
+  mingw* | pw32* | cegcc*)
+    lt_cv_dlopen="LoadLibrary"
+    lt_cv_dlopen_libs=
+    ;;
+
+  cygwin*)
+    lt_cv_dlopen="dlopen"
+    lt_cv_dlopen_libs=
+    ;;
+
+  darwin*)
+  # if libdl is installed we need to link against it
+    AC_CHECK_LIB([dl], [dlopen],
+		[lt_cv_dlopen="dlopen" lt_cv_dlopen_libs="-ldl"],[
+    lt_cv_dlopen="dyld"
+    lt_cv_dlopen_libs=
+    lt_cv_dlopen_self=yes
+    ])
+    ;;
+
+  *)
+    AC_CHECK_FUNC([shl_load],
+	  [lt_cv_dlopen="shl_load"],
+      [AC_CHECK_LIB([dld], [shl_load],
+	    [lt_cv_dlopen="shl_load" lt_cv_dlopen_libs="-ldld"],
+	[AC_CHECK_FUNC([dlopen],
+	      [lt_cv_dlopen="dlopen"],
+	  [AC_CHECK_LIB([dl], [dlopen],
+		[lt_cv_dlopen="dlopen" lt_cv_dlopen_libs="-ldl"],
+	    [AC_CHECK_LIB([svld], [dlopen],
+		  [lt_cv_dlopen="dlopen" lt_cv_dlopen_libs="-lsvld"],
+	      [AC_CHECK_LIB([dld], [dld_link],
+		    [lt_cv_dlopen="dld_link" lt_cv_dlopen_libs="-ldld"])
+	      ])
+	    ])
+	  ])
+	])
+      ])
+    ;;
+  esac
+
+  if test "x$lt_cv_dlopen" != xno; then
+    enable_dlopen=yes
+  else
+    enable_dlopen=no
+  fi
+
+  case $lt_cv_dlopen in
+  dlopen)
+    save_CPPFLAGS="$CPPFLAGS"
+    test "x$ac_cv_header_dlfcn_h" = xyes && CPPFLAGS="$CPPFLAGS -DHAVE_DLFCN_H"
+
+    save_LDFLAGS="$LDFLAGS"
+    wl=$lt_prog_compiler_wl eval LDFLAGS=\"\$LDFLAGS $export_dynamic_flag_spec\"
+
+    save_LIBS="$LIBS"
+    LIBS="$lt_cv_dlopen_libs $LIBS"
+
+    AC_CACHE_CHECK([whether a program can dlopen itself],
+	  lt_cv_dlopen_self, [dnl
+	  _LT_TRY_DLOPEN_SELF(
+	    lt_cv_dlopen_self=yes, lt_cv_dlopen_self=yes,
+	    lt_cv_dlopen_self=no, lt_cv_dlopen_self=cross)
+    ])
+
+    if test "x$lt_cv_dlopen_self" = xyes; then
+      wl=$lt_prog_compiler_wl eval LDFLAGS=\"\$LDFLAGS $lt_prog_compiler_static\"
+      AC_CACHE_CHECK([whether a statically linked program can dlopen itself],
+	  lt_cv_dlopen_self_static, [dnl
+	  _LT_TRY_DLOPEN_SELF(
+	    lt_cv_dlopen_self_static=yes, lt_cv_dlopen_self_static=yes,
+	    lt_cv_dlopen_self_static=no,  lt_cv_dlopen_self_static=cross)
+      ])
+    fi
+
+    CPPFLAGS="$save_CPPFLAGS"
+    LDFLAGS="$save_LDFLAGS"
+    LIBS="$save_LIBS"
+    ;;
+  esac
+
+  case $lt_cv_dlopen_self in
+  yes|no) enable_dlopen_self=$lt_cv_dlopen_self ;;
+  *) enable_dlopen_self=unknown ;;
+  esac
+
+  case $lt_cv_dlopen_self_static in
+  yes|no) enable_dlopen_self_static=$lt_cv_dlopen_self_static ;;
+  *) enable_dlopen_self_static=unknown ;;
+  esac
+fi
+_LT_DECL([dlopen_support], [enable_dlopen], [0],
+	 [Whether dlopen is supported])
+_LT_DECL([dlopen_self], [enable_dlopen_self], [0],
+	 [Whether dlopen of programs is supported])
+_LT_DECL([dlopen_self_static], [enable_dlopen_self_static], [0],
+	 [Whether dlopen of statically linked programs is supported])
+])# LT_SYS_DLOPEN_SELF
+
+# Old name:
+AU_ALIAS([AC_LIBTOOL_DLOPEN_SELF], [LT_SYS_DLOPEN_SELF])
+dnl aclocal-1.4 backwards compatibility:
+dnl AC_DEFUN([AC_LIBTOOL_DLOPEN_SELF], [])
+
+
+# _LT_COMPILER_C_O([TAGNAME])
+# ---------------------------
+# Check to see if options -c and -o are simultaneously supported by compiler.
+# This macro does not hard code the compiler like AC_PROG_CC_C_O.
+m4_defun([_LT_COMPILER_C_O],
+[m4_require([_LT_DECL_SED])dnl
+m4_require([_LT_FILEUTILS_DEFAULTS])dnl
+m4_require([_LT_TAG_COMPILER])dnl
+AC_CACHE_CHECK([if $compiler supports -c -o file.$ac_objext],
+  [_LT_TAGVAR(lt_cv_prog_compiler_c_o, $1)],
+  [_LT_TAGVAR(lt_cv_prog_compiler_c_o, $1)=no
+   $RM -r conftest 2>/dev/null
+   mkdir conftest
+   cd conftest
+   mkdir out
+   echo "$lt_simple_compile_test_code" > conftest.$ac_ext
+
+   lt_compiler_flag="-o out/conftest2.$ac_objext"
+   # Insert the option either (1) after the last *FLAGS variable, or
+   # (2) before a word containing "conftest.", or (3) at the end.
+   # Note that $ac_compile itself does not contain backslashes and begins
+   # with a dollar sign (not a hyphen), so the echo should work correctly.
+   lt_compile=`echo "$ac_compile" | $SED \
+   -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \
+   -e 's: [[^ ]]*conftest\.: $lt_compiler_flag&:; t' \
+   -e 's:$: $lt_compiler_flag:'`
+   (eval echo "\"\$as_me:$LINENO: $lt_compile\"" >&AS_MESSAGE_LOG_FD)
+   (eval "$lt_compile" 2>out/conftest.err)
+   ac_status=$?
+   cat out/conftest.err >&AS_MESSAGE_LOG_FD
+   echo "$as_me:$LINENO: \$? = $ac_status" >&AS_MESSAGE_LOG_FD
+   if (exit $ac_status) && test -s out/conftest2.$ac_objext
+   then
+     # The compiler can only warn and ignore the option if not recognized
+     # So say no if there are warnings
+     $ECHO "$_lt_compiler_boilerplate" | $SED '/^$/d' > out/conftest.exp
+     $SED '/^$/d; /^ *+/d' out/conftest.err >out/conftest.er2
+     if test ! -s out/conftest.er2 || diff out/conftest.exp out/conftest.er2 >/dev/null; then
+       _LT_TAGVAR(lt_cv_prog_compiler_c_o, $1)=yes
+     fi
+   fi
+   chmod u+w . 2>&AS_MESSAGE_LOG_FD
+   $RM conftest*
+   # SGI C++ compiler will create directory out/ii_files/ for
+   # template instantiation
+   test -d out/ii_files && $RM out/ii_files/* && rmdir out/ii_files
+   $RM out/* && rmdir out
+   cd ..
+   $RM -r conftest
+   $RM conftest*
+])
+_LT_TAGDECL([compiler_c_o], [lt_cv_prog_compiler_c_o], [1],
+	[Does compiler simultaneously support -c and -o options?])
+])# _LT_COMPILER_C_O
+
+
+# _LT_COMPILER_FILE_LOCKS([TAGNAME])
+# ----------------------------------
+# Check to see if we can do hard links to lock some files if needed
+m4_defun([_LT_COMPILER_FILE_LOCKS],
+[m4_require([_LT_ENABLE_LOCK])dnl
+m4_require([_LT_FILEUTILS_DEFAULTS])dnl
+_LT_COMPILER_C_O([$1])
+
+hard_links="nottested"
+if test "$_LT_TAGVAR(lt_cv_prog_compiler_c_o, $1)" = no && test "$need_locks" != no; then
+  # do not overwrite the value of need_locks provided by the user
+  AC_MSG_CHECKING([if we can lock with hard links])
+  hard_links=yes
+  $RM conftest*
+  ln conftest.a conftest.b 2>/dev/null && hard_links=no
+  touch conftest.a
+  ln conftest.a conftest.b 2>&5 || hard_links=no
+  ln conftest.a conftest.b 2>/dev/null && hard_links=no
+  AC_MSG_RESULT([$hard_links])
+  if test "$hard_links" = no; then
+    AC_MSG_WARN([`$CC' does not support `-c -o', so `make -j' may be unsafe])
+    need_locks=warn
+  fi
+else
+  need_locks=no
+fi
+_LT_DECL([], [need_locks], [1], [Must we lock files when doing compilation?])
+])# _LT_COMPILER_FILE_LOCKS
+
+
+# _LT_CHECK_OBJDIR
+# ----------------
+m4_defun([_LT_CHECK_OBJDIR],
+[AC_CACHE_CHECK([for objdir], [lt_cv_objdir],
+[rm -f .libs 2>/dev/null
+mkdir .libs 2>/dev/null
+if test -d .libs; then
+  lt_cv_objdir=.libs
+else
+  # MS-DOS does not allow filenames that begin with a dot.
+  lt_cv_objdir=_libs
+fi
+rmdir .libs 2>/dev/null])
+objdir=$lt_cv_objdir
+_LT_DECL([], [objdir], [0],
+         [The name of the directory that contains temporary libtool files])dnl
+m4_pattern_allow([LT_OBJDIR])dnl
+AC_DEFINE_UNQUOTED(LT_OBJDIR, "$lt_cv_objdir/",
+  [Define to the sub-directory in which libtool stores uninstalled libraries.])
+])# _LT_CHECK_OBJDIR
+
+
+# _LT_LINKER_HARDCODE_LIBPATH([TAGNAME])
+# --------------------------------------
+# Check hardcoding attributes.
+m4_defun([_LT_LINKER_HARDCODE_LIBPATH],
+[AC_MSG_CHECKING([how to hardcode library paths into programs])
+_LT_TAGVAR(hardcode_action, $1)=
+if test -n "$_LT_TAGVAR(hardcode_libdir_flag_spec, $1)" ||
+   test -n "$_LT_TAGVAR(runpath_var, $1)" ||
+   test "X$_LT_TAGVAR(hardcode_automatic, $1)" = "Xyes" ; then
+
+  # We can hardcode non-existent directories.
+  if test "$_LT_TAGVAR(hardcode_direct, $1)" != no &&
+     # If the only mechanism to avoid hardcoding is shlibpath_var, we
+     # have to relink, otherwise we might link with an installed library
+     # when we should be linking with a yet-to-be-installed one
+     ## test "$_LT_TAGVAR(hardcode_shlibpath_var, $1)" != no &&
+     test "$_LT_TAGVAR(hardcode_minus_L, $1)" != no; then
+    # Linking always hardcodes the temporary library directory.
+    _LT_TAGVAR(hardcode_action, $1)=relink
+  else
+    # We can link without hardcoding, and we can hardcode nonexisting dirs.
+    _LT_TAGVAR(hardcode_action, $1)=immediate
+  fi
+else
+  # We cannot hardcode anything, or else we can only hardcode existing
+  # directories.
+  _LT_TAGVAR(hardcode_action, $1)=unsupported
+fi
+AC_MSG_RESULT([$_LT_TAGVAR(hardcode_action, $1)])
+
+if test "$_LT_TAGVAR(hardcode_action, $1)" = relink ||
+   test "$_LT_TAGVAR(inherit_rpath, $1)" = yes; then
+  # Fast installation is not supported
+  enable_fast_install=no
+elif test "$shlibpath_overrides_runpath" = yes ||
+     test "$enable_shared" = no; then
+  # Fast installation is not necessary
+  enable_fast_install=needless
+fi
+_LT_TAGDECL([], [hardcode_action], [0],
+    [How to hardcode a shared library path into an executable])
+])# _LT_LINKER_HARDCODE_LIBPATH
+
+
+# _LT_CMD_STRIPLIB
+# ----------------
+m4_defun([_LT_CMD_STRIPLIB],
+[m4_require([_LT_DECL_EGREP])
+striplib=
+old_striplib=
+AC_MSG_CHECKING([whether stripping libraries is possible])
+if test -n "$STRIP" && $STRIP -V 2>&1 | $GREP "GNU strip" >/dev/null; then
+  test -z "$old_striplib" && old_striplib="$STRIP --strip-debug"
+  test -z "$striplib" && striplib="$STRIP --strip-unneeded"
+  AC_MSG_RESULT([yes])
+else
+# FIXME - insert some real tests, host_os isn't really good enough
+  case $host_os in
+  darwin*)
+    if test -n "$STRIP" ; then
+      striplib="$STRIP -x"
+      old_striplib="$STRIP -S"
+      AC_MSG_RESULT([yes])
+    else
+      AC_MSG_RESULT([no])
+    fi
+    ;;
+  *)
+    AC_MSG_RESULT([no])
+    ;;
+  esac
+fi
+_LT_DECL([], [old_striplib], [1], [Commands to strip libraries])
+_LT_DECL([], [striplib], [1])
+])# _LT_CMD_STRIPLIB
+
+
+# _LT_SYS_DYNAMIC_LINKER([TAG])
+# -----------------------------
+# PORTME Fill in your ld.so characteristics
+m4_defun([_LT_SYS_DYNAMIC_LINKER],
+[AC_REQUIRE([AC_CANONICAL_HOST])dnl
+m4_require([_LT_DECL_EGREP])dnl
+m4_require([_LT_FILEUTILS_DEFAULTS])dnl
+m4_require([_LT_DECL_OBJDUMP])dnl
+m4_require([_LT_DECL_SED])dnl
+m4_require([_LT_CHECK_SHELL_FEATURES])dnl
+AC_MSG_CHECKING([dynamic linker characteristics])
+m4_if([$1],
+	[], [
+if test "$GCC" = yes; then
+  case $host_os in
+    darwin*) lt_awk_arg="/^libraries:/,/LR/" ;;
+    *) lt_awk_arg="/^libraries:/" ;;
+  esac
+  case $host_os in
+    mingw* | cegcc*) lt_sed_strip_eq="s,=\([[A-Za-z]]:\),\1,g" ;;
+    *) lt_sed_strip_eq="s,=/,/,g" ;;
+  esac
+  lt_search_path_spec=`$CC -print-search-dirs | awk $lt_awk_arg | $SED -e "s/^libraries://" -e $lt_sed_strip_eq`
+  case $lt_search_path_spec in
+  *\;*)
+    # if the path contains ";" then we assume it to be the separator
+    # otherwise default to the standard path separator (i.e. ":") - it is
+    # assumed that no part of a normal pathname contains ";" but that should
+    # okay in the real world where ";" in dirpaths is itself problematic.
+    lt_search_path_spec=`$ECHO "$lt_search_path_spec" | $SED 's/;/ /g'`
+    ;;
+  *)
+    lt_search_path_spec=`$ECHO "$lt_search_path_spec" | $SED "s/$PATH_SEPARATOR/ /g"`
+    ;;
+  esac
+  # Ok, now we have the path, separated by spaces, we can step through it
+  # and add multilib dir if necessary.
+  lt_tmp_lt_search_path_spec=
+  lt_multi_os_dir=`$CC $CPPFLAGS $CFLAGS $LDFLAGS -print-multi-os-directory 2>/dev/null`
+  for lt_sys_path in $lt_search_path_spec; do
+    if test -d "$lt_sys_path/$lt_multi_os_dir"; then
+      lt_tmp_lt_search_path_spec="$lt_tmp_lt_search_path_spec $lt_sys_path/$lt_multi_os_dir"
+    else
+      test -d "$lt_sys_path" && \
+	lt_tmp_lt_search_path_spec="$lt_tmp_lt_search_path_spec $lt_sys_path"
+    fi
+  done
+  lt_search_path_spec=`$ECHO "$lt_tmp_lt_search_path_spec" | awk '
+BEGIN {RS=" "; FS="/|\n";} {
+  lt_foo="";
+  lt_count=0;
+  for (lt_i = NF; lt_i > 0; lt_i--) {
+    if ($lt_i != "" && $lt_i != ".") {
+      if ($lt_i == "..") {
+        lt_count++;
+      } else {
+        if (lt_count == 0) {
+          lt_foo="/" $lt_i lt_foo;
+        } else {
+          lt_count--;
+        }
+      }
+    }
+  }
+  if (lt_foo != "") { lt_freq[[lt_foo]]++; }
+  if (lt_freq[[lt_foo]] == 1) { print lt_foo; }
+}'`
+  # AWK program above erroneously prepends '/' to C:/dos/paths
+  # for these hosts.
+  case $host_os in
+    mingw* | cegcc*) lt_search_path_spec=`$ECHO "$lt_search_path_spec" |\
+      $SED 's,/\([[A-Za-z]]:\),\1,g'` ;;
+  esac
+  sys_lib_search_path_spec=`$ECHO "$lt_search_path_spec" | $lt_NL2SP`
+else
+  sys_lib_search_path_spec="/lib /usr/lib /usr/local/lib"
+fi])
+library_names_spec=
+libname_spec='lib$name'
+soname_spec=
+shrext_cmds=".so"
+postinstall_cmds=
+postuninstall_cmds=
+finish_cmds=
+finish_eval=
+shlibpath_var=
+shlibpath_overrides_runpath=unknown
+version_type=none
+dynamic_linker="$host_os ld.so"
+sys_lib_dlsearch_path_spec="/lib /usr/lib"
+need_lib_prefix=unknown
+hardcode_into_libs=no
+
+# when you set need_version to no, make sure it does not cause -set_version
+# flags to be left without arguments
+need_version=unknown
+
+case $host_os in
+aix3*)
+  version_type=linux # correct to gnu/linux during the next big refactor
+  library_names_spec='${libname}${release}${shared_ext}$versuffix $libname.a'
+  shlibpath_var=LIBPATH
+
+  # AIX 3 has no versioning support, so we append a major version to the name.
+  soname_spec='${libname}${release}${shared_ext}$major'
+  ;;
+
+aix[[4-9]]*)
+  version_type=linux # correct to gnu/linux during the next big refactor
+  need_lib_prefix=no
+  need_version=no
+  hardcode_into_libs=yes
+  if test "$host_cpu" = ia64; then
+    # AIX 5 supports IA64
+    library_names_spec='${libname}${release}${shared_ext}$major ${libname}${release}${shared_ext}$versuffix $libname${shared_ext}'
+    shlibpath_var=LD_LIBRARY_PATH
+  else
+    # With GCC up to 2.95.x, collect2 would create an import file
+    # for dependence libraries.  The import file would start with
+    # the line `#! .'.  This would cause the generated library to
+    # depend on `.', always an invalid library.  This was fixed in
+    # development snapshots of GCC prior to 3.0.
+    case $host_os in
+      aix4 | aix4.[[01]] | aix4.[[01]].*)
+      if { echo '#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 97)'
+	   echo ' yes '
+	   echo '#endif'; } | ${CC} -E - | $GREP yes > /dev/null; then
+	:
+      else
+	can_build_shared=no
+      fi
+      ;;
+    esac
+    # AIX (on Power*) has no versioning support, so currently we can not hardcode correct
+    # soname into executable. Probably we can add versioning support to
+    # collect2, so additional links can be useful in future.
+    if test "$aix_use_runtimelinking" = yes; then
+      # If using run time linking (on AIX 4.2 or later) use lib<name>.so
+      # instead of lib<name>.a to let people know that these are not
+      # typical AIX shared libraries.
+      library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+    else
+      # We preserve .a as extension for shared libraries through AIX4.2
+      # and later when we are not doing run time linking.
+      library_names_spec='${libname}${release}.a $libname.a'
+      soname_spec='${libname}${release}${shared_ext}$major'
+    fi
+    shlibpath_var=LIBPATH
+  fi
+  ;;
+
+amigaos*)
+  case $host_cpu in
+  powerpc)
+    # Since July 2007 AmigaOS4 officially supports .so libraries.
+    # When compiling the executable, add -use-dynld -Lsobjs: to the compileline.
+    library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+    ;;
+  m68k)
+    library_names_spec='$libname.ixlibrary $libname.a'
+    # Create ${libname}_ixlibrary.a entries in /sys/libs.
+    finish_eval='for lib in `ls $libdir/*.ixlibrary 2>/dev/null`; do libname=`func_echo_all "$lib" | $SED '\''s%^.*/\([[^/]]*\)\.ixlibrary$%\1%'\''`; test $RM /sys/libs/${libname}_ixlibrary.a; $show "cd /sys/libs && $LN_S $lib ${libname}_ixlibrary.a"; cd /sys/libs && $LN_S $lib ${libname}_ixlibrary.a || exit 1; done'
+    ;;
+  esac
+  ;;
+
+beos*)
+  library_names_spec='${libname}${shared_ext}'
+  dynamic_linker="$host_os ld.so"
+  shlibpath_var=LIBRARY_PATH
+  ;;
+
+bsdi[[45]]*)
+  version_type=linux # correct to gnu/linux during the next big refactor
+  need_version=no
+  library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+  soname_spec='${libname}${release}${shared_ext}$major'
+  finish_cmds='PATH="\$PATH:/sbin" ldconfig $libdir'
+  shlibpath_var=LD_LIBRARY_PATH
+  sys_lib_search_path_spec="/shlib /usr/lib /usr/X11/lib /usr/contrib/lib /lib /usr/local/lib"
+  sys_lib_dlsearch_path_spec="/shlib /usr/lib /usr/local/lib"
+  # the default ld.so.conf also contains /usr/contrib/lib and
+  # /usr/X11R6/lib (/usr/X11 is a link to /usr/X11R6), but let us allow
+  # libtool to hard-code these into programs
+  ;;
+
+cygwin* | mingw* | pw32* | cegcc*)
+  version_type=windows
+  shrext_cmds=".dll"
+  need_version=no
+  need_lib_prefix=no
+
+  case $GCC,$cc_basename in
+  yes,*)
+    # gcc
+    library_names_spec='$libname.dll.a'
+    # DLL is installed to $(libdir)/../bin by postinstall_cmds
+    postinstall_cmds='base_file=`basename \${file}`~
+      dlpath=`$SHELL 2>&1 -c '\''. $dir/'\''\${base_file}'\''i; echo \$dlname'\''`~
+      dldir=$destdir/`dirname \$dlpath`~
+      test -d \$dldir || mkdir -p \$dldir~
+      $install_prog $dir/$dlname \$dldir/$dlname~
+      chmod a+x \$dldir/$dlname~
+      if test -n '\''$stripme'\'' && test -n '\''$striplib'\''; then
+        eval '\''$striplib \$dldir/$dlname'\'' || exit \$?;
+      fi'
+    postuninstall_cmds='dldll=`$SHELL 2>&1 -c '\''. $file; echo \$dlname'\''`~
+      dlpath=$dir/\$dldll~
+       $RM \$dlpath'
+    shlibpath_overrides_runpath=yes
+
+    case $host_os in
+    cygwin*)
+      # Cygwin DLLs use 'cyg' prefix rather than 'lib'
+      soname_spec='`echo ${libname} | sed -e 's/^lib/cyg/'``echo ${release} | $SED -e 's/[[.]]/-/g'`${versuffix}${shared_ext}'
+m4_if([$1], [],[
+      sys_lib_search_path_spec="$sys_lib_search_path_spec /usr/lib/w32api"])
+      ;;
+    mingw* | cegcc*)
+      # MinGW DLLs use traditional 'lib' prefix
+      soname_spec='${libname}`echo ${release} | $SED -e 's/[[.]]/-/g'`${versuffix}${shared_ext}'
+      ;;
+    pw32*)
+      # pw32 DLLs use 'pw' prefix rather than 'lib'
+      library_names_spec='`echo ${libname} | sed -e 's/^lib/pw/'``echo ${release} | $SED -e 's/[[.]]/-/g'`${versuffix}${shared_ext}'
+      ;;
+    esac
+    dynamic_linker='Win32 ld.exe'
+    ;;
+
+  *,cl*)
+    # Native MSVC
+    libname_spec='$name'
+    soname_spec='${libname}`echo ${release} | $SED -e 's/[[.]]/-/g'`${versuffix}${shared_ext}'
+    library_names_spec='${libname}.dll.lib'
+
+    case $build_os in
+    mingw*)
+      sys_lib_search_path_spec=
+      lt_save_ifs=$IFS
+      IFS=';'
+      for lt_path in $LIB
+      do
+        IFS=$lt_save_ifs
+        # Let DOS variable expansion print the short 8.3 style file name.
+        lt_path=`cd "$lt_path" 2>/dev/null && cmd //C "for %i in (".") do @echo %~si"`
+        sys_lib_search_path_spec="$sys_lib_search_path_spec $lt_path"
+      done
+      IFS=$lt_save_ifs
+      # Convert to MSYS style.
+      sys_lib_search_path_spec=`$ECHO "$sys_lib_search_path_spec" | sed -e 's|\\\\|/|g' -e 's| \\([[a-zA-Z]]\\):| /\\1|g' -e 's|^ ||'`
+      ;;
+    cygwin*)
+      # Convert to unix form, then to dos form, then back to unix form
+      # but this time dos style (no spaces!) so that the unix form looks
+      # like /cygdrive/c/PROGRA~1:/cygdr...
+      sys_lib_search_path_spec=`cygpath --path --unix "$LIB"`
+      sys_lib_search_path_spec=`cygpath --path --dos "$sys_lib_search_path_spec" 2>/dev/null`
+      sys_lib_search_path_spec=`cygpath --path --unix "$sys_lib_search_path_spec" | $SED -e "s/$PATH_SEPARATOR/ /g"`
+      ;;
+    *)
+      sys_lib_search_path_spec="$LIB"
+      if $ECHO "$sys_lib_search_path_spec" | [$GREP ';[c-zC-Z]:/' >/dev/null]; then
+        # It is most probably a Windows format PATH.
+        sys_lib_search_path_spec=`$ECHO "$sys_lib_search_path_spec" | $SED -e 's/;/ /g'`
+      else
+        sys_lib_search_path_spec=`$ECHO "$sys_lib_search_path_spec" | $SED -e "s/$PATH_SEPARATOR/ /g"`
+      fi
+      # FIXME: find the short name or the path components, as spaces are
+      # common. (e.g. "Program Files" -> "PROGRA~1")
+      ;;
+    esac
+
+    # DLL is installed to $(libdir)/../bin by postinstall_cmds
+    postinstall_cmds='base_file=`basename \${file}`~
+      dlpath=`$SHELL 2>&1 -c '\''. $dir/'\''\${base_file}'\''i; echo \$dlname'\''`~
+      dldir=$destdir/`dirname \$dlpath`~
+      test -d \$dldir || mkdir -p \$dldir~
+      $install_prog $dir/$dlname \$dldir/$dlname'
+    postuninstall_cmds='dldll=`$SHELL 2>&1 -c '\''. $file; echo \$dlname'\''`~
+      dlpath=$dir/\$dldll~
+       $RM \$dlpath'
+    shlibpath_overrides_runpath=yes
+    dynamic_linker='Win32 link.exe'
+    ;;
+
+  *)
+    # Assume MSVC wrapper
+    library_names_spec='${libname}`echo ${release} | $SED -e 's/[[.]]/-/g'`${versuffix}${shared_ext} $libname.lib'
+    dynamic_linker='Win32 ld.exe'
+    ;;
+  esac
+  # FIXME: first we should search . and the directory the executable is in
+  shlibpath_var=PATH
+  ;;
+
+darwin* | rhapsody*)
+  dynamic_linker="$host_os dyld"
+  version_type=darwin
+  need_lib_prefix=no
+  need_version=no
+  library_names_spec='${libname}${release}${major}$shared_ext ${libname}$shared_ext'
+  soname_spec='${libname}${release}${major}$shared_ext'
+  shlibpath_overrides_runpath=yes
+  shlibpath_var=DYLD_LIBRARY_PATH
+  shrext_cmds='`test .$module = .yes && echo .so || echo .dylib`'
+m4_if([$1], [],[
+  sys_lib_search_path_spec="$sys_lib_search_path_spec /usr/local/lib"])
+  sys_lib_dlsearch_path_spec='/usr/local/lib /lib /usr/lib'
+  ;;
+
+dgux*)
+  version_type=linux # correct to gnu/linux during the next big refactor
+  need_lib_prefix=no
+  need_version=no
+  library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname$shared_ext'
+  soname_spec='${libname}${release}${shared_ext}$major'
+  shlibpath_var=LD_LIBRARY_PATH
+  ;;
+
+freebsd* | dragonfly*)
+  # DragonFly does not have aout.  When/if they implement a new
+  # versioning mechanism, adjust this.
+  if test -x /usr/bin/objformat; then
+    objformat=`/usr/bin/objformat`
+  else
+    case $host_os in
+    freebsd[[23]].*) objformat=aout ;;
+    *) objformat=elf ;;
+    esac
+  fi
+  version_type=freebsd-$objformat
+  case $version_type in
+    freebsd-elf*)
+      library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext} $libname${shared_ext}'
+      need_version=no
+      need_lib_prefix=no
+      ;;
+    freebsd-*)
+      library_names_spec='${libname}${release}${shared_ext}$versuffix $libname${shared_ext}$versuffix'
+      need_version=yes
+      ;;
+  esac
+  shlibpath_var=LD_LIBRARY_PATH
+  case $host_os in
+  freebsd2.*)
+    shlibpath_overrides_runpath=yes
+    ;;
+  freebsd3.[[01]]* | freebsdelf3.[[01]]*)
+    shlibpath_overrides_runpath=yes
+    hardcode_into_libs=yes
+    ;;
+  freebsd3.[[2-9]]* | freebsdelf3.[[2-9]]* | \
+  freebsd4.[[0-5]] | freebsdelf4.[[0-5]] | freebsd4.1.1 | freebsdelf4.1.1)
+    shlibpath_overrides_runpath=no
+    hardcode_into_libs=yes
+    ;;
+  *) # from 4.6 on, and DragonFly
+    shlibpath_overrides_runpath=yes
+    hardcode_into_libs=yes
+    ;;
+  esac
+  ;;
+
+haiku*)
+  version_type=linux # correct to gnu/linux during the next big refactor
+  need_lib_prefix=no
+  need_version=no
+  dynamic_linker="$host_os runtime_loader"
+  library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}${major} ${libname}${shared_ext}'
+  soname_spec='${libname}${release}${shared_ext}$major'
+  shlibpath_var=LIBRARY_PATH
+  shlibpath_overrides_runpath=yes
+  sys_lib_dlsearch_path_spec='/boot/home/config/lib /boot/common/lib /boot/system/lib'
+  hardcode_into_libs=yes
+  ;;
+
+hpux9* | hpux10* | hpux11*)
+  # Give a soname corresponding to the major version so that dld.sl refuses to
+  # link against other versions.
+  version_type=sunos
+  need_lib_prefix=no
+  need_version=no
+  case $host_cpu in
+  ia64*)
+    shrext_cmds='.so'
+    hardcode_into_libs=yes
+    dynamic_linker="$host_os dld.so"
+    shlibpath_var=LD_LIBRARY_PATH
+    shlibpath_overrides_runpath=yes # Unless +noenvvar is specified.
+    library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+    soname_spec='${libname}${release}${shared_ext}$major'
+    if test "X$HPUX_IA64_MODE" = X32; then
+      sys_lib_search_path_spec="/usr/lib/hpux32 /usr/local/lib/hpux32 /usr/local/lib"
+    else
+      sys_lib_search_path_spec="/usr/lib/hpux64 /usr/local/lib/hpux64"
+    fi
+    sys_lib_dlsearch_path_spec=$sys_lib_search_path_spec
+    ;;
+  hppa*64*)
+    shrext_cmds='.sl'
+    hardcode_into_libs=yes
+    dynamic_linker="$host_os dld.sl"
+    shlibpath_var=LD_LIBRARY_PATH # How should we handle SHLIB_PATH
+    shlibpath_overrides_runpath=yes # Unless +noenvvar is specified.
+    library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+    soname_spec='${libname}${release}${shared_ext}$major'
+    sys_lib_search_path_spec="/usr/lib/pa20_64 /usr/ccs/lib/pa20_64"
+    sys_lib_dlsearch_path_spec=$sys_lib_search_path_spec
+    ;;
+  *)
+    shrext_cmds='.sl'
+    dynamic_linker="$host_os dld.sl"
+    shlibpath_var=SHLIB_PATH
+    shlibpath_overrides_runpath=no # +s is required to enable SHLIB_PATH
+    library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+    soname_spec='${libname}${release}${shared_ext}$major'
+    ;;
+  esac
+  # HP-UX runs *really* slowly unless shared libraries are mode 555, ...
+  postinstall_cmds='chmod 555 $lib'
+  # or fails outright, so override atomically:
+  install_override_mode=555
+  ;;
+
+interix[[3-9]]*)
+  version_type=linux # correct to gnu/linux during the next big refactor
+  need_lib_prefix=no
+  need_version=no
+  library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${shared_ext}'
+  soname_spec='${libname}${release}${shared_ext}$major'
+  dynamic_linker='Interix 3.x ld.so.1 (PE, like ELF)'
+  shlibpath_var=LD_LIBRARY_PATH
+  shlibpath_overrides_runpath=no
+  hardcode_into_libs=yes
+  ;;
+
+irix5* | irix6* | nonstopux*)
+  case $host_os in
+    nonstopux*) version_type=nonstopux ;;
+    *)
+	if test "$lt_cv_prog_gnu_ld" = yes; then
+		version_type=linux # correct to gnu/linux during the next big refactor
+	else
+		version_type=irix
+	fi ;;
+  esac
+  need_lib_prefix=no
+  need_version=no
+  soname_spec='${libname}${release}${shared_ext}$major'
+  library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${release}${shared_ext} $libname${shared_ext}'
+  case $host_os in
+  irix5* | nonstopux*)
+    libsuff= shlibsuff=
+    ;;
+  *)
+    case $LD in # libtool.m4 will add one of these switches to LD
+    *-32|*"-32 "|*-melf32bsmip|*"-melf32bsmip ")
+      libsuff= shlibsuff= libmagic=32-bit;;
+    *-n32|*"-n32 "|*-melf32bmipn32|*"-melf32bmipn32 ")
+      libsuff=32 shlibsuff=N32 libmagic=N32;;
+    *-64|*"-64 "|*-melf64bmip|*"-melf64bmip ")
+      libsuff=64 shlibsuff=64 libmagic=64-bit;;
+    *) libsuff= shlibsuff= libmagic=never-match;;
+    esac
+    ;;
+  esac
+  shlibpath_var=LD_LIBRARY${shlibsuff}_PATH
+  shlibpath_overrides_runpath=no
+  sys_lib_search_path_spec="/usr/lib${libsuff} /lib${libsuff} /usr/local/lib${libsuff}"
+  sys_lib_dlsearch_path_spec="/usr/lib${libsuff} /lib${libsuff}"
+  hardcode_into_libs=yes
+  ;;
+
+# No shared lib support for Linux oldld, aout, or coff.
+linux*oldld* | linux*aout* | linux*coff*)
+  dynamic_linker=no
+  ;;
+
+# This must be glibc/ELF.
+linux* | k*bsd*-gnu | kopensolaris*-gnu | gnu*)
+  version_type=linux # correct to gnu/linux during the next big refactor
+  need_lib_prefix=no
+  need_version=no
+  library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+  soname_spec='${libname}${release}${shared_ext}$major'
+  finish_cmds='PATH="\$PATH:/sbin" ldconfig -n $libdir'
+  shlibpath_var=LD_LIBRARY_PATH
+  shlibpath_overrides_runpath=no
+
+  # Some binutils ld are patched to set DT_RUNPATH
+  AC_CACHE_VAL([lt_cv_shlibpath_overrides_runpath],
+    [lt_cv_shlibpath_overrides_runpath=no
+    save_LDFLAGS=$LDFLAGS
+    save_libdir=$libdir
+    eval "libdir=/foo; wl=\"$_LT_TAGVAR(lt_prog_compiler_wl, $1)\"; \
+	 LDFLAGS=\"\$LDFLAGS $_LT_TAGVAR(hardcode_libdir_flag_spec, $1)\""
+    AC_LINK_IFELSE([AC_LANG_PROGRAM([],[])],
+      [AS_IF([ ($OBJDUMP -p conftest$ac_exeext) 2>/dev/null | grep "RUNPATH.*$libdir" >/dev/null],
+	 [lt_cv_shlibpath_overrides_runpath=yes])])
+    LDFLAGS=$save_LDFLAGS
+    libdir=$save_libdir
+    ])
+  shlibpath_overrides_runpath=$lt_cv_shlibpath_overrides_runpath
+
+  # This implies no fast_install, which is unacceptable.
+  # Some rework will be needed to allow for fast_install
+  # before this can be enabled.
+  hardcode_into_libs=yes
+
+  # Append ld.so.conf contents to the search path
+  if test -f /etc/ld.so.conf; then
+    lt_ld_extra=`awk '/^include / { system(sprintf("cd /etc; cat %s 2>/dev/null", \[$]2)); skip = 1; } { if (!skip) print \[$]0; skip = 0; }' < /etc/ld.so.conf | $SED -e 's/#.*//;/^[	 ]*hwcap[	 ]/d;s/[:,	]/ /g;s/=[^=]*$//;s/=[^= ]* / /g;s/"//g;/^$/d' | tr '\n' ' '`
+    sys_lib_dlsearch_path_spec="/lib /usr/lib $lt_ld_extra"
+  fi
+
+  # We used to test for /lib/ld.so.1 and disable shared libraries on
+  # powerpc, because MkLinux only supported shared libraries with the
+  # GNU dynamic linker.  Since this was broken with cross compilers,
+  # most powerpc-linux boxes support dynamic linking these days and
+  # people can always --disable-shared, the test was removed, and we
+  # assume the GNU/Linux dynamic linker is in use.
+  dynamic_linker='GNU/Linux ld.so'
+  ;;
+
+netbsdelf*-gnu)
+  version_type=linux
+  need_lib_prefix=no
+  need_version=no
+  library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${shared_ext}'
+  soname_spec='${libname}${release}${shared_ext}$major'
+  shlibpath_var=LD_LIBRARY_PATH
+  shlibpath_overrides_runpath=no
+  hardcode_into_libs=yes
+  dynamic_linker='NetBSD ld.elf_so'
+  ;;
+
+netbsd*)
+  version_type=sunos
+  need_lib_prefix=no
+  need_version=no
+  if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then
+    library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${shared_ext}$versuffix'
+    finish_cmds='PATH="\$PATH:/sbin" ldconfig -m $libdir'
+    dynamic_linker='NetBSD (a.out) ld.so'
+  else
+    library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${shared_ext}'
+    soname_spec='${libname}${release}${shared_ext}$major'
+    dynamic_linker='NetBSD ld.elf_so'
+  fi
+  shlibpath_var=LD_LIBRARY_PATH
+  shlibpath_overrides_runpath=yes
+  hardcode_into_libs=yes
+  ;;
+
+newsos6)
+  version_type=linux # correct to gnu/linux during the next big refactor
+  library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+  shlibpath_var=LD_LIBRARY_PATH
+  shlibpath_overrides_runpath=yes
+  ;;
+
+*nto* | *qnx*)
+  version_type=qnx
+  need_lib_prefix=no
+  need_version=no
+  library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+  soname_spec='${libname}${release}${shared_ext}$major'
+  shlibpath_var=LD_LIBRARY_PATH
+  shlibpath_overrides_runpath=no
+  hardcode_into_libs=yes
+  dynamic_linker='ldqnx.so'
+  ;;
+
+openbsd*)
+  version_type=sunos
+  sys_lib_dlsearch_path_spec="/usr/lib"
+  need_lib_prefix=no
+  # Some older versions of OpenBSD (3.3 at least) *do* need versioned libs.
+  case $host_os in
+    openbsd3.3 | openbsd3.3.*)	need_version=yes ;;
+    *)				need_version=no  ;;
+  esac
+  library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${shared_ext}$versuffix'
+  finish_cmds='PATH="\$PATH:/sbin" ldconfig -m $libdir'
+  shlibpath_var=LD_LIBRARY_PATH
+  if test -z "`echo __ELF__ | $CC -E - | $GREP __ELF__`" || test "$host_os-$host_cpu" = "openbsd2.8-powerpc"; then
+    case $host_os in
+      openbsd2.[[89]] | openbsd2.[[89]].*)
+	shlibpath_overrides_runpath=no
+	;;
+      *)
+	shlibpath_overrides_runpath=yes
+	;;
+      esac
+  else
+    shlibpath_overrides_runpath=yes
+  fi
+  ;;
+
+os2*)
+  libname_spec='$name'
+  shrext_cmds=".dll"
+  need_lib_prefix=no
+  library_names_spec='$libname${shared_ext} $libname.a'
+  dynamic_linker='OS/2 ld.exe'
+  shlibpath_var=LIBPATH
+  ;;
+
+osf3* | osf4* | osf5*)
+  version_type=osf
+  need_lib_prefix=no
+  need_version=no
+  soname_spec='${libname}${release}${shared_ext}$major'
+  library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+  shlibpath_var=LD_LIBRARY_PATH
+  sys_lib_search_path_spec="/usr/shlib /usr/ccs/lib /usr/lib/cmplrs/cc /usr/lib /usr/local/lib /var/shlib"
+  sys_lib_dlsearch_path_spec="$sys_lib_search_path_spec"
+  ;;
+
+rdos*)
+  dynamic_linker=no
+  ;;
+
+solaris*)
+  version_type=linux # correct to gnu/linux during the next big refactor
+  need_lib_prefix=no
+  need_version=no
+  library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+  soname_spec='${libname}${release}${shared_ext}$major'
+  shlibpath_var=LD_LIBRARY_PATH
+  shlibpath_overrides_runpath=yes
+  hardcode_into_libs=yes
+  # ldd complains unless libraries are executable
+  postinstall_cmds='chmod +x $lib'
+  ;;
+
+sunos4*)
+  version_type=sunos
+  library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${shared_ext}$versuffix'
+  finish_cmds='PATH="\$PATH:/usr/etc" ldconfig $libdir'
+  shlibpath_var=LD_LIBRARY_PATH
+  shlibpath_overrides_runpath=yes
+  if test "$with_gnu_ld" = yes; then
+    need_lib_prefix=no
+  fi
+  need_version=yes
+  ;;
+
+sysv4 | sysv4.3*)
+  version_type=linux # correct to gnu/linux during the next big refactor
+  library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+  soname_spec='${libname}${release}${shared_ext}$major'
+  shlibpath_var=LD_LIBRARY_PATH
+  case $host_vendor in
+    sni)
+      shlibpath_overrides_runpath=no
+      need_lib_prefix=no
+      runpath_var=LD_RUN_PATH
+      ;;
+    siemens)
+      need_lib_prefix=no
+      ;;
+    motorola)
+      need_lib_prefix=no
+      need_version=no
+      shlibpath_overrides_runpath=no
+      sys_lib_search_path_spec='/lib /usr/lib /usr/ccs/lib'
+      ;;
+  esac
+  ;;
+
+sysv4*MP*)
+  if test -d /usr/nec ;then
+    version_type=linux # correct to gnu/linux during the next big refactor
+    library_names_spec='$libname${shared_ext}.$versuffix $libname${shared_ext}.$major $libname${shared_ext}'
+    soname_spec='$libname${shared_ext}.$major'
+    shlibpath_var=LD_LIBRARY_PATH
+  fi
+  ;;
+
+sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX* | sysv4*uw2*)
+  version_type=freebsd-elf
+  need_lib_prefix=no
+  need_version=no
+  library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext} $libname${shared_ext}'
+  soname_spec='${libname}${release}${shared_ext}$major'
+  shlibpath_var=LD_LIBRARY_PATH
+  shlibpath_overrides_runpath=yes
+  hardcode_into_libs=yes
+  if test "$with_gnu_ld" = yes; then
+    sys_lib_search_path_spec='/usr/local/lib /usr/gnu/lib /usr/ccs/lib /usr/lib /lib'
+  else
+    sys_lib_search_path_spec='/usr/ccs/lib /usr/lib'
+    case $host_os in
+      sco3.2v5*)
+        sys_lib_search_path_spec="$sys_lib_search_path_spec /lib"
+	;;
+    esac
+  fi
+  sys_lib_dlsearch_path_spec='/usr/lib'
+  ;;
+
+tpf*)
+  # TPF is a cross-target only.  Preferred cross-host = GNU/Linux.
+  version_type=linux # correct to gnu/linux during the next big refactor
+  need_lib_prefix=no
+  need_version=no
+  library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+  shlibpath_var=LD_LIBRARY_PATH
+  shlibpath_overrides_runpath=no
+  hardcode_into_libs=yes
+  ;;
+
+uts4*)
+  version_type=linux # correct to gnu/linux during the next big refactor
+  library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+  soname_spec='${libname}${release}${shared_ext}$major'
+  shlibpath_var=LD_LIBRARY_PATH
+  ;;
+
+*)
+  dynamic_linker=no
+  ;;
+esac
+AC_MSG_RESULT([$dynamic_linker])
+test "$dynamic_linker" = no && can_build_shared=no
+
+variables_saved_for_relink="PATH $shlibpath_var $runpath_var"
+if test "$GCC" = yes; then
+  variables_saved_for_relink="$variables_saved_for_relink GCC_EXEC_PREFIX COMPILER_PATH LIBRARY_PATH"
+fi
+
+if test "${lt_cv_sys_lib_search_path_spec+set}" = set; then
+  sys_lib_search_path_spec="$lt_cv_sys_lib_search_path_spec"
+fi
+if test "${lt_cv_sys_lib_dlsearch_path_spec+set}" = set; then
+  sys_lib_dlsearch_path_spec="$lt_cv_sys_lib_dlsearch_path_spec"
+fi
+
+_LT_DECL([], [variables_saved_for_relink], [1],
+    [Variables whose values should be saved in libtool wrapper scripts and
+    restored at link time])
+_LT_DECL([], [need_lib_prefix], [0],
+    [Do we need the "lib" prefix for modules?])
+_LT_DECL([], [need_version], [0], [Do we need a version for libraries?])
+_LT_DECL([], [version_type], [0], [Library versioning type])
+_LT_DECL([], [runpath_var], [0],  [Shared library runtime path variable])
+_LT_DECL([], [shlibpath_var], [0],[Shared library path variable])
+_LT_DECL([], [shlibpath_overrides_runpath], [0],
+    [Is shlibpath searched before the hard-coded library search path?])
+_LT_DECL([], [libname_spec], [1], [Format of library name prefix])
+_LT_DECL([], [library_names_spec], [1],
+    [[List of archive names.  First name is the real one, the rest are links.
+    The last name is the one that the linker finds with -lNAME]])
+_LT_DECL([], [soname_spec], [1],
+    [[The coded name of the library, if different from the real name]])
+_LT_DECL([], [install_override_mode], [1],
+    [Permission mode override for installation of shared libraries])
+_LT_DECL([], [postinstall_cmds], [2],
+    [Command to use after installation of a shared archive])
+_LT_DECL([], [postuninstall_cmds], [2],
+    [Command to use after uninstallation of a shared archive])
+_LT_DECL([], [finish_cmds], [2],
+    [Commands used to finish a libtool library installation in a directory])
+_LT_DECL([], [finish_eval], [1],
+    [[As "finish_cmds", except a single script fragment to be evaled but
+    not shown]])
+_LT_DECL([], [hardcode_into_libs], [0],
+    [Whether we should hardcode library paths into libraries])
+_LT_DECL([], [sys_lib_search_path_spec], [2],
+    [Compile-time system search path for libraries])
+_LT_DECL([], [sys_lib_dlsearch_path_spec], [2],
+    [Run-time system search path for libraries])
+])# _LT_SYS_DYNAMIC_LINKER
+
+
+# _LT_PATH_TOOL_PREFIX(TOOL)
+# --------------------------
+# find a file program which can recognize shared library
+AC_DEFUN([_LT_PATH_TOOL_PREFIX],
+[m4_require([_LT_DECL_EGREP])dnl
+AC_MSG_CHECKING([for $1])
+AC_CACHE_VAL(lt_cv_path_MAGIC_CMD,
+[case $MAGIC_CMD in
+[[\\/*] |  ?:[\\/]*])
+  lt_cv_path_MAGIC_CMD="$MAGIC_CMD" # Let the user override the test with a path.
+  ;;
+*)
+  lt_save_MAGIC_CMD="$MAGIC_CMD"
+  lt_save_ifs="$IFS"; IFS=$PATH_SEPARATOR
+dnl $ac_dummy forces splitting on constant user-supplied paths.
+dnl POSIX.2 word splitting is done only on the output of word expansions,
+dnl not every word.  This closes a longstanding sh security hole.
+  ac_dummy="m4_if([$2], , $PATH, [$2])"
+  for ac_dir in $ac_dummy; do
+    IFS="$lt_save_ifs"
+    test -z "$ac_dir" && ac_dir=.
+    if test -f $ac_dir/$1; then
+      lt_cv_path_MAGIC_CMD="$ac_dir/$1"
+      if test -n "$file_magic_test_file"; then
+	case $deplibs_check_method in
+	"file_magic "*)
+	  file_magic_regex=`expr "$deplibs_check_method" : "file_magic \(.*\)"`
+	  MAGIC_CMD="$lt_cv_path_MAGIC_CMD"
+	  if eval $file_magic_cmd \$file_magic_test_file 2> /dev/null |
+	    $EGREP "$file_magic_regex" > /dev/null; then
+	    :
+	  else
+	    cat <<_LT_EOF 1>&2
+
+*** Warning: the command libtool uses to detect shared libraries,
+*** $file_magic_cmd, produces output that libtool cannot recognize.
+*** The result is that libtool may fail to recognize shared libraries
+*** as such.  This will affect the creation of libtool libraries that
+*** depend on shared libraries, but programs linked with such libtool
+*** libraries will work regardless of this problem.  Nevertheless, you
+*** may want to report the problem to your system manager and/or to
+*** bug-libtool@gnu.org
+
+_LT_EOF
+	  fi ;;
+	esac
+      fi
+      break
+    fi
+  done
+  IFS="$lt_save_ifs"
+  MAGIC_CMD="$lt_save_MAGIC_CMD"
+  ;;
+esac])
+MAGIC_CMD="$lt_cv_path_MAGIC_CMD"
+if test -n "$MAGIC_CMD"; then
+  AC_MSG_RESULT($MAGIC_CMD)
+else
+  AC_MSG_RESULT(no)
+fi
+_LT_DECL([], [MAGIC_CMD], [0],
+	 [Used to examine libraries when file_magic_cmd begins with "file"])dnl
+])# _LT_PATH_TOOL_PREFIX
+
+# Old name:
+AU_ALIAS([AC_PATH_TOOL_PREFIX], [_LT_PATH_TOOL_PREFIX])
+dnl aclocal-1.4 backwards compatibility:
+dnl AC_DEFUN([AC_PATH_TOOL_PREFIX], [])
+
+
+# _LT_PATH_MAGIC
+# --------------
+# find a file program which can recognize a shared library
+m4_defun([_LT_PATH_MAGIC],
+[_LT_PATH_TOOL_PREFIX(${ac_tool_prefix}file, /usr/bin$PATH_SEPARATOR$PATH)
+if test -z "$lt_cv_path_MAGIC_CMD"; then
+  if test -n "$ac_tool_prefix"; then
+    _LT_PATH_TOOL_PREFIX(file, /usr/bin$PATH_SEPARATOR$PATH)
+  else
+    MAGIC_CMD=:
+  fi
+fi
+])# _LT_PATH_MAGIC
+
+
+# LT_PATH_LD
+# ----------
+# find the pathname to the GNU or non-GNU linker
+AC_DEFUN([LT_PATH_LD],
+[AC_REQUIRE([AC_PROG_CC])dnl
+AC_REQUIRE([AC_CANONICAL_HOST])dnl
+AC_REQUIRE([AC_CANONICAL_BUILD])dnl
+m4_require([_LT_DECL_SED])dnl
+m4_require([_LT_DECL_EGREP])dnl
+m4_require([_LT_PROG_ECHO_BACKSLASH])dnl
+
+AC_ARG_WITH([gnu-ld],
+    [AS_HELP_STRING([--with-gnu-ld],
+	[assume the C compiler uses GNU ld @<:@default=no@:>@])],
+    [test "$withval" = no || with_gnu_ld=yes],
+    [with_gnu_ld=no])dnl
+
+ac_prog=ld
+if test "$GCC" = yes; then
+  # Check if gcc -print-prog-name=ld gives a path.
+  AC_MSG_CHECKING([for ld used by $CC])
+  case $host in
+  *-*-mingw*)
+    # gcc leaves a trailing carriage return which upsets mingw
+    ac_prog=`($CC -print-prog-name=ld) 2>&5 | tr -d '\015'` ;;
+  *)
+    ac_prog=`($CC -print-prog-name=ld) 2>&5` ;;
+  esac
+  case $ac_prog in
+    # Accept absolute paths.
+    [[\\/]]* | ?:[[\\/]]*)
+      re_direlt='/[[^/]][[^/]]*/\.\./'
+      # Canonicalize the pathname of ld
+      ac_prog=`$ECHO "$ac_prog"| $SED 's%\\\\%/%g'`
+      while $ECHO "$ac_prog" | $GREP "$re_direlt" > /dev/null 2>&1; do
+	ac_prog=`$ECHO $ac_prog| $SED "s%$re_direlt%/%"`
+      done
+      test -z "$LD" && LD="$ac_prog"
+      ;;
+  "")
+    # If it fails, then pretend we aren't using GCC.
+    ac_prog=ld
+    ;;
+  *)
+    # If it is relative, then search for the first ld in PATH.
+    with_gnu_ld=unknown
+    ;;
+  esac
+elif test "$with_gnu_ld" = yes; then
+  AC_MSG_CHECKING([for GNU ld])
+else
+  AC_MSG_CHECKING([for non-GNU ld])
+fi
+AC_CACHE_VAL(lt_cv_path_LD,
+[if test -z "$LD"; then
+  lt_save_ifs="$IFS"; IFS=$PATH_SEPARATOR
+  for ac_dir in $PATH; do
+    IFS="$lt_save_ifs"
+    test -z "$ac_dir" && ac_dir=.
+    if test -f "$ac_dir/$ac_prog" || test -f "$ac_dir/$ac_prog$ac_exeext"; then
+      lt_cv_path_LD="$ac_dir/$ac_prog"
+      # Check to see if the program is GNU ld.  I'd rather use --version,
+      # but apparently some variants of GNU ld only accept -v.
+      # Break only if it was the GNU/non-GNU ld that we prefer.
+      case `"$lt_cv_path_LD" -v 2>&1 </dev/null` in
+      *GNU* | *'with BFD'*)
+	test "$with_gnu_ld" != no && break
+	;;
+      *)
+	test "$with_gnu_ld" != yes && break
+	;;
+      esac
+    fi
+  done
+  IFS="$lt_save_ifs"
+else
+  lt_cv_path_LD="$LD" # Let the user override the test with a path.
+fi])
+LD="$lt_cv_path_LD"
+if test -n "$LD"; then
+  AC_MSG_RESULT($LD)
+else
+  AC_MSG_RESULT(no)
+fi
+test -z "$LD" && AC_MSG_ERROR([no acceptable ld found in \$PATH])
+_LT_PATH_LD_GNU
+AC_SUBST([LD])
+
+_LT_TAGDECL([], [LD], [1], [The linker used to build libraries])
+])# LT_PATH_LD
+
+# Old names:
+AU_ALIAS([AM_PROG_LD], [LT_PATH_LD])
+AU_ALIAS([AC_PROG_LD], [LT_PATH_LD])
+dnl aclocal-1.4 backwards compatibility:
+dnl AC_DEFUN([AM_PROG_LD], [])
+dnl AC_DEFUN([AC_PROG_LD], [])
+
+
+# _LT_PATH_LD_GNU
+#- --------------
+m4_defun([_LT_PATH_LD_GNU],
+[AC_CACHE_CHECK([if the linker ($LD) is GNU ld], lt_cv_prog_gnu_ld,
+[# I'd rather use --version here, but apparently some GNU lds only accept -v.
+case `$LD -v 2>&1 </dev/null` in
+*GNU* | *'with BFD'*)
+  lt_cv_prog_gnu_ld=yes
+  ;;
+*)
+  lt_cv_prog_gnu_ld=no
+  ;;
+esac])
+with_gnu_ld=$lt_cv_prog_gnu_ld
+])# _LT_PATH_LD_GNU
+
+
+# _LT_CMD_RELOAD
+# --------------
+# find reload flag for linker
+#   -- PORTME Some linkers may need a different reload flag.
+m4_defun([_LT_CMD_RELOAD],
+[AC_CACHE_CHECK([for $LD option to reload object files],
+  lt_cv_ld_reload_flag,
+  [lt_cv_ld_reload_flag='-r'])
+reload_flag=$lt_cv_ld_reload_flag
+case $reload_flag in
+"" | " "*) ;;
+*) reload_flag=" $reload_flag" ;;
+esac
+reload_cmds='$LD$reload_flag -o $output$reload_objs'
+case $host_os in
+  cygwin* | mingw* | pw32* | cegcc*)
+    if test "$GCC" != yes; then
+      reload_cmds=false
+    fi
+    ;;
+  darwin*)
+    if test "$GCC" = yes; then
+      reload_cmds='$LTCC $LTCFLAGS -nostdlib ${wl}-r -o $output$reload_objs'
+    else
+      reload_cmds='$LD$reload_flag -o $output$reload_objs'
+    fi
+    ;;
+esac
+_LT_TAGDECL([], [reload_flag], [1], [How to create reloadable object files])dnl
+_LT_TAGDECL([], [reload_cmds], [2])dnl
+])# _LT_CMD_RELOAD
+
+
+# _LT_CHECK_MAGIC_METHOD
+# ----------------------
+# how to check for library dependencies
+#  -- PORTME fill in with the dynamic library characteristics
+m4_defun([_LT_CHECK_MAGIC_METHOD],
+[m4_require([_LT_DECL_EGREP])
+m4_require([_LT_DECL_OBJDUMP])
+AC_CACHE_CHECK([how to recognize dependent libraries],
+lt_cv_deplibs_check_method,
+[lt_cv_file_magic_cmd='$MAGIC_CMD'
+lt_cv_file_magic_test_file=
+lt_cv_deplibs_check_method='unknown'
+# Need to set the preceding variable on all platforms that support
+# interlibrary dependencies.
+# 'none' -- dependencies not supported.
+# `unknown' -- same as none, but documents that we really don't know.
+# 'pass_all' -- all dependencies passed with no checks.
+# 'test_compile' -- check by making test program.
+# 'file_magic [[regex]]' -- check by looking for files in library path
+# which responds to the $file_magic_cmd with a given extended regex.
+# If you have `file' or equivalent on your system and you're not sure
+# whether `pass_all' will *always* work, you probably want this one.
+
+case $host_os in
+aix[[4-9]]*)
+  lt_cv_deplibs_check_method=pass_all
+  ;;
+
+beos*)
+  lt_cv_deplibs_check_method=pass_all
+  ;;
+
+bsdi[[45]]*)
+  lt_cv_deplibs_check_method='file_magic ELF [[0-9]][[0-9]]*-bit [[ML]]SB (shared object|dynamic lib)'
+  lt_cv_file_magic_cmd='/usr/bin/file -L'
+  lt_cv_file_magic_test_file=/shlib/libc.so
+  ;;
+
+cygwin*)
+  # func_win32_libid is a shell function defined in ltmain.sh
+  lt_cv_deplibs_check_method='file_magic ^x86 archive import|^x86 DLL'
+  lt_cv_file_magic_cmd='func_win32_libid'
+  ;;
+
+mingw* | pw32*)
+  # Base MSYS/MinGW do not provide the 'file' command needed by
+  # func_win32_libid shell function, so use a weaker test based on 'objdump',
+  # unless we find 'file', for example because we are cross-compiling.
+  # func_win32_libid assumes BSD nm, so disallow it if using MS dumpbin.
+  if ( test "$lt_cv_nm_interface" = "BSD nm" && file / ) >/dev/null 2>&1; then
+    lt_cv_deplibs_check_method='file_magic ^x86 archive import|^x86 DLL'
+    lt_cv_file_magic_cmd='func_win32_libid'
+  else
+    # Keep this pattern in sync with the one in func_win32_libid.
+    lt_cv_deplibs_check_method='file_magic file format (pei*-i386(.*architecture: i386)?|pe-arm-wince|pe-x86-64)'
+    lt_cv_file_magic_cmd='$OBJDUMP -f'
+  fi
+  ;;
+
+cegcc*)
+  # use the weaker test based on 'objdump'. See mingw*.
+  lt_cv_deplibs_check_method='file_magic file format pe-arm-.*little(.*architecture: arm)?'
+  lt_cv_file_magic_cmd='$OBJDUMP -f'
+  ;;
+
+darwin* | rhapsody*)
+  lt_cv_deplibs_check_method=pass_all
+  ;;
+
+freebsd* | dragonfly*)
+  if echo __ELF__ | $CC -E - | $GREP __ELF__ > /dev/null; then
+    case $host_cpu in
+    i*86 )
+      # Not sure whether the presence of OpenBSD here was a mistake.
+      # Let's accept both of them until this is cleared up.
+      lt_cv_deplibs_check_method='file_magic (FreeBSD|OpenBSD|DragonFly)/i[[3-9]]86 (compact )?demand paged shared library'
+      lt_cv_file_magic_cmd=/usr/bin/file
+      lt_cv_file_magic_test_file=`echo /usr/lib/libc.so.*`
+      ;;
+    esac
+  else
+    lt_cv_deplibs_check_method=pass_all
+  fi
+  ;;
+
+haiku*)
+  lt_cv_deplibs_check_method=pass_all
+  ;;
+
+hpux10.20* | hpux11*)
+  lt_cv_file_magic_cmd=/usr/bin/file
+  case $host_cpu in
+  ia64*)
+    lt_cv_deplibs_check_method='file_magic (s[[0-9]][[0-9]][[0-9]]|ELF-[[0-9]][[0-9]]) shared object file - IA64'
+    lt_cv_file_magic_test_file=/usr/lib/hpux32/libc.so
+    ;;
+  hppa*64*)
+    [lt_cv_deplibs_check_method='file_magic (s[0-9][0-9][0-9]|ELF[ -][0-9][0-9])(-bit)?( [LM]SB)? shared object( file)?[, -]* PA-RISC [0-9]\.[0-9]']
+    lt_cv_file_magic_test_file=/usr/lib/pa20_64/libc.sl
+    ;;
+  *)
+    lt_cv_deplibs_check_method='file_magic (s[[0-9]][[0-9]][[0-9]]|PA-RISC[[0-9]]\.[[0-9]]) shared library'
+    lt_cv_file_magic_test_file=/usr/lib/libc.sl
+    ;;
+  esac
+  ;;
+
+interix[[3-9]]*)
+  # PIC code is broken on Interix 3.x, that's why |\.a not |_pic\.a here
+  lt_cv_deplibs_check_method='match_pattern /lib[[^/]]+(\.so|\.a)$'
+  ;;
+
+irix5* | irix6* | nonstopux*)
+  case $LD in
+  *-32|*"-32 ") libmagic=32-bit;;
+  *-n32|*"-n32 ") libmagic=N32;;
+  *-64|*"-64 ") libmagic=64-bit;;
+  *) libmagic=never-match;;
+  esac
+  lt_cv_deplibs_check_method=pass_all
+  ;;
+
+# This must be glibc/ELF.
+linux* | k*bsd*-gnu | kopensolaris*-gnu | gnu*)
+  lt_cv_deplibs_check_method=pass_all
+  ;;
+
+netbsd* | netbsdelf*-gnu)
+  if echo __ELF__ | $CC -E - | $GREP __ELF__ > /dev/null; then
+    lt_cv_deplibs_check_method='match_pattern /lib[[^/]]+(\.so\.[[0-9]]+\.[[0-9]]+|_pic\.a)$'
+  else
+    lt_cv_deplibs_check_method='match_pattern /lib[[^/]]+(\.so|_pic\.a)$'
+  fi
+  ;;
+
+newos6*)
+  lt_cv_deplibs_check_method='file_magic ELF [[0-9]][[0-9]]*-bit [[ML]]SB (executable|dynamic lib)'
+  lt_cv_file_magic_cmd=/usr/bin/file
+  lt_cv_file_magic_test_file=/usr/lib/libnls.so
+  ;;
+
+*nto* | *qnx*)
+  lt_cv_deplibs_check_method=pass_all
+  ;;
+
+openbsd*)
+  if test -z "`echo __ELF__ | $CC -E - | $GREP __ELF__`" || test "$host_os-$host_cpu" = "openbsd2.8-powerpc"; then
+    lt_cv_deplibs_check_method='match_pattern /lib[[^/]]+(\.so\.[[0-9]]+\.[[0-9]]+|\.so|_pic\.a)$'
+  else
+    lt_cv_deplibs_check_method='match_pattern /lib[[^/]]+(\.so\.[[0-9]]+\.[[0-9]]+|_pic\.a)$'
+  fi
+  ;;
+
+osf3* | osf4* | osf5*)
+  lt_cv_deplibs_check_method=pass_all
+  ;;
+
+rdos*)
+  lt_cv_deplibs_check_method=pass_all
+  ;;
+
+solaris*)
+  lt_cv_deplibs_check_method=pass_all
+  ;;
+
+sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX* | sysv4*uw2*)
+  lt_cv_deplibs_check_method=pass_all
+  ;;
+
+sysv4 | sysv4.3*)
+  case $host_vendor in
+  motorola)
+    lt_cv_deplibs_check_method='file_magic ELF [[0-9]][[0-9]]*-bit [[ML]]SB (shared object|dynamic lib) M[[0-9]][[0-9]]* Version [[0-9]]'
+    lt_cv_file_magic_test_file=`echo /usr/lib/libc.so*`
+    ;;
+  ncr)
+    lt_cv_deplibs_check_method=pass_all
+    ;;
+  sequent)
+    lt_cv_file_magic_cmd='/bin/file'
+    lt_cv_deplibs_check_method='file_magic ELF [[0-9]][[0-9]]*-bit [[LM]]SB (shared object|dynamic lib )'
+    ;;
+  sni)
+    lt_cv_file_magic_cmd='/bin/file'
+    lt_cv_deplibs_check_method="file_magic ELF [[0-9]][[0-9]]*-bit [[LM]]SB dynamic lib"
+    lt_cv_file_magic_test_file=/lib/libc.so
+    ;;
+  siemens)
+    lt_cv_deplibs_check_method=pass_all
+    ;;
+  pc)
+    lt_cv_deplibs_check_method=pass_all
+    ;;
+  esac
+  ;;
+
+tpf*)
+  lt_cv_deplibs_check_method=pass_all
+  ;;
+esac
+])
+
+file_magic_glob=
+want_nocaseglob=no
+if test "$build" = "$host"; then
+  case $host_os in
+  mingw* | pw32*)
+    if ( shopt | grep nocaseglob ) >/dev/null 2>&1; then
+      want_nocaseglob=yes
+    else
+      file_magic_glob=`echo aAbBcCdDeEfFgGhHiIjJkKlLmMnNoOpPqQrRsStTuUvVwWxXyYzZ | $SED -e "s/\(..\)/s\/[[\1]]\/[[\1]]\/g;/g"`
+    fi
+    ;;
+  esac
+fi
+
+file_magic_cmd=$lt_cv_file_magic_cmd
+deplibs_check_method=$lt_cv_deplibs_check_method
+test -z "$deplibs_check_method" && deplibs_check_method=unknown
+
+_LT_DECL([], [deplibs_check_method], [1],
+    [Method to check whether dependent libraries are shared objects])
+_LT_DECL([], [file_magic_cmd], [1],
+    [Command to use when deplibs_check_method = "file_magic"])
+_LT_DECL([], [file_magic_glob], [1],
+    [How to find potential files when deplibs_check_method = "file_magic"])
+_LT_DECL([], [want_nocaseglob], [1],
+    [Find potential files using nocaseglob when deplibs_check_method = "file_magic"])
+])# _LT_CHECK_MAGIC_METHOD
+
+
+# LT_PATH_NM
+# ----------
+# find the pathname to a BSD- or MS-compatible name lister
+AC_DEFUN([LT_PATH_NM],
+[AC_REQUIRE([AC_PROG_CC])dnl
+AC_CACHE_CHECK([for BSD- or MS-compatible name lister (nm)], lt_cv_path_NM,
+[if test -n "$NM"; then
+  # Let the user override the test.
+  lt_cv_path_NM="$NM"
+else
+  lt_nm_to_check="${ac_tool_prefix}nm"
+  if test -n "$ac_tool_prefix" && test "$build" = "$host"; then
+    lt_nm_to_check="$lt_nm_to_check nm"
+  fi
+  for lt_tmp_nm in $lt_nm_to_check; do
+    lt_save_ifs="$IFS"; IFS=$PATH_SEPARATOR
+    for ac_dir in $PATH /usr/ccs/bin/elf /usr/ccs/bin /usr/ucb /bin; do
+      IFS="$lt_save_ifs"
+      test -z "$ac_dir" && ac_dir=.
+      tmp_nm="$ac_dir/$lt_tmp_nm"
+      if test -f "$tmp_nm" || test -f "$tmp_nm$ac_exeext" ; then
+	# Check to see if the nm accepts a BSD-compat flag.
+	# Adding the `sed 1q' prevents false positives on HP-UX, which says:
+	#   nm: unknown option "B" ignored
+	# Tru64's nm complains that /dev/null is an invalid object file
+	case `"$tmp_nm" -B /dev/null 2>&1 | sed '1q'` in
+	*/dev/null* | *'Invalid file or object type'*)
+	  lt_cv_path_NM="$tmp_nm -B"
+	  break
+	  ;;
+	*)
+	  case `"$tmp_nm" -p /dev/null 2>&1 | sed '1q'` in
+	  */dev/null*)
+	    lt_cv_path_NM="$tmp_nm -p"
+	    break
+	    ;;
+	  *)
+	    lt_cv_path_NM=${lt_cv_path_NM="$tmp_nm"} # keep the first match, but
+	    continue # so that we can try to find one that supports BSD flags
+	    ;;
+	  esac
+	  ;;
+	esac
+      fi
+    done
+    IFS="$lt_save_ifs"
+  done
+  : ${lt_cv_path_NM=no}
+fi])
+if test "$lt_cv_path_NM" != "no"; then
+  NM="$lt_cv_path_NM"
+else
+  # Didn't find any BSD compatible name lister, look for dumpbin.
+  if test -n "$DUMPBIN"; then :
+    # Let the user override the test.
+  else
+    AC_CHECK_TOOLS(DUMPBIN, [dumpbin "link -dump"], :)
+    case `$DUMPBIN -symbols /dev/null 2>&1 | sed '1q'` in
+    *COFF*)
+      DUMPBIN="$DUMPBIN -symbols"
+      ;;
+    *)
+      DUMPBIN=:
+      ;;
+    esac
+  fi
+  AC_SUBST([DUMPBIN])
+  if test "$DUMPBIN" != ":"; then
+    NM="$DUMPBIN"
+  fi
+fi
+test -z "$NM" && NM=nm
+AC_SUBST([NM])
+_LT_DECL([], [NM], [1], [A BSD- or MS-compatible name lister])dnl
+
+AC_CACHE_CHECK([the name lister ($NM) interface], [lt_cv_nm_interface],
+  [lt_cv_nm_interface="BSD nm"
+  echo "int some_variable = 0;" > conftest.$ac_ext
+  (eval echo "\"\$as_me:$LINENO: $ac_compile\"" >&AS_MESSAGE_LOG_FD)
+  (eval "$ac_compile" 2>conftest.err)
+  cat conftest.err >&AS_MESSAGE_LOG_FD
+  (eval echo "\"\$as_me:$LINENO: $NM \\\"conftest.$ac_objext\\\"\"" >&AS_MESSAGE_LOG_FD)
+  (eval "$NM \"conftest.$ac_objext\"" 2>conftest.err > conftest.out)
+  cat conftest.err >&AS_MESSAGE_LOG_FD
+  (eval echo "\"\$as_me:$LINENO: output\"" >&AS_MESSAGE_LOG_FD)
+  cat conftest.out >&AS_MESSAGE_LOG_FD
+  if $GREP 'External.*some_variable' conftest.out > /dev/null; then
+    lt_cv_nm_interface="MS dumpbin"
+  fi
+  rm -f conftest*])
+])# LT_PATH_NM
+
+# Old names:
+AU_ALIAS([AM_PROG_NM], [LT_PATH_NM])
+AU_ALIAS([AC_PROG_NM], [LT_PATH_NM])
+dnl aclocal-1.4 backwards compatibility:
+dnl AC_DEFUN([AM_PROG_NM], [])
+dnl AC_DEFUN([AC_PROG_NM], [])
+
+# _LT_CHECK_SHAREDLIB_FROM_LINKLIB
+# --------------------------------
+# how to determine the name of the shared library
+# associated with a specific link library.
+#  -- PORTME fill in with the dynamic library characteristics
+m4_defun([_LT_CHECK_SHAREDLIB_FROM_LINKLIB],
+[m4_require([_LT_DECL_EGREP])
+m4_require([_LT_DECL_OBJDUMP])
+m4_require([_LT_DECL_DLLTOOL])
+AC_CACHE_CHECK([how to associate runtime and link libraries],
+lt_cv_sharedlib_from_linklib_cmd,
+[lt_cv_sharedlib_from_linklib_cmd='unknown'
+
+case $host_os in
+cygwin* | mingw* | pw32* | cegcc*)
+  # two different shell functions defined in ltmain.sh
+  # decide which to use based on capabilities of $DLLTOOL
+  case `$DLLTOOL --help 2>&1` in
+  *--identify-strict*)
+    lt_cv_sharedlib_from_linklib_cmd=func_cygming_dll_for_implib
+    ;;
+  *)
+    lt_cv_sharedlib_from_linklib_cmd=func_cygming_dll_for_implib_fallback
+    ;;
+  esac
+  ;;
+*)
+  # fallback: assume linklib IS sharedlib
+  lt_cv_sharedlib_from_linklib_cmd="$ECHO"
+  ;;
+esac
+])
+sharedlib_from_linklib_cmd=$lt_cv_sharedlib_from_linklib_cmd
+test -z "$sharedlib_from_linklib_cmd" && sharedlib_from_linklib_cmd=$ECHO
+
+_LT_DECL([], [sharedlib_from_linklib_cmd], [1],
+    [Command to associate shared and link libraries])
+])# _LT_CHECK_SHAREDLIB_FROM_LINKLIB
+
+
+# _LT_PATH_MANIFEST_TOOL
+# ----------------------
+# locate the manifest tool
+m4_defun([_LT_PATH_MANIFEST_TOOL],
+[AC_CHECK_TOOL(MANIFEST_TOOL, mt, :)
+test -z "$MANIFEST_TOOL" && MANIFEST_TOOL=mt
+AC_CACHE_CHECK([if $MANIFEST_TOOL is a manifest tool], [lt_cv_path_mainfest_tool],
+  [lt_cv_path_mainfest_tool=no
+  echo "$as_me:$LINENO: $MANIFEST_TOOL '-?'" >&AS_MESSAGE_LOG_FD
+  $MANIFEST_TOOL '-?' 2>conftest.err > conftest.out
+  cat conftest.err >&AS_MESSAGE_LOG_FD
+  if $GREP 'Manifest Tool' conftest.out > /dev/null; then
+    lt_cv_path_mainfest_tool=yes
+  fi
+  rm -f conftest*])
+if test "x$lt_cv_path_mainfest_tool" != xyes; then
+  MANIFEST_TOOL=:
+fi
+_LT_DECL([], [MANIFEST_TOOL], [1], [Manifest tool])dnl
+])# _LT_PATH_MANIFEST_TOOL
+
+
+# LT_LIB_M
+# --------
+# check for math library
+AC_DEFUN([LT_LIB_M],
+[AC_REQUIRE([AC_CANONICAL_HOST])dnl
+LIBM=
+case $host in
+*-*-beos* | *-*-cegcc* | *-*-cygwin* | *-*-haiku* | *-*-pw32* | *-*-darwin*)
+  # These system don't have libm, or don't need it
+  ;;
+*-ncr-sysv4.3*)
+  AC_CHECK_LIB(mw, _mwvalidcheckl, LIBM="-lmw")
+  AC_CHECK_LIB(m, cos, LIBM="$LIBM -lm")
+  ;;
+*)
+  AC_CHECK_LIB(m, cos, LIBM="-lm")
+  ;;
+esac
+AC_SUBST([LIBM])
+])# LT_LIB_M
+
+# Old name:
+AU_ALIAS([AC_CHECK_LIBM], [LT_LIB_M])
+dnl aclocal-1.4 backwards compatibility:
+dnl AC_DEFUN([AC_CHECK_LIBM], [])
+
+
+# _LT_COMPILER_NO_RTTI([TAGNAME])
+# -------------------------------
+m4_defun([_LT_COMPILER_NO_RTTI],
+[m4_require([_LT_TAG_COMPILER])dnl
+
+_LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1)=
+
+if test "$GCC" = yes; then
+  case $cc_basename in
+  nvcc*)
+    _LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1)=' -Xcompiler -fno-builtin' ;;
+  *)
+    _LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1)=' -fno-builtin' ;;
+  esac
+
+  _LT_COMPILER_OPTION([if $compiler supports -fno-rtti -fno-exceptions],
+    lt_cv_prog_compiler_rtti_exceptions,
+    [-fno-rtti -fno-exceptions], [],
+    [_LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1)="$_LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1) -fno-rtti -fno-exceptions"])
+fi
+_LT_TAGDECL([no_builtin_flag], [lt_prog_compiler_no_builtin_flag], [1],
+	[Compiler flag to turn off builtin functions])
+])# _LT_COMPILER_NO_RTTI
+
+
+# _LT_CMD_GLOBAL_SYMBOLS
+# ----------------------
+m4_defun([_LT_CMD_GLOBAL_SYMBOLS],
+[AC_REQUIRE([AC_CANONICAL_HOST])dnl
+AC_REQUIRE([AC_PROG_CC])dnl
+AC_REQUIRE([AC_PROG_AWK])dnl
+AC_REQUIRE([LT_PATH_NM])dnl
+AC_REQUIRE([LT_PATH_LD])dnl
+m4_require([_LT_DECL_SED])dnl
+m4_require([_LT_DECL_EGREP])dnl
+m4_require([_LT_TAG_COMPILER])dnl
+
+# Check for command to grab the raw symbol name followed by C symbol from nm.
+AC_MSG_CHECKING([command to parse $NM output from $compiler object])
+AC_CACHE_VAL([lt_cv_sys_global_symbol_pipe],
+[
+# These are sane defaults that work on at least a few old systems.
+# [They come from Ultrix.  What could be older than Ultrix?!! ;)]
+
+# Character class describing NM global symbol codes.
+symcode='[[BCDEGRST]]'
+
+# Regexp to match symbols that can be accessed directly from C.
+sympat='\([[_A-Za-z]][[_A-Za-z0-9]]*\)'
+
+# Define system-specific variables.
+case $host_os in
+aix*)
+  symcode='[[BCDT]]'
+  ;;
+cygwin* | mingw* | pw32* | cegcc*)
+  symcode='[[ABCDGISTW]]'
+  ;;
+hpux*)
+  if test "$host_cpu" = ia64; then
+    symcode='[[ABCDEGRST]]'
+  fi
+  ;;
+irix* | nonstopux*)
+  symcode='[[BCDEGRST]]'
+  ;;
+osf*)
+  symcode='[[BCDEGQRST]]'
+  ;;
+solaris*)
+  symcode='[[BDRT]]'
+  ;;
+sco3.2v5*)
+  symcode='[[DT]]'
+  ;;
+sysv4.2uw2*)
+  symcode='[[DT]]'
+  ;;
+sysv5* | sco5v6* | unixware* | OpenUNIX*)
+  symcode='[[ABDT]]'
+  ;;
+sysv4)
+  symcode='[[DFNSTU]]'
+  ;;
+esac
+
+# If we're using GNU nm, then use its standard symbol codes.
+case `$NM -V 2>&1` in
+*GNU* | *'with BFD'*)
+  symcode='[[ABCDGIRSTW]]' ;;
+esac
+
+# Transform an extracted symbol line into a proper C declaration.
+# Some systems (esp. on ia64) link data and code symbols differently,
+# so use this general approach.
+lt_cv_sys_global_symbol_to_cdecl="sed -n -e 's/^T .* \(.*\)$/extern int \1();/p' -e 's/^$symcode* .* \(.*\)$/extern char \1;/p'"
+
+# Transform an extracted symbol line into symbol name and symbol address
+lt_cv_sys_global_symbol_to_c_name_address="sed -n -e 's/^: \([[^ ]]*\)[[ ]]*$/  {\\\"\1\\\", (void *) 0},/p' -e 's/^$symcode* \([[^ ]]*\) \([[^ ]]*\)$/  {\"\2\", (void *) \&\2},/p'"
+lt_cv_sys_global_symbol_to_c_name_address_lib_prefix="sed -n -e 's/^: \([[^ ]]*\)[[ ]]*$/  {\\\"\1\\\", (void *) 0},/p' -e 's/^$symcode* \([[^ ]]*\) \(lib[[^ ]]*\)$/  {\"\2\", (void *) \&\2},/p' -e 's/^$symcode* \([[^ ]]*\) \([[^ ]]*\)$/  {\"lib\2\", (void *) \&\2},/p'"
+
+# Handle CRLF in mingw tool chain
+opt_cr=
+case $build_os in
+mingw*)
+  opt_cr=`$ECHO 'x\{0,1\}' | tr x '\015'` # option cr in regexp
+  ;;
+esac
+
+# Try without a prefix underscore, then with it.
+for ac_symprfx in "" "_"; do
+
+  # Transform symcode, sympat, and symprfx into a raw symbol and a C symbol.
+  symxfrm="\\1 $ac_symprfx\\2 \\2"
+
+  # Write the raw and C identifiers.
+  if test "$lt_cv_nm_interface" = "MS dumpbin"; then
+    # Fake it for dumpbin and say T for any non-static function
+    # and D for any global variable.
+    # Also find C++ and __fastcall symbols from MSVC++,
+    # which start with @ or ?.
+    lt_cv_sys_global_symbol_pipe="$AWK ['"\
+"     {last_section=section; section=\$ 3};"\
+"     /^COFF SYMBOL TABLE/{for(i in hide) delete hide[i]};"\
+"     /Section length .*#relocs.*(pick any)/{hide[last_section]=1};"\
+"     \$ 0!~/External *\|/{next};"\
+"     / 0+ UNDEF /{next}; / UNDEF \([^|]\)*()/{next};"\
+"     {if(hide[section]) next};"\
+"     {f=0}; \$ 0~/\(\).*\|/{f=1}; {printf f ? \"T \" : \"D \"};"\
+"     {split(\$ 0, a, /\||\r/); split(a[2], s)};"\
+"     s[1]~/^[@?]/{print s[1], s[1]; next};"\
+"     s[1]~prfx {split(s[1],t,\"@\"); print t[1], substr(t[1],length(prfx))}"\
+"     ' prfx=^$ac_symprfx]"
+  else
+    lt_cv_sys_global_symbol_pipe="sed -n -e 's/^.*[[	 ]]\($symcode$symcode*\)[[	 ]][[	 ]]*$ac_symprfx$sympat$opt_cr$/$symxfrm/p'"
+  fi
+  lt_cv_sys_global_symbol_pipe="$lt_cv_sys_global_symbol_pipe | sed '/ __gnu_lto/d'"
+
+  # Check to see that the pipe works correctly.
+  pipe_works=no
+
+  rm -f conftest*
+  cat > conftest.$ac_ext <<_LT_EOF
+#ifdef __cplusplus
+extern "C" {
+#endif
+char nm_test_var;
+void nm_test_func(void);
+void nm_test_func(void){}
+#ifdef __cplusplus
+}
+#endif
+int main(){nm_test_var='a';nm_test_func();return(0);}
+_LT_EOF
+
+  if AC_TRY_EVAL(ac_compile); then
+    # Now try to grab the symbols.
+    nlist=conftest.nm
+    if AC_TRY_EVAL(NM conftest.$ac_objext \| "$lt_cv_sys_global_symbol_pipe" \> $nlist) && test -s "$nlist"; then
+      # Try sorting and uniquifying the output.
+      if sort "$nlist" | uniq > "$nlist"T; then
+	mv -f "$nlist"T "$nlist"
+      else
+	rm -f "$nlist"T
+      fi
+
+      # Make sure that we snagged all the symbols we need.
+      if $GREP ' nm_test_var$' "$nlist" >/dev/null; then
+	if $GREP ' nm_test_func$' "$nlist" >/dev/null; then
+	  cat <<_LT_EOF > conftest.$ac_ext
+/* Keep this code in sync between libtool.m4, ltmain, lt_system.h, and tests.  */
+#if defined(_WIN32) || defined(__CYGWIN__) || defined(_WIN32_WCE)
+/* DATA imports from DLLs on WIN32 con't be const, because runtime
+   relocations are performed -- see ld's documentation on pseudo-relocs.  */
+# define LT@&t@_DLSYM_CONST
+#elif defined(__osf__)
+/* This system does not cope well with relocations in const data.  */
+# define LT@&t@_DLSYM_CONST
+#else
+# define LT@&t@_DLSYM_CONST const
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+_LT_EOF
+	  # Now generate the symbol file.
+	  eval "$lt_cv_sys_global_symbol_to_cdecl"' < "$nlist" | $GREP -v main >> conftest.$ac_ext'
+
+	  cat <<_LT_EOF >> conftest.$ac_ext
+
+/* The mapping between symbol names and symbols.  */
+LT@&t@_DLSYM_CONST struct {
+  const char *name;
+  void       *address;
+}
+lt__PROGRAM__LTX_preloaded_symbols[[]] =
+{
+  { "@PROGRAM@", (void *) 0 },
+_LT_EOF
+	  $SED "s/^$symcode$symcode* \(.*\) \(.*\)$/  {\"\2\", (void *) \&\2},/" < "$nlist" | $GREP -v main >> conftest.$ac_ext
+	  cat <<\_LT_EOF >> conftest.$ac_ext
+  {0, (void *) 0}
+};
+
+/* This works around a problem in FreeBSD linker */
+#ifdef FREEBSD_WORKAROUND
+static const void *lt_preloaded_setup() {
+  return lt__PROGRAM__LTX_preloaded_symbols;
+}
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+_LT_EOF
+	  # Now try linking the two files.
+	  mv conftest.$ac_objext conftstm.$ac_objext
+	  lt_globsym_save_LIBS=$LIBS
+	  lt_globsym_save_CFLAGS=$CFLAGS
+	  LIBS="conftstm.$ac_objext"
+	  CFLAGS="$CFLAGS$_LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1)"
+	  if AC_TRY_EVAL(ac_link) && test -s conftest${ac_exeext}; then
+	    pipe_works=yes
+	  fi
+	  LIBS=$lt_globsym_save_LIBS
+	  CFLAGS=$lt_globsym_save_CFLAGS
+	else
+	  echo "cannot find nm_test_func in $nlist" >&AS_MESSAGE_LOG_FD
+	fi
+      else
+	echo "cannot find nm_test_var in $nlist" >&AS_MESSAGE_LOG_FD
+      fi
+    else
+      echo "cannot run $lt_cv_sys_global_symbol_pipe" >&AS_MESSAGE_LOG_FD
+    fi
+  else
+    echo "$progname: failed program was:" >&AS_MESSAGE_LOG_FD
+    cat conftest.$ac_ext >&5
+  fi
+  rm -rf conftest* conftst*
+
+  # Do not use the global_symbol_pipe unless it works.
+  if test "$pipe_works" = yes; then
+    break
+  else
+    lt_cv_sys_global_symbol_pipe=
+  fi
+done
+])
+if test -z "$lt_cv_sys_global_symbol_pipe"; then
+  lt_cv_sys_global_symbol_to_cdecl=
+fi
+if test -z "$lt_cv_sys_global_symbol_pipe$lt_cv_sys_global_symbol_to_cdecl"; then
+  AC_MSG_RESULT(failed)
+else
+  AC_MSG_RESULT(ok)
+fi
+
+# Response file support.
+if test "$lt_cv_nm_interface" = "MS dumpbin"; then
+  nm_file_list_spec='@'
+elif $NM --help 2>/dev/null | grep '[[@]]FILE' >/dev/null; then
+  nm_file_list_spec='@'
+fi
+
+_LT_DECL([global_symbol_pipe], [lt_cv_sys_global_symbol_pipe], [1],
+    [Take the output of nm and produce a listing of raw symbols and C names])
+_LT_DECL([global_symbol_to_cdecl], [lt_cv_sys_global_symbol_to_cdecl], [1],
+    [Transform the output of nm in a proper C declaration])
+_LT_DECL([global_symbol_to_c_name_address],
+    [lt_cv_sys_global_symbol_to_c_name_address], [1],
+    [Transform the output of nm in a C name address pair])
+_LT_DECL([global_symbol_to_c_name_address_lib_prefix],
+    [lt_cv_sys_global_symbol_to_c_name_address_lib_prefix], [1],
+    [Transform the output of nm in a C name address pair when lib prefix is needed])
+_LT_DECL([], [nm_file_list_spec], [1],
+    [Specify filename containing input files for $NM])
+]) # _LT_CMD_GLOBAL_SYMBOLS
+
+
+# _LT_COMPILER_PIC([TAGNAME])
+# ---------------------------
+m4_defun([_LT_COMPILER_PIC],
+[m4_require([_LT_TAG_COMPILER])dnl
+_LT_TAGVAR(lt_prog_compiler_wl, $1)=
+_LT_TAGVAR(lt_prog_compiler_pic, $1)=
+_LT_TAGVAR(lt_prog_compiler_static, $1)=
+
+m4_if([$1], [CXX], [
+  # C++ specific cases for pic, static, wl, etc.
+  if test "$GXX" = yes; then
+    _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
+    _LT_TAGVAR(lt_prog_compiler_static, $1)='-static'
+
+    case $host_os in
+    aix*)
+      # All AIX code is PIC.
+      if test "$host_cpu" = ia64; then
+	# AIX 5 now supports IA64 processor
+	_LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic'
+      fi
+      ;;
+
+    amigaos*)
+      case $host_cpu in
+      powerpc)
+            # see comment about AmigaOS4 .so support
+            _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC'
+        ;;
+      m68k)
+            # FIXME: we need at least 68020 code to build shared libraries, but
+            # adding the `-m68020' flag to GCC prevents building anything better,
+            # like `-m68040'.
+            _LT_TAGVAR(lt_prog_compiler_pic, $1)='-m68020 -resident32 -malways-restore-a4'
+        ;;
+      esac
+      ;;
+
+    beos* | irix5* | irix6* | nonstopux* | osf3* | osf4* | osf5*)
+      # PIC is the default for these OSes.
+      ;;
+    mingw* | cygwin* | os2* | pw32* | cegcc*)
+      # This hack is so that the source file can tell whether it is being
+      # built for inclusion in a dll (and should export symbols for example).
+      # Although the cygwin gcc ignores -fPIC, still need this for old-style
+      # (--disable-auto-import) libraries
+      m4_if([$1], [GCJ], [],
+	[_LT_TAGVAR(lt_prog_compiler_pic, $1)='-DDLL_EXPORT'])
+      ;;
+    darwin* | rhapsody*)
+      # PIC is the default on this platform
+      # Common symbols not allowed in MH_DYLIB files
+      _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fno-common'
+      ;;
+    *djgpp*)
+      # DJGPP does not support shared libraries at all
+      _LT_TAGVAR(lt_prog_compiler_pic, $1)=
+      ;;
+    haiku*)
+      # PIC is the default for Haiku.
+      # The "-static" flag exists, but is broken.
+      _LT_TAGVAR(lt_prog_compiler_static, $1)=
+      ;;
+    interix[[3-9]]*)
+      # Interix 3.x gcc -fpic/-fPIC options generate broken code.
+      # Instead, we relocate shared libraries at runtime.
+      ;;
+    sysv4*MP*)
+      if test -d /usr/nec; then
+	_LT_TAGVAR(lt_prog_compiler_pic, $1)=-Kconform_pic
+      fi
+      ;;
+    hpux*)
+      # PIC is the default for 64-bit PA HP-UX, but not for 32-bit
+      # PA HP-UX.  On IA64 HP-UX, PIC is the default but the pic flag
+      # sets the default TLS model and affects inlining.
+      case $host_cpu in
+      hppa*64*)
+	;;
+      *)
+	_LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC'
+	;;
+      esac
+      ;;
+    *qnx* | *nto*)
+      # QNX uses GNU C++, but need to define -shared option too, otherwise
+      # it will coredump.
+      _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC -shared'
+      ;;
+    *)
+      _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC'
+      ;;
+    esac
+  else
+    case $host_os in
+      aix[[4-9]]*)
+	# All AIX code is PIC.
+	if test "$host_cpu" = ia64; then
+	  # AIX 5 now supports IA64 processor
+	  _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic'
+	else
+	  _LT_TAGVAR(lt_prog_compiler_static, $1)='-bnso -bI:/lib/syscalls.exp'
+	fi
+	;;
+      chorus*)
+	case $cc_basename in
+	cxch68*)
+	  # Green Hills C++ Compiler
+	  # _LT_TAGVAR(lt_prog_compiler_static, $1)="--no_auto_instantiation -u __main -u __premain -u _abort -r $COOL_DIR/lib/libOrb.a $MVME_DIR/lib/CC/libC.a $MVME_DIR/lib/classix/libcx.s.a"
+	  ;;
+	esac
+	;;
+      mingw* | cygwin* | os2* | pw32* | cegcc*)
+	# This hack is so that the source file can tell whether it is being
+	# built for inclusion in a dll (and should export symbols for example).
+	m4_if([$1], [GCJ], [],
+	  [_LT_TAGVAR(lt_prog_compiler_pic, $1)='-DDLL_EXPORT'])
+	;;
+      dgux*)
+	case $cc_basename in
+	  ec++*)
+	    _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC'
+	    ;;
+	  ghcx*)
+	    # Green Hills C++ Compiler
+	    _LT_TAGVAR(lt_prog_compiler_pic, $1)='-pic'
+	    ;;
+	  *)
+	    ;;
+	esac
+	;;
+      freebsd* | dragonfly*)
+	# FreeBSD uses GNU C++
+	;;
+      hpux9* | hpux10* | hpux11*)
+	case $cc_basename in
+	  CC*)
+	    _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
+	    _LT_TAGVAR(lt_prog_compiler_static, $1)='${wl}-a ${wl}archive'
+	    if test "$host_cpu" != ia64; then
+	      _LT_TAGVAR(lt_prog_compiler_pic, $1)='+Z'
+	    fi
+	    ;;
+	  aCC*)
+	    _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
+	    _LT_TAGVAR(lt_prog_compiler_static, $1)='${wl}-a ${wl}archive'
+	    case $host_cpu in
+	    hppa*64*|ia64*)
+	      # +Z the default
+	      ;;
+	    *)
+	      _LT_TAGVAR(lt_prog_compiler_pic, $1)='+Z'
+	      ;;
+	    esac
+	    ;;
+	  *)
+	    ;;
+	esac
+	;;
+      interix*)
+	# This is c89, which is MS Visual C++ (no shared libs)
+	# Anyone wants to do a port?
+	;;
+      irix5* | irix6* | nonstopux*)
+	case $cc_basename in
+	  CC*)
+	    _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
+	    _LT_TAGVAR(lt_prog_compiler_static, $1)='-non_shared'
+	    # CC pic flag -KPIC is the default.
+	    ;;
+	  *)
+	    ;;
+	esac
+	;;
+      linux* | k*bsd*-gnu | kopensolaris*-gnu | gnu*)
+	case $cc_basename in
+	  KCC*)
+	    # KAI C++ Compiler
+	    _LT_TAGVAR(lt_prog_compiler_wl, $1)='--backend -Wl,'
+	    _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC'
+	    ;;
+	  ecpc* )
+	    # old Intel C++ for x86_64 which still supported -KPIC.
+	    _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
+	    _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC'
+	    _LT_TAGVAR(lt_prog_compiler_static, $1)='-static'
+	    ;;
+	  icpc* )
+	    # Intel C++, used to be incompatible with GCC.
+	    # ICC 10 doesn't accept -KPIC any more.
+	    _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
+	    _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC'
+	    _LT_TAGVAR(lt_prog_compiler_static, $1)='-static'
+	    ;;
+	  pgCC* | pgcpp*)
+	    # Portland Group C++ compiler
+	    _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
+	    _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fpic'
+	    _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic'
+	    ;;
+	  cxx*)
+	    # Compaq C++
+	    # Make sure the PIC flag is empty.  It appears that all Alpha
+	    # Linux and Compaq Tru64 Unix objects are PIC.
+	    _LT_TAGVAR(lt_prog_compiler_pic, $1)=
+	    _LT_TAGVAR(lt_prog_compiler_static, $1)='-non_shared'
+	    ;;
+	  xlc* | xlC* | bgxl[[cC]]* | mpixl[[cC]]*)
+	    # IBM XL 8.0, 9.0 on PPC and BlueGene
+	    _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
+	    _LT_TAGVAR(lt_prog_compiler_pic, $1)='-qpic'
+	    _LT_TAGVAR(lt_prog_compiler_static, $1)='-qstaticlink'
+	    ;;
+	  *)
+	    case `$CC -V 2>&1 | sed 5q` in
+	    *Sun\ C*)
+	      # Sun C++ 5.9
+	      _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC'
+	      _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic'
+	      _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Qoption ld '
+	      ;;
+	    esac
+	    ;;
+	esac
+	;;
+      lynxos*)
+	;;
+      m88k*)
+	;;
+      mvs*)
+	case $cc_basename in
+	  cxx*)
+	    _LT_TAGVAR(lt_prog_compiler_pic, $1)='-W c,exportall'
+	    ;;
+	  *)
+	    ;;
+	esac
+	;;
+      netbsd* | netbsdelf*-gnu)
+	;;
+      *qnx* | *nto*)
+        # QNX uses GNU C++, but need to define -shared option too, otherwise
+        # it will coredump.
+        _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC -shared'
+        ;;
+      osf3* | osf4* | osf5*)
+	case $cc_basename in
+	  KCC*)
+	    _LT_TAGVAR(lt_prog_compiler_wl, $1)='--backend -Wl,'
+	    ;;
+	  RCC*)
+	    # Rational C++ 2.4.1
+	    _LT_TAGVAR(lt_prog_compiler_pic, $1)='-pic'
+	    ;;
+	  cxx*)
+	    # Digital/Compaq C++
+	    _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
+	    # Make sure the PIC flag is empty.  It appears that all Alpha
+	    # Linux and Compaq Tru64 Unix objects are PIC.
+	    _LT_TAGVAR(lt_prog_compiler_pic, $1)=
+	    _LT_TAGVAR(lt_prog_compiler_static, $1)='-non_shared'
+	    ;;
+	  *)
+	    ;;
+	esac
+	;;
+      psos*)
+	;;
+      solaris*)
+	case $cc_basename in
+	  CC* | sunCC*)
+	    # Sun C++ 4.2, 5.x and Centerline C++
+	    _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC'
+	    _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic'
+	    _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Qoption ld '
+	    ;;
+	  gcx*)
+	    # Green Hills C++ Compiler
+	    _LT_TAGVAR(lt_prog_compiler_pic, $1)='-PIC'
+	    ;;
+	  *)
+	    ;;
+	esac
+	;;
+      sunos4*)
+	case $cc_basename in
+	  CC*)
+	    # Sun C++ 4.x
+	    _LT_TAGVAR(lt_prog_compiler_pic, $1)='-pic'
+	    _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic'
+	    ;;
+	  lcc*)
+	    # Lucid
+	    _LT_TAGVAR(lt_prog_compiler_pic, $1)='-pic'
+	    ;;
+	  *)
+	    ;;
+	esac
+	;;
+      sysv5* | unixware* | sco3.2v5* | sco5v6* | OpenUNIX*)
+	case $cc_basename in
+	  CC*)
+	    _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
+	    _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC'
+	    _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic'
+	    ;;
+	esac
+	;;
+      tandem*)
+	case $cc_basename in
+	  NCC*)
+	    # NonStop-UX NCC 3.20
+	    _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC'
+	    ;;
+	  *)
+	    ;;
+	esac
+	;;
+      vxworks*)
+	;;
+      *)
+	_LT_TAGVAR(lt_prog_compiler_can_build_shared, $1)=no
+	;;
+    esac
+  fi
+],
+[
+  if test "$GCC" = yes; then
+    _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
+    _LT_TAGVAR(lt_prog_compiler_static, $1)='-static'
+
+    case $host_os in
+      aix*)
+      # All AIX code is PIC.
+      if test "$host_cpu" = ia64; then
+	# AIX 5 now supports IA64 processor
+	_LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic'
+      fi
+      ;;
+
+    amigaos*)
+      case $host_cpu in
+      powerpc)
+            # see comment about AmigaOS4 .so support
+            _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC'
+        ;;
+      m68k)
+            # FIXME: we need at least 68020 code to build shared libraries, but
+            # adding the `-m68020' flag to GCC prevents building anything better,
+            # like `-m68040'.
+            _LT_TAGVAR(lt_prog_compiler_pic, $1)='-m68020 -resident32 -malways-restore-a4'
+        ;;
+      esac
+      ;;
+
+    beos* | irix5* | irix6* | nonstopux* | osf3* | osf4* | osf5*)
+      # PIC is the default for these OSes.
+      ;;
+
+    mingw* | cygwin* | pw32* | os2* | cegcc*)
+      # This hack is so that the source file can tell whether it is being
+      # built for inclusion in a dll (and should export symbols for example).
+      # Although the cygwin gcc ignores -fPIC, still need this for old-style
+      # (--disable-auto-import) libraries
+      m4_if([$1], [GCJ], [],
+	[_LT_TAGVAR(lt_prog_compiler_pic, $1)='-DDLL_EXPORT'])
+      ;;
+
+    darwin* | rhapsody*)
+      # PIC is the default on this platform
+      # Common symbols not allowed in MH_DYLIB files
+      _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fno-common'
+      ;;
+
+    haiku*)
+      # PIC is the default for Haiku.
+      # The "-static" flag exists, but is broken.
+      _LT_TAGVAR(lt_prog_compiler_static, $1)=
+      ;;
+
+    hpux*)
+      # PIC is the default for 64-bit PA HP-UX, but not for 32-bit
+      # PA HP-UX.  On IA64 HP-UX, PIC is the default but the pic flag
+      # sets the default TLS model and affects inlining.
+      case $host_cpu in
+      hppa*64*)
+	# +Z the default
+	;;
+      *)
+	_LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC'
+	;;
+      esac
+      ;;
+
+    interix[[3-9]]*)
+      # Interix 3.x gcc -fpic/-fPIC options generate broken code.
+      # Instead, we relocate shared libraries at runtime.
+      ;;
+
+    msdosdjgpp*)
+      # Just because we use GCC doesn't mean we suddenly get shared libraries
+      # on systems that don't support them.
+      _LT_TAGVAR(lt_prog_compiler_can_build_shared, $1)=no
+      enable_shared=no
+      ;;
+
+    *nto* | *qnx*)
+      # QNX uses GNU C++, but need to define -shared option too, otherwise
+      # it will coredump.
+      _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC -shared'
+      ;;
+
+    sysv4*MP*)
+      if test -d /usr/nec; then
+	_LT_TAGVAR(lt_prog_compiler_pic, $1)=-Kconform_pic
+      fi
+      ;;
+
+    *)
+      _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC'
+      ;;
+    esac
+
+    case $cc_basename in
+    nvcc*) # Cuda Compiler Driver 2.2
+      _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Xlinker '
+      if test -n "$_LT_TAGVAR(lt_prog_compiler_pic, $1)"; then
+        _LT_TAGVAR(lt_prog_compiler_pic, $1)="-Xcompiler $_LT_TAGVAR(lt_prog_compiler_pic, $1)"
+      fi
+      ;;
+    esac
+  else
+    # PORTME Check for flag to pass linker flags through the system compiler.
+    case $host_os in
+    aix*)
+      _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
+      if test "$host_cpu" = ia64; then
+	# AIX 5 now supports IA64 processor
+	_LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic'
+      else
+	_LT_TAGVAR(lt_prog_compiler_static, $1)='-bnso -bI:/lib/syscalls.exp'
+      fi
+      ;;
+
+    mingw* | cygwin* | pw32* | os2* | cegcc*)
+      # This hack is so that the source file can tell whether it is being
+      # built for inclusion in a dll (and should export symbols for example).
+      m4_if([$1], [GCJ], [],
+	[_LT_TAGVAR(lt_prog_compiler_pic, $1)='-DDLL_EXPORT'])
+      ;;
+
+    hpux9* | hpux10* | hpux11*)
+      _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
+      # PIC is the default for IA64 HP-UX and 64-bit HP-UX, but
+      # not for PA HP-UX.
+      case $host_cpu in
+      hppa*64*|ia64*)
+	# +Z the default
+	;;
+      *)
+	_LT_TAGVAR(lt_prog_compiler_pic, $1)='+Z'
+	;;
+      esac
+      # Is there a better lt_prog_compiler_static that works with the bundled CC?
+      _LT_TAGVAR(lt_prog_compiler_static, $1)='${wl}-a ${wl}archive'
+      ;;
+
+    irix5* | irix6* | nonstopux*)
+      _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
+      # PIC (with -KPIC) is the default.
+      _LT_TAGVAR(lt_prog_compiler_static, $1)='-non_shared'
+      ;;
+
+    linux* | k*bsd*-gnu | kopensolaris*-gnu | gnu*)
+      case $cc_basename in
+      # old Intel for x86_64 which still supported -KPIC.
+      ecc*)
+	_LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
+	_LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC'
+	_LT_TAGVAR(lt_prog_compiler_static, $1)='-static'
+        ;;
+      # icc used to be incompatible with GCC.
+      # ICC 10 doesn't accept -KPIC any more.
+      icc* | ifort*)
+	_LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
+	_LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC'
+	_LT_TAGVAR(lt_prog_compiler_static, $1)='-static'
+        ;;
+      # Lahey Fortran 8.1.
+      lf95*)
+	_LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
+	_LT_TAGVAR(lt_prog_compiler_pic, $1)='--shared'
+	_LT_TAGVAR(lt_prog_compiler_static, $1)='--static'
+	;;
+      nagfor*)
+	# NAG Fortran compiler
+	_LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,-Wl,,'
+	_LT_TAGVAR(lt_prog_compiler_pic, $1)='-PIC'
+	_LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic'
+	;;
+      pgcc* | pgf77* | pgf90* | pgf95* | pgfortran*)
+        # Portland Group compilers (*not* the Pentium gcc compiler,
+	# which looks to be a dead project)
+	_LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
+	_LT_TAGVAR(lt_prog_compiler_pic, $1)='-fpic'
+	_LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic'
+        ;;
+      ccc*)
+        _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
+        # All Alpha code is PIC.
+        _LT_TAGVAR(lt_prog_compiler_static, $1)='-non_shared'
+        ;;
+      xl* | bgxl* | bgf* | mpixl*)
+	# IBM XL C 8.0/Fortran 10.1, 11.1 on PPC and BlueGene
+	_LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
+	_LT_TAGVAR(lt_prog_compiler_pic, $1)='-qpic'
+	_LT_TAGVAR(lt_prog_compiler_static, $1)='-qstaticlink'
+	;;
+      *)
+	case `$CC -V 2>&1 | sed 5q` in
+	*Sun\ Ceres\ Fortran* | *Sun*Fortran*\ [[1-7]].* | *Sun*Fortran*\ 8.[[0-3]]*)
+	  # Sun Fortran 8.3 passes all unrecognized flags to the linker
+	  _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC'
+	  _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic'
+	  _LT_TAGVAR(lt_prog_compiler_wl, $1)=''
+	  ;;
+	*Sun\ F* | *Sun*Fortran*)
+	  _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC'
+	  _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic'
+	  _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Qoption ld '
+	  ;;
+	*Sun\ C*)
+	  # Sun C 5.9
+	  _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC'
+	  _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic'
+	  _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
+	  ;;
+        *Intel*\ [[CF]]*Compiler*)
+	  _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
+	  _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC'
+	  _LT_TAGVAR(lt_prog_compiler_static, $1)='-static'
+	  ;;
+	*Portland\ Group*)
+	  _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
+	  _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fpic'
+	  _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic'
+	  ;;
+	esac
+	;;
+      esac
+      ;;
+
+    newsos6)
+      _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC'
+      _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic'
+      ;;
+
+    *nto* | *qnx*)
+      # QNX uses GNU C++, but need to define -shared option too, otherwise
+      # it will coredump.
+      _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC -shared'
+      ;;
+
+    osf3* | osf4* | osf5*)
+      _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
+      # All OSF/1 code is PIC.
+      _LT_TAGVAR(lt_prog_compiler_static, $1)='-non_shared'
+      ;;
+
+    rdos*)
+      _LT_TAGVAR(lt_prog_compiler_static, $1)='-non_shared'
+      ;;
+
+    solaris*)
+      _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC'
+      _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic'
+      case $cc_basename in
+      f77* | f90* | f95* | sunf77* | sunf90* | sunf95*)
+	_LT_TAGVAR(lt_prog_compiler_wl, $1)='-Qoption ld ';;
+      *)
+	_LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,';;
+      esac
+      ;;
+
+    sunos4*)
+      _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Qoption ld '
+      _LT_TAGVAR(lt_prog_compiler_pic, $1)='-PIC'
+      _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic'
+      ;;
+
+    sysv4 | sysv4.2uw2* | sysv4.3*)
+      _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
+      _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC'
+      _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic'
+      ;;
+
+    sysv4*MP*)
+      if test -d /usr/nec ;then
+	_LT_TAGVAR(lt_prog_compiler_pic, $1)='-Kconform_pic'
+	_LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic'
+      fi
+      ;;
+
+    sysv5* | unixware* | sco3.2v5* | sco5v6* | OpenUNIX*)
+      _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
+      _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC'
+      _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic'
+      ;;
+
+    unicos*)
+      _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
+      _LT_TAGVAR(lt_prog_compiler_can_build_shared, $1)=no
+      ;;
+
+    uts4*)
+      _LT_TAGVAR(lt_prog_compiler_pic, $1)='-pic'
+      _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic'
+      ;;
+
+    *)
+      _LT_TAGVAR(lt_prog_compiler_can_build_shared, $1)=no
+      ;;
+    esac
+  fi
+])
+case $host_os in
+  # For platforms which do not support PIC, -DPIC is meaningless:
+  *djgpp*)
+    _LT_TAGVAR(lt_prog_compiler_pic, $1)=
+    ;;
+  *)
+    _LT_TAGVAR(lt_prog_compiler_pic, $1)="$_LT_TAGVAR(lt_prog_compiler_pic, $1)@&t@m4_if([$1],[],[ -DPIC],[m4_if([$1],[CXX],[ -DPIC],[])])"
+    ;;
+esac
+
+AC_CACHE_CHECK([for $compiler option to produce PIC],
+  [_LT_TAGVAR(lt_cv_prog_compiler_pic, $1)],
+  [_LT_TAGVAR(lt_cv_prog_compiler_pic, $1)=$_LT_TAGVAR(lt_prog_compiler_pic, $1)])
+_LT_TAGVAR(lt_prog_compiler_pic, $1)=$_LT_TAGVAR(lt_cv_prog_compiler_pic, $1)
+
+#
+# Check to make sure the PIC flag actually works.
+#
+if test -n "$_LT_TAGVAR(lt_prog_compiler_pic, $1)"; then
+  _LT_COMPILER_OPTION([if $compiler PIC flag $_LT_TAGVAR(lt_prog_compiler_pic, $1) works],
+    [_LT_TAGVAR(lt_cv_prog_compiler_pic_works, $1)],
+    [$_LT_TAGVAR(lt_prog_compiler_pic, $1)@&t@m4_if([$1],[],[ -DPIC],[m4_if([$1],[CXX],[ -DPIC],[])])], [],
+    [case $_LT_TAGVAR(lt_prog_compiler_pic, $1) in
+     "" | " "*) ;;
+     *) _LT_TAGVAR(lt_prog_compiler_pic, $1)=" $_LT_TAGVAR(lt_prog_compiler_pic, $1)" ;;
+     esac],
+    [_LT_TAGVAR(lt_prog_compiler_pic, $1)=
+     _LT_TAGVAR(lt_prog_compiler_can_build_shared, $1)=no])
+fi
+_LT_TAGDECL([pic_flag], [lt_prog_compiler_pic], [1],
+	[Additional compiler flags for building library objects])
+
+_LT_TAGDECL([wl], [lt_prog_compiler_wl], [1],
+	[How to pass a linker flag through the compiler])
+#
+# Check to make sure the static flag actually works.
+#
+wl=$_LT_TAGVAR(lt_prog_compiler_wl, $1) eval lt_tmp_static_flag=\"$_LT_TAGVAR(lt_prog_compiler_static, $1)\"
+_LT_LINKER_OPTION([if $compiler static flag $lt_tmp_static_flag works],
+  _LT_TAGVAR(lt_cv_prog_compiler_static_works, $1),
+  $lt_tmp_static_flag,
+  [],
+  [_LT_TAGVAR(lt_prog_compiler_static, $1)=])
+_LT_TAGDECL([link_static_flag], [lt_prog_compiler_static], [1],
+	[Compiler flag to prevent dynamic linking])
+])# _LT_COMPILER_PIC
+
+
+# _LT_LINKER_SHLIBS([TAGNAME])
+# ----------------------------
+# See if the linker supports building shared libraries.
+m4_defun([_LT_LINKER_SHLIBS],
+[AC_REQUIRE([LT_PATH_LD])dnl
+AC_REQUIRE([LT_PATH_NM])dnl
+m4_require([_LT_PATH_MANIFEST_TOOL])dnl
+m4_require([_LT_FILEUTILS_DEFAULTS])dnl
+m4_require([_LT_DECL_EGREP])dnl
+m4_require([_LT_DECL_SED])dnl
+m4_require([_LT_CMD_GLOBAL_SYMBOLS])dnl
+m4_require([_LT_TAG_COMPILER])dnl
+AC_MSG_CHECKING([whether the $compiler linker ($LD) supports shared libraries])
+m4_if([$1], [CXX], [
+  _LT_TAGVAR(export_symbols_cmds, $1)='$NM $libobjs $convenience | $global_symbol_pipe | $SED '\''s/.* //'\'' | sort | uniq > $export_symbols'
+  _LT_TAGVAR(exclude_expsyms, $1)=['_GLOBAL_OFFSET_TABLE_|_GLOBAL__F[ID]_.*']
+  case $host_os in
+  aix[[4-9]]*)
+    # If we're using GNU nm, then we don't want the "-C" option.
+    # -C means demangle to AIX nm, but means don't demangle with GNU nm
+    # Also, AIX nm treats weak defined symbols like other global defined
+    # symbols, whereas GNU nm marks them as "W".
+    if $NM -V 2>&1 | $GREP 'GNU' > /dev/null; then
+      _LT_TAGVAR(export_symbols_cmds, $1)='$NM -Bpg $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B") || (\$ 2 == "W")) && ([substr](\$ 3,1,1) != ".")) { print \$ 3 } }'\'' | sort -u > $export_symbols'
+    else
+      _LT_TAGVAR(export_symbols_cmds, $1)='$NM -BCpg $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B")) && ([substr](\$ 3,1,1) != ".")) { print \$ 3 } }'\'' | sort -u > $export_symbols'
+    fi
+    ;;
+  pw32*)
+    _LT_TAGVAR(export_symbols_cmds, $1)="$ltdll_cmds"
+    ;;
+  cygwin* | mingw* | cegcc*)
+    case $cc_basename in
+    cl*)
+      _LT_TAGVAR(exclude_expsyms, $1)='_NULL_IMPORT_DESCRIPTOR|_IMPORT_DESCRIPTOR_.*'
+      ;;
+    *)
+      _LT_TAGVAR(export_symbols_cmds, $1)='$NM $libobjs $convenience | $global_symbol_pipe | $SED -e '\''/^[[BCDGRS]][[ ]]/s/.*[[ ]]\([[^ ]]*\)/\1 DATA/;s/^.*[[ ]]__nm__\([[^ ]]*\)[[ ]][[^ ]]*/\1 DATA/;/^I[[ ]]/d;/^[[AITW]][[ ]]/s/.* //'\'' | sort | uniq > $export_symbols'
+      _LT_TAGVAR(exclude_expsyms, $1)=['[_]+GLOBAL_OFFSET_TABLE_|[_]+GLOBAL__[FID]_.*|[_]+head_[A-Za-z0-9_]+_dll|[A-Za-z0-9_]+_dll_iname']
+      ;;
+    esac
+    ;;
+  linux* | k*bsd*-gnu | gnu*)
+    _LT_TAGVAR(link_all_deplibs, $1)=no
+    ;;
+  *)
+    _LT_TAGVAR(export_symbols_cmds, $1)='$NM $libobjs $convenience | $global_symbol_pipe | $SED '\''s/.* //'\'' | sort | uniq > $export_symbols'
+    ;;
+  esac
+], [
+  runpath_var=
+  _LT_TAGVAR(allow_undefined_flag, $1)=
+  _LT_TAGVAR(always_export_symbols, $1)=no
+  _LT_TAGVAR(archive_cmds, $1)=
+  _LT_TAGVAR(archive_expsym_cmds, $1)=
+  _LT_TAGVAR(compiler_needs_object, $1)=no
+  _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=no
+  _LT_TAGVAR(export_dynamic_flag_spec, $1)=
+  _LT_TAGVAR(export_symbols_cmds, $1)='$NM $libobjs $convenience | $global_symbol_pipe | $SED '\''s/.* //'\'' | sort | uniq > $export_symbols'
+  _LT_TAGVAR(hardcode_automatic, $1)=no
+  _LT_TAGVAR(hardcode_direct, $1)=no
+  _LT_TAGVAR(hardcode_direct_absolute, $1)=no
+  _LT_TAGVAR(hardcode_libdir_flag_spec, $1)=
+  _LT_TAGVAR(hardcode_libdir_separator, $1)=
+  _LT_TAGVAR(hardcode_minus_L, $1)=no
+  _LT_TAGVAR(hardcode_shlibpath_var, $1)=unsupported
+  _LT_TAGVAR(inherit_rpath, $1)=no
+  _LT_TAGVAR(link_all_deplibs, $1)=unknown
+  _LT_TAGVAR(module_cmds, $1)=
+  _LT_TAGVAR(module_expsym_cmds, $1)=
+  _LT_TAGVAR(old_archive_from_new_cmds, $1)=
+  _LT_TAGVAR(old_archive_from_expsyms_cmds, $1)=
+  _LT_TAGVAR(thread_safe_flag_spec, $1)=
+  _LT_TAGVAR(whole_archive_flag_spec, $1)=
+  # include_expsyms should be a list of space-separated symbols to be *always*
+  # included in the symbol list
+  _LT_TAGVAR(include_expsyms, $1)=
+  # exclude_expsyms can be an extended regexp of symbols to exclude
+  # it will be wrapped by ` (' and `)$', so one must not match beginning or
+  # end of line.  Example: `a|bc|.*d.*' will exclude the symbols `a' and `bc',
+  # as well as any symbol that contains `d'.
+  _LT_TAGVAR(exclude_expsyms, $1)=['_GLOBAL_OFFSET_TABLE_|_GLOBAL__F[ID]_.*']
+  # Although _GLOBAL_OFFSET_TABLE_ is a valid symbol C name, most a.out
+  # platforms (ab)use it in PIC code, but their linkers get confused if
+  # the symbol is explicitly referenced.  Since portable code cannot
+  # rely on this symbol name, it's probably fine to never include it in
+  # preloaded symbol tables.
+  # Exclude shared library initialization/finalization symbols.
+dnl Note also adjust exclude_expsyms for C++ above.
+  extract_expsyms_cmds=
+
+  case $host_os in
+  cygwin* | mingw* | pw32* | cegcc*)
+    # FIXME: the MSVC++ port hasn't been tested in a loooong time
+    # When not using gcc, we currently assume that we are using
+    # Microsoft Visual C++.
+    if test "$GCC" != yes; then
+      with_gnu_ld=no
+    fi
+    ;;
+  interix*)
+    # we just hope/assume this is gcc and not c89 (= MSVC++)
+    with_gnu_ld=yes
+    ;;
+  openbsd*)
+    with_gnu_ld=no
+    ;;
+  linux* | k*bsd*-gnu | gnu*)
+    _LT_TAGVAR(link_all_deplibs, $1)=no
+    ;;
+  esac
+
+  _LT_TAGVAR(ld_shlibs, $1)=yes
+
+  # On some targets, GNU ld is compatible enough with the native linker
+  # that we're better off using the native interface for both.
+  lt_use_gnu_ld_interface=no
+  if test "$with_gnu_ld" = yes; then
+    case $host_os in
+      aix*)
+	# The AIX port of GNU ld has always aspired to compatibility
+	# with the native linker.  However, as the warning in the GNU ld
+	# block says, versions before 2.19.5* couldn't really create working
+	# shared libraries, regardless of the interface used.
+	case `$LD -v 2>&1` in
+	  *\ \(GNU\ Binutils\)\ 2.19.5*) ;;
+	  *\ \(GNU\ Binutils\)\ 2.[[2-9]]*) ;;
+	  *\ \(GNU\ Binutils\)\ [[3-9]]*) ;;
+	  *)
+	    lt_use_gnu_ld_interface=yes
+	    ;;
+	esac
+	;;
+      *)
+	lt_use_gnu_ld_interface=yes
+	;;
+    esac
+  fi
+
+  if test "$lt_use_gnu_ld_interface" = yes; then
+    # If archive_cmds runs LD, not CC, wlarc should be empty
+    wlarc='${wl}'
+
+    # Set some defaults for GNU ld with shared library support. These
+    # are reset later if shared libraries are not supported. Putting them
+    # here allows them to be overridden if necessary.
+    runpath_var=LD_RUN_PATH
+    _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir'
+    _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}--export-dynamic'
+    # ancient GNU ld didn't support --whole-archive et. al.
+    if $LD --help 2>&1 | $GREP 'no-whole-archive' > /dev/null; then
+      _LT_TAGVAR(whole_archive_flag_spec, $1)="$wlarc"'--whole-archive$convenience '"$wlarc"'--no-whole-archive'
+    else
+      _LT_TAGVAR(whole_archive_flag_spec, $1)=
+    fi
+    supports_anon_versioning=no
+    case `$LD -v 2>&1` in
+      *GNU\ gold*) supports_anon_versioning=yes ;;
+      *\ [[01]].* | *\ 2.[[0-9]].* | *\ 2.10.*) ;; # catch versions < 2.11
+      *\ 2.11.93.0.2\ *) supports_anon_versioning=yes ;; # RH7.3 ...
+      *\ 2.11.92.0.12\ *) supports_anon_versioning=yes ;; # Mandrake 8.2 ...
+      *\ 2.11.*) ;; # other 2.11 versions
+      *) supports_anon_versioning=yes ;;
+    esac
+
+    # See if GNU ld supports shared libraries.
+    case $host_os in
+    aix[[3-9]]*)
+      # On AIX/PPC, the GNU linker is very broken
+      if test "$host_cpu" != ia64; then
+	_LT_TAGVAR(ld_shlibs, $1)=no
+	cat <<_LT_EOF 1>&2
+
+*** Warning: the GNU linker, at least up to release 2.19, is reported
+*** to be unable to reliably create shared libraries on AIX.
+*** Therefore, libtool is disabling shared libraries support.  If you
+*** really care for shared libraries, you may want to install binutils
+*** 2.20 or above, or modify your PATH so that a non-GNU linker is found.
+*** You will then need to restart the configuration process.
+
+_LT_EOF
+      fi
+      ;;
+
+    amigaos*)
+      case $host_cpu in
+      powerpc)
+            # see comment about AmigaOS4 .so support
+            _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib'
+            _LT_TAGVAR(archive_expsym_cmds, $1)=''
+        ;;
+      m68k)
+            _LT_TAGVAR(archive_cmds, $1)='$RM $output_objdir/a2ixlibrary.data~$ECHO "#define NAME $libname" > $output_objdir/a2ixlibrary.data~$ECHO "#define LIBRARY_ID 1" >> $output_objdir/a2ixlibrary.data~$ECHO "#define VERSION $major" >> $output_objdir/a2ixlibrary.data~$ECHO "#define REVISION $revision" >> $output_objdir/a2ixlibrary.data~$AR $AR_FLAGS $lib $libobjs~$RANLIB $lib~(cd $output_objdir && a2ixlibrary -32)'
+            _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir'
+            _LT_TAGVAR(hardcode_minus_L, $1)=yes
+        ;;
+      esac
+      ;;
+
+    beos*)
+      if $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then
+	_LT_TAGVAR(allow_undefined_flag, $1)=unsupported
+	# Joseph Beckenbach <jrb3@best.com> says some releases of gcc
+	# support --undefined.  This deserves some investigation.  FIXME
+	_LT_TAGVAR(archive_cmds, $1)='$CC -nostart $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib'
+      else
+	_LT_TAGVAR(ld_shlibs, $1)=no
+      fi
+      ;;
+
+    cygwin* | mingw* | pw32* | cegcc*)
+      # _LT_TAGVAR(hardcode_libdir_flag_spec, $1) is actually meaningless,
+      # as there is no search path for DLLs.
+      _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir'
+      _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}--export-all-symbols'
+      _LT_TAGVAR(allow_undefined_flag, $1)=unsupported
+      _LT_TAGVAR(always_export_symbols, $1)=no
+      _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=yes
+      _LT_TAGVAR(export_symbols_cmds, $1)='$NM $libobjs $convenience | $global_symbol_pipe | $SED -e '\''/^[[BCDGRS]][[ ]]/s/.*[[ ]]\([[^ ]]*\)/\1 DATA/;s/^.*[[ ]]__nm__\([[^ ]]*\)[[ ]][[^ ]]*/\1 DATA/;/^I[[ ]]/d;/^[[AITW]][[ ]]/s/.* //'\'' | sort | uniq > $export_symbols'
+      _LT_TAGVAR(exclude_expsyms, $1)=['[_]+GLOBAL_OFFSET_TABLE_|[_]+GLOBAL__[FID]_.*|[_]+head_[A-Za-z0-9_]+_dll|[A-Za-z0-9_]+_dll_iname']
+
+      if $LD --help 2>&1 | $GREP 'auto-import' > /dev/null; then
+        _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags -o $output_objdir/$soname ${wl}--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib'
+	# If the export-symbols file already is a .def file (1st line
+	# is EXPORTS), use it as is; otherwise, prepend...
+	_LT_TAGVAR(archive_expsym_cmds, $1)='if test "x`$SED 1q $export_symbols`" = xEXPORTS; then
+	  cp $export_symbols $output_objdir/$soname.def;
+	else
+	  echo EXPORTS > $output_objdir/$soname.def;
+	  cat $export_symbols >> $output_objdir/$soname.def;
+	fi~
+	$CC -shared $output_objdir/$soname.def $libobjs $deplibs $compiler_flags -o $output_objdir/$soname ${wl}--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib'
+      else
+	_LT_TAGVAR(ld_shlibs, $1)=no
+      fi
+      ;;
+
+    haiku*)
+      _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib'
+      _LT_TAGVAR(link_all_deplibs, $1)=yes
+      ;;
+
+    interix[[3-9]]*)
+      _LT_TAGVAR(hardcode_direct, $1)=no
+      _LT_TAGVAR(hardcode_shlibpath_var, $1)=no
+      _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath,$libdir'
+      _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-E'
+      # Hack: On Interix 3.x, we cannot compile PIC because of a broken gcc.
+      # Instead, shared libraries are loaded at an image base (0x10000000 by
+      # default) and relocated if they conflict, which is a slow very memory
+      # consuming and fragmenting process.  To avoid this, we pick a random,
+      # 256 KiB-aligned image base between 0x50000000 and 0x6FFC0000 at link
+      # time.  Moving up from 0x10000000 also allows more sbrk(2) space.
+      _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-h,$soname ${wl}--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib'
+      _LT_TAGVAR(archive_expsym_cmds, $1)='sed "s,^,_," $export_symbols >$output_objdir/$soname.expsym~$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-h,$soname ${wl}--retain-symbols-file,$output_objdir/$soname.expsym ${wl}--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib'
+      ;;
+
+    gnu* | linux* | tpf* | k*bsd*-gnu | kopensolaris*-gnu)
+      tmp_diet=no
+      if test "$host_os" = linux-dietlibc; then
+	case $cc_basename in
+	  diet\ *) tmp_diet=yes;;	# linux-dietlibc with static linking (!diet-dyn)
+	esac
+      fi
+      if $LD --help 2>&1 | $EGREP ': supported targets:.* elf' > /dev/null \
+	 && test "$tmp_diet" = no
+      then
+	tmp_addflag=' $pic_flag'
+	tmp_sharedflag='-shared'
+	case $cc_basename,$host_cpu in
+        pgcc*)				# Portland Group C compiler
+	  _LT_TAGVAR(whole_archive_flag_spec, $1)='${wl}--whole-archive`for conv in $convenience\"\"; do test  -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` ${wl}--no-whole-archive'
+	  tmp_addflag=' $pic_flag'
+	  ;;
+	pgf77* | pgf90* | pgf95* | pgfortran*)
+					# Portland Group f77 and f90 compilers
+	  _LT_TAGVAR(whole_archive_flag_spec, $1)='${wl}--whole-archive`for conv in $convenience\"\"; do test  -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` ${wl}--no-whole-archive'
+	  tmp_addflag=' $pic_flag -Mnomain' ;;
+	ecc*,ia64* | icc*,ia64*)	# Intel C compiler on ia64
+	  tmp_addflag=' -i_dynamic' ;;
+	efc*,ia64* | ifort*,ia64*)	# Intel Fortran compiler on ia64
+	  tmp_addflag=' -i_dynamic -nofor_main' ;;
+	ifc* | ifort*)			# Intel Fortran compiler
+	  tmp_addflag=' -nofor_main' ;;
+	lf95*)				# Lahey Fortran 8.1
+	  _LT_TAGVAR(whole_archive_flag_spec, $1)=
+	  tmp_sharedflag='--shared' ;;
+	xl[[cC]]* | bgxl[[cC]]* | mpixl[[cC]]*) # IBM XL C 8.0 on PPC (deal with xlf below)
+	  tmp_sharedflag='-qmkshrobj'
+	  tmp_addflag= ;;
+	nvcc*)	# Cuda Compiler Driver 2.2
+	  _LT_TAGVAR(whole_archive_flag_spec, $1)='${wl}--whole-archive`for conv in $convenience\"\"; do test  -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` ${wl}--no-whole-archive'
+	  _LT_TAGVAR(compiler_needs_object, $1)=yes
+	  ;;
+	esac
+	case `$CC -V 2>&1 | sed 5q` in
+	*Sun\ C*)			# Sun C 5.9
+	  _LT_TAGVAR(whole_archive_flag_spec, $1)='${wl}--whole-archive`new_convenience=; for conv in $convenience\"\"; do test -z \"$conv\" || new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` ${wl}--no-whole-archive'
+	  _LT_TAGVAR(compiler_needs_object, $1)=yes
+	  tmp_sharedflag='-G' ;;
+	*Sun\ F*)			# Sun Fortran 8.3
+	  tmp_sharedflag='-G' ;;
+	esac
+	_LT_TAGVAR(archive_cmds, $1)='$CC '"$tmp_sharedflag""$tmp_addflag"' $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib'
+
+        if test "x$supports_anon_versioning" = xyes; then
+          _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $output_objdir/$libname.ver~
+	    cat $export_symbols | sed -e "s/\(.*\)/\1;/" >> $output_objdir/$libname.ver~
+	    echo "local: *; };" >> $output_objdir/$libname.ver~
+	    $CC '"$tmp_sharedflag""$tmp_addflag"' $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-version-script ${wl}$output_objdir/$libname.ver -o $lib'
+        fi
+
+	case $cc_basename in
+	xlf* | bgf* | bgxlf* | mpixlf*)
+	  # IBM XL Fortran 10.1 on PPC cannot create shared libs itself
+	  _LT_TAGVAR(whole_archive_flag_spec, $1)='--whole-archive$convenience --no-whole-archive'
+	  _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir'
+	  _LT_TAGVAR(archive_cmds, $1)='$LD -shared $libobjs $deplibs $linker_flags -soname $soname -o $lib'
+	  if test "x$supports_anon_versioning" = xyes; then
+	    _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $output_objdir/$libname.ver~
+	      cat $export_symbols | sed -e "s/\(.*\)/\1;/" >> $output_objdir/$libname.ver~
+	      echo "local: *; };" >> $output_objdir/$libname.ver~
+	      $LD -shared $libobjs $deplibs $linker_flags -soname $soname -version-script $output_objdir/$libname.ver -o $lib'
+	  fi
+	  ;;
+	esac
+      else
+        _LT_TAGVAR(ld_shlibs, $1)=no
+      fi
+      ;;
+
+    netbsd* | netbsdelf*-gnu)
+      if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then
+	_LT_TAGVAR(archive_cmds, $1)='$LD -Bshareable $libobjs $deplibs $linker_flags -o $lib'
+	wlarc=
+      else
+	_LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib'
+	_LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib'
+      fi
+      ;;
+
+    solaris*)
+      if $LD -v 2>&1 | $GREP 'BFD 2\.8' > /dev/null; then
+	_LT_TAGVAR(ld_shlibs, $1)=no
+	cat <<_LT_EOF 1>&2
+
+*** Warning: The releases 2.8.* of the GNU linker cannot reliably
+*** create shared libraries on Solaris systems.  Therefore, libtool
+*** is disabling shared libraries support.  We urge you to upgrade GNU
+*** binutils to release 2.9.1 or newer.  Another option is to modify
+*** your PATH or compiler configuration so that the native linker is
+*** used, and then restart.
+
+_LT_EOF
+      elif $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then
+	_LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib'
+	_LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib'
+      else
+	_LT_TAGVAR(ld_shlibs, $1)=no
+      fi
+      ;;
+
+    sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX*)
+      case `$LD -v 2>&1` in
+        *\ [[01]].* | *\ 2.[[0-9]].* | *\ 2.1[[0-5]].*)
+	_LT_TAGVAR(ld_shlibs, $1)=no
+	cat <<_LT_EOF 1>&2
+
+*** Warning: Releases of the GNU linker prior to 2.16.91.0.3 can not
+*** reliably create shared libraries on SCO systems.  Therefore, libtool
+*** is disabling shared libraries support.  We urge you to upgrade GNU
+*** binutils to release 2.16.91.0.3 or newer.  Another option is to modify
+*** your PATH or compiler configuration so that the native linker is
+*** used, and then restart.
+
+_LT_EOF
+	;;
+	*)
+	  # For security reasons, it is highly recommended that you always
+	  # use absolute paths for naming shared libraries, and exclude the
+	  # DT_RUNPATH tag from executables and libraries.  But doing so
+	  # requires that you compile everything twice, which is a pain.
+	  if $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then
+	    _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir'
+	    _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib'
+	    _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib'
+	  else
+	    _LT_TAGVAR(ld_shlibs, $1)=no
+	  fi
+	;;
+      esac
+      ;;
+
+    sunos4*)
+      _LT_TAGVAR(archive_cmds, $1)='$LD -assert pure-text -Bshareable -o $lib $libobjs $deplibs $linker_flags'
+      wlarc=
+      _LT_TAGVAR(hardcode_direct, $1)=yes
+      _LT_TAGVAR(hardcode_shlibpath_var, $1)=no
+      ;;
+
+    *)
+      if $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then
+	_LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib'
+	_LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib'
+      else
+	_LT_TAGVAR(ld_shlibs, $1)=no
+      fi
+      ;;
+    esac
+
+    if test "$_LT_TAGVAR(ld_shlibs, $1)" = no; then
+      runpath_var=
+      _LT_TAGVAR(hardcode_libdir_flag_spec, $1)=
+      _LT_TAGVAR(export_dynamic_flag_spec, $1)=
+      _LT_TAGVAR(whole_archive_flag_spec, $1)=
+    fi
+  else
+    # PORTME fill in a description of your system's linker (not GNU ld)
+    case $host_os in
+    aix3*)
+      _LT_TAGVAR(allow_undefined_flag, $1)=unsupported
+      _LT_TAGVAR(always_export_symbols, $1)=yes
+      _LT_TAGVAR(archive_expsym_cmds, $1)='$LD -o $output_objdir/$soname $libobjs $deplibs $linker_flags -bE:$export_symbols -T512 -H512 -bM:SRE~$AR $AR_FLAGS $lib $output_objdir/$soname'
+      # Note: this linker hardcodes the directories in LIBPATH if there
+      # are no directories specified by -L.
+      _LT_TAGVAR(hardcode_minus_L, $1)=yes
+      if test "$GCC" = yes && test -z "$lt_prog_compiler_static"; then
+	# Neither direct hardcoding nor static linking is supported with a
+	# broken collect2.
+	_LT_TAGVAR(hardcode_direct, $1)=unsupported
+      fi
+      ;;
+
+    aix[[4-9]]*)
+      if test "$host_cpu" = ia64; then
+	# On IA64, the linker does run time linking by default, so we don't
+	# have to do anything special.
+	aix_use_runtimelinking=no
+	exp_sym_flag='-Bexport'
+	no_entry_flag=""
+      else
+	# If we're using GNU nm, then we don't want the "-C" option.
+	# -C means demangle to AIX nm, but means don't demangle with GNU nm
+	# Also, AIX nm treats weak defined symbols like other global
+	# defined symbols, whereas GNU nm marks them as "W".
+	if $NM -V 2>&1 | $GREP 'GNU' > /dev/null; then
+	  _LT_TAGVAR(export_symbols_cmds, $1)='$NM -Bpg $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B") || (\$ 2 == "W")) && ([substr](\$ 3,1,1) != ".")) { print \$ 3 } }'\'' | sort -u > $export_symbols'
+	else
+	  _LT_TAGVAR(export_symbols_cmds, $1)='$NM -BCpg $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B")) && ([substr](\$ 3,1,1) != ".")) { print \$ 3 } }'\'' | sort -u > $export_symbols'
+	fi
+	aix_use_runtimelinking=no
+
+	# Test if we are trying to use run time linking or normal
+	# AIX style linking. If -brtl is somewhere in LDFLAGS, we
+	# need to do runtime linking.
+	case $host_os in aix4.[[23]]|aix4.[[23]].*|aix[[5-9]]*)
+	  for ld_flag in $LDFLAGS; do
+	  if (test $ld_flag = "-brtl" || test $ld_flag = "-Wl,-brtl"); then
+	    aix_use_runtimelinking=yes
+	    break
+	  fi
+	  done
+	  ;;
+	esac
+
+	exp_sym_flag='-bexport'
+	no_entry_flag='-bnoentry'
+      fi
+
+      # When large executables or shared objects are built, AIX ld can
+      # have problems creating the table of contents.  If linking a library
+      # or program results in "error TOC overflow" add -mminimal-toc to
+      # CXXFLAGS/CFLAGS for g++/gcc.  In the cases where that is not
+      # enough to fix the problem, add -Wl,-bbigtoc to LDFLAGS.
+
+      _LT_TAGVAR(archive_cmds, $1)=''
+      _LT_TAGVAR(hardcode_direct, $1)=yes
+      _LT_TAGVAR(hardcode_direct_absolute, $1)=yes
+      _LT_TAGVAR(hardcode_libdir_separator, $1)=':'
+      _LT_TAGVAR(link_all_deplibs, $1)=yes
+      _LT_TAGVAR(file_list_spec, $1)='${wl}-f,'
+
+      if test "$GCC" = yes; then
+	case $host_os in aix4.[[012]]|aix4.[[012]].*)
+	# We only want to do this on AIX 4.2 and lower, the check
+	# below for broken collect2 doesn't work under 4.3+
+	  collect2name=`${CC} -print-prog-name=collect2`
+	  if test -f "$collect2name" &&
+	   strings "$collect2name" | $GREP resolve_lib_name >/dev/null
+	  then
+	  # We have reworked collect2
+	  :
+	  else
+	  # We have old collect2
+	  _LT_TAGVAR(hardcode_direct, $1)=unsupported
+	  # It fails to find uninstalled libraries when the uninstalled
+	  # path is not listed in the libpath.  Setting hardcode_minus_L
+	  # to unsupported forces relinking
+	  _LT_TAGVAR(hardcode_minus_L, $1)=yes
+	  _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir'
+	  _LT_TAGVAR(hardcode_libdir_separator, $1)=
+	  fi
+	  ;;
+	esac
+	shared_flag='-shared'
+	if test "$aix_use_runtimelinking" = yes; then
+	  shared_flag="$shared_flag "'${wl}-G'
+	fi
+	_LT_TAGVAR(link_all_deplibs, $1)=no
+      else
+	# not using gcc
+	if test "$host_cpu" = ia64; then
+	# VisualAge C++, Version 5.5 for AIX 5L for IA-64, Beta 3 Release
+	# chokes on -Wl,-G. The following line is correct:
+	  shared_flag='-G'
+	else
+	  if test "$aix_use_runtimelinking" = yes; then
+	    shared_flag='${wl}-G'
+	  else
+	    shared_flag='${wl}-bM:SRE'
+	  fi
+	fi
+      fi
+
+      _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-bexpall'
+      # It seems that -bexpall does not export symbols beginning with
+      # underscore (_), so it is better to generate a list of symbols to export.
+      _LT_TAGVAR(always_export_symbols, $1)=yes
+      if test "$aix_use_runtimelinking" = yes; then
+	# Warning - without using the other runtime loading flags (-brtl),
+	# -berok will link without error, but may produce a broken library.
+	_LT_TAGVAR(allow_undefined_flag, $1)='-berok'
+        # Determine the default libpath from the value encoded in an
+        # empty executable.
+        _LT_SYS_MODULE_PATH_AIX([$1])
+        _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-blibpath:$libdir:'"$aix_libpath"
+        _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -o $output_objdir/$soname $libobjs $deplibs '"\${wl}$no_entry_flag"' $compiler_flags `if test "x${allow_undefined_flag}" != "x"; then func_echo_all "${wl}${allow_undefined_flag}"; else :; fi` '"\${wl}$exp_sym_flag:\$export_symbols $shared_flag"
+      else
+	if test "$host_cpu" = ia64; then
+	  _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-R $libdir:/usr/lib:/lib'
+	  _LT_TAGVAR(allow_undefined_flag, $1)="-z nodefs"
+	  _LT_TAGVAR(archive_expsym_cmds, $1)="\$CC $shared_flag"' -o $output_objdir/$soname $libobjs $deplibs '"\${wl}$no_entry_flag"' $compiler_flags ${wl}${allow_undefined_flag} '"\${wl}$exp_sym_flag:\$export_symbols"
+	else
+	 # Determine the default libpath from the value encoded in an
+	 # empty executable.
+	 _LT_SYS_MODULE_PATH_AIX([$1])
+	 _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-blibpath:$libdir:'"$aix_libpath"
+	  # Warning - without using the other run time loading flags,
+	  # -berok will link without error, but may produce a broken library.
+	  _LT_TAGVAR(no_undefined_flag, $1)=' ${wl}-bernotok'
+	  _LT_TAGVAR(allow_undefined_flag, $1)=' ${wl}-berok'
+	  if test "$with_gnu_ld" = yes; then
+	    # We only use this code for GNU lds that support --whole-archive.
+	    _LT_TAGVAR(whole_archive_flag_spec, $1)='${wl}--whole-archive$convenience ${wl}--no-whole-archive'
+	  else
+	    # Exported symbols can be pulled into shared objects from archives
+	    _LT_TAGVAR(whole_archive_flag_spec, $1)='$convenience'
+	  fi
+	  _LT_TAGVAR(archive_cmds_need_lc, $1)=yes
+	  # This is similar to how AIX traditionally builds its shared libraries.
+	  _LT_TAGVAR(archive_expsym_cmds, $1)="\$CC $shared_flag"' -o $output_objdir/$soname $libobjs $deplibs ${wl}-bnoentry $compiler_flags ${wl}-bE:$export_symbols${allow_undefined_flag}~$AR $AR_FLAGS $output_objdir/$libname$release.a $output_objdir/$soname'
+	fi
+      fi
+      ;;
+
+    amigaos*)
+      case $host_cpu in
+      powerpc)
+            # see comment about AmigaOS4 .so support
+            _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib'
+            _LT_TAGVAR(archive_expsym_cmds, $1)=''
+        ;;
+      m68k)
+            _LT_TAGVAR(archive_cmds, $1)='$RM $output_objdir/a2ixlibrary.data~$ECHO "#define NAME $libname" > $output_objdir/a2ixlibrary.data~$ECHO "#define LIBRARY_ID 1" >> $output_objdir/a2ixlibrary.data~$ECHO "#define VERSION $major" >> $output_objdir/a2ixlibrary.data~$ECHO "#define REVISION $revision" >> $output_objdir/a2ixlibrary.data~$AR $AR_FLAGS $lib $libobjs~$RANLIB $lib~(cd $output_objdir && a2ixlibrary -32)'
+            _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir'
+            _LT_TAGVAR(hardcode_minus_L, $1)=yes
+        ;;
+      esac
+      ;;
+
+    bsdi[[45]]*)
+      _LT_TAGVAR(export_dynamic_flag_spec, $1)=-rdynamic
+      ;;
+
+    cygwin* | mingw* | pw32* | cegcc*)
+      # When not using gcc, we currently assume that we are using
+      # Microsoft Visual C++.
+      # hardcode_libdir_flag_spec is actually meaningless, as there is
+      # no search path for DLLs.
+      case $cc_basename in
+      cl*)
+	# Native MSVC
+	_LT_TAGVAR(hardcode_libdir_flag_spec, $1)=' '
+	_LT_TAGVAR(allow_undefined_flag, $1)=unsupported
+	_LT_TAGVAR(always_export_symbols, $1)=yes
+	_LT_TAGVAR(file_list_spec, $1)='@'
+	# Tell ltmain to make .lib files, not .a files.
+	libext=lib
+	# Tell ltmain to make .dll files, not .so files.
+	shrext_cmds=".dll"
+	# FIXME: Setting linknames here is a bad hack.
+	_LT_TAGVAR(archive_cmds, $1)='$CC -o $output_objdir/$soname $libobjs $compiler_flags $deplibs -Wl,-dll~linknames='
+	_LT_TAGVAR(archive_expsym_cmds, $1)='if test "x`$SED 1q $export_symbols`" = xEXPORTS; then
+	    sed -n -e 's/\\\\\\\(.*\\\\\\\)/-link\\\ -EXPORT:\\\\\\\1/' -e '1\\\!p' < $export_symbols > $output_objdir/$soname.exp;
+	  else
+	    sed -e 's/\\\\\\\(.*\\\\\\\)/-link\\\ -EXPORT:\\\\\\\1/' < $export_symbols > $output_objdir/$soname.exp;
+	  fi~
+	  $CC -o $tool_output_objdir$soname $libobjs $compiler_flags $deplibs "@$tool_output_objdir$soname.exp" -Wl,-DLL,-IMPLIB:"$tool_output_objdir$libname.dll.lib"~
+	  linknames='
+	# The linker will not automatically build a static lib if we build a DLL.
+	# _LT_TAGVAR(old_archive_from_new_cmds, $1)='true'
+	_LT_TAGVAR(enable_shared_with_static_runtimes, $1)=yes
+	_LT_TAGVAR(exclude_expsyms, $1)='_NULL_IMPORT_DESCRIPTOR|_IMPORT_DESCRIPTOR_.*'
+	_LT_TAGVAR(export_symbols_cmds, $1)='$NM $libobjs $convenience | $global_symbol_pipe | $SED -e '\''/^[[BCDGRS]][[ ]]/s/.*[[ ]]\([[^ ]]*\)/\1,DATA/'\'' | $SED -e '\''/^[[AITW]][[ ]]/s/.*[[ ]]//'\'' | sort | uniq > $export_symbols'
+	# Don't use ranlib
+	_LT_TAGVAR(old_postinstall_cmds, $1)='chmod 644 $oldlib'
+	_LT_TAGVAR(postlink_cmds, $1)='lt_outputfile="@OUTPUT@"~
+	  lt_tool_outputfile="@TOOL_OUTPUT@"~
+	  case $lt_outputfile in
+	    *.exe|*.EXE) ;;
+	    *)
+	      lt_outputfile="$lt_outputfile.exe"
+	      lt_tool_outputfile="$lt_tool_outputfile.exe"
+	      ;;
+	  esac~
+	  if test "$MANIFEST_TOOL" != ":" && test -f "$lt_outputfile.manifest"; then
+	    $MANIFEST_TOOL -manifest "$lt_tool_outputfile.manifest" -outputresource:"$lt_tool_outputfile" || exit 1;
+	    $RM "$lt_outputfile.manifest";
+	  fi'
+	;;
+      *)
+	# Assume MSVC wrapper
+	_LT_TAGVAR(hardcode_libdir_flag_spec, $1)=' '
+	_LT_TAGVAR(allow_undefined_flag, $1)=unsupported
+	# Tell ltmain to make .lib files, not .a files.
+	libext=lib
+	# Tell ltmain to make .dll files, not .so files.
+	shrext_cmds=".dll"
+	# FIXME: Setting linknames here is a bad hack.
+	_LT_TAGVAR(archive_cmds, $1)='$CC -o $lib $libobjs $compiler_flags `func_echo_all "$deplibs" | $SED '\''s/ -lc$//'\''` -link -dll~linknames='
+	# The linker will automatically build a .lib file if we build a DLL.
+	_LT_TAGVAR(old_archive_from_new_cmds, $1)='true'
+	# FIXME: Should let the user specify the lib program.
+	_LT_TAGVAR(old_archive_cmds, $1)='lib -OUT:$oldlib$oldobjs$old_deplibs'
+	_LT_TAGVAR(enable_shared_with_static_runtimes, $1)=yes
+	;;
+      esac
+      ;;
+
+    darwin* | rhapsody*)
+      _LT_DARWIN_LINKER_FEATURES($1)
+      ;;
+
+    dgux*)
+      _LT_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags'
+      _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir'
+      _LT_TAGVAR(hardcode_shlibpath_var, $1)=no
+      ;;
+
+    # FreeBSD 2.2.[012] allows us to include c++rt0.o to get C++ constructor
+    # support.  Future versions do this automatically, but an explicit c++rt0.o
+    # does not break anything, and helps significantly (at the cost of a little
+    # extra space).
+    freebsd2.2*)
+      _LT_TAGVAR(archive_cmds, $1)='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags /usr/lib/c++rt0.o'
+      _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir'
+      _LT_TAGVAR(hardcode_direct, $1)=yes
+      _LT_TAGVAR(hardcode_shlibpath_var, $1)=no
+      ;;
+
+    # Unfortunately, older versions of FreeBSD 2 do not have this feature.
+    freebsd2.*)
+      _LT_TAGVAR(archive_cmds, $1)='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags'
+      _LT_TAGVAR(hardcode_direct, $1)=yes
+      _LT_TAGVAR(hardcode_minus_L, $1)=yes
+      _LT_TAGVAR(hardcode_shlibpath_var, $1)=no
+      ;;
+
+    # FreeBSD 3 and greater uses gcc -shared to do shared libraries.
+    freebsd* | dragonfly*)
+      _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags'
+      _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir'
+      _LT_TAGVAR(hardcode_direct, $1)=yes
+      _LT_TAGVAR(hardcode_shlibpath_var, $1)=no
+      ;;
+
+    hpux9*)
+      if test "$GCC" = yes; then
+	_LT_TAGVAR(archive_cmds, $1)='$RM $output_objdir/$soname~$CC -shared $pic_flag ${wl}+b ${wl}$install_libdir -o $output_objdir/$soname $libobjs $deplibs $compiler_flags~test $output_objdir/$soname = $lib || mv $output_objdir/$soname $lib'
+      else
+	_LT_TAGVAR(archive_cmds, $1)='$RM $output_objdir/$soname~$LD -b +b $install_libdir -o $output_objdir/$soname $libobjs $deplibs $linker_flags~test $output_objdir/$soname = $lib || mv $output_objdir/$soname $lib'
+      fi
+      _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}+b ${wl}$libdir'
+      _LT_TAGVAR(hardcode_libdir_separator, $1)=:
+      _LT_TAGVAR(hardcode_direct, $1)=yes
+
+      # hardcode_minus_L: Not really in the search PATH,
+      # but as the default location of the library.
+      _LT_TAGVAR(hardcode_minus_L, $1)=yes
+      _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-E'
+      ;;
+
+    hpux10*)
+      if test "$GCC" = yes && test "$with_gnu_ld" = no; then
+	_LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag ${wl}+h ${wl}$soname ${wl}+b ${wl}$install_libdir -o $lib $libobjs $deplibs $compiler_flags'
+      else
+	_LT_TAGVAR(archive_cmds, $1)='$LD -b +h $soname +b $install_libdir -o $lib $libobjs $deplibs $linker_flags'
+      fi
+      if test "$with_gnu_ld" = no; then
+	_LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}+b ${wl}$libdir'
+	_LT_TAGVAR(hardcode_libdir_separator, $1)=:
+	_LT_TAGVAR(hardcode_direct, $1)=yes
+	_LT_TAGVAR(hardcode_direct_absolute, $1)=yes
+	_LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-E'
+	# hardcode_minus_L: Not really in the search PATH,
+	# but as the default location of the library.
+	_LT_TAGVAR(hardcode_minus_L, $1)=yes
+      fi
+      ;;
+
+    hpux11*)
+      if test "$GCC" = yes && test "$with_gnu_ld" = no; then
+	case $host_cpu in
+	hppa*64*)
+	  _LT_TAGVAR(archive_cmds, $1)='$CC -shared ${wl}+h ${wl}$soname -o $lib $libobjs $deplibs $compiler_flags'
+	  ;;
+	ia64*)
+	  _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag ${wl}+h ${wl}$soname ${wl}+nodefaultrpath -o $lib $libobjs $deplibs $compiler_flags'
+	  ;;
+	*)
+	  _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag ${wl}+h ${wl}$soname ${wl}+b ${wl}$install_libdir -o $lib $libobjs $deplibs $compiler_flags'
+	  ;;
+	esac
+      else
+	case $host_cpu in
+	hppa*64*)
+	  _LT_TAGVAR(archive_cmds, $1)='$CC -b ${wl}+h ${wl}$soname -o $lib $libobjs $deplibs $compiler_flags'
+	  ;;
+	ia64*)
+	  _LT_TAGVAR(archive_cmds, $1)='$CC -b ${wl}+h ${wl}$soname ${wl}+nodefaultrpath -o $lib $libobjs $deplibs $compiler_flags'
+	  ;;
+	*)
+	m4_if($1, [], [
+	  # Older versions of the 11.00 compiler do not understand -b yet
+	  # (HP92453-01 A.11.01.20 doesn't, HP92453-01 B.11.X.35175-35176.GP does)
+	  _LT_LINKER_OPTION([if $CC understands -b],
+	    _LT_TAGVAR(lt_cv_prog_compiler__b, $1), [-b],
+	    [_LT_TAGVAR(archive_cmds, $1)='$CC -b ${wl}+h ${wl}$soname ${wl}+b ${wl}$install_libdir -o $lib $libobjs $deplibs $compiler_flags'],
+	    [_LT_TAGVAR(archive_cmds, $1)='$LD -b +h $soname +b $install_libdir -o $lib $libobjs $deplibs $linker_flags'])],
+	  [_LT_TAGVAR(archive_cmds, $1)='$CC -b ${wl}+h ${wl}$soname ${wl}+b ${wl}$install_libdir -o $lib $libobjs $deplibs $compiler_flags'])
+	  ;;
+	esac
+      fi
+      if test "$with_gnu_ld" = no; then
+	_LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}+b ${wl}$libdir'
+	_LT_TAGVAR(hardcode_libdir_separator, $1)=:
+
+	case $host_cpu in
+	hppa*64*|ia64*)
+	  _LT_TAGVAR(hardcode_direct, $1)=no
+	  _LT_TAGVAR(hardcode_shlibpath_var, $1)=no
+	  ;;
+	*)
+	  _LT_TAGVAR(hardcode_direct, $1)=yes
+	  _LT_TAGVAR(hardcode_direct_absolute, $1)=yes
+	  _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-E'
+
+	  # hardcode_minus_L: Not really in the search PATH,
+	  # but as the default location of the library.
+	  _LT_TAGVAR(hardcode_minus_L, $1)=yes
+	  ;;
+	esac
+      fi
+      ;;
+
+    irix5* | irix6* | nonstopux*)
+      if test "$GCC" = yes; then
+	_LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-soname ${wl}$soname `test -n "$verstring" && func_echo_all "${wl}-set_version ${wl}$verstring"` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib'
+	# Try to use the -exported_symbol ld option, if it does not
+	# work, assume that -exports_file does not work either and
+	# implicitly export all symbols.
+	# This should be the same for all languages, so no per-tag cache variable.
+	AC_CACHE_CHECK([whether the $host_os linker accepts -exported_symbol],
+	  [lt_cv_irix_exported_symbol],
+	  [save_LDFLAGS="$LDFLAGS"
+	   LDFLAGS="$LDFLAGS -shared ${wl}-exported_symbol ${wl}foo ${wl}-update_registry ${wl}/dev/null"
+	   AC_LINK_IFELSE(
+	     [AC_LANG_SOURCE(
+	        [AC_LANG_CASE([C], [[int foo (void) { return 0; }]],
+			      [C++], [[int foo (void) { return 0; }]],
+			      [Fortran 77], [[
+      subroutine foo
+      end]],
+			      [Fortran], [[
+      subroutine foo
+      end]])])],
+	      [lt_cv_irix_exported_symbol=yes],
+	      [lt_cv_irix_exported_symbol=no])
+           LDFLAGS="$save_LDFLAGS"])
+	if test "$lt_cv_irix_exported_symbol" = yes; then
+          _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-soname ${wl}$soname `test -n "$verstring" && func_echo_all "${wl}-set_version ${wl}$verstring"` ${wl}-update_registry ${wl}${output_objdir}/so_locations ${wl}-exports_file ${wl}$export_symbols -o $lib'
+	fi
+      else
+	_LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry ${output_objdir}/so_locations -o $lib'
+	_LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry ${output_objdir}/so_locations -exports_file $export_symbols -o $lib'
+      fi
+      _LT_TAGVAR(archive_cmds_need_lc, $1)='no'
+      _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir'
+      _LT_TAGVAR(hardcode_libdir_separator, $1)=:
+      _LT_TAGVAR(inherit_rpath, $1)=yes
+      _LT_TAGVAR(link_all_deplibs, $1)=yes
+      ;;
+
+    netbsd* | netbsdelf*-gnu)
+      if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then
+	_LT_TAGVAR(archive_cmds, $1)='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags'  # a.out
+      else
+	_LT_TAGVAR(archive_cmds, $1)='$LD -shared -o $lib $libobjs $deplibs $linker_flags'      # ELF
+      fi
+      _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir'
+      _LT_TAGVAR(hardcode_direct, $1)=yes
+      _LT_TAGVAR(hardcode_shlibpath_var, $1)=no
+      ;;
+
+    newsos6)
+      _LT_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags'
+      _LT_TAGVAR(hardcode_direct, $1)=yes
+      _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir'
+      _LT_TAGVAR(hardcode_libdir_separator, $1)=:
+      _LT_TAGVAR(hardcode_shlibpath_var, $1)=no
+      ;;
+
+    *nto* | *qnx*)
+      ;;
+
+    openbsd*)
+      if test -f /usr/libexec/ld.so; then
+	_LT_TAGVAR(hardcode_direct, $1)=yes
+	_LT_TAGVAR(hardcode_shlibpath_var, $1)=no
+	_LT_TAGVAR(hardcode_direct_absolute, $1)=yes
+	if test -z "`echo __ELF__ | $CC -E - | $GREP __ELF__`" || test "$host_os-$host_cpu" = "openbsd2.8-powerpc"; then
+	  _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags'
+	  _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags ${wl}-retain-symbols-file,$export_symbols'
+	  _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath,$libdir'
+	  _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-E'
+	else
+	  case $host_os in
+	   openbsd[[01]].* | openbsd2.[[0-7]] | openbsd2.[[0-7]].*)
+	     _LT_TAGVAR(archive_cmds, $1)='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags'
+	     _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir'
+	     ;;
+	   *)
+	     _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags'
+	     _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath,$libdir'
+	     ;;
+	  esac
+	fi
+      else
+	_LT_TAGVAR(ld_shlibs, $1)=no
+      fi
+      ;;
+
+    os2*)
+      _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir'
+      _LT_TAGVAR(hardcode_minus_L, $1)=yes
+      _LT_TAGVAR(allow_undefined_flag, $1)=unsupported
+      _LT_TAGVAR(archive_cmds, $1)='$ECHO "LIBRARY $libname INITINSTANCE" > $output_objdir/$libname.def~$ECHO "DESCRIPTION \"$libname\"" >> $output_objdir/$libname.def~echo DATA >> $output_objdir/$libname.def~echo " SINGLE NONSHARED" >> $output_objdir/$libname.def~echo EXPORTS >> $output_objdir/$libname.def~emxexp $libobjs >> $output_objdir/$libname.def~$CC -Zdll -Zcrtdll -o $lib $libobjs $deplibs $compiler_flags $output_objdir/$libname.def'
+      _LT_TAGVAR(old_archive_from_new_cmds, $1)='emximp -o $output_objdir/$libname.a $output_objdir/$libname.def'
+      ;;
+
+    osf3*)
+      if test "$GCC" = yes; then
+	_LT_TAGVAR(allow_undefined_flag, $1)=' ${wl}-expect_unresolved ${wl}\*'
+	_LT_TAGVAR(archive_cmds, $1)='$CC -shared${allow_undefined_flag} $libobjs $deplibs $compiler_flags ${wl}-soname ${wl}$soname `test -n "$verstring" && func_echo_all "${wl}-set_version ${wl}$verstring"` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib'
+      else
+	_LT_TAGVAR(allow_undefined_flag, $1)=' -expect_unresolved \*'
+	_LT_TAGVAR(archive_cmds, $1)='$CC -shared${allow_undefined_flag} $libobjs $deplibs $compiler_flags -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry ${output_objdir}/so_locations -o $lib'
+      fi
+      _LT_TAGVAR(archive_cmds_need_lc, $1)='no'
+      _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir'
+      _LT_TAGVAR(hardcode_libdir_separator, $1)=:
+      ;;
+
+    osf4* | osf5*)	# as osf3* with the addition of -msym flag
+      if test "$GCC" = yes; then
+	_LT_TAGVAR(allow_undefined_flag, $1)=' ${wl}-expect_unresolved ${wl}\*'
+	_LT_TAGVAR(archive_cmds, $1)='$CC -shared${allow_undefined_flag} $pic_flag $libobjs $deplibs $compiler_flags ${wl}-msym ${wl}-soname ${wl}$soname `test -n "$verstring" && func_echo_all "${wl}-set_version ${wl}$verstring"` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib'
+	_LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir'
+      else
+	_LT_TAGVAR(allow_undefined_flag, $1)=' -expect_unresolved \*'
+	_LT_TAGVAR(archive_cmds, $1)='$CC -shared${allow_undefined_flag} $libobjs $deplibs $compiler_flags -msym -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry ${output_objdir}/so_locations -o $lib'
+	_LT_TAGVAR(archive_expsym_cmds, $1)='for i in `cat $export_symbols`; do printf "%s %s\\n" -exported_symbol "\$i" >> $lib.exp; done; printf "%s\\n" "-hidden">> $lib.exp~
+	$CC -shared${allow_undefined_flag} ${wl}-input ${wl}$lib.exp $compiler_flags $libobjs $deplibs -soname $soname `test -n "$verstring" && $ECHO "-set_version $verstring"` -update_registry ${output_objdir}/so_locations -o $lib~$RM $lib.exp'
+
+	# Both c and cxx compiler support -rpath directly
+	_LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-rpath $libdir'
+      fi
+      _LT_TAGVAR(archive_cmds_need_lc, $1)='no'
+      _LT_TAGVAR(hardcode_libdir_separator, $1)=:
+      ;;
+
+    solaris*)
+      _LT_TAGVAR(no_undefined_flag, $1)=' -z defs'
+      if test "$GCC" = yes; then
+	wlarc='${wl}'
+	_LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag ${wl}-z ${wl}text ${wl}-h ${wl}$soname -o $lib $libobjs $deplibs $compiler_flags'
+	_LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~
+	  $CC -shared $pic_flag ${wl}-z ${wl}text ${wl}-M ${wl}$lib.exp ${wl}-h ${wl}$soname -o $lib $libobjs $deplibs $compiler_flags~$RM $lib.exp'
+      else
+	case `$CC -V 2>&1` in
+	*"Compilers 5.0"*)
+	  wlarc=''
+	  _LT_TAGVAR(archive_cmds, $1)='$LD -G${allow_undefined_flag} -h $soname -o $lib $libobjs $deplibs $linker_flags'
+	  _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~
+	  $LD -G${allow_undefined_flag} -M $lib.exp -h $soname -o $lib $libobjs $deplibs $linker_flags~$RM $lib.exp'
+	  ;;
+	*)
+	  wlarc='${wl}'
+	  _LT_TAGVAR(archive_cmds, $1)='$CC -G${allow_undefined_flag} -h $soname -o $lib $libobjs $deplibs $compiler_flags'
+	  _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~
+	  $CC -G${allow_undefined_flag} -M $lib.exp -h $soname -o $lib $libobjs $deplibs $compiler_flags~$RM $lib.exp'
+	  ;;
+	esac
+      fi
+      _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir'
+      _LT_TAGVAR(hardcode_shlibpath_var, $1)=no
+      case $host_os in
+      solaris2.[[0-5]] | solaris2.[[0-5]].*) ;;
+      *)
+	# The compiler driver will combine and reorder linker options,
+	# but understands `-z linker_flag'.  GCC discards it without `$wl',
+	# but is careful enough not to reorder.
+	# Supported since Solaris 2.6 (maybe 2.5.1?)
+	if test "$GCC" = yes; then
+	  _LT_TAGVAR(whole_archive_flag_spec, $1)='${wl}-z ${wl}allextract$convenience ${wl}-z ${wl}defaultextract'
+	else
+	  _LT_TAGVAR(whole_archive_flag_spec, $1)='-z allextract$convenience -z defaultextract'
+	fi
+	;;
+      esac
+      _LT_TAGVAR(link_all_deplibs, $1)=yes
+      ;;
+
+    sunos4*)
+      if test "x$host_vendor" = xsequent; then
+	# Use $CC to link under sequent, because it throws in some extra .o
+	# files that make .init and .fini sections work.
+	_LT_TAGVAR(archive_cmds, $1)='$CC -G ${wl}-h $soname -o $lib $libobjs $deplibs $compiler_flags'
+      else
+	_LT_TAGVAR(archive_cmds, $1)='$LD -assert pure-text -Bstatic -o $lib $libobjs $deplibs $linker_flags'
+      fi
+      _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir'
+      _LT_TAGVAR(hardcode_direct, $1)=yes
+      _LT_TAGVAR(hardcode_minus_L, $1)=yes
+      _LT_TAGVAR(hardcode_shlibpath_var, $1)=no
+      ;;
+
+    sysv4)
+      case $host_vendor in
+	sni)
+	  _LT_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags'
+	  _LT_TAGVAR(hardcode_direct, $1)=yes # is this really true???
+	;;
+	siemens)
+	  ## LD is ld it makes a PLAMLIB
+	  ## CC just makes a GrossModule.
+	  _LT_TAGVAR(archive_cmds, $1)='$LD -G -o $lib $libobjs $deplibs $linker_flags'
+	  _LT_TAGVAR(reload_cmds, $1)='$CC -r -o $output$reload_objs'
+	  _LT_TAGVAR(hardcode_direct, $1)=no
+        ;;
+	motorola)
+	  _LT_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags'
+	  _LT_TAGVAR(hardcode_direct, $1)=no #Motorola manual says yes, but my tests say they lie
+	;;
+      esac
+      runpath_var='LD_RUN_PATH'
+      _LT_TAGVAR(hardcode_shlibpath_var, $1)=no
+      ;;
+
+    sysv4.3*)
+      _LT_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags'
+      _LT_TAGVAR(hardcode_shlibpath_var, $1)=no
+      _LT_TAGVAR(export_dynamic_flag_spec, $1)='-Bexport'
+      ;;
+
+    sysv4*MP*)
+      if test -d /usr/nec; then
+	_LT_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags'
+	_LT_TAGVAR(hardcode_shlibpath_var, $1)=no
+	runpath_var=LD_RUN_PATH
+	hardcode_runpath_var=yes
+	_LT_TAGVAR(ld_shlibs, $1)=yes
+      fi
+      ;;
+
+    sysv4*uw2* | sysv5OpenUNIX* | sysv5UnixWare7.[[01]].[[10]]* | unixware7* | sco3.2v5.0.[[024]]*)
+      _LT_TAGVAR(no_undefined_flag, $1)='${wl}-z,text'
+      _LT_TAGVAR(archive_cmds_need_lc, $1)=no
+      _LT_TAGVAR(hardcode_shlibpath_var, $1)=no
+      runpath_var='LD_RUN_PATH'
+
+      if test "$GCC" = yes; then
+	_LT_TAGVAR(archive_cmds, $1)='$CC -shared ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+	_LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+      else
+	_LT_TAGVAR(archive_cmds, $1)='$CC -G ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+	_LT_TAGVAR(archive_expsym_cmds, $1)='$CC -G ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+      fi
+      ;;
+
+    sysv5* | sco3.2v5* | sco5v6*)
+      # Note: We can NOT use -z defs as we might desire, because we do not
+      # link with -lc, and that would cause any symbols used from libc to
+      # always be unresolved, which means just about no library would
+      # ever link correctly.  If we're not using GNU ld we use -z text
+      # though, which does catch some bad symbols but isn't as heavy-handed
+      # as -z defs.
+      _LT_TAGVAR(no_undefined_flag, $1)='${wl}-z,text'
+      _LT_TAGVAR(allow_undefined_flag, $1)='${wl}-z,nodefs'
+      _LT_TAGVAR(archive_cmds_need_lc, $1)=no
+      _LT_TAGVAR(hardcode_shlibpath_var, $1)=no
+      _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-R,$libdir'
+      _LT_TAGVAR(hardcode_libdir_separator, $1)=':'
+      _LT_TAGVAR(link_all_deplibs, $1)=yes
+      _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-Bexport'
+      runpath_var='LD_RUN_PATH'
+
+      if test "$GCC" = yes; then
+	_LT_TAGVAR(archive_cmds, $1)='$CC -shared ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+	_LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+      else
+	_LT_TAGVAR(archive_cmds, $1)='$CC -G ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+	_LT_TAGVAR(archive_expsym_cmds, $1)='$CC -G ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+      fi
+      ;;
+
+    uts4*)
+      _LT_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags'
+      _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir'
+      _LT_TAGVAR(hardcode_shlibpath_var, $1)=no
+      ;;
+
+    *)
+      _LT_TAGVAR(ld_shlibs, $1)=no
+      ;;
+    esac
+
+    if test x$host_vendor = xsni; then
+      case $host in
+      sysv4 | sysv4.2uw2* | sysv4.3* | sysv5*)
+	_LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-Blargedynsym'
+	;;
+      esac
+    fi
+  fi
+])
+AC_MSG_RESULT([$_LT_TAGVAR(ld_shlibs, $1)])
+test "$_LT_TAGVAR(ld_shlibs, $1)" = no && can_build_shared=no
+
+_LT_TAGVAR(with_gnu_ld, $1)=$with_gnu_ld
+
+_LT_DECL([], [libext], [0], [Old archive suffix (normally "a")])dnl
+_LT_DECL([], [shrext_cmds], [1], [Shared library suffix (normally ".so")])dnl
+_LT_DECL([], [extract_expsyms_cmds], [2],
+    [The commands to extract the exported symbol list from a shared archive])
+
+#
+# Do we need to explicitly link libc?
+#
+case "x$_LT_TAGVAR(archive_cmds_need_lc, $1)" in
+x|xyes)
+  # Assume -lc should be added
+  _LT_TAGVAR(archive_cmds_need_lc, $1)=yes
+
+  if test "$enable_shared" = yes && test "$GCC" = yes; then
+    case $_LT_TAGVAR(archive_cmds, $1) in
+    *'~'*)
+      # FIXME: we may have to deal with multi-command sequences.
+      ;;
+    '$CC '*)
+      # Test whether the compiler implicitly links with -lc since on some
+      # systems, -lgcc has to come before -lc. If gcc already passes -lc
+      # to ld, don't add -lc before -lgcc.
+      AC_CACHE_CHECK([whether -lc should be explicitly linked in],
+	[lt_cv_]_LT_TAGVAR(archive_cmds_need_lc, $1),
+	[$RM conftest*
+	echo "$lt_simple_compile_test_code" > conftest.$ac_ext
+
+	if AC_TRY_EVAL(ac_compile) 2>conftest.err; then
+	  soname=conftest
+	  lib=conftest
+	  libobjs=conftest.$ac_objext
+	  deplibs=
+	  wl=$_LT_TAGVAR(lt_prog_compiler_wl, $1)
+	  pic_flag=$_LT_TAGVAR(lt_prog_compiler_pic, $1)
+	  compiler_flags=-v
+	  linker_flags=-v
+	  verstring=
+	  output_objdir=.
+	  libname=conftest
+	  lt_save_allow_undefined_flag=$_LT_TAGVAR(allow_undefined_flag, $1)
+	  _LT_TAGVAR(allow_undefined_flag, $1)=
+	  if AC_TRY_EVAL(_LT_TAGVAR(archive_cmds, $1) 2\>\&1 \| $GREP \" -lc \" \>/dev/null 2\>\&1)
+	  then
+	    lt_cv_[]_LT_TAGVAR(archive_cmds_need_lc, $1)=no
+	  else
+	    lt_cv_[]_LT_TAGVAR(archive_cmds_need_lc, $1)=yes
+	  fi
+	  _LT_TAGVAR(allow_undefined_flag, $1)=$lt_save_allow_undefined_flag
+	else
+	  cat conftest.err 1>&5
+	fi
+	$RM conftest*
+	])
+      _LT_TAGVAR(archive_cmds_need_lc, $1)=$lt_cv_[]_LT_TAGVAR(archive_cmds_need_lc, $1)
+      ;;
+    esac
+  fi
+  ;;
+esac
+
+_LT_TAGDECL([build_libtool_need_lc], [archive_cmds_need_lc], [0],
+    [Whether or not to add -lc for building shared libraries])
+_LT_TAGDECL([allow_libtool_libs_with_static_runtimes],
+    [enable_shared_with_static_runtimes], [0],
+    [Whether or not to disallow shared libs when runtime libs are static])
+_LT_TAGDECL([], [export_dynamic_flag_spec], [1],
+    [Compiler flag to allow reflexive dlopens])
+_LT_TAGDECL([], [whole_archive_flag_spec], [1],
+    [Compiler flag to generate shared objects directly from archives])
+_LT_TAGDECL([], [compiler_needs_object], [1],
+    [Whether the compiler copes with passing no objects directly])
+_LT_TAGDECL([], [old_archive_from_new_cmds], [2],
+    [Create an old-style archive from a shared archive])
+_LT_TAGDECL([], [old_archive_from_expsyms_cmds], [2],
+    [Create a temporary old-style archive to link instead of a shared archive])
+_LT_TAGDECL([], [archive_cmds], [2], [Commands used to build a shared archive])
+_LT_TAGDECL([], [archive_expsym_cmds], [2])
+_LT_TAGDECL([], [module_cmds], [2],
+    [Commands used to build a loadable module if different from building
+    a shared archive.])
+_LT_TAGDECL([], [module_expsym_cmds], [2])
+_LT_TAGDECL([], [with_gnu_ld], [1],
+    [Whether we are building with GNU ld or not])
+_LT_TAGDECL([], [allow_undefined_flag], [1],
+    [Flag that allows shared libraries with undefined symbols to be built])
+_LT_TAGDECL([], [no_undefined_flag], [1],
+    [Flag that enforces no undefined symbols])
+_LT_TAGDECL([], [hardcode_libdir_flag_spec], [1],
+    [Flag to hardcode $libdir into a binary during linking.
+    This must work even if $libdir does not exist])
+_LT_TAGDECL([], [hardcode_libdir_separator], [1],
+    [Whether we need a single "-rpath" flag with a separated argument])
+_LT_TAGDECL([], [hardcode_direct], [0],
+    [Set to "yes" if using DIR/libNAME${shared_ext} during linking hardcodes
+    DIR into the resulting binary])
+_LT_TAGDECL([], [hardcode_direct_absolute], [0],
+    [Set to "yes" if using DIR/libNAME${shared_ext} during linking hardcodes
+    DIR into the resulting binary and the resulting library dependency is
+    "absolute", i.e impossible to change by setting ${shlibpath_var} if the
+    library is relocated])
+_LT_TAGDECL([], [hardcode_minus_L], [0],
+    [Set to "yes" if using the -LDIR flag during linking hardcodes DIR
+    into the resulting binary])
+_LT_TAGDECL([], [hardcode_shlibpath_var], [0],
+    [Set to "yes" if using SHLIBPATH_VAR=DIR during linking hardcodes DIR
+    into the resulting binary])
+_LT_TAGDECL([], [hardcode_automatic], [0],
+    [Set to "yes" if building a shared library automatically hardcodes DIR
+    into the library and all subsequent libraries and executables linked
+    against it])
+_LT_TAGDECL([], [inherit_rpath], [0],
+    [Set to yes if linker adds runtime paths of dependent libraries
+    to runtime path list])
+_LT_TAGDECL([], [link_all_deplibs], [0],
+    [Whether libtool must link a program against all its dependency libraries])
+_LT_TAGDECL([], [always_export_symbols], [0],
+    [Set to "yes" if exported symbols are required])
+_LT_TAGDECL([], [export_symbols_cmds], [2],
+    [The commands to list exported symbols])
+_LT_TAGDECL([], [exclude_expsyms], [1],
+    [Symbols that should not be listed in the preloaded symbols])
+_LT_TAGDECL([], [include_expsyms], [1],
+    [Symbols that must always be exported])
+_LT_TAGDECL([], [prelink_cmds], [2],
+    [Commands necessary for linking programs (against libraries) with templates])
+_LT_TAGDECL([], [postlink_cmds], [2],
+    [Commands necessary for finishing linking programs])
+_LT_TAGDECL([], [file_list_spec], [1],
+    [Specify filename containing input files])
+dnl FIXME: Not yet implemented
+dnl _LT_TAGDECL([], [thread_safe_flag_spec], [1],
+dnl    [Compiler flag to generate thread safe objects])
+])# _LT_LINKER_SHLIBS
+
+
+# _LT_LANG_C_CONFIG([TAG])
+# ------------------------
+# Ensure that the configuration variables for a C compiler are suitably
+# defined.  These variables are subsequently used by _LT_CONFIG to write
+# the compiler configuration to `libtool'.
+m4_defun([_LT_LANG_C_CONFIG],
+[m4_require([_LT_DECL_EGREP])dnl
+lt_save_CC="$CC"
+AC_LANG_PUSH(C)
+
+# Source file extension for C test sources.
+ac_ext=c
+
+# Object file extension for compiled C test sources.
+objext=o
+_LT_TAGVAR(objext, $1)=$objext
+
+# Code to be used in simple compile tests
+lt_simple_compile_test_code="int some_variable = 0;"
+
+# Code to be used in simple link tests
+lt_simple_link_test_code='int main(){return(0);}'
+
+_LT_TAG_COMPILER
+# Save the default compiler, since it gets overwritten when the other
+# tags are being tested, and _LT_TAGVAR(compiler, []) is a NOP.
+compiler_DEFAULT=$CC
+
+# save warnings/boilerplate of simple test code
+_LT_COMPILER_BOILERPLATE
+_LT_LINKER_BOILERPLATE
+
+if test -n "$compiler"; then
+  _LT_COMPILER_NO_RTTI($1)
+  _LT_COMPILER_PIC($1)
+  _LT_COMPILER_C_O($1)
+  _LT_COMPILER_FILE_LOCKS($1)
+  _LT_LINKER_SHLIBS($1)
+  _LT_SYS_DYNAMIC_LINKER($1)
+  _LT_LINKER_HARDCODE_LIBPATH($1)
+  LT_SYS_DLOPEN_SELF
+  _LT_CMD_STRIPLIB
+
+  # Report which library types will actually be built
+  AC_MSG_CHECKING([if libtool supports shared libraries])
+  AC_MSG_RESULT([$can_build_shared])
+
+  AC_MSG_CHECKING([whether to build shared libraries])
+  test "$can_build_shared" = "no" && enable_shared=no
+
+  # On AIX, shared libraries and static libraries use the same namespace, and
+  # are all built from PIC.
+  case $host_os in
+  aix3*)
+    test "$enable_shared" = yes && enable_static=no
+    if test -n "$RANLIB"; then
+      archive_cmds="$archive_cmds~\$RANLIB \$lib"
+      postinstall_cmds='$RANLIB $lib'
+    fi
+    ;;
+
+  aix[[4-9]]*)
+    if test "$host_cpu" != ia64 && test "$aix_use_runtimelinking" = no ; then
+      test "$enable_shared" = yes && enable_static=no
+    fi
+    ;;
+  esac
+  AC_MSG_RESULT([$enable_shared])
+
+  AC_MSG_CHECKING([whether to build static libraries])
+  # Make sure either enable_shared or enable_static is yes.
+  test "$enable_shared" = yes || enable_static=yes
+  AC_MSG_RESULT([$enable_static])
+
+  _LT_CONFIG($1)
+fi
+AC_LANG_POP
+CC="$lt_save_CC"
+])# _LT_LANG_C_CONFIG
+
+
+# _LT_LANG_CXX_CONFIG([TAG])
+# --------------------------
+# Ensure that the configuration variables for a C++ compiler are suitably
+# defined.  These variables are subsequently used by _LT_CONFIG to write
+# the compiler configuration to `libtool'.
+m4_defun([_LT_LANG_CXX_CONFIG],
+[m4_require([_LT_FILEUTILS_DEFAULTS])dnl
+m4_require([_LT_DECL_EGREP])dnl
+m4_require([_LT_PATH_MANIFEST_TOOL])dnl
+if test -n "$CXX" && ( test "X$CXX" != "Xno" &&
+    ( (test "X$CXX" = "Xg++" && `g++ -v >/dev/null 2>&1` ) ||
+    (test "X$CXX" != "Xg++"))) ; then
+  AC_PROG_CXXCPP
+else
+  _lt_caught_CXX_error=yes
+fi
+
+AC_LANG_PUSH(C++)
+_LT_TAGVAR(archive_cmds_need_lc, $1)=no
+_LT_TAGVAR(allow_undefined_flag, $1)=
+_LT_TAGVAR(always_export_symbols, $1)=no
+_LT_TAGVAR(archive_expsym_cmds, $1)=
+_LT_TAGVAR(compiler_needs_object, $1)=no
+_LT_TAGVAR(export_dynamic_flag_spec, $1)=
+_LT_TAGVAR(hardcode_direct, $1)=no
+_LT_TAGVAR(hardcode_direct_absolute, $1)=no
+_LT_TAGVAR(hardcode_libdir_flag_spec, $1)=
+_LT_TAGVAR(hardcode_libdir_separator, $1)=
+_LT_TAGVAR(hardcode_minus_L, $1)=no
+_LT_TAGVAR(hardcode_shlibpath_var, $1)=unsupported
+_LT_TAGVAR(hardcode_automatic, $1)=no
+_LT_TAGVAR(inherit_rpath, $1)=no
+_LT_TAGVAR(module_cmds, $1)=
+_LT_TAGVAR(module_expsym_cmds, $1)=
+_LT_TAGVAR(link_all_deplibs, $1)=unknown
+_LT_TAGVAR(old_archive_cmds, $1)=$old_archive_cmds
+_LT_TAGVAR(reload_flag, $1)=$reload_flag
+_LT_TAGVAR(reload_cmds, $1)=$reload_cmds
+_LT_TAGVAR(no_undefined_flag, $1)=
+_LT_TAGVAR(whole_archive_flag_spec, $1)=
+_LT_TAGVAR(enable_shared_with_static_runtimes, $1)=no
+
+# Source file extension for C++ test sources.
+ac_ext=cpp
+
+# Object file extension for compiled C++ test sources.
+objext=o
+_LT_TAGVAR(objext, $1)=$objext
+
+# No sense in running all these tests if we already determined that
+# the CXX compiler isn't working.  Some variables (like enable_shared)
+# are currently assumed to apply to all compilers on this platform,
+# and will be corrupted by setting them based on a non-working compiler.
+if test "$_lt_caught_CXX_error" != yes; then
+  # Code to be used in simple compile tests
+  lt_simple_compile_test_code="int some_variable = 0;"
+
+  # Code to be used in simple link tests
+  lt_simple_link_test_code='int main(int, char *[[]]) { return(0); }'
+
+  # ltmain only uses $CC for tagged configurations so make sure $CC is set.
+  _LT_TAG_COMPILER
+
+  # save warnings/boilerplate of simple test code
+  _LT_COMPILER_BOILERPLATE
+  _LT_LINKER_BOILERPLATE
+
+  # Allow CC to be a program name with arguments.
+  lt_save_CC=$CC
+  lt_save_CFLAGS=$CFLAGS
+  lt_save_LD=$LD
+  lt_save_GCC=$GCC
+  GCC=$GXX
+  lt_save_with_gnu_ld=$with_gnu_ld
+  lt_save_path_LD=$lt_cv_path_LD
+  if test -n "${lt_cv_prog_gnu_ldcxx+set}"; then
+    lt_cv_prog_gnu_ld=$lt_cv_prog_gnu_ldcxx
+  else
+    $as_unset lt_cv_prog_gnu_ld
+  fi
+  if test -n "${lt_cv_path_LDCXX+set}"; then
+    lt_cv_path_LD=$lt_cv_path_LDCXX
+  else
+    $as_unset lt_cv_path_LD
+  fi
+  test -z "${LDCXX+set}" || LD=$LDCXX
+  CC=${CXX-"c++"}
+  CFLAGS=$CXXFLAGS
+  compiler=$CC
+  _LT_TAGVAR(compiler, $1)=$CC
+  _LT_CC_BASENAME([$compiler])
+
+  if test -n "$compiler"; then
+    # We don't want -fno-exception when compiling C++ code, so set the
+    # no_builtin_flag separately
+    if test "$GXX" = yes; then
+      _LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1)=' -fno-builtin'
+    else
+      _LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1)=
+    fi
+
+    if test "$GXX" = yes; then
+      # Set up default GNU C++ configuration
+
+      LT_PATH_LD
+
+      # Check if GNU C++ uses GNU ld as the underlying linker, since the
+      # archiving commands below assume that GNU ld is being used.
+      if test "$with_gnu_ld" = yes; then
+        _LT_TAGVAR(archive_cmds, $1)='$CC $pic_flag -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $wl$soname -o $lib'
+        _LT_TAGVAR(archive_expsym_cmds, $1)='$CC $pic_flag -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib'
+
+        _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir'
+        _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}--export-dynamic'
+
+        # If archive_cmds runs LD, not CC, wlarc should be empty
+        # XXX I think wlarc can be eliminated in ltcf-cxx, but I need to
+        #     investigate it a little bit more. (MM)
+        wlarc='${wl}'
+
+        # ancient GNU ld didn't support --whole-archive et. al.
+        if eval "`$CC -print-prog-name=ld` --help 2>&1" |
+	  $GREP 'no-whole-archive' > /dev/null; then
+          _LT_TAGVAR(whole_archive_flag_spec, $1)="$wlarc"'--whole-archive$convenience '"$wlarc"'--no-whole-archive'
+        else
+          _LT_TAGVAR(whole_archive_flag_spec, $1)=
+        fi
+      else
+        with_gnu_ld=no
+        wlarc=
+
+        # A generic and very simple default shared library creation
+        # command for GNU C++ for the case where it uses the native
+        # linker, instead of GNU ld.  If possible, this setting should
+        # overridden to take advantage of the native linker features on
+        # the platform it is being used on.
+        _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -o $lib'
+      fi
+
+      # Commands to make compiler produce verbose output that lists
+      # what "hidden" libraries, object files and flags are used when
+      # linking a shared library.
+      output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP -v "^Configured with:" | $GREP "\-L"'
+
+    else
+      GXX=no
+      with_gnu_ld=no
+      wlarc=
+    fi
+
+    # PORTME: fill in a description of your system's C++ link characteristics
+    AC_MSG_CHECKING([whether the $compiler linker ($LD) supports shared libraries])
+    _LT_TAGVAR(ld_shlibs, $1)=yes
+    case $host_os in
+      aix3*)
+        # FIXME: insert proper C++ library support
+        _LT_TAGVAR(ld_shlibs, $1)=no
+        ;;
+      aix[[4-9]]*)
+        if test "$host_cpu" = ia64; then
+          # On IA64, the linker does run time linking by default, so we don't
+          # have to do anything special.
+          aix_use_runtimelinking=no
+          exp_sym_flag='-Bexport'
+          no_entry_flag=""
+        else
+          aix_use_runtimelinking=no
+
+          # Test if we are trying to use run time linking or normal
+          # AIX style linking. If -brtl is somewhere in LDFLAGS, we
+          # need to do runtime linking.
+          case $host_os in aix4.[[23]]|aix4.[[23]].*|aix[[5-9]]*)
+	    for ld_flag in $LDFLAGS; do
+	      case $ld_flag in
+	      *-brtl*)
+	        aix_use_runtimelinking=yes
+	        break
+	        ;;
+	      esac
+	    done
+	    ;;
+          esac
+
+          exp_sym_flag='-bexport'
+          no_entry_flag='-bnoentry'
+        fi
+
+        # When large executables or shared objects are built, AIX ld can
+        # have problems creating the table of contents.  If linking a library
+        # or program results in "error TOC overflow" add -mminimal-toc to
+        # CXXFLAGS/CFLAGS for g++/gcc.  In the cases where that is not
+        # enough to fix the problem, add -Wl,-bbigtoc to LDFLAGS.
+
+        _LT_TAGVAR(archive_cmds, $1)=''
+        _LT_TAGVAR(hardcode_direct, $1)=yes
+        _LT_TAGVAR(hardcode_direct_absolute, $1)=yes
+        _LT_TAGVAR(hardcode_libdir_separator, $1)=':'
+        _LT_TAGVAR(link_all_deplibs, $1)=yes
+        _LT_TAGVAR(file_list_spec, $1)='${wl}-f,'
+
+        if test "$GXX" = yes; then
+          case $host_os in aix4.[[012]]|aix4.[[012]].*)
+          # We only want to do this on AIX 4.2 and lower, the check
+          # below for broken collect2 doesn't work under 4.3+
+	  collect2name=`${CC} -print-prog-name=collect2`
+	  if test -f "$collect2name" &&
+	     strings "$collect2name" | $GREP resolve_lib_name >/dev/null
+	  then
+	    # We have reworked collect2
+	    :
+	  else
+	    # We have old collect2
+	    _LT_TAGVAR(hardcode_direct, $1)=unsupported
+	    # It fails to find uninstalled libraries when the uninstalled
+	    # path is not listed in the libpath.  Setting hardcode_minus_L
+	    # to unsupported forces relinking
+	    _LT_TAGVAR(hardcode_minus_L, $1)=yes
+	    _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir'
+	    _LT_TAGVAR(hardcode_libdir_separator, $1)=
+	  fi
+          esac
+          shared_flag='-shared'
+	  if test "$aix_use_runtimelinking" = yes; then
+	    shared_flag="$shared_flag "'${wl}-G'
+	  fi
+        else
+          # not using gcc
+          if test "$host_cpu" = ia64; then
+	  # VisualAge C++, Version 5.5 for AIX 5L for IA-64, Beta 3 Release
+	  # chokes on -Wl,-G. The following line is correct:
+	  shared_flag='-G'
+          else
+	    if test "$aix_use_runtimelinking" = yes; then
+	      shared_flag='${wl}-G'
+	    else
+	      shared_flag='${wl}-bM:SRE'
+	    fi
+          fi
+        fi
+
+        _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-bexpall'
+        # It seems that -bexpall does not export symbols beginning with
+        # underscore (_), so it is better to generate a list of symbols to
+	# export.
+        _LT_TAGVAR(always_export_symbols, $1)=yes
+        if test "$aix_use_runtimelinking" = yes; then
+          # Warning - without using the other runtime loading flags (-brtl),
+          # -berok will link without error, but may produce a broken library.
+          _LT_TAGVAR(allow_undefined_flag, $1)='-berok'
+          # Determine the default libpath from the value encoded in an empty
+          # executable.
+          _LT_SYS_MODULE_PATH_AIX([$1])
+          _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-blibpath:$libdir:'"$aix_libpath"
+
+          _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -o $output_objdir/$soname $libobjs $deplibs '"\${wl}$no_entry_flag"' $compiler_flags `if test "x${allow_undefined_flag}" != "x"; then func_echo_all "${wl}${allow_undefined_flag}"; else :; fi` '"\${wl}$exp_sym_flag:\$export_symbols $shared_flag"
+        else
+          if test "$host_cpu" = ia64; then
+	    _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-R $libdir:/usr/lib:/lib'
+	    _LT_TAGVAR(allow_undefined_flag, $1)="-z nodefs"
+	    _LT_TAGVAR(archive_expsym_cmds, $1)="\$CC $shared_flag"' -o $output_objdir/$soname $libobjs $deplibs '"\${wl}$no_entry_flag"' $compiler_flags ${wl}${allow_undefined_flag} '"\${wl}$exp_sym_flag:\$export_symbols"
+          else
+	    # Determine the default libpath from the value encoded in an
+	    # empty executable.
+	    _LT_SYS_MODULE_PATH_AIX([$1])
+	    _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-blibpath:$libdir:'"$aix_libpath"
+	    # Warning - without using the other run time loading flags,
+	    # -berok will link without error, but may produce a broken library.
+	    _LT_TAGVAR(no_undefined_flag, $1)=' ${wl}-bernotok'
+	    _LT_TAGVAR(allow_undefined_flag, $1)=' ${wl}-berok'
+	    if test "$with_gnu_ld" = yes; then
+	      # We only use this code for GNU lds that support --whole-archive.
+	      _LT_TAGVAR(whole_archive_flag_spec, $1)='${wl}--whole-archive$convenience ${wl}--no-whole-archive'
+	    else
+	      # Exported symbols can be pulled into shared objects from archives
+	      _LT_TAGVAR(whole_archive_flag_spec, $1)='$convenience'
+	    fi
+	    _LT_TAGVAR(archive_cmds_need_lc, $1)=yes
+	    # This is similar to how AIX traditionally builds its shared
+	    # libraries.
+	    _LT_TAGVAR(archive_expsym_cmds, $1)="\$CC $shared_flag"' -o $output_objdir/$soname $libobjs $deplibs ${wl}-bnoentry $compiler_flags ${wl}-bE:$export_symbols${allow_undefined_flag}~$AR $AR_FLAGS $output_objdir/$libname$release.a $output_objdir/$soname'
+          fi
+        fi
+        ;;
+
+      beos*)
+	if $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then
+	  _LT_TAGVAR(allow_undefined_flag, $1)=unsupported
+	  # Joseph Beckenbach <jrb3@best.com> says some releases of gcc
+	  # support --undefined.  This deserves some investigation.  FIXME
+	  _LT_TAGVAR(archive_cmds, $1)='$CC -nostart $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib'
+	else
+	  _LT_TAGVAR(ld_shlibs, $1)=no
+	fi
+	;;
+
+      chorus*)
+        case $cc_basename in
+          *)
+	  # FIXME: insert proper C++ library support
+	  _LT_TAGVAR(ld_shlibs, $1)=no
+	  ;;
+        esac
+        ;;
+
+      cygwin* | mingw* | pw32* | cegcc*)
+	case $GXX,$cc_basename in
+	,cl* | no,cl*)
+	  # Native MSVC
+	  # hardcode_libdir_flag_spec is actually meaningless, as there is
+	  # no search path for DLLs.
+	  _LT_TAGVAR(hardcode_libdir_flag_spec, $1)=' '
+	  _LT_TAGVAR(allow_undefined_flag, $1)=unsupported
+	  _LT_TAGVAR(always_export_symbols, $1)=yes
+	  _LT_TAGVAR(file_list_spec, $1)='@'
+	  # Tell ltmain to make .lib files, not .a files.
+	  libext=lib
+	  # Tell ltmain to make .dll files, not .so files.
+	  shrext_cmds=".dll"
+	  # FIXME: Setting linknames here is a bad hack.
+	  _LT_TAGVAR(archive_cmds, $1)='$CC -o $output_objdir/$soname $libobjs $compiler_flags $deplibs -Wl,-dll~linknames='
+	  _LT_TAGVAR(archive_expsym_cmds, $1)='if test "x`$SED 1q $export_symbols`" = xEXPORTS; then
+	      $SED -n -e 's/\\\\\\\(.*\\\\\\\)/-link\\\ -EXPORT:\\\\\\\1/' -e '1\\\!p' < $export_symbols > $output_objdir/$soname.exp;
+	    else
+	      $SED -e 's/\\\\\\\(.*\\\\\\\)/-link\\\ -EXPORT:\\\\\\\1/' < $export_symbols > $output_objdir/$soname.exp;
+	    fi~
+	    $CC -o $tool_output_objdir$soname $libobjs $compiler_flags $deplibs "@$tool_output_objdir$soname.exp" -Wl,-DLL,-IMPLIB:"$tool_output_objdir$libname.dll.lib"~
+	    linknames='
+	  # The linker will not automatically build a static lib if we build a DLL.
+	  # _LT_TAGVAR(old_archive_from_new_cmds, $1)='true'
+	  _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=yes
+	  # Don't use ranlib
+	  _LT_TAGVAR(old_postinstall_cmds, $1)='chmod 644 $oldlib'
+	  _LT_TAGVAR(postlink_cmds, $1)='lt_outputfile="@OUTPUT@"~
+	    lt_tool_outputfile="@TOOL_OUTPUT@"~
+	    case $lt_outputfile in
+	      *.exe|*.EXE) ;;
+	      *)
+		lt_outputfile="$lt_outputfile.exe"
+		lt_tool_outputfile="$lt_tool_outputfile.exe"
+		;;
+	    esac~
+	    func_to_tool_file "$lt_outputfile"~
+	    if test "$MANIFEST_TOOL" != ":" && test -f "$lt_outputfile.manifest"; then
+	      $MANIFEST_TOOL -manifest "$lt_tool_outputfile.manifest" -outputresource:"$lt_tool_outputfile" || exit 1;
+	      $RM "$lt_outputfile.manifest";
+	    fi'
+	  ;;
+	*)
+	  # g++
+	  # _LT_TAGVAR(hardcode_libdir_flag_spec, $1) is actually meaningless,
+	  # as there is no search path for DLLs.
+	  _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir'
+	  _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}--export-all-symbols'
+	  _LT_TAGVAR(allow_undefined_flag, $1)=unsupported
+	  _LT_TAGVAR(always_export_symbols, $1)=no
+	  _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=yes
+
+	  if $LD --help 2>&1 | $GREP 'auto-import' > /dev/null; then
+	    _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -o $output_objdir/$soname ${wl}--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib'
+	    # If the export-symbols file already is a .def file (1st line
+	    # is EXPORTS), use it as is; otherwise, prepend...
+	    _LT_TAGVAR(archive_expsym_cmds, $1)='if test "x`$SED 1q $export_symbols`" = xEXPORTS; then
+	      cp $export_symbols $output_objdir/$soname.def;
+	    else
+	      echo EXPORTS > $output_objdir/$soname.def;
+	      cat $export_symbols >> $output_objdir/$soname.def;
+	    fi~
+	    $CC -shared -nostdlib $output_objdir/$soname.def $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -o $output_objdir/$soname ${wl}--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib'
+	  else
+	    _LT_TAGVAR(ld_shlibs, $1)=no
+	  fi
+	  ;;
+	esac
+	;;
+      darwin* | rhapsody*)
+        _LT_DARWIN_LINKER_FEATURES($1)
+	;;
+
+      dgux*)
+        case $cc_basename in
+          ec++*)
+	    # FIXME: insert proper C++ library support
+	    _LT_TAGVAR(ld_shlibs, $1)=no
+	    ;;
+          ghcx*)
+	    # Green Hills C++ Compiler
+	    # FIXME: insert proper C++ library support
+	    _LT_TAGVAR(ld_shlibs, $1)=no
+	    ;;
+          *)
+	    # FIXME: insert proper C++ library support
+	    _LT_TAGVAR(ld_shlibs, $1)=no
+	    ;;
+        esac
+        ;;
+
+      freebsd2.*)
+        # C++ shared libraries reported to be fairly broken before
+	# switch to ELF
+        _LT_TAGVAR(ld_shlibs, $1)=no
+        ;;
+
+      freebsd-elf*)
+        _LT_TAGVAR(archive_cmds_need_lc, $1)=no
+        ;;
+
+      freebsd* | dragonfly*)
+        # FreeBSD 3 and later use GNU C++ and GNU ld with standard ELF
+        # conventions
+        _LT_TAGVAR(ld_shlibs, $1)=yes
+        ;;
+
+      haiku*)
+        _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib'
+        _LT_TAGVAR(link_all_deplibs, $1)=yes
+        ;;
+
+      hpux9*)
+        _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}+b ${wl}$libdir'
+        _LT_TAGVAR(hardcode_libdir_separator, $1)=:
+        _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-E'
+        _LT_TAGVAR(hardcode_direct, $1)=yes
+        _LT_TAGVAR(hardcode_minus_L, $1)=yes # Not in the search PATH,
+				             # but as the default
+				             # location of the library.
+
+        case $cc_basename in
+          CC*)
+            # FIXME: insert proper C++ library support
+            _LT_TAGVAR(ld_shlibs, $1)=no
+            ;;
+          aCC*)
+            _LT_TAGVAR(archive_cmds, $1)='$RM $output_objdir/$soname~$CC -b ${wl}+b ${wl}$install_libdir -o $output_objdir/$soname $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~test $output_objdir/$soname = $lib || mv $output_objdir/$soname $lib'
+            # Commands to make compiler produce verbose output that lists
+            # what "hidden" libraries, object files and flags are used when
+            # linking a shared library.
+            #
+            # There doesn't appear to be a way to prevent this compiler from
+            # explicitly linking system object files so we need to strip them
+            # from the output so that they don't get included in the library
+            # dependencies.
+            output_verbose_link_cmd='templist=`($CC -b $CFLAGS -v conftest.$objext 2>&1) | $EGREP "\-L"`; list=""; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; func_echo_all "$list"'
+            ;;
+          *)
+            if test "$GXX" = yes; then
+              _LT_TAGVAR(archive_cmds, $1)='$RM $output_objdir/$soname~$CC -shared -nostdlib $pic_flag ${wl}+b ${wl}$install_libdir -o $output_objdir/$soname $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~test $output_objdir/$soname = $lib || mv $output_objdir/$soname $lib'
+            else
+              # FIXME: insert proper C++ library support
+              _LT_TAGVAR(ld_shlibs, $1)=no
+            fi
+            ;;
+        esac
+        ;;
+
+      hpux10*|hpux11*)
+        if test $with_gnu_ld = no; then
+	  _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}+b ${wl}$libdir'
+	  _LT_TAGVAR(hardcode_libdir_separator, $1)=:
+
+          case $host_cpu in
+            hppa*64*|ia64*)
+              ;;
+            *)
+	      _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-E'
+              ;;
+          esac
+        fi
+        case $host_cpu in
+          hppa*64*|ia64*)
+            _LT_TAGVAR(hardcode_direct, $1)=no
+            _LT_TAGVAR(hardcode_shlibpath_var, $1)=no
+            ;;
+          *)
+            _LT_TAGVAR(hardcode_direct, $1)=yes
+            _LT_TAGVAR(hardcode_direct_absolute, $1)=yes
+            _LT_TAGVAR(hardcode_minus_L, $1)=yes # Not in the search PATH,
+					         # but as the default
+					         # location of the library.
+            ;;
+        esac
+
+        case $cc_basename in
+          CC*)
+	    # FIXME: insert proper C++ library support
+	    _LT_TAGVAR(ld_shlibs, $1)=no
+	    ;;
+          aCC*)
+	    case $host_cpu in
+	      hppa*64*)
+	        _LT_TAGVAR(archive_cmds, $1)='$CC -b ${wl}+h ${wl}$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags'
+	        ;;
+	      ia64*)
+	        _LT_TAGVAR(archive_cmds, $1)='$CC -b ${wl}+h ${wl}$soname ${wl}+nodefaultrpath -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags'
+	        ;;
+	      *)
+	        _LT_TAGVAR(archive_cmds, $1)='$CC -b ${wl}+h ${wl}$soname ${wl}+b ${wl}$install_libdir -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags'
+	        ;;
+	    esac
+	    # Commands to make compiler produce verbose output that lists
+	    # what "hidden" libraries, object files and flags are used when
+	    # linking a shared library.
+	    #
+	    # There doesn't appear to be a way to prevent this compiler from
+	    # explicitly linking system object files so we need to strip them
+	    # from the output so that they don't get included in the library
+	    # dependencies.
+	    output_verbose_link_cmd='templist=`($CC -b $CFLAGS -v conftest.$objext 2>&1) | $GREP "\-L"`; list=""; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; func_echo_all "$list"'
+	    ;;
+          *)
+	    if test "$GXX" = yes; then
+	      if test $with_gnu_ld = no; then
+	        case $host_cpu in
+	          hppa*64*)
+	            _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib -fPIC ${wl}+h ${wl}$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags'
+	            ;;
+	          ia64*)
+	            _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib $pic_flag ${wl}+h ${wl}$soname ${wl}+nodefaultrpath -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags'
+	            ;;
+	          *)
+	            _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib $pic_flag ${wl}+h ${wl}$soname ${wl}+b ${wl}$install_libdir -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags'
+	            ;;
+	        esac
+	      fi
+	    else
+	      # FIXME: insert proper C++ library support
+	      _LT_TAGVAR(ld_shlibs, $1)=no
+	    fi
+	    ;;
+        esac
+        ;;
+
+      interix[[3-9]]*)
+	_LT_TAGVAR(hardcode_direct, $1)=no
+	_LT_TAGVAR(hardcode_shlibpath_var, $1)=no
+	_LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath,$libdir'
+	_LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-E'
+	# Hack: On Interix 3.x, we cannot compile PIC because of a broken gcc.
+	# Instead, shared libraries are loaded at an image base (0x10000000 by
+	# default) and relocated if they conflict, which is a slow very memory
+	# consuming and fragmenting process.  To avoid this, we pick a random,
+	# 256 KiB-aligned image base between 0x50000000 and 0x6FFC0000 at link
+	# time.  Moving up from 0x10000000 also allows more sbrk(2) space.
+	_LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-h,$soname ${wl}--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib'
+	_LT_TAGVAR(archive_expsym_cmds, $1)='sed "s,^,_," $export_symbols >$output_objdir/$soname.expsym~$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-h,$soname ${wl}--retain-symbols-file,$output_objdir/$soname.expsym ${wl}--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib'
+	;;
+      irix5* | irix6*)
+        case $cc_basename in
+          CC*)
+	    # SGI C++
+	    _LT_TAGVAR(archive_cmds, $1)='$CC -shared -all -multigot $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry ${output_objdir}/so_locations -o $lib'
+
+	    # Archives containing C++ object files must be created using
+	    # "CC -ar", where "CC" is the IRIX C++ compiler.  This is
+	    # necessary to make sure instantiated templates are included
+	    # in the archive.
+	    _LT_TAGVAR(old_archive_cmds, $1)='$CC -ar -WR,-u -o $oldlib $oldobjs'
+	    ;;
+          *)
+	    if test "$GXX" = yes; then
+	      if test "$with_gnu_ld" = no; then
+	        _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname `test -n "$verstring" && func_echo_all "${wl}-set_version ${wl}$verstring"` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib'
+	      else
+	        _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname `test -n "$verstring" && func_echo_all "${wl}-set_version ${wl}$verstring"` -o $lib'
+	      fi
+	    fi
+	    _LT_TAGVAR(link_all_deplibs, $1)=yes
+	    ;;
+        esac
+        _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir'
+        _LT_TAGVAR(hardcode_libdir_separator, $1)=:
+        _LT_TAGVAR(inherit_rpath, $1)=yes
+        ;;
+
+      linux* | k*bsd*-gnu | kopensolaris*-gnu | gnu*)
+        case $cc_basename in
+          KCC*)
+	    # Kuck and Associates, Inc. (KAI) C++ Compiler
+
+	    # KCC will only create a shared library if the output file
+	    # ends with ".so" (or ".sl" for HP-UX), so rename the library
+	    # to its proper name (with version) after linking.
+	    _LT_TAGVAR(archive_cmds, $1)='tempext=`echo $shared_ext | $SED -e '\''s/\([[^()0-9A-Za-z{}]]\)/\\\\\1/g'\''`; templib=`echo $lib | $SED -e "s/\${tempext}\..*/.so/"`; $CC $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags --soname $soname -o \$templib; mv \$templib $lib'
+	    _LT_TAGVAR(archive_expsym_cmds, $1)='tempext=`echo $shared_ext | $SED -e '\''s/\([[^()0-9A-Za-z{}]]\)/\\\\\1/g'\''`; templib=`echo $lib | $SED -e "s/\${tempext}\..*/.so/"`; $CC $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags --soname $soname -o \$templib ${wl}-retain-symbols-file,$export_symbols; mv \$templib $lib'
+	    # Commands to make compiler produce verbose output that lists
+	    # what "hidden" libraries, object files and flags are used when
+	    # linking a shared library.
+	    #
+	    # There doesn't appear to be a way to prevent this compiler from
+	    # explicitly linking system object files so we need to strip them
+	    # from the output so that they don't get included in the library
+	    # dependencies.
+	    output_verbose_link_cmd='templist=`$CC $CFLAGS -v conftest.$objext -o libconftest$shared_ext 2>&1 | $GREP "ld"`; rm -f libconftest$shared_ext; list=""; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; func_echo_all "$list"'
+
+	    _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath,$libdir'
+	    _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}--export-dynamic'
+
+	    # Archives containing C++ object files must be created using
+	    # "CC -Bstatic", where "CC" is the KAI C++ compiler.
+	    _LT_TAGVAR(old_archive_cmds, $1)='$CC -Bstatic -o $oldlib $oldobjs'
+	    ;;
+	  icpc* | ecpc* )
+	    # Intel C++
+	    with_gnu_ld=yes
+	    # version 8.0 and above of icpc choke on multiply defined symbols
+	    # if we add $predep_objects and $postdep_objects, however 7.1 and
+	    # earlier do not add the objects themselves.
+	    case `$CC -V 2>&1` in
+	      *"Version 7."*)
+	        _LT_TAGVAR(archive_cmds, $1)='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $wl$soname -o $lib'
+		_LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib'
+		;;
+	      *)  # Version 8.0 or newer
+	        tmp_idyn=
+	        case $host_cpu in
+		  ia64*) tmp_idyn=' -i_dynamic';;
+		esac
+	        _LT_TAGVAR(archive_cmds, $1)='$CC -shared'"$tmp_idyn"' $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib'
+		_LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared'"$tmp_idyn"' $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib'
+		;;
+	    esac
+	    _LT_TAGVAR(archive_cmds_need_lc, $1)=no
+	    _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath,$libdir'
+	    _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}--export-dynamic'
+	    _LT_TAGVAR(whole_archive_flag_spec, $1)='${wl}--whole-archive$convenience ${wl}--no-whole-archive'
+	    ;;
+          pgCC* | pgcpp*)
+            # Portland Group C++ compiler
+	    case `$CC -V` in
+	    *pgCC\ [[1-5]].* | *pgcpp\ [[1-5]].*)
+	      _LT_TAGVAR(prelink_cmds, $1)='tpldir=Template.dir~
+		rm -rf $tpldir~
+		$CC --prelink_objects --instantiation_dir $tpldir $objs $libobjs $compile_deplibs~
+		compile_command="$compile_command `find $tpldir -name \*.o | sort | $NL2SP`"'
+	      _LT_TAGVAR(old_archive_cmds, $1)='tpldir=Template.dir~
+		rm -rf $tpldir~
+		$CC --prelink_objects --instantiation_dir $tpldir $oldobjs$old_deplibs~
+		$AR $AR_FLAGS $oldlib$oldobjs$old_deplibs `find $tpldir -name \*.o | sort | $NL2SP`~
+		$RANLIB $oldlib'
+	      _LT_TAGVAR(archive_cmds, $1)='tpldir=Template.dir~
+		rm -rf $tpldir~
+		$CC --prelink_objects --instantiation_dir $tpldir $predep_objects $libobjs $deplibs $convenience $postdep_objects~
+		$CC -shared $pic_flag $predep_objects $libobjs $deplibs `find $tpldir -name \*.o | sort | $NL2SP` $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname -o $lib'
+	      _LT_TAGVAR(archive_expsym_cmds, $1)='tpldir=Template.dir~
+		rm -rf $tpldir~
+		$CC --prelink_objects --instantiation_dir $tpldir $predep_objects $libobjs $deplibs $convenience $postdep_objects~
+		$CC -shared $pic_flag $predep_objects $libobjs $deplibs `find $tpldir -name \*.o | sort | $NL2SP` $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname ${wl}-retain-symbols-file ${wl}$export_symbols -o $lib'
+	      ;;
+	    *) # Version 6 and above use weak symbols
+	      _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname -o $lib'
+	      _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $pic_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname ${wl}-retain-symbols-file ${wl}$export_symbols -o $lib'
+	      ;;
+	    esac
+
+	    _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}--rpath ${wl}$libdir'
+	    _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}--export-dynamic'
+	    _LT_TAGVAR(whole_archive_flag_spec, $1)='${wl}--whole-archive`for conv in $convenience\"\"; do test  -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` ${wl}--no-whole-archive'
+            ;;
+	  cxx*)
+	    # Compaq C++
+	    _LT_TAGVAR(archive_cmds, $1)='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $wl$soname -o $lib'
+	    _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $wl$soname  -o $lib ${wl}-retain-symbols-file $wl$export_symbols'
+
+	    runpath_var=LD_RUN_PATH
+	    _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-rpath $libdir'
+	    _LT_TAGVAR(hardcode_libdir_separator, $1)=:
+
+	    # Commands to make compiler produce verbose output that lists
+	    # what "hidden" libraries, object files and flags are used when
+	    # linking a shared library.
+	    #
+	    # There doesn't appear to be a way to prevent this compiler from
+	    # explicitly linking system object files so we need to strip them
+	    # from the output so that they don't get included in the library
+	    # dependencies.
+	    output_verbose_link_cmd='templist=`$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP "ld"`; templist=`func_echo_all "$templist" | $SED "s/\(^.*ld.*\)\( .*ld .*$\)/\1/"`; list=""; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; func_echo_all "X$list" | $Xsed'
+	    ;;
+	  xl* | mpixl* | bgxl*)
+	    # IBM XL 8.0 on PPC, with GNU ld
+	    _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir'
+	    _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}--export-dynamic'
+	    _LT_TAGVAR(archive_cmds, $1)='$CC -qmkshrobj $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib'
+	    if test "x$supports_anon_versioning" = xyes; then
+	      _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $output_objdir/$libname.ver~
+		cat $export_symbols | sed -e "s/\(.*\)/\1;/" >> $output_objdir/$libname.ver~
+		echo "local: *; };" >> $output_objdir/$libname.ver~
+		$CC -qmkshrobj $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-version-script ${wl}$output_objdir/$libname.ver -o $lib'
+	    fi
+	    ;;
+	  *)
+	    case `$CC -V 2>&1 | sed 5q` in
+	    *Sun\ C*)
+	      # Sun C++ 5.9
+	      _LT_TAGVAR(no_undefined_flag, $1)=' -zdefs'
+	      _LT_TAGVAR(archive_cmds, $1)='$CC -G${allow_undefined_flag} -h$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags'
+	      _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -G${allow_undefined_flag} -h$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-retain-symbols-file ${wl}$export_symbols'
+	      _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir'
+	      _LT_TAGVAR(whole_archive_flag_spec, $1)='${wl}--whole-archive`new_convenience=; for conv in $convenience\"\"; do test -z \"$conv\" || new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` ${wl}--no-whole-archive'
+	      _LT_TAGVAR(compiler_needs_object, $1)=yes
+
+	      # Not sure whether something based on
+	      # $CC $CFLAGS -v conftest.$objext -o libconftest$shared_ext 2>&1
+	      # would be better.
+	      output_verbose_link_cmd='func_echo_all'
+
+	      # Archives containing C++ object files must be created using
+	      # "CC -xar", where "CC" is the Sun C++ compiler.  This is
+	      # necessary to make sure instantiated templates are included
+	      # in the archive.
+	      _LT_TAGVAR(old_archive_cmds, $1)='$CC -xar -o $oldlib $oldobjs'
+	      ;;
+	    esac
+	    ;;
+	esac
+	;;
+
+      lynxos*)
+        # FIXME: insert proper C++ library support
+	_LT_TAGVAR(ld_shlibs, $1)=no
+	;;
+
+      m88k*)
+        # FIXME: insert proper C++ library support
+        _LT_TAGVAR(ld_shlibs, $1)=no
+	;;
+
+      mvs*)
+        case $cc_basename in
+          cxx*)
+	    # FIXME: insert proper C++ library support
+	    _LT_TAGVAR(ld_shlibs, $1)=no
+	    ;;
+	  *)
+	    # FIXME: insert proper C++ library support
+	    _LT_TAGVAR(ld_shlibs, $1)=no
+	    ;;
+	esac
+	;;
+
+      netbsd*)
+        if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then
+	  _LT_TAGVAR(archive_cmds, $1)='$LD -Bshareable  -o $lib $predep_objects $libobjs $deplibs $postdep_objects $linker_flags'
+	  wlarc=
+	  _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir'
+	  _LT_TAGVAR(hardcode_direct, $1)=yes
+	  _LT_TAGVAR(hardcode_shlibpath_var, $1)=no
+	fi
+	# Workaround some broken pre-1.5 toolchains
+	output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP conftest.$objext | $SED -e "s:-lgcc -lc -lgcc::"'
+	;;
+
+      *nto* | *qnx*)
+        _LT_TAGVAR(ld_shlibs, $1)=yes
+	;;
+
+      openbsd2*)
+        # C++ shared libraries are fairly broken
+	_LT_TAGVAR(ld_shlibs, $1)=no
+	;;
+
+      openbsd*)
+	if test -f /usr/libexec/ld.so; then
+	  _LT_TAGVAR(hardcode_direct, $1)=yes
+	  _LT_TAGVAR(hardcode_shlibpath_var, $1)=no
+	  _LT_TAGVAR(hardcode_direct_absolute, $1)=yes
+	  _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -o $lib'
+	  _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath,$libdir'
+	  if test -z "`echo __ELF__ | $CC -E - | grep __ELF__`" || test "$host_os-$host_cpu" = "openbsd2.8-powerpc"; then
+	    _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $pic_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-retain-symbols-file,$export_symbols -o $lib'
+	    _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-E'
+	    _LT_TAGVAR(whole_archive_flag_spec, $1)="$wlarc"'--whole-archive$convenience '"$wlarc"'--no-whole-archive'
+	  fi
+	  output_verbose_link_cmd=func_echo_all
+	else
+	  _LT_TAGVAR(ld_shlibs, $1)=no
+	fi
+	;;
+
+      osf3* | osf4* | osf5*)
+        case $cc_basename in
+          KCC*)
+	    # Kuck and Associates, Inc. (KAI) C++ Compiler
+
+	    # KCC will only create a shared library if the output file
+	    # ends with ".so" (or ".sl" for HP-UX), so rename the library
+	    # to its proper name (with version) after linking.
+	    _LT_TAGVAR(archive_cmds, $1)='tempext=`echo $shared_ext | $SED -e '\''s/\([[^()0-9A-Za-z{}]]\)/\\\\\1/g'\''`; templib=`echo "$lib" | $SED -e "s/\${tempext}\..*/.so/"`; $CC $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags --soname $soname -o \$templib; mv \$templib $lib'
+
+	    _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath,$libdir'
+	    _LT_TAGVAR(hardcode_libdir_separator, $1)=:
+
+	    # Archives containing C++ object files must be created using
+	    # the KAI C++ compiler.
+	    case $host in
+	      osf3*) _LT_TAGVAR(old_archive_cmds, $1)='$CC -Bstatic -o $oldlib $oldobjs' ;;
+	      *) _LT_TAGVAR(old_archive_cmds, $1)='$CC -o $oldlib $oldobjs' ;;
+	    esac
+	    ;;
+          RCC*)
+	    # Rational C++ 2.4.1
+	    # FIXME: insert proper C++ library support
+	    _LT_TAGVAR(ld_shlibs, $1)=no
+	    ;;
+          cxx*)
+	    case $host in
+	      osf3*)
+	        _LT_TAGVAR(allow_undefined_flag, $1)=' ${wl}-expect_unresolved ${wl}\*'
+	        _LT_TAGVAR(archive_cmds, $1)='$CC -shared${allow_undefined_flag} $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $soname `test -n "$verstring" && func_echo_all "${wl}-set_version $verstring"` -update_registry ${output_objdir}/so_locations -o $lib'
+	        _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir'
+		;;
+	      *)
+	        _LT_TAGVAR(allow_undefined_flag, $1)=' -expect_unresolved \*'
+	        _LT_TAGVAR(archive_cmds, $1)='$CC -shared${allow_undefined_flag} $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -msym -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry ${output_objdir}/so_locations -o $lib'
+	        _LT_TAGVAR(archive_expsym_cmds, $1)='for i in `cat $export_symbols`; do printf "%s %s\\n" -exported_symbol "\$i" >> $lib.exp; done~
+	          echo "-hidden">> $lib.exp~
+	          $CC -shared$allow_undefined_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -msym -soname $soname ${wl}-input ${wl}$lib.exp  `test -n "$verstring" && $ECHO "-set_version $verstring"` -update_registry ${output_objdir}/so_locations -o $lib~
+	          $RM $lib.exp'
+	        _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-rpath $libdir'
+		;;
+	    esac
+
+	    _LT_TAGVAR(hardcode_libdir_separator, $1)=:
+
+	    # Commands to make compiler produce verbose output that lists
+	    # what "hidden" libraries, object files and flags are used when
+	    # linking a shared library.
+	    #
+	    # There doesn't appear to be a way to prevent this compiler from
+	    # explicitly linking system object files so we need to strip them
+	    # from the output so that they don't get included in the library
+	    # dependencies.
+	    output_verbose_link_cmd='templist=`$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP "ld" | $GREP -v "ld:"`; templist=`func_echo_all "$templist" | $SED "s/\(^.*ld.*\)\( .*ld.*$\)/\1/"`; list=""; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; func_echo_all "$list"'
+	    ;;
+	  *)
+	    if test "$GXX" = yes && test "$with_gnu_ld" = no; then
+	      _LT_TAGVAR(allow_undefined_flag, $1)=' ${wl}-expect_unresolved ${wl}\*'
+	      case $host in
+	        osf3*)
+	          _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib ${allow_undefined_flag} $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname `test -n "$verstring" && func_echo_all "${wl}-set_version ${wl}$verstring"` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib'
+		  ;;
+	        *)
+	          _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag -nostdlib ${allow_undefined_flag} $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-msym ${wl}-soname ${wl}$soname `test -n "$verstring" && func_echo_all "${wl}-set_version ${wl}$verstring"` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib'
+		  ;;
+	      esac
+
+	      _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir'
+	      _LT_TAGVAR(hardcode_libdir_separator, $1)=:
+
+	      # Commands to make compiler produce verbose output that lists
+	      # what "hidden" libraries, object files and flags are used when
+	      # linking a shared library.
+	      output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP -v "^Configured with:" | $GREP "\-L"'
+
+	    else
+	      # FIXME: insert proper C++ library support
+	      _LT_TAGVAR(ld_shlibs, $1)=no
+	    fi
+	    ;;
+        esac
+        ;;
+
+      psos*)
+        # FIXME: insert proper C++ library support
+        _LT_TAGVAR(ld_shlibs, $1)=no
+        ;;
+
+      sunos4*)
+        case $cc_basename in
+          CC*)
+	    # Sun C++ 4.x
+	    # FIXME: insert proper C++ library support
+	    _LT_TAGVAR(ld_shlibs, $1)=no
+	    ;;
+          lcc*)
+	    # Lucid
+	    # FIXME: insert proper C++ library support
+	    _LT_TAGVAR(ld_shlibs, $1)=no
+	    ;;
+          *)
+	    # FIXME: insert proper C++ library support
+	    _LT_TAGVAR(ld_shlibs, $1)=no
+	    ;;
+        esac
+        ;;
+
+      solaris*)
+        case $cc_basename in
+          CC* | sunCC*)
+	    # Sun C++ 4.2, 5.x and Centerline C++
+            _LT_TAGVAR(archive_cmds_need_lc,$1)=yes
+	    _LT_TAGVAR(no_undefined_flag, $1)=' -zdefs'
+	    _LT_TAGVAR(archive_cmds, $1)='$CC -G${allow_undefined_flag}  -h$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags'
+	    _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~
+	      $CC -G${allow_undefined_flag} ${wl}-M ${wl}$lib.exp -h$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~$RM $lib.exp'
+
+	    _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir'
+	    _LT_TAGVAR(hardcode_shlibpath_var, $1)=no
+	    case $host_os in
+	      solaris2.[[0-5]] | solaris2.[[0-5]].*) ;;
+	      *)
+		# The compiler driver will combine and reorder linker options,
+		# but understands `-z linker_flag'.
+	        # Supported since Solaris 2.6 (maybe 2.5.1?)
+		_LT_TAGVAR(whole_archive_flag_spec, $1)='-z allextract$convenience -z defaultextract'
+	        ;;
+	    esac
+	    _LT_TAGVAR(link_all_deplibs, $1)=yes
+
+	    output_verbose_link_cmd='func_echo_all'
+
+	    # Archives containing C++ object files must be created using
+	    # "CC -xar", where "CC" is the Sun C++ compiler.  This is
+	    # necessary to make sure instantiated templates are included
+	    # in the archive.
+	    _LT_TAGVAR(old_archive_cmds, $1)='$CC -xar -o $oldlib $oldobjs'
+	    ;;
+          gcx*)
+	    # Green Hills C++ Compiler
+	    _LT_TAGVAR(archive_cmds, $1)='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-h $wl$soname -o $lib'
+
+	    # The C++ compiler must be used to create the archive.
+	    _LT_TAGVAR(old_archive_cmds, $1)='$CC $LDFLAGS -archive -o $oldlib $oldobjs'
+	    ;;
+          *)
+	    # GNU C++ compiler with Solaris linker
+	    if test "$GXX" = yes && test "$with_gnu_ld" = no; then
+	      _LT_TAGVAR(no_undefined_flag, $1)=' ${wl}-z ${wl}defs'
+	      if $CC --version | $GREP -v '^2\.7' > /dev/null; then
+	        _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag -nostdlib $LDFLAGS $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-h $wl$soname -o $lib'
+	        _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~
+		  $CC -shared $pic_flag -nostdlib ${wl}-M $wl$lib.exp -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~$RM $lib.exp'
+
+	        # Commands to make compiler produce verbose output that lists
+	        # what "hidden" libraries, object files and flags are used when
+	        # linking a shared library.
+	        output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP -v "^Configured with:" | $GREP "\-L"'
+	      else
+	        # g++ 2.7 appears to require `-G' NOT `-shared' on this
+	        # platform.
+	        _LT_TAGVAR(archive_cmds, $1)='$CC -G -nostdlib $LDFLAGS $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-h $wl$soname -o $lib'
+	        _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~
+		  $CC -G -nostdlib ${wl}-M $wl$lib.exp -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~$RM $lib.exp'
+
+	        # Commands to make compiler produce verbose output that lists
+	        # what "hidden" libraries, object files and flags are used when
+	        # linking a shared library.
+	        output_verbose_link_cmd='$CC -G $CFLAGS -v conftest.$objext 2>&1 | $GREP -v "^Configured with:" | $GREP "\-L"'
+	      fi
+
+	      _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-R $wl$libdir'
+	      case $host_os in
+		solaris2.[[0-5]] | solaris2.[[0-5]].*) ;;
+		*)
+		  _LT_TAGVAR(whole_archive_flag_spec, $1)='${wl}-z ${wl}allextract$convenience ${wl}-z ${wl}defaultextract'
+		  ;;
+	      esac
+	    fi
+	    ;;
+        esac
+        ;;
+
+    sysv4*uw2* | sysv5OpenUNIX* | sysv5UnixWare7.[[01]].[[10]]* | unixware7* | sco3.2v5.0.[[024]]*)
+      _LT_TAGVAR(no_undefined_flag, $1)='${wl}-z,text'
+      _LT_TAGVAR(archive_cmds_need_lc, $1)=no
+      _LT_TAGVAR(hardcode_shlibpath_var, $1)=no
+      runpath_var='LD_RUN_PATH'
+
+      case $cc_basename in
+        CC*)
+	  _LT_TAGVAR(archive_cmds, $1)='$CC -G ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+	  _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -G ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+	  ;;
+	*)
+	  _LT_TAGVAR(archive_cmds, $1)='$CC -shared ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+	  _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+	  ;;
+      esac
+      ;;
+
+      sysv5* | sco3.2v5* | sco5v6*)
+	# Note: We can NOT use -z defs as we might desire, because we do not
+	# link with -lc, and that would cause any symbols used from libc to
+	# always be unresolved, which means just about no library would
+	# ever link correctly.  If we're not using GNU ld we use -z text
+	# though, which does catch some bad symbols but isn't as heavy-handed
+	# as -z defs.
+	_LT_TAGVAR(no_undefined_flag, $1)='${wl}-z,text'
+	_LT_TAGVAR(allow_undefined_flag, $1)='${wl}-z,nodefs'
+	_LT_TAGVAR(archive_cmds_need_lc, $1)=no
+	_LT_TAGVAR(hardcode_shlibpath_var, $1)=no
+	_LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-R,$libdir'
+	_LT_TAGVAR(hardcode_libdir_separator, $1)=':'
+	_LT_TAGVAR(link_all_deplibs, $1)=yes
+	_LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-Bexport'
+	runpath_var='LD_RUN_PATH'
+
+	case $cc_basename in
+          CC*)
+	    _LT_TAGVAR(archive_cmds, $1)='$CC -G ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+	    _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -G ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+	    _LT_TAGVAR(old_archive_cmds, $1)='$CC -Tprelink_objects $oldobjs~
+	      '"$_LT_TAGVAR(old_archive_cmds, $1)"
+	    _LT_TAGVAR(reload_cmds, $1)='$CC -Tprelink_objects $reload_objs~
+	      '"$_LT_TAGVAR(reload_cmds, $1)"
+	    ;;
+	  *)
+	    _LT_TAGVAR(archive_cmds, $1)='$CC -shared ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+	    _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+	    ;;
+	esac
+      ;;
+
+      tandem*)
+        case $cc_basename in
+          NCC*)
+	    # NonStop-UX NCC 3.20
+	    # FIXME: insert proper C++ library support
+	    _LT_TAGVAR(ld_shlibs, $1)=no
+	    ;;
+          *)
+	    # FIXME: insert proper C++ library support
+	    _LT_TAGVAR(ld_shlibs, $1)=no
+	    ;;
+        esac
+        ;;
+
+      vxworks*)
+        # FIXME: insert proper C++ library support
+        _LT_TAGVAR(ld_shlibs, $1)=no
+        ;;
+
+      *)
+        # FIXME: insert proper C++ library support
+        _LT_TAGVAR(ld_shlibs, $1)=no
+        ;;
+    esac
+
+    AC_MSG_RESULT([$_LT_TAGVAR(ld_shlibs, $1)])
+    test "$_LT_TAGVAR(ld_shlibs, $1)" = no && can_build_shared=no
+
+    _LT_TAGVAR(GCC, $1)="$GXX"
+    _LT_TAGVAR(LD, $1)="$LD"
+
+    ## CAVEAT EMPTOR:
+    ## There is no encapsulation within the following macros, do not change
+    ## the running order or otherwise move them around unless you know exactly
+    ## what you are doing...
+    _LT_SYS_HIDDEN_LIBDEPS($1)
+    _LT_COMPILER_PIC($1)
+    _LT_COMPILER_C_O($1)
+    _LT_COMPILER_FILE_LOCKS($1)
+    _LT_LINKER_SHLIBS($1)
+    _LT_SYS_DYNAMIC_LINKER($1)
+    _LT_LINKER_HARDCODE_LIBPATH($1)
+
+    _LT_CONFIG($1)
+  fi # test -n "$compiler"
+
+  CC=$lt_save_CC
+  CFLAGS=$lt_save_CFLAGS
+  LDCXX=$LD
+  LD=$lt_save_LD
+  GCC=$lt_save_GCC
+  with_gnu_ld=$lt_save_with_gnu_ld
+  lt_cv_path_LDCXX=$lt_cv_path_LD
+  lt_cv_path_LD=$lt_save_path_LD
+  lt_cv_prog_gnu_ldcxx=$lt_cv_prog_gnu_ld
+  lt_cv_prog_gnu_ld=$lt_save_with_gnu_ld
+fi # test "$_lt_caught_CXX_error" != yes
+
+AC_LANG_POP
+])# _LT_LANG_CXX_CONFIG
+
+
+# _LT_FUNC_STRIPNAME_CNF
+# ----------------------
+# func_stripname_cnf prefix suffix name
+# strip PREFIX and SUFFIX off of NAME.
+# PREFIX and SUFFIX must not contain globbing or regex special
+# characters, hashes, percent signs, but SUFFIX may contain a leading
+# dot (in which case that matches only a dot).
+#
+# This function is identical to the (non-XSI) version of func_stripname,
+# except this one can be used by m4 code that may be executed by configure,
+# rather than the libtool script.
+m4_defun([_LT_FUNC_STRIPNAME_CNF],[dnl
+AC_REQUIRE([_LT_DECL_SED])
+AC_REQUIRE([_LT_PROG_ECHO_BACKSLASH])
+func_stripname_cnf ()
+{
+  case ${2} in
+  .*) func_stripname_result=`$ECHO "${3}" | $SED "s%^${1}%%; s%\\\\${2}\$%%"`;;
+  *)  func_stripname_result=`$ECHO "${3}" | $SED "s%^${1}%%; s%${2}\$%%"`;;
+  esac
+} # func_stripname_cnf
+])# _LT_FUNC_STRIPNAME_CNF
+
+# _LT_SYS_HIDDEN_LIBDEPS([TAGNAME])
+# ---------------------------------
+# Figure out "hidden" library dependencies from verbose
+# compiler output when linking a shared library.
+# Parse the compiler output and extract the necessary
+# objects, libraries and library flags.
+m4_defun([_LT_SYS_HIDDEN_LIBDEPS],
+[m4_require([_LT_FILEUTILS_DEFAULTS])dnl
+AC_REQUIRE([_LT_FUNC_STRIPNAME_CNF])dnl
+# Dependencies to place before and after the object being linked:
+_LT_TAGVAR(predep_objects, $1)=
+_LT_TAGVAR(postdep_objects, $1)=
+_LT_TAGVAR(predeps, $1)=
+_LT_TAGVAR(postdeps, $1)=
+_LT_TAGVAR(compiler_lib_search_path, $1)=
+
+dnl we can't use the lt_simple_compile_test_code here,
+dnl because it contains code intended for an executable,
+dnl not a library.  It's possible we should let each
+dnl tag define a new lt_????_link_test_code variable,
+dnl but it's only used here...
+m4_if([$1], [], [cat > conftest.$ac_ext <<_LT_EOF
+int a;
+void foo (void) { a = 0; }
+_LT_EOF
+], [$1], [CXX], [cat > conftest.$ac_ext <<_LT_EOF
+class Foo
+{
+public:
+  Foo (void) { a = 0; }
+private:
+  int a;
+};
+_LT_EOF
+], [$1], [F77], [cat > conftest.$ac_ext <<_LT_EOF
+      subroutine foo
+      implicit none
+      integer*4 a
+      a=0
+      return
+      end
+_LT_EOF
+], [$1], [FC], [cat > conftest.$ac_ext <<_LT_EOF
+      subroutine foo
+      implicit none
+      integer a
+      a=0
+      return
+      end
+_LT_EOF
+], [$1], [GCJ], [cat > conftest.$ac_ext <<_LT_EOF
+public class foo {
+  private int a;
+  public void bar (void) {
+    a = 0;
+  }
+};
+_LT_EOF
+], [$1], [GO], [cat > conftest.$ac_ext <<_LT_EOF
+package foo
+func foo() {
+}
+_LT_EOF
+])
+
+_lt_libdeps_save_CFLAGS=$CFLAGS
+case "$CC $CFLAGS " in #(
+*\ -flto*\ *) CFLAGS="$CFLAGS -fno-lto" ;;
+*\ -fwhopr*\ *) CFLAGS="$CFLAGS -fno-whopr" ;;
+*\ -fuse-linker-plugin*\ *) CFLAGS="$CFLAGS -fno-use-linker-plugin" ;;
+esac
+
+dnl Parse the compiler output and extract the necessary
+dnl objects, libraries and library flags.
+if AC_TRY_EVAL(ac_compile); then
+  # Parse the compiler output and extract the necessary
+  # objects, libraries and library flags.
+
+  # Sentinel used to keep track of whether or not we are before
+  # the conftest object file.
+  pre_test_object_deps_done=no
+
+  for p in `eval "$output_verbose_link_cmd"`; do
+    case ${prev}${p} in
+
+    -L* | -R* | -l*)
+       # Some compilers place space between "-{L,R}" and the path.
+       # Remove the space.
+       if test $p = "-L" ||
+          test $p = "-R"; then
+	 prev=$p
+	 continue
+       fi
+
+       # Expand the sysroot to ease extracting the directories later.
+       if test -z "$prev"; then
+         case $p in
+         -L*) func_stripname_cnf '-L' '' "$p"; prev=-L; p=$func_stripname_result ;;
+         -R*) func_stripname_cnf '-R' '' "$p"; prev=-R; p=$func_stripname_result ;;
+         -l*) func_stripname_cnf '-l' '' "$p"; prev=-l; p=$func_stripname_result ;;
+         esac
+       fi
+       case $p in
+       =*) func_stripname_cnf '=' '' "$p"; p=$lt_sysroot$func_stripname_result ;;
+       esac
+       if test "$pre_test_object_deps_done" = no; then
+	 case ${prev} in
+	 -L | -R)
+	   # Internal compiler library paths should come after those
+	   # provided the user.  The postdeps already come after the
+	   # user supplied libs so there is no need to process them.
+	   if test -z "$_LT_TAGVAR(compiler_lib_search_path, $1)"; then
+	     _LT_TAGVAR(compiler_lib_search_path, $1)="${prev}${p}"
+	   else
+	     _LT_TAGVAR(compiler_lib_search_path, $1)="${_LT_TAGVAR(compiler_lib_search_path, $1)} ${prev}${p}"
+	   fi
+	   ;;
+	 # The "-l" case would never come before the object being
+	 # linked, so don't bother handling this case.
+	 esac
+       else
+	 if test -z "$_LT_TAGVAR(postdeps, $1)"; then
+	   _LT_TAGVAR(postdeps, $1)="${prev}${p}"
+	 else
+	   _LT_TAGVAR(postdeps, $1)="${_LT_TAGVAR(postdeps, $1)} ${prev}${p}"
+	 fi
+       fi
+       prev=
+       ;;
+
+    *.lto.$objext) ;; # Ignore GCC LTO objects
+    *.$objext)
+       # This assumes that the test object file only shows up
+       # once in the compiler output.
+       if test "$p" = "conftest.$objext"; then
+	 pre_test_object_deps_done=yes
+	 continue
+       fi
+
+       if test "$pre_test_object_deps_done" = no; then
+	 if test -z "$_LT_TAGVAR(predep_objects, $1)"; then
+	   _LT_TAGVAR(predep_objects, $1)="$p"
+	 else
+	   _LT_TAGVAR(predep_objects, $1)="$_LT_TAGVAR(predep_objects, $1) $p"
+	 fi
+       else
+	 if test -z "$_LT_TAGVAR(postdep_objects, $1)"; then
+	   _LT_TAGVAR(postdep_objects, $1)="$p"
+	 else
+	   _LT_TAGVAR(postdep_objects, $1)="$_LT_TAGVAR(postdep_objects, $1) $p"
+	 fi
+       fi
+       ;;
+
+    *) ;; # Ignore the rest.
+
+    esac
+  done
+
+  # Clean up.
+  rm -f a.out a.exe
+else
+  echo "libtool.m4: error: problem compiling $1 test program"
+fi
+
+$RM -f confest.$objext
+CFLAGS=$_lt_libdeps_save_CFLAGS
+
+# PORTME: override above test on systems where it is broken
+m4_if([$1], [CXX],
+[case $host_os in
+interix[[3-9]]*)
+  # Interix 3.5 installs completely hosed .la files for C++, so rather than
+  # hack all around it, let's just trust "g++" to DTRT.
+  _LT_TAGVAR(predep_objects,$1)=
+  _LT_TAGVAR(postdep_objects,$1)=
+  _LT_TAGVAR(postdeps,$1)=
+  ;;
+
+linux*)
+  case `$CC -V 2>&1 | sed 5q` in
+  *Sun\ C*)
+    # Sun C++ 5.9
+
+    # The more standards-conforming stlport4 library is
+    # incompatible with the Cstd library. Avoid specifying
+    # it if it's in CXXFLAGS. Ignore libCrun as
+    # -library=stlport4 depends on it.
+    case " $CXX $CXXFLAGS " in
+    *" -library=stlport4 "*)
+      solaris_use_stlport4=yes
+      ;;
+    esac
+
+    if test "$solaris_use_stlport4" != yes; then
+      _LT_TAGVAR(postdeps,$1)='-library=Cstd -library=Crun'
+    fi
+    ;;
+  esac
+  ;;
+
+solaris*)
+  case $cc_basename in
+  CC* | sunCC*)
+    # The more standards-conforming stlport4 library is
+    # incompatible with the Cstd library. Avoid specifying
+    # it if it's in CXXFLAGS. Ignore libCrun as
+    # -library=stlport4 depends on it.
+    case " $CXX $CXXFLAGS " in
+    *" -library=stlport4 "*)
+      solaris_use_stlport4=yes
+      ;;
+    esac
+
+    # Adding this requires a known-good setup of shared libraries for
+    # Sun compiler versions before 5.6, else PIC objects from an old
+    # archive will be linked into the output, leading to subtle bugs.
+    if test "$solaris_use_stlport4" != yes; then
+      _LT_TAGVAR(postdeps,$1)='-library=Cstd -library=Crun'
+    fi
+    ;;
+  esac
+  ;;
+esac
+])
+
+case " $_LT_TAGVAR(postdeps, $1) " in
+*" -lc "*) _LT_TAGVAR(archive_cmds_need_lc, $1)=no ;;
+esac
+ _LT_TAGVAR(compiler_lib_search_dirs, $1)=
+if test -n "${_LT_TAGVAR(compiler_lib_search_path, $1)}"; then
+ _LT_TAGVAR(compiler_lib_search_dirs, $1)=`echo " ${_LT_TAGVAR(compiler_lib_search_path, $1)}" | ${SED} -e 's! -L! !g' -e 's!^ !!'`
+fi
+_LT_TAGDECL([], [compiler_lib_search_dirs], [1],
+    [The directories searched by this compiler when creating a shared library])
+_LT_TAGDECL([], [predep_objects], [1],
+    [Dependencies to place before and after the objects being linked to
+    create a shared library])
+_LT_TAGDECL([], [postdep_objects], [1])
+_LT_TAGDECL([], [predeps], [1])
+_LT_TAGDECL([], [postdeps], [1])
+_LT_TAGDECL([], [compiler_lib_search_path], [1],
+    [The library search path used internally by the compiler when linking
+    a shared library])
+])# _LT_SYS_HIDDEN_LIBDEPS
+
+
+# _LT_LANG_F77_CONFIG([TAG])
+# --------------------------
+# Ensure that the configuration variables for a Fortran 77 compiler are
+# suitably defined.  These variables are subsequently used by _LT_CONFIG
+# to write the compiler configuration to `libtool'.
+m4_defun([_LT_LANG_F77_CONFIG],
+[AC_LANG_PUSH(Fortran 77)
+if test -z "$F77" || test "X$F77" = "Xno"; then
+  _lt_disable_F77=yes
+fi
+
+_LT_TAGVAR(archive_cmds_need_lc, $1)=no
+_LT_TAGVAR(allow_undefined_flag, $1)=
+_LT_TAGVAR(always_export_symbols, $1)=no
+_LT_TAGVAR(archive_expsym_cmds, $1)=
+_LT_TAGVAR(export_dynamic_flag_spec, $1)=
+_LT_TAGVAR(hardcode_direct, $1)=no
+_LT_TAGVAR(hardcode_direct_absolute, $1)=no
+_LT_TAGVAR(hardcode_libdir_flag_spec, $1)=
+_LT_TAGVAR(hardcode_libdir_separator, $1)=
+_LT_TAGVAR(hardcode_minus_L, $1)=no
+_LT_TAGVAR(hardcode_automatic, $1)=no
+_LT_TAGVAR(inherit_rpath, $1)=no
+_LT_TAGVAR(module_cmds, $1)=
+_LT_TAGVAR(module_expsym_cmds, $1)=
+_LT_TAGVAR(link_all_deplibs, $1)=unknown
+_LT_TAGVAR(old_archive_cmds, $1)=$old_archive_cmds
+_LT_TAGVAR(reload_flag, $1)=$reload_flag
+_LT_TAGVAR(reload_cmds, $1)=$reload_cmds
+_LT_TAGVAR(no_undefined_flag, $1)=
+_LT_TAGVAR(whole_archive_flag_spec, $1)=
+_LT_TAGVAR(enable_shared_with_static_runtimes, $1)=no
+
+# Source file extension for f77 test sources.
+ac_ext=f
+
+# Object file extension for compiled f77 test sources.
+objext=o
+_LT_TAGVAR(objext, $1)=$objext
+
+# No sense in running all these tests if we already determined that
+# the F77 compiler isn't working.  Some variables (like enable_shared)
+# are currently assumed to apply to all compilers on this platform,
+# and will be corrupted by setting them based on a non-working compiler.
+if test "$_lt_disable_F77" != yes; then
+  # Code to be used in simple compile tests
+  lt_simple_compile_test_code="\
+      subroutine t
+      return
+      end
+"
+
+  # Code to be used in simple link tests
+  lt_simple_link_test_code="\
+      program t
+      end
+"
+
+  # ltmain only uses $CC for tagged configurations so make sure $CC is set.
+  _LT_TAG_COMPILER
+
+  # save warnings/boilerplate of simple test code
+  _LT_COMPILER_BOILERPLATE
+  _LT_LINKER_BOILERPLATE
+
+  # Allow CC to be a program name with arguments.
+  lt_save_CC="$CC"
+  lt_save_GCC=$GCC
+  lt_save_CFLAGS=$CFLAGS
+  CC=${F77-"f77"}
+  CFLAGS=$FFLAGS
+  compiler=$CC
+  _LT_TAGVAR(compiler, $1)=$CC
+  _LT_CC_BASENAME([$compiler])
+  GCC=$G77
+  if test -n "$compiler"; then
+    AC_MSG_CHECKING([if libtool supports shared libraries])
+    AC_MSG_RESULT([$can_build_shared])
+
+    AC_MSG_CHECKING([whether to build shared libraries])
+    test "$can_build_shared" = "no" && enable_shared=no
+
+    # On AIX, shared libraries and static libraries use the same namespace, and
+    # are all built from PIC.
+    case $host_os in
+      aix3*)
+        test "$enable_shared" = yes && enable_static=no
+        if test -n "$RANLIB"; then
+          archive_cmds="$archive_cmds~\$RANLIB \$lib"
+          postinstall_cmds='$RANLIB $lib'
+        fi
+        ;;
+      aix[[4-9]]*)
+	if test "$host_cpu" != ia64 && test "$aix_use_runtimelinking" = no ; then
+	  test "$enable_shared" = yes && enable_static=no
+	fi
+        ;;
+    esac
+    AC_MSG_RESULT([$enable_shared])
+
+    AC_MSG_CHECKING([whether to build static libraries])
+    # Make sure either enable_shared or enable_static is yes.
+    test "$enable_shared" = yes || enable_static=yes
+    AC_MSG_RESULT([$enable_static])
+
+    _LT_TAGVAR(GCC, $1)="$G77"
+    _LT_TAGVAR(LD, $1)="$LD"
+
+    ## CAVEAT EMPTOR:
+    ## There is no encapsulation within the following macros, do not change
+    ## the running order or otherwise move them around unless you know exactly
+    ## what you are doing...
+    _LT_COMPILER_PIC($1)
+    _LT_COMPILER_C_O($1)
+    _LT_COMPILER_FILE_LOCKS($1)
+    _LT_LINKER_SHLIBS($1)
+    _LT_SYS_DYNAMIC_LINKER($1)
+    _LT_LINKER_HARDCODE_LIBPATH($1)
+
+    _LT_CONFIG($1)
+  fi # test -n "$compiler"
+
+  GCC=$lt_save_GCC
+  CC="$lt_save_CC"
+  CFLAGS="$lt_save_CFLAGS"
+fi # test "$_lt_disable_F77" != yes
+
+AC_LANG_POP
+])# _LT_LANG_F77_CONFIG
+
+
+# _LT_LANG_FC_CONFIG([TAG])
+# -------------------------
+# Ensure that the configuration variables for a Fortran compiler are
+# suitably defined.  These variables are subsequently used by _LT_CONFIG
+# to write the compiler configuration to `libtool'.
+m4_defun([_LT_LANG_FC_CONFIG],
+[AC_LANG_PUSH(Fortran)
+
+if test -z "$FC" || test "X$FC" = "Xno"; then
+  _lt_disable_FC=yes
+fi
+
+_LT_TAGVAR(archive_cmds_need_lc, $1)=no
+_LT_TAGVAR(allow_undefined_flag, $1)=
+_LT_TAGVAR(always_export_symbols, $1)=no
+_LT_TAGVAR(archive_expsym_cmds, $1)=
+_LT_TAGVAR(export_dynamic_flag_spec, $1)=
+_LT_TAGVAR(hardcode_direct, $1)=no
+_LT_TAGVAR(hardcode_direct_absolute, $1)=no
+_LT_TAGVAR(hardcode_libdir_flag_spec, $1)=
+_LT_TAGVAR(hardcode_libdir_separator, $1)=
+_LT_TAGVAR(hardcode_minus_L, $1)=no
+_LT_TAGVAR(hardcode_automatic, $1)=no
+_LT_TAGVAR(inherit_rpath, $1)=no
+_LT_TAGVAR(module_cmds, $1)=
+_LT_TAGVAR(module_expsym_cmds, $1)=
+_LT_TAGVAR(link_all_deplibs, $1)=unknown
+_LT_TAGVAR(old_archive_cmds, $1)=$old_archive_cmds
+_LT_TAGVAR(reload_flag, $1)=$reload_flag
+_LT_TAGVAR(reload_cmds, $1)=$reload_cmds
+_LT_TAGVAR(no_undefined_flag, $1)=
+_LT_TAGVAR(whole_archive_flag_spec, $1)=
+_LT_TAGVAR(enable_shared_with_static_runtimes, $1)=no
+
+# Source file extension for fc test sources.
+ac_ext=${ac_fc_srcext-f}
+
+# Object file extension for compiled fc test sources.
+objext=o
+_LT_TAGVAR(objext, $1)=$objext
+
+# No sense in running all these tests if we already determined that
+# the FC compiler isn't working.  Some variables (like enable_shared)
+# are currently assumed to apply to all compilers on this platform,
+# and will be corrupted by setting them based on a non-working compiler.
+if test "$_lt_disable_FC" != yes; then
+  # Code to be used in simple compile tests
+  lt_simple_compile_test_code="\
+      subroutine t
+      return
+      end
+"
+
+  # Code to be used in simple link tests
+  lt_simple_link_test_code="\
+      program t
+      end
+"
+
+  # ltmain only uses $CC for tagged configurations so make sure $CC is set.
+  _LT_TAG_COMPILER
+
+  # save warnings/boilerplate of simple test code
+  _LT_COMPILER_BOILERPLATE
+  _LT_LINKER_BOILERPLATE
+
+  # Allow CC to be a program name with arguments.
+  lt_save_CC="$CC"
+  lt_save_GCC=$GCC
+  lt_save_CFLAGS=$CFLAGS
+  CC=${FC-"f95"}
+  CFLAGS=$FCFLAGS
+  compiler=$CC
+  GCC=$ac_cv_fc_compiler_gnu
+
+  _LT_TAGVAR(compiler, $1)=$CC
+  _LT_CC_BASENAME([$compiler])
+
+  if test -n "$compiler"; then
+    AC_MSG_CHECKING([if libtool supports shared libraries])
+    AC_MSG_RESULT([$can_build_shared])
+
+    AC_MSG_CHECKING([whether to build shared libraries])
+    test "$can_build_shared" = "no" && enable_shared=no
+
+    # On AIX, shared libraries and static libraries use the same namespace, and
+    # are all built from PIC.
+    case $host_os in
+      aix3*)
+        test "$enable_shared" = yes && enable_static=no
+        if test -n "$RANLIB"; then
+          archive_cmds="$archive_cmds~\$RANLIB \$lib"
+          postinstall_cmds='$RANLIB $lib'
+        fi
+        ;;
+      aix[[4-9]]*)
+	if test "$host_cpu" != ia64 && test "$aix_use_runtimelinking" = no ; then
+	  test "$enable_shared" = yes && enable_static=no
+	fi
+        ;;
+    esac
+    AC_MSG_RESULT([$enable_shared])
+
+    AC_MSG_CHECKING([whether to build static libraries])
+    # Make sure either enable_shared or enable_static is yes.
+    test "$enable_shared" = yes || enable_static=yes
+    AC_MSG_RESULT([$enable_static])
+
+    _LT_TAGVAR(GCC, $1)="$ac_cv_fc_compiler_gnu"
+    _LT_TAGVAR(LD, $1)="$LD"
+
+    ## CAVEAT EMPTOR:
+    ## There is no encapsulation within the following macros, do not change
+    ## the running order or otherwise move them around unless you know exactly
+    ## what you are doing...
+    _LT_SYS_HIDDEN_LIBDEPS($1)
+    _LT_COMPILER_PIC($1)
+    _LT_COMPILER_C_O($1)
+    _LT_COMPILER_FILE_LOCKS($1)
+    _LT_LINKER_SHLIBS($1)
+    _LT_SYS_DYNAMIC_LINKER($1)
+    _LT_LINKER_HARDCODE_LIBPATH($1)
+
+    _LT_CONFIG($1)
+  fi # test -n "$compiler"
+
+  GCC=$lt_save_GCC
+  CC=$lt_save_CC
+  CFLAGS=$lt_save_CFLAGS
+fi # test "$_lt_disable_FC" != yes
+
+AC_LANG_POP
+])# _LT_LANG_FC_CONFIG
+
+
+# _LT_LANG_GCJ_CONFIG([TAG])
+# --------------------------
+# Ensure that the configuration variables for the GNU Java Compiler compiler
+# are suitably defined.  These variables are subsequently used by _LT_CONFIG
+# to write the compiler configuration to `libtool'.
+m4_defun([_LT_LANG_GCJ_CONFIG],
+[AC_REQUIRE([LT_PROG_GCJ])dnl
+AC_LANG_SAVE
+
+# Source file extension for Java test sources.
+ac_ext=java
+
+# Object file extension for compiled Java test sources.
+objext=o
+_LT_TAGVAR(objext, $1)=$objext
+
+# Code to be used in simple compile tests
+lt_simple_compile_test_code="class foo {}"
+
+# Code to be used in simple link tests
+lt_simple_link_test_code='public class conftest { public static void main(String[[]] argv) {}; }'
+
+# ltmain only uses $CC for tagged configurations so make sure $CC is set.
+_LT_TAG_COMPILER
+
+# save warnings/boilerplate of simple test code
+_LT_COMPILER_BOILERPLATE
+_LT_LINKER_BOILERPLATE
+
+# Allow CC to be a program name with arguments.
+lt_save_CC=$CC
+lt_save_CFLAGS=$CFLAGS
+lt_save_GCC=$GCC
+GCC=yes
+CC=${GCJ-"gcj"}
+CFLAGS=$GCJFLAGS
+compiler=$CC
+_LT_TAGVAR(compiler, $1)=$CC
+_LT_TAGVAR(LD, $1)="$LD"
+_LT_CC_BASENAME([$compiler])
+
+# GCJ did not exist at the time GCC didn't implicitly link libc in.
+_LT_TAGVAR(archive_cmds_need_lc, $1)=no
+
+_LT_TAGVAR(old_archive_cmds, $1)=$old_archive_cmds
+_LT_TAGVAR(reload_flag, $1)=$reload_flag
+_LT_TAGVAR(reload_cmds, $1)=$reload_cmds
+
+if test -n "$compiler"; then
+  _LT_COMPILER_NO_RTTI($1)
+  _LT_COMPILER_PIC($1)
+  _LT_COMPILER_C_O($1)
+  _LT_COMPILER_FILE_LOCKS($1)
+  _LT_LINKER_SHLIBS($1)
+  _LT_LINKER_HARDCODE_LIBPATH($1)
+
+  _LT_CONFIG($1)
+fi
+
+AC_LANG_RESTORE
+
+GCC=$lt_save_GCC
+CC=$lt_save_CC
+CFLAGS=$lt_save_CFLAGS
+])# _LT_LANG_GCJ_CONFIG
+
+
+# _LT_LANG_GO_CONFIG([TAG])
+# --------------------------
+# Ensure that the configuration variables for the GNU Go compiler
+# are suitably defined.  These variables are subsequently used by _LT_CONFIG
+# to write the compiler configuration to `libtool'.
+m4_defun([_LT_LANG_GO_CONFIG],
+[AC_REQUIRE([LT_PROG_GO])dnl
+AC_LANG_SAVE
+
+# Source file extension for Go test sources.
+ac_ext=go
+
+# Object file extension for compiled Go test sources.
+objext=o
+_LT_TAGVAR(objext, $1)=$objext
+
+# Code to be used in simple compile tests
+lt_simple_compile_test_code="package main; func main() { }"
+
+# Code to be used in simple link tests
+lt_simple_link_test_code='package main; func main() { }'
+
+# ltmain only uses $CC for tagged configurations so make sure $CC is set.
+_LT_TAG_COMPILER
+
+# save warnings/boilerplate of simple test code
+_LT_COMPILER_BOILERPLATE
+_LT_LINKER_BOILERPLATE
+
+# Allow CC to be a program name with arguments.
+lt_save_CC=$CC
+lt_save_CFLAGS=$CFLAGS
+lt_save_GCC=$GCC
+GCC=yes
+CC=${GOC-"gccgo"}
+CFLAGS=$GOFLAGS
+compiler=$CC
+_LT_TAGVAR(compiler, $1)=$CC
+_LT_TAGVAR(LD, $1)="$LD"
+_LT_CC_BASENAME([$compiler])
+
+# Go did not exist at the time GCC didn't implicitly link libc in.
+_LT_TAGVAR(archive_cmds_need_lc, $1)=no
+
+_LT_TAGVAR(old_archive_cmds, $1)=$old_archive_cmds
+_LT_TAGVAR(reload_flag, $1)=$reload_flag
+_LT_TAGVAR(reload_cmds, $1)=$reload_cmds
+
+if test -n "$compiler"; then
+  _LT_COMPILER_NO_RTTI($1)
+  _LT_COMPILER_PIC($1)
+  _LT_COMPILER_C_O($1)
+  _LT_COMPILER_FILE_LOCKS($1)
+  _LT_LINKER_SHLIBS($1)
+  _LT_LINKER_HARDCODE_LIBPATH($1)
+
+  _LT_CONFIG($1)
+fi
+
+AC_LANG_RESTORE
+
+GCC=$lt_save_GCC
+CC=$lt_save_CC
+CFLAGS=$lt_save_CFLAGS
+])# _LT_LANG_GO_CONFIG
+
+
+# _LT_LANG_RC_CONFIG([TAG])
+# -------------------------
+# Ensure that the configuration variables for the Windows resource compiler
+# are suitably defined.  These variables are subsequently used by _LT_CONFIG
+# to write the compiler configuration to `libtool'.
+m4_defun([_LT_LANG_RC_CONFIG],
+[AC_REQUIRE([LT_PROG_RC])dnl
+AC_LANG_SAVE
+
+# Source file extension for RC test sources.
+ac_ext=rc
+
+# Object file extension for compiled RC test sources.
+objext=o
+_LT_TAGVAR(objext, $1)=$objext
+
+# Code to be used in simple compile tests
+lt_simple_compile_test_code='sample MENU { MENUITEM "&Soup", 100, CHECKED }'
+
+# Code to be used in simple link tests
+lt_simple_link_test_code="$lt_simple_compile_test_code"
+
+# ltmain only uses $CC for tagged configurations so make sure $CC is set.
+_LT_TAG_COMPILER
+
+# save warnings/boilerplate of simple test code
+_LT_COMPILER_BOILERPLATE
+_LT_LINKER_BOILERPLATE
+
+# Allow CC to be a program name with arguments.
+lt_save_CC="$CC"
+lt_save_CFLAGS=$CFLAGS
+lt_save_GCC=$GCC
+GCC=
+CC=${RC-"windres"}
+CFLAGS=
+compiler=$CC
+_LT_TAGVAR(compiler, $1)=$CC
+_LT_CC_BASENAME([$compiler])
+_LT_TAGVAR(lt_cv_prog_compiler_c_o, $1)=yes
+
+if test -n "$compiler"; then
+  :
+  _LT_CONFIG($1)
+fi
+
+GCC=$lt_save_GCC
+AC_LANG_RESTORE
+CC=$lt_save_CC
+CFLAGS=$lt_save_CFLAGS
+])# _LT_LANG_RC_CONFIG
+
+
+# LT_PROG_GCJ
+# -----------
+AC_DEFUN([LT_PROG_GCJ],
+[m4_ifdef([AC_PROG_GCJ], [AC_PROG_GCJ],
+  [m4_ifdef([A][M_PROG_GCJ], [A][M_PROG_GCJ],
+    [AC_CHECK_TOOL(GCJ, gcj,)
+      test "x${GCJFLAGS+set}" = xset || GCJFLAGS="-g -O2"
+      AC_SUBST(GCJFLAGS)])])[]dnl
+])
+
+# Old name:
+AU_ALIAS([LT_AC_PROG_GCJ], [LT_PROG_GCJ])
+dnl aclocal-1.4 backwards compatibility:
+dnl AC_DEFUN([LT_AC_PROG_GCJ], [])
+
+
+# LT_PROG_GO
+# ----------
+AC_DEFUN([LT_PROG_GO],
+[AC_CHECK_TOOL(GOC, gccgo,)
+])
+
+
+# LT_PROG_RC
+# ----------
+AC_DEFUN([LT_PROG_RC],
+[AC_CHECK_TOOL(RC, windres,)
+])
+
+# Old name:
+AU_ALIAS([LT_AC_PROG_RC], [LT_PROG_RC])
+dnl aclocal-1.4 backwards compatibility:
+dnl AC_DEFUN([LT_AC_PROG_RC], [])
+
+
+# _LT_DECL_EGREP
+# --------------
+# If we don't have a new enough Autoconf to choose the best grep
+# available, choose the one first in the user's PATH.
+m4_defun([_LT_DECL_EGREP],
+[AC_REQUIRE([AC_PROG_EGREP])dnl
+AC_REQUIRE([AC_PROG_FGREP])dnl
+test -z "$GREP" && GREP=grep
+_LT_DECL([], [GREP], [1], [A grep program that handles long lines])
+_LT_DECL([], [EGREP], [1], [An ERE matcher])
+_LT_DECL([], [FGREP], [1], [A literal string matcher])
+dnl Non-bleeding-edge autoconf doesn't subst GREP, so do it here too
+AC_SUBST([GREP])
+])
+
+
+# _LT_DECL_OBJDUMP
+# --------------
+# If we don't have a new enough Autoconf to choose the best objdump
+# available, choose the one first in the user's PATH.
+m4_defun([_LT_DECL_OBJDUMP],
+[AC_CHECK_TOOL(OBJDUMP, objdump, false)
+test -z "$OBJDUMP" && OBJDUMP=objdump
+_LT_DECL([], [OBJDUMP], [1], [An object symbol dumper])
+AC_SUBST([OBJDUMP])
+])
+
+# _LT_DECL_DLLTOOL
+# ----------------
+# Ensure DLLTOOL variable is set.
+m4_defun([_LT_DECL_DLLTOOL],
+[AC_CHECK_TOOL(DLLTOOL, dlltool, false)
+test -z "$DLLTOOL" && DLLTOOL=dlltool
+_LT_DECL([], [DLLTOOL], [1], [DLL creation program])
+AC_SUBST([DLLTOOL])
+])
+
+# _LT_DECL_SED
+# ------------
+# Check for a fully-functional sed program, that truncates
+# as few characters as possible.  Prefer GNU sed if found.
+m4_defun([_LT_DECL_SED],
+[AC_PROG_SED
+test -z "$SED" && SED=sed
+Xsed="$SED -e 1s/^X//"
+_LT_DECL([], [SED], [1], [A sed program that does not truncate output])
+_LT_DECL([], [Xsed], ["\$SED -e 1s/^X//"],
+    [Sed that helps us avoid accidentally triggering echo(1) options like -n])
+])# _LT_DECL_SED
+
+m4_ifndef([AC_PROG_SED], [
+# NOTE: This macro has been submitted for inclusion into   #
+#  GNU Autoconf as AC_PROG_SED.  When it is available in   #
+#  a released version of Autoconf we should remove this    #
+#  macro and use it instead.                               #
+
+m4_defun([AC_PROG_SED],
+[AC_MSG_CHECKING([for a sed that does not truncate output])
+AC_CACHE_VAL(lt_cv_path_SED,
+[# Loop through the user's path and test for sed and gsed.
+# Then use that list of sed's as ones to test for truncation.
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  for lt_ac_prog in sed gsed; do
+    for ac_exec_ext in '' $ac_executable_extensions; do
+      if $as_executable_p "$as_dir/$lt_ac_prog$ac_exec_ext"; then
+        lt_ac_sed_list="$lt_ac_sed_list $as_dir/$lt_ac_prog$ac_exec_ext"
+      fi
+    done
+  done
+done
+IFS=$as_save_IFS
+lt_ac_max=0
+lt_ac_count=0
+# Add /usr/xpg4/bin/sed as it is typically found on Solaris
+# along with /bin/sed that truncates output.
+for lt_ac_sed in $lt_ac_sed_list /usr/xpg4/bin/sed; do
+  test ! -f $lt_ac_sed && continue
+  cat /dev/null > conftest.in
+  lt_ac_count=0
+  echo $ECHO_N "0123456789$ECHO_C" >conftest.in
+  # Check for GNU sed and select it if it is found.
+  if "$lt_ac_sed" --version 2>&1 < /dev/null | grep 'GNU' > /dev/null; then
+    lt_cv_path_SED=$lt_ac_sed
+    break
+  fi
+  while true; do
+    cat conftest.in conftest.in >conftest.tmp
+    mv conftest.tmp conftest.in
+    cp conftest.in conftest.nl
+    echo >>conftest.nl
+    $lt_ac_sed -e 's/a$//' < conftest.nl >conftest.out || break
+    cmp -s conftest.out conftest.nl || break
+    # 10000 chars as input seems more than enough
+    test $lt_ac_count -gt 10 && break
+    lt_ac_count=`expr $lt_ac_count + 1`
+    if test $lt_ac_count -gt $lt_ac_max; then
+      lt_ac_max=$lt_ac_count
+      lt_cv_path_SED=$lt_ac_sed
+    fi
+  done
+done
+])
+SED=$lt_cv_path_SED
+AC_SUBST([SED])
+AC_MSG_RESULT([$SED])
+])#AC_PROG_SED
+])#m4_ifndef
+
+# Old name:
+AU_ALIAS([LT_AC_PROG_SED], [AC_PROG_SED])
+dnl aclocal-1.4 backwards compatibility:
+dnl AC_DEFUN([LT_AC_PROG_SED], [])
+
+
+# _LT_CHECK_SHELL_FEATURES
+# ------------------------
+# Find out whether the shell is Bourne or XSI compatible,
+# or has some other useful features.
+m4_defun([_LT_CHECK_SHELL_FEATURES],
+[AC_MSG_CHECKING([whether the shell understands some XSI constructs])
+# Try some XSI features
+xsi_shell=no
+( _lt_dummy="a/b/c"
+  test "${_lt_dummy##*/},${_lt_dummy%/*},${_lt_dummy#??}"${_lt_dummy%"$_lt_dummy"}, \
+      = c,a/b,b/c, \
+    && eval 'test $(( 1 + 1 )) -eq 2 \
+    && test "${#_lt_dummy}" -eq 5' ) >/dev/null 2>&1 \
+  && xsi_shell=yes
+AC_MSG_RESULT([$xsi_shell])
+_LT_CONFIG_LIBTOOL_INIT([xsi_shell='$xsi_shell'])
+
+AC_MSG_CHECKING([whether the shell understands "+="])
+lt_shell_append=no
+( foo=bar; set foo baz; eval "$[1]+=\$[2]" && test "$foo" = barbaz ) \
+    >/dev/null 2>&1 \
+  && lt_shell_append=yes
+AC_MSG_RESULT([$lt_shell_append])
+_LT_CONFIG_LIBTOOL_INIT([lt_shell_append='$lt_shell_append'])
+
+if ( (MAIL=60; unset MAIL) || exit) >/dev/null 2>&1; then
+  lt_unset=unset
+else
+  lt_unset=false
+fi
+_LT_DECL([], [lt_unset], [0], [whether the shell understands "unset"])dnl
+
+# test EBCDIC or ASCII
+case `echo X|tr X '\101'` in
+ A) # ASCII based system
+    # \n is not interpreted correctly by Solaris 8 /usr/ucb/tr
+  lt_SP2NL='tr \040 \012'
+  lt_NL2SP='tr \015\012 \040\040'
+  ;;
+ *) # EBCDIC based system
+  lt_SP2NL='tr \100 \n'
+  lt_NL2SP='tr \r\n \100\100'
+  ;;
+esac
+_LT_DECL([SP2NL], [lt_SP2NL], [1], [turn spaces into newlines])dnl
+_LT_DECL([NL2SP], [lt_NL2SP], [1], [turn newlines into spaces])dnl
+])# _LT_CHECK_SHELL_FEATURES
+
+
+# _LT_PROG_FUNCTION_REPLACE (FUNCNAME, REPLACEMENT-BODY)
+# ------------------------------------------------------
+# In `$cfgfile', look for function FUNCNAME delimited by `^FUNCNAME ()$' and
+# '^} FUNCNAME ', and replace its body with REPLACEMENT-BODY.
+m4_defun([_LT_PROG_FUNCTION_REPLACE],
+[dnl {
+sed -e '/^$1 ()$/,/^} # $1 /c\
+$1 ()\
+{\
+m4_bpatsubsts([$2], [$], [\\], [^\([	 ]\)], [\\\1])
+} # Extended-shell $1 implementation' "$cfgfile" > $cfgfile.tmp \
+  && mv -f "$cfgfile.tmp" "$cfgfile" \
+    || (rm -f "$cfgfile" && cp "$cfgfile.tmp" "$cfgfile" && rm -f "$cfgfile.tmp")
+test 0 -eq $? || _lt_function_replace_fail=:
+])
+
+
+# _LT_PROG_REPLACE_SHELLFNS
+# -------------------------
+# Replace existing portable implementations of several shell functions with
+# equivalent extended shell implementations where those features are available..
+m4_defun([_LT_PROG_REPLACE_SHELLFNS],
+[if test x"$xsi_shell" = xyes; then
+  _LT_PROG_FUNCTION_REPLACE([func_dirname], [dnl
+    case ${1} in
+      */*) func_dirname_result="${1%/*}${2}" ;;
+      *  ) func_dirname_result="${3}" ;;
+    esac])
+
+  _LT_PROG_FUNCTION_REPLACE([func_basename], [dnl
+    func_basename_result="${1##*/}"])
+
+  _LT_PROG_FUNCTION_REPLACE([func_dirname_and_basename], [dnl
+    case ${1} in
+      */*) func_dirname_result="${1%/*}${2}" ;;
+      *  ) func_dirname_result="${3}" ;;
+    esac
+    func_basename_result="${1##*/}"])
+
+  _LT_PROG_FUNCTION_REPLACE([func_stripname], [dnl
+    # pdksh 5.2.14 does not do ${X%$Y} correctly if both X and Y are
+    # positional parameters, so assign one to ordinary parameter first.
+    func_stripname_result=${3}
+    func_stripname_result=${func_stripname_result#"${1}"}
+    func_stripname_result=${func_stripname_result%"${2}"}])
+
+  _LT_PROG_FUNCTION_REPLACE([func_split_long_opt], [dnl
+    func_split_long_opt_name=${1%%=*}
+    func_split_long_opt_arg=${1#*=}])
+
+  _LT_PROG_FUNCTION_REPLACE([func_split_short_opt], [dnl
+    func_split_short_opt_arg=${1#??}
+    func_split_short_opt_name=${1%"$func_split_short_opt_arg"}])
+
+  _LT_PROG_FUNCTION_REPLACE([func_lo2o], [dnl
+    case ${1} in
+      *.lo) func_lo2o_result=${1%.lo}.${objext} ;;
+      *)    func_lo2o_result=${1} ;;
+    esac])
+
+  _LT_PROG_FUNCTION_REPLACE([func_xform], [    func_xform_result=${1%.*}.lo])
+
+  _LT_PROG_FUNCTION_REPLACE([func_arith], [    func_arith_result=$(( $[*] ))])
+
+  _LT_PROG_FUNCTION_REPLACE([func_len], [    func_len_result=${#1}])
+fi
+
+if test x"$lt_shell_append" = xyes; then
+  _LT_PROG_FUNCTION_REPLACE([func_append], [    eval "${1}+=\\${2}"])
+
+  _LT_PROG_FUNCTION_REPLACE([func_append_quoted], [dnl
+    func_quote_for_eval "${2}"
+dnl m4 expansion turns \\\\ into \\, and then the shell eval turns that into \
+    eval "${1}+=\\\\ \\$func_quote_for_eval_result"])
+
+  # Save a `func_append' function call where possible by direct use of '+='
+  sed -e 's%func_append \([[a-zA-Z_]]\{1,\}\) "%\1+="%g' $cfgfile > $cfgfile.tmp \
+    && mv -f "$cfgfile.tmp" "$cfgfile" \
+      || (rm -f "$cfgfile" && cp "$cfgfile.tmp" "$cfgfile" && rm -f "$cfgfile.tmp")
+  test 0 -eq $? || _lt_function_replace_fail=:
+else
+  # Save a `func_append' function call even when '+=' is not available
+  sed -e 's%func_append \([[a-zA-Z_]]\{1,\}\) "%\1="$\1%g' $cfgfile > $cfgfile.tmp \
+    && mv -f "$cfgfile.tmp" "$cfgfile" \
+      || (rm -f "$cfgfile" && cp "$cfgfile.tmp" "$cfgfile" && rm -f "$cfgfile.tmp")
+  test 0 -eq $? || _lt_function_replace_fail=:
+fi
+
+if test x"$_lt_function_replace_fail" = x":"; then
+  AC_MSG_WARN([Unable to substitute extended shell functions in $ofile])
+fi
+])
+
+# _LT_PATH_CONVERSION_FUNCTIONS
+# -----------------------------
+# Determine which file name conversion functions should be used by
+# func_to_host_file (and, implicitly, by func_to_host_path).  These are needed
+# for certain cross-compile configurations and native mingw.
+m4_defun([_LT_PATH_CONVERSION_FUNCTIONS],
+[AC_REQUIRE([AC_CANONICAL_HOST])dnl
+AC_REQUIRE([AC_CANONICAL_BUILD])dnl
+AC_MSG_CHECKING([how to convert $build file names to $host format])
+AC_CACHE_VAL(lt_cv_to_host_file_cmd,
+[case $host in
+  *-*-mingw* )
+    case $build in
+      *-*-mingw* ) # actually msys
+        lt_cv_to_host_file_cmd=func_convert_file_msys_to_w32
+        ;;
+      *-*-cygwin* )
+        lt_cv_to_host_file_cmd=func_convert_file_cygwin_to_w32
+        ;;
+      * ) # otherwise, assume *nix
+        lt_cv_to_host_file_cmd=func_convert_file_nix_to_w32
+        ;;
+    esac
+    ;;
+  *-*-cygwin* )
+    case $build in
+      *-*-mingw* ) # actually msys
+        lt_cv_to_host_file_cmd=func_convert_file_msys_to_cygwin
+        ;;
+      *-*-cygwin* )
+        lt_cv_to_host_file_cmd=func_convert_file_noop
+        ;;
+      * ) # otherwise, assume *nix
+        lt_cv_to_host_file_cmd=func_convert_file_nix_to_cygwin
+        ;;
+    esac
+    ;;
+  * ) # unhandled hosts (and "normal" native builds)
+    lt_cv_to_host_file_cmd=func_convert_file_noop
+    ;;
+esac
+])
+to_host_file_cmd=$lt_cv_to_host_file_cmd
+AC_MSG_RESULT([$lt_cv_to_host_file_cmd])
+_LT_DECL([to_host_file_cmd], [lt_cv_to_host_file_cmd],
+         [0], [convert $build file names to $host format])dnl
+
+AC_MSG_CHECKING([how to convert $build file names to toolchain format])
+AC_CACHE_VAL(lt_cv_to_tool_file_cmd,
+[#assume ordinary cross tools, or native build.
+lt_cv_to_tool_file_cmd=func_convert_file_noop
+case $host in
+  *-*-mingw* )
+    case $build in
+      *-*-mingw* ) # actually msys
+        lt_cv_to_tool_file_cmd=func_convert_file_msys_to_w32
+        ;;
+    esac
+    ;;
+esac
+])
+to_tool_file_cmd=$lt_cv_to_tool_file_cmd
+AC_MSG_RESULT([$lt_cv_to_tool_file_cmd])
+_LT_DECL([to_tool_file_cmd], [lt_cv_to_tool_file_cmd],
+         [0], [convert $build files to toolchain format])dnl
+])# _LT_PATH_CONVERSION_FUNCTIONS
+
+# Helper functions for option handling.                    -*- Autoconf -*-
+#
+#   Copyright (C) 2004, 2005, 2007, 2008, 2009 Free Software Foundation,
+#   Inc.
+#   Written by Gary V. Vaughan, 2004
+#
+# This file is free software; the Free Software Foundation gives
+# unlimited permission to copy and/or distribute it, with or without
+# modifications, as long as this notice is preserved.
+
+# serial 7 ltoptions.m4
+
+# This is to help aclocal find these macros, as it can't see m4_define.
+AC_DEFUN([LTOPTIONS_VERSION], [m4_if([1])])
+
+
+# _LT_MANGLE_OPTION(MACRO-NAME, OPTION-NAME)
+# ------------------------------------------
+m4_define([_LT_MANGLE_OPTION],
+[[_LT_OPTION_]m4_bpatsubst($1__$2, [[^a-zA-Z0-9_]], [_])])
+
+
+# _LT_SET_OPTION(MACRO-NAME, OPTION-NAME)
+# ---------------------------------------
+# Set option OPTION-NAME for macro MACRO-NAME, and if there is a
+# matching handler defined, dispatch to it.  Other OPTION-NAMEs are
+# saved as a flag.
+m4_define([_LT_SET_OPTION],
+[m4_define(_LT_MANGLE_OPTION([$1], [$2]))dnl
+m4_ifdef(_LT_MANGLE_DEFUN([$1], [$2]),
+        _LT_MANGLE_DEFUN([$1], [$2]),
+    [m4_warning([Unknown $1 option `$2'])])[]dnl
+])
+
+
+# _LT_IF_OPTION(MACRO-NAME, OPTION-NAME, IF-SET, [IF-NOT-SET])
+# ------------------------------------------------------------
+# Execute IF-SET if OPTION is set, IF-NOT-SET otherwise.
+m4_define([_LT_IF_OPTION],
+[m4_ifdef(_LT_MANGLE_OPTION([$1], [$2]), [$3], [$4])])
+
+
+# _LT_UNLESS_OPTIONS(MACRO-NAME, OPTION-LIST, IF-NOT-SET)
+# -------------------------------------------------------
+# Execute IF-NOT-SET unless all options in OPTION-LIST for MACRO-NAME
+# are set.
+m4_define([_LT_UNLESS_OPTIONS],
+[m4_foreach([_LT_Option], m4_split(m4_normalize([$2])),
+	    [m4_ifdef(_LT_MANGLE_OPTION([$1], _LT_Option),
+		      [m4_define([$0_found])])])[]dnl
+m4_ifdef([$0_found], [m4_undefine([$0_found])], [$3
+])[]dnl
+])
+
+
+# _LT_SET_OPTIONS(MACRO-NAME, OPTION-LIST)
+# ----------------------------------------
+# OPTION-LIST is a space-separated list of Libtool options associated
+# with MACRO-NAME.  If any OPTION has a matching handler declared with
+# LT_OPTION_DEFINE, dispatch to that macro; otherwise complain about
+# the unknown option and exit.
+m4_defun([_LT_SET_OPTIONS],
+[# Set options
+m4_foreach([_LT_Option], m4_split(m4_normalize([$2])),
+    [_LT_SET_OPTION([$1], _LT_Option)])
+
+m4_if([$1],[LT_INIT],[
+  dnl
+  dnl Simply set some default values (i.e off) if boolean options were not
+  dnl specified:
+  _LT_UNLESS_OPTIONS([LT_INIT], [dlopen], [enable_dlopen=no
+  ])
+  _LT_UNLESS_OPTIONS([LT_INIT], [win32-dll], [enable_win32_dll=no
+  ])
+  dnl
+  dnl If no reference was made to various pairs of opposing options, then
+  dnl we run the default mode handler for the pair.  For example, if neither
+  dnl `shared' nor `disable-shared' was passed, we enable building of shared
+  dnl archives by default:
+  _LT_UNLESS_OPTIONS([LT_INIT], [shared disable-shared], [_LT_ENABLE_SHARED])
+  _LT_UNLESS_OPTIONS([LT_INIT], [static disable-static], [_LT_ENABLE_STATIC])
+  _LT_UNLESS_OPTIONS([LT_INIT], [pic-only no-pic], [_LT_WITH_PIC])
+  _LT_UNLESS_OPTIONS([LT_INIT], [fast-install disable-fast-install],
+  		   [_LT_ENABLE_FAST_INSTALL])
+  ])
+])# _LT_SET_OPTIONS
+
+
+
+# _LT_MANGLE_DEFUN(MACRO-NAME, OPTION-NAME)
+# -----------------------------------------
+m4_define([_LT_MANGLE_DEFUN],
+[[_LT_OPTION_DEFUN_]m4_bpatsubst(m4_toupper([$1__$2]), [[^A-Z0-9_]], [_])])
+
+
+# LT_OPTION_DEFINE(MACRO-NAME, OPTION-NAME, CODE)
+# -----------------------------------------------
+m4_define([LT_OPTION_DEFINE],
+[m4_define(_LT_MANGLE_DEFUN([$1], [$2]), [$3])[]dnl
+])# LT_OPTION_DEFINE
+
+
+# dlopen
+# ------
+LT_OPTION_DEFINE([LT_INIT], [dlopen], [enable_dlopen=yes
+])
+
+AU_DEFUN([AC_LIBTOOL_DLOPEN],
+[_LT_SET_OPTION([LT_INIT], [dlopen])
+AC_DIAGNOSE([obsolete],
+[$0: Remove this warning and the call to _LT_SET_OPTION when you
+put the `dlopen' option into LT_INIT's first parameter.])
+])
+
+dnl aclocal-1.4 backwards compatibility:
+dnl AC_DEFUN([AC_LIBTOOL_DLOPEN], [])
+
+
+# win32-dll
+# ---------
+# Declare package support for building win32 dll's.
+LT_OPTION_DEFINE([LT_INIT], [win32-dll],
+[enable_win32_dll=yes
+
+case $host in
+*-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-cegcc*)
+  AC_CHECK_TOOL(AS, as, false)
+  AC_CHECK_TOOL(DLLTOOL, dlltool, false)
+  AC_CHECK_TOOL(OBJDUMP, objdump, false)
+  ;;
+esac
+
+test -z "$AS" && AS=as
+_LT_DECL([], [AS],      [1], [Assembler program])dnl
+
+test -z "$DLLTOOL" && DLLTOOL=dlltool
+_LT_DECL([], [DLLTOOL], [1], [DLL creation program])dnl
+
+test -z "$OBJDUMP" && OBJDUMP=objdump
+_LT_DECL([], [OBJDUMP], [1], [Object dumper program])dnl
+])# win32-dll
+
+AU_DEFUN([AC_LIBTOOL_WIN32_DLL],
+[AC_REQUIRE([AC_CANONICAL_HOST])dnl
+_LT_SET_OPTION([LT_INIT], [win32-dll])
+AC_DIAGNOSE([obsolete],
+[$0: Remove this warning and the call to _LT_SET_OPTION when you
+put the `win32-dll' option into LT_INIT's first parameter.])
+])
+
+dnl aclocal-1.4 backwards compatibility:
+dnl AC_DEFUN([AC_LIBTOOL_WIN32_DLL], [])
+
+
+# _LT_ENABLE_SHARED([DEFAULT])
+# ----------------------------
+# implement the --enable-shared flag, and supports the `shared' and
+# `disable-shared' LT_INIT options.
+# DEFAULT is either `yes' or `no'.  If omitted, it defaults to `yes'.
+m4_define([_LT_ENABLE_SHARED],
+[m4_define([_LT_ENABLE_SHARED_DEFAULT], [m4_if($1, no, no, yes)])dnl
+AC_ARG_ENABLE([shared],
+    [AS_HELP_STRING([--enable-shared@<:@=PKGS@:>@],
+	[build shared libraries @<:@default=]_LT_ENABLE_SHARED_DEFAULT[@:>@])],
+    [p=${PACKAGE-default}
+    case $enableval in
+    yes) enable_shared=yes ;;
+    no) enable_shared=no ;;
+    *)
+      enable_shared=no
+      # Look at the argument we got.  We use all the common list separators.
+      lt_save_ifs="$IFS"; IFS="${IFS}$PATH_SEPARATOR,"
+      for pkg in $enableval; do
+	IFS="$lt_save_ifs"
+	if test "X$pkg" = "X$p"; then
+	  enable_shared=yes
+	fi
+      done
+      IFS="$lt_save_ifs"
+      ;;
+    esac],
+    [enable_shared=]_LT_ENABLE_SHARED_DEFAULT)
+
+    _LT_DECL([build_libtool_libs], [enable_shared], [0],
+	[Whether or not to build shared libraries])
+])# _LT_ENABLE_SHARED
+
+LT_OPTION_DEFINE([LT_INIT], [shared], [_LT_ENABLE_SHARED([yes])])
+LT_OPTION_DEFINE([LT_INIT], [disable-shared], [_LT_ENABLE_SHARED([no])])
+
+# Old names:
+AC_DEFUN([AC_ENABLE_SHARED],
+[_LT_SET_OPTION([LT_INIT], m4_if([$1], [no], [disable-])[shared])
+])
+
+AC_DEFUN([AC_DISABLE_SHARED],
+[_LT_SET_OPTION([LT_INIT], [disable-shared])
+])
+
+AU_DEFUN([AM_ENABLE_SHARED], [AC_ENABLE_SHARED($@)])
+AU_DEFUN([AM_DISABLE_SHARED], [AC_DISABLE_SHARED($@)])
+
+dnl aclocal-1.4 backwards compatibility:
+dnl AC_DEFUN([AM_ENABLE_SHARED], [])
+dnl AC_DEFUN([AM_DISABLE_SHARED], [])
+
+
+
+# _LT_ENABLE_STATIC([DEFAULT])
+# ----------------------------
+# implement the --enable-static flag, and support the `static' and
+# `disable-static' LT_INIT options.
+# DEFAULT is either `yes' or `no'.  If omitted, it defaults to `yes'.
+m4_define([_LT_ENABLE_STATIC],
+[m4_define([_LT_ENABLE_STATIC_DEFAULT], [m4_if($1, no, no, yes)])dnl
+AC_ARG_ENABLE([static],
+    [AS_HELP_STRING([--enable-static@<:@=PKGS@:>@],
+	[build static libraries @<:@default=]_LT_ENABLE_STATIC_DEFAULT[@:>@])],
+    [p=${PACKAGE-default}
+    case $enableval in
+    yes) enable_static=yes ;;
+    no) enable_static=no ;;
+    *)
+     enable_static=no
+      # Look at the argument we got.  We use all the common list separators.
+      lt_save_ifs="$IFS"; IFS="${IFS}$PATH_SEPARATOR,"
+      for pkg in $enableval; do
+	IFS="$lt_save_ifs"
+	if test "X$pkg" = "X$p"; then
+	  enable_static=yes
+	fi
+      done
+      IFS="$lt_save_ifs"
+      ;;
+    esac],
+    [enable_static=]_LT_ENABLE_STATIC_DEFAULT)
+
+    _LT_DECL([build_old_libs], [enable_static], [0],
+	[Whether or not to build static libraries])
+])# _LT_ENABLE_STATIC
+
+LT_OPTION_DEFINE([LT_INIT], [static], [_LT_ENABLE_STATIC([yes])])
+LT_OPTION_DEFINE([LT_INIT], [disable-static], [_LT_ENABLE_STATIC([no])])
+
+# Old names:
+AC_DEFUN([AC_ENABLE_STATIC],
+[_LT_SET_OPTION([LT_INIT], m4_if([$1], [no], [disable-])[static])
+])
+
+AC_DEFUN([AC_DISABLE_STATIC],
+[_LT_SET_OPTION([LT_INIT], [disable-static])
+])
+
+AU_DEFUN([AM_ENABLE_STATIC], [AC_ENABLE_STATIC($@)])
+AU_DEFUN([AM_DISABLE_STATIC], [AC_DISABLE_STATIC($@)])
+
+dnl aclocal-1.4 backwards compatibility:
+dnl AC_DEFUN([AM_ENABLE_STATIC], [])
+dnl AC_DEFUN([AM_DISABLE_STATIC], [])
+
+
+
+# _LT_ENABLE_FAST_INSTALL([DEFAULT])
+# ----------------------------------
+# implement the --enable-fast-install flag, and support the `fast-install'
+# and `disable-fast-install' LT_INIT options.
+# DEFAULT is either `yes' or `no'.  If omitted, it defaults to `yes'.
+m4_define([_LT_ENABLE_FAST_INSTALL],
+[m4_define([_LT_ENABLE_FAST_INSTALL_DEFAULT], [m4_if($1, no, no, yes)])dnl
+AC_ARG_ENABLE([fast-install],
+    [AS_HELP_STRING([--enable-fast-install@<:@=PKGS@:>@],
+    [optimize for fast installation @<:@default=]_LT_ENABLE_FAST_INSTALL_DEFAULT[@:>@])],
+    [p=${PACKAGE-default}
+    case $enableval in
+    yes) enable_fast_install=yes ;;
+    no) enable_fast_install=no ;;
+    *)
+      enable_fast_install=no
+      # Look at the argument we got.  We use all the common list separators.
+      lt_save_ifs="$IFS"; IFS="${IFS}$PATH_SEPARATOR,"
+      for pkg in $enableval; do
+	IFS="$lt_save_ifs"
+	if test "X$pkg" = "X$p"; then
+	  enable_fast_install=yes
+	fi
+      done
+      IFS="$lt_save_ifs"
+      ;;
+    esac],
+    [enable_fast_install=]_LT_ENABLE_FAST_INSTALL_DEFAULT)
+
+_LT_DECL([fast_install], [enable_fast_install], [0],
+	 [Whether or not to optimize for fast installation])dnl
+])# _LT_ENABLE_FAST_INSTALL
+
+LT_OPTION_DEFINE([LT_INIT], [fast-install], [_LT_ENABLE_FAST_INSTALL([yes])])
+LT_OPTION_DEFINE([LT_INIT], [disable-fast-install], [_LT_ENABLE_FAST_INSTALL([no])])
+
+# Old names:
+AU_DEFUN([AC_ENABLE_FAST_INSTALL],
+[_LT_SET_OPTION([LT_INIT], m4_if([$1], [no], [disable-])[fast-install])
+AC_DIAGNOSE([obsolete],
+[$0: Remove this warning and the call to _LT_SET_OPTION when you put
+the `fast-install' option into LT_INIT's first parameter.])
+])
+
+AU_DEFUN([AC_DISABLE_FAST_INSTALL],
+[_LT_SET_OPTION([LT_INIT], [disable-fast-install])
+AC_DIAGNOSE([obsolete],
+[$0: Remove this warning and the call to _LT_SET_OPTION when you put
+the `disable-fast-install' option into LT_INIT's first parameter.])
+])
+
+dnl aclocal-1.4 backwards compatibility:
+dnl AC_DEFUN([AC_ENABLE_FAST_INSTALL], [])
+dnl AC_DEFUN([AM_DISABLE_FAST_INSTALL], [])
+
+
+# _LT_WITH_PIC([MODE])
+# --------------------
+# implement the --with-pic flag, and support the `pic-only' and `no-pic'
+# LT_INIT options.
+# MODE is either `yes' or `no'.  If omitted, it defaults to `both'.
+m4_define([_LT_WITH_PIC],
+[AC_ARG_WITH([pic],
+    [AS_HELP_STRING([--with-pic@<:@=PKGS@:>@],
+	[try to use only PIC/non-PIC objects @<:@default=use both@:>@])],
+    [lt_p=${PACKAGE-default}
+    case $withval in
+    yes|no) pic_mode=$withval ;;
+    *)
+      pic_mode=default
+      # Look at the argument we got.  We use all the common list separators.
+      lt_save_ifs="$IFS"; IFS="${IFS}$PATH_SEPARATOR,"
+      for lt_pkg in $withval; do
+	IFS="$lt_save_ifs"
+	if test "X$lt_pkg" = "X$lt_p"; then
+	  pic_mode=yes
+	fi
+      done
+      IFS="$lt_save_ifs"
+      ;;
+    esac],
+    [pic_mode=default])
+
+test -z "$pic_mode" && pic_mode=m4_default([$1], [default])
+
+_LT_DECL([], [pic_mode], [0], [What type of objects to build])dnl
+])# _LT_WITH_PIC
+
+LT_OPTION_DEFINE([LT_INIT], [pic-only], [_LT_WITH_PIC([yes])])
+LT_OPTION_DEFINE([LT_INIT], [no-pic], [_LT_WITH_PIC([no])])
+
+# Old name:
+AU_DEFUN([AC_LIBTOOL_PICMODE],
+[_LT_SET_OPTION([LT_INIT], [pic-only])
+AC_DIAGNOSE([obsolete],
+[$0: Remove this warning and the call to _LT_SET_OPTION when you
+put the `pic-only' option into LT_INIT's first parameter.])
+])
+
+dnl aclocal-1.4 backwards compatibility:
+dnl AC_DEFUN([AC_LIBTOOL_PICMODE], [])
+
+
+m4_define([_LTDL_MODE], [])
+LT_OPTION_DEFINE([LTDL_INIT], [nonrecursive],
+		 [m4_define([_LTDL_MODE], [nonrecursive])])
+LT_OPTION_DEFINE([LTDL_INIT], [recursive],
+		 [m4_define([_LTDL_MODE], [recursive])])
+LT_OPTION_DEFINE([LTDL_INIT], [subproject],
+		 [m4_define([_LTDL_MODE], [subproject])])
+
+m4_define([_LTDL_TYPE], [])
+LT_OPTION_DEFINE([LTDL_INIT], [installable],
+		 [m4_define([_LTDL_TYPE], [installable])])
+LT_OPTION_DEFINE([LTDL_INIT], [convenience],
+		 [m4_define([_LTDL_TYPE], [convenience])])
+
+# ltsugar.m4 -- libtool m4 base layer.                         -*-Autoconf-*-
+#
+# Copyright (C) 2004, 2005, 2007, 2008 Free Software Foundation, Inc.
+# Written by Gary V. Vaughan, 2004
+#
+# This file is free software; the Free Software Foundation gives
+# unlimited permission to copy and/or distribute it, with or without
+# modifications, as long as this notice is preserved.
+
+# serial 6 ltsugar.m4
+
+# This is to help aclocal find these macros, as it can't see m4_define.
+AC_DEFUN([LTSUGAR_VERSION], [m4_if([0.1])])
+
+
+# lt_join(SEP, ARG1, [ARG2...])
+# -----------------------------
+# Produce ARG1SEPARG2...SEPARGn, omitting [] arguments and their
+# associated separator.
+# Needed until we can rely on m4_join from Autoconf 2.62, since all earlier
+# versions in m4sugar had bugs.
+m4_define([lt_join],
+[m4_if([$#], [1], [],
+       [$#], [2], [[$2]],
+       [m4_if([$2], [], [], [[$2]_])$0([$1], m4_shift(m4_shift($@)))])])
+m4_define([_lt_join],
+[m4_if([$#$2], [2], [],
+       [m4_if([$2], [], [], [[$1$2]])$0([$1], m4_shift(m4_shift($@)))])])
+
+
+# lt_car(LIST)
+# lt_cdr(LIST)
+# ------------
+# Manipulate m4 lists.
+# These macros are necessary as long as will still need to support
+# Autoconf-2.59 which quotes differently.
+m4_define([lt_car], [[$1]])
+m4_define([lt_cdr],
+[m4_if([$#], 0, [m4_fatal([$0: cannot be called without arguments])],
+       [$#], 1, [],
+       [m4_dquote(m4_shift($@))])])
+m4_define([lt_unquote], $1)
+
+
+# lt_append(MACRO-NAME, STRING, [SEPARATOR])
+# ------------------------------------------
+# Redefine MACRO-NAME to hold its former content plus `SEPARATOR'`STRING'.
+# Note that neither SEPARATOR nor STRING are expanded; they are appended
+# to MACRO-NAME as is (leaving the expansion for when MACRO-NAME is invoked).
+# No SEPARATOR is output if MACRO-NAME was previously undefined (different
+# than defined and empty).
+#
+# This macro is needed until we can rely on Autoconf 2.62, since earlier
+# versions of m4sugar mistakenly expanded SEPARATOR but not STRING.
+m4_define([lt_append],
+[m4_define([$1],
+	   m4_ifdef([$1], [m4_defn([$1])[$3]])[$2])])
+
+
+
+# lt_combine(SEP, PREFIX-LIST, INFIX, SUFFIX1, [SUFFIX2...])
+# ----------------------------------------------------------
+# Produce a SEP delimited list of all paired combinations of elements of
+# PREFIX-LIST with SUFFIX1 through SUFFIXn.  Each element of the list
+# has the form PREFIXmINFIXSUFFIXn.
+# Needed until we can rely on m4_combine added in Autoconf 2.62.
+m4_define([lt_combine],
+[m4_if(m4_eval([$# > 3]), [1],
+       [m4_pushdef([_Lt_sep], [m4_define([_Lt_sep], m4_defn([lt_car]))])]]dnl
+[[m4_foreach([_Lt_prefix], [$2],
+	     [m4_foreach([_Lt_suffix],
+		]m4_dquote(m4_dquote(m4_shift(m4_shift(m4_shift($@)))))[,
+	[_Lt_sep([$1])[]m4_defn([_Lt_prefix])[$3]m4_defn([_Lt_suffix])])])])])
+
+
+# lt_if_append_uniq(MACRO-NAME, VARNAME, [SEPARATOR], [UNIQ], [NOT-UNIQ])
+# -----------------------------------------------------------------------
+# Iff MACRO-NAME does not yet contain VARNAME, then append it (delimited
+# by SEPARATOR if supplied) and expand UNIQ, else NOT-UNIQ.
+m4_define([lt_if_append_uniq],
+[m4_ifdef([$1],
+	  [m4_if(m4_index([$3]m4_defn([$1])[$3], [$3$2$3]), [-1],
+		 [lt_append([$1], [$2], [$3])$4],
+		 [$5])],
+	  [lt_append([$1], [$2], [$3])$4])])
+
+
+# lt_dict_add(DICT, KEY, VALUE)
+# -----------------------------
+m4_define([lt_dict_add],
+[m4_define([$1($2)], [$3])])
+
+
+# lt_dict_add_subkey(DICT, KEY, SUBKEY, VALUE)
+# --------------------------------------------
+m4_define([lt_dict_add_subkey],
+[m4_define([$1($2:$3)], [$4])])
+
+
+# lt_dict_fetch(DICT, KEY, [SUBKEY])
+# ----------------------------------
+m4_define([lt_dict_fetch],
+[m4_ifval([$3],
+	m4_ifdef([$1($2:$3)], [m4_defn([$1($2:$3)])]),
+    m4_ifdef([$1($2)], [m4_defn([$1($2)])]))])
+
+
+# lt_if_dict_fetch(DICT, KEY, [SUBKEY], VALUE, IF-TRUE, [IF-FALSE])
+# -----------------------------------------------------------------
+m4_define([lt_if_dict_fetch],
+[m4_if(lt_dict_fetch([$1], [$2], [$3]), [$4],
+	[$5],
+    [$6])])
+
+
+# lt_dict_filter(DICT, [SUBKEY], VALUE, [SEPARATOR], KEY, [...])
+# --------------------------------------------------------------
+m4_define([lt_dict_filter],
+[m4_if([$5], [], [],
+  [lt_join(m4_quote(m4_default([$4], [[, ]])),
+           lt_unquote(m4_split(m4_normalize(m4_foreach(_Lt_key, lt_car([m4_shiftn(4, $@)]),
+		      [lt_if_dict_fetch([$1], _Lt_key, [$2], [$3], [_Lt_key ])])))))])[]dnl
+])
+
+# ltversion.m4 -- version numbers			-*- Autoconf -*-
+#
+#   Copyright (C) 2004 Free Software Foundation, Inc.
+#   Written by Scott James Remnant, 2004
+#
+# This file is free software; the Free Software Foundation gives
+# unlimited permission to copy and/or distribute it, with or without
+# modifications, as long as this notice is preserved.
+
+# @configure_input@
+
+# serial 3337 ltversion.m4
+# This file is part of GNU Libtool
+
+m4_define([LT_PACKAGE_VERSION], [2.4.2])
+m4_define([LT_PACKAGE_REVISION], [1.3337])
+
+AC_DEFUN([LTVERSION_VERSION],
+[macro_version='2.4.2'
+macro_revision='1.3337'
+_LT_DECL(, macro_version, 0, [Which release of libtool.m4 was used?])
+_LT_DECL(, macro_revision, 0)
+])
+
+# lt~obsolete.m4 -- aclocal satisfying obsolete definitions.    -*-Autoconf-*-
+#
+#   Copyright (C) 2004, 2005, 2007, 2009 Free Software Foundation, Inc.
+#   Written by Scott James Remnant, 2004.
+#
+# This file is free software; the Free Software Foundation gives
+# unlimited permission to copy and/or distribute it, with or without
+# modifications, as long as this notice is preserved.
+
+# serial 5 lt~obsolete.m4
+
+# These exist entirely to fool aclocal when bootstrapping libtool.
+#
+# In the past libtool.m4 has provided macros via AC_DEFUN (or AU_DEFUN)
+# which have later been changed to m4_define as they aren't part of the
+# exported API, or moved to Autoconf or Automake where they belong.
+#
+# The trouble is, aclocal is a bit thick.  It'll see the old AC_DEFUN
+# in /usr/share/aclocal/libtool.m4 and remember it, then when it sees us
+# using a macro with the same name in our local m4/libtool.m4 it'll
+# pull the old libtool.m4 in (it doesn't see our shiny new m4_define
+# and doesn't know about Autoconf macros at all.)
+#
+# So we provide this file, which has a silly filename so it's always
+# included after everything else.  This provides aclocal with the
+# AC_DEFUNs it wants, but when m4 processes it, it doesn't do anything
+# because those macros already exist, or will be overwritten later.
+# We use AC_DEFUN over AU_DEFUN for compatibility with aclocal-1.6. 
+#
+# Anytime we withdraw an AC_DEFUN or AU_DEFUN, remember to add it here.
+# Yes, that means every name once taken will need to remain here until
+# we give up compatibility with versions before 1.7, at which point
+# we need to keep only those names which we still refer to.
+
+# This is to help aclocal find these macros, as it can't see m4_define.
+AC_DEFUN([LTOBSOLETE_VERSION], [m4_if([1])])
+
+m4_ifndef([AC_LIBTOOL_LINKER_OPTION],	[AC_DEFUN([AC_LIBTOOL_LINKER_OPTION])])
+m4_ifndef([AC_PROG_EGREP],		[AC_DEFUN([AC_PROG_EGREP])])
+m4_ifndef([_LT_AC_PROG_ECHO_BACKSLASH],	[AC_DEFUN([_LT_AC_PROG_ECHO_BACKSLASH])])
+m4_ifndef([_LT_AC_SHELL_INIT],		[AC_DEFUN([_LT_AC_SHELL_INIT])])
+m4_ifndef([_LT_AC_SYS_LIBPATH_AIX],	[AC_DEFUN([_LT_AC_SYS_LIBPATH_AIX])])
+m4_ifndef([_LT_PROG_LTMAIN],		[AC_DEFUN([_LT_PROG_LTMAIN])])
+m4_ifndef([_LT_AC_TAGVAR],		[AC_DEFUN([_LT_AC_TAGVAR])])
+m4_ifndef([AC_LTDL_ENABLE_INSTALL],	[AC_DEFUN([AC_LTDL_ENABLE_INSTALL])])
+m4_ifndef([AC_LTDL_PREOPEN],		[AC_DEFUN([AC_LTDL_PREOPEN])])
+m4_ifndef([_LT_AC_SYS_COMPILER],	[AC_DEFUN([_LT_AC_SYS_COMPILER])])
+m4_ifndef([_LT_AC_LOCK],		[AC_DEFUN([_LT_AC_LOCK])])
+m4_ifndef([AC_LIBTOOL_SYS_OLD_ARCHIVE],	[AC_DEFUN([AC_LIBTOOL_SYS_OLD_ARCHIVE])])
+m4_ifndef([_LT_AC_TRY_DLOPEN_SELF],	[AC_DEFUN([_LT_AC_TRY_DLOPEN_SELF])])
+m4_ifndef([AC_LIBTOOL_PROG_CC_C_O],	[AC_DEFUN([AC_LIBTOOL_PROG_CC_C_O])])
+m4_ifndef([AC_LIBTOOL_SYS_HARD_LINK_LOCKS], [AC_DEFUN([AC_LIBTOOL_SYS_HARD_LINK_LOCKS])])
+m4_ifndef([AC_LIBTOOL_OBJDIR],		[AC_DEFUN([AC_LIBTOOL_OBJDIR])])
+m4_ifndef([AC_LTDL_OBJDIR],		[AC_DEFUN([AC_LTDL_OBJDIR])])
+m4_ifndef([AC_LIBTOOL_PROG_LD_HARDCODE_LIBPATH], [AC_DEFUN([AC_LIBTOOL_PROG_LD_HARDCODE_LIBPATH])])
+m4_ifndef([AC_LIBTOOL_SYS_LIB_STRIP],	[AC_DEFUN([AC_LIBTOOL_SYS_LIB_STRIP])])
+m4_ifndef([AC_PATH_MAGIC],		[AC_DEFUN([AC_PATH_MAGIC])])
+m4_ifndef([AC_PROG_LD_GNU],		[AC_DEFUN([AC_PROG_LD_GNU])])
+m4_ifndef([AC_PROG_LD_RELOAD_FLAG],	[AC_DEFUN([AC_PROG_LD_RELOAD_FLAG])])
+m4_ifndef([AC_DEPLIBS_CHECK_METHOD],	[AC_DEFUN([AC_DEPLIBS_CHECK_METHOD])])
+m4_ifndef([AC_LIBTOOL_PROG_COMPILER_NO_RTTI], [AC_DEFUN([AC_LIBTOOL_PROG_COMPILER_NO_RTTI])])
+m4_ifndef([AC_LIBTOOL_SYS_GLOBAL_SYMBOL_PIPE], [AC_DEFUN([AC_LIBTOOL_SYS_GLOBAL_SYMBOL_PIPE])])
+m4_ifndef([AC_LIBTOOL_PROG_COMPILER_PIC], [AC_DEFUN([AC_LIBTOOL_PROG_COMPILER_PIC])])
+m4_ifndef([AC_LIBTOOL_PROG_LD_SHLIBS],	[AC_DEFUN([AC_LIBTOOL_PROG_LD_SHLIBS])])
+m4_ifndef([AC_LIBTOOL_POSTDEP_PREDEP],	[AC_DEFUN([AC_LIBTOOL_POSTDEP_PREDEP])])
+m4_ifndef([LT_AC_PROG_EGREP],		[AC_DEFUN([LT_AC_PROG_EGREP])])
+m4_ifndef([LT_AC_PROG_SED],		[AC_DEFUN([LT_AC_PROG_SED])])
+m4_ifndef([_LT_CC_BASENAME],		[AC_DEFUN([_LT_CC_BASENAME])])
+m4_ifndef([_LT_COMPILER_BOILERPLATE],	[AC_DEFUN([_LT_COMPILER_BOILERPLATE])])
+m4_ifndef([_LT_LINKER_BOILERPLATE],	[AC_DEFUN([_LT_LINKER_BOILERPLATE])])
+m4_ifndef([_AC_PROG_LIBTOOL],		[AC_DEFUN([_AC_PROG_LIBTOOL])])
+m4_ifndef([AC_LIBTOOL_SETUP],		[AC_DEFUN([AC_LIBTOOL_SETUP])])
+m4_ifndef([_LT_AC_CHECK_DLFCN],		[AC_DEFUN([_LT_AC_CHECK_DLFCN])])
+m4_ifndef([AC_LIBTOOL_SYS_DYNAMIC_LINKER],	[AC_DEFUN([AC_LIBTOOL_SYS_DYNAMIC_LINKER])])
+m4_ifndef([_LT_AC_TAGCONFIG],		[AC_DEFUN([_LT_AC_TAGCONFIG])])
+m4_ifndef([AC_DISABLE_FAST_INSTALL],	[AC_DEFUN([AC_DISABLE_FAST_INSTALL])])
+m4_ifndef([_LT_AC_LANG_CXX],		[AC_DEFUN([_LT_AC_LANG_CXX])])
+m4_ifndef([_LT_AC_LANG_F77],		[AC_DEFUN([_LT_AC_LANG_F77])])
+m4_ifndef([_LT_AC_LANG_GCJ],		[AC_DEFUN([_LT_AC_LANG_GCJ])])
+m4_ifndef([AC_LIBTOOL_LANG_C_CONFIG],	[AC_DEFUN([AC_LIBTOOL_LANG_C_CONFIG])])
+m4_ifndef([_LT_AC_LANG_C_CONFIG],	[AC_DEFUN([_LT_AC_LANG_C_CONFIG])])
+m4_ifndef([AC_LIBTOOL_LANG_CXX_CONFIG],	[AC_DEFUN([AC_LIBTOOL_LANG_CXX_CONFIG])])
+m4_ifndef([_LT_AC_LANG_CXX_CONFIG],	[AC_DEFUN([_LT_AC_LANG_CXX_CONFIG])])
+m4_ifndef([AC_LIBTOOL_LANG_F77_CONFIG],	[AC_DEFUN([AC_LIBTOOL_LANG_F77_CONFIG])])
+m4_ifndef([_LT_AC_LANG_F77_CONFIG],	[AC_DEFUN([_LT_AC_LANG_F77_CONFIG])])
+m4_ifndef([AC_LIBTOOL_LANG_GCJ_CONFIG],	[AC_DEFUN([AC_LIBTOOL_LANG_GCJ_CONFIG])])
+m4_ifndef([_LT_AC_LANG_GCJ_CONFIG],	[AC_DEFUN([_LT_AC_LANG_GCJ_CONFIG])])
+m4_ifndef([AC_LIBTOOL_LANG_RC_CONFIG],	[AC_DEFUN([AC_LIBTOOL_LANG_RC_CONFIG])])
+m4_ifndef([_LT_AC_LANG_RC_CONFIG],	[AC_DEFUN([_LT_AC_LANG_RC_CONFIG])])
+m4_ifndef([AC_LIBTOOL_CONFIG],		[AC_DEFUN([AC_LIBTOOL_CONFIG])])
+m4_ifndef([_LT_AC_FILE_LTDLL_C],	[AC_DEFUN([_LT_AC_FILE_LTDLL_C])])
+m4_ifndef([_LT_REQUIRED_DARWIN_CHECKS],	[AC_DEFUN([_LT_REQUIRED_DARWIN_CHECKS])])
+m4_ifndef([_LT_AC_PROG_CXXCPP],		[AC_DEFUN([_LT_AC_PROG_CXXCPP])])
+m4_ifndef([_LT_PREPARE_SED_QUOTE_VARS],	[AC_DEFUN([_LT_PREPARE_SED_QUOTE_VARS])])
+m4_ifndef([_LT_PROG_ECHO_BACKSLASH],	[AC_DEFUN([_LT_PROG_ECHO_BACKSLASH])])
+m4_ifndef([_LT_PROG_F77],		[AC_DEFUN([_LT_PROG_F77])])
+m4_ifndef([_LT_PROG_FC],		[AC_DEFUN([_LT_PROG_FC])])
+m4_ifndef([_LT_PROG_CXX],		[AC_DEFUN([_LT_PROG_CXX])])
+
+# pkg.m4 - Macros to locate and utilise pkg-config.            -*- Autoconf -*-
+# serial 1 (pkg-config-0.24)
+# 
+# Copyright © 2004 Scott James Remnant <scott@netsplit.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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+#
+# As a special exception to the GNU General Public License, if you
+# distribute this file as part of a program that contains a
+# configuration script generated by Autoconf, you may include it under
+# the same distribution terms that you use for the rest of that program.
+
+# PKG_PROG_PKG_CONFIG([MIN-VERSION])
+# ----------------------------------
+AC_DEFUN([PKG_PROG_PKG_CONFIG],
+[m4_pattern_forbid([^_?PKG_[A-Z_]+$])
+m4_pattern_allow([^PKG_CONFIG(_(PATH|LIBDIR|SYSROOT_DIR|ALLOW_SYSTEM_(CFLAGS|LIBS)))?$])
+m4_pattern_allow([^PKG_CONFIG_(DISABLE_UNINSTALLED|TOP_BUILD_DIR|DEBUG_SPEW)$])
+AC_ARG_VAR([PKG_CONFIG], [path to pkg-config utility])
+AC_ARG_VAR([PKG_CONFIG_PATH], [directories to add to pkg-config's search path])
+AC_ARG_VAR([PKG_CONFIG_LIBDIR], [path overriding pkg-config's built-in search path])
+
+if test "x$ac_cv_env_PKG_CONFIG_set" != "xset"; then
+	AC_PATH_TOOL([PKG_CONFIG], [pkg-config])
+fi
+if test -n "$PKG_CONFIG"; then
+	_pkg_min_version=m4_default([$1], [0.9.0])
+	AC_MSG_CHECKING([pkg-config is at least version $_pkg_min_version])
+	if $PKG_CONFIG --atleast-pkgconfig-version $_pkg_min_version; then
+		AC_MSG_RESULT([yes])
+	else
+		AC_MSG_RESULT([no])
+		PKG_CONFIG=""
+	fi
+fi[]dnl
+])# PKG_PROG_PKG_CONFIG
+
+# PKG_CHECK_EXISTS(MODULES, [ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND])
+#
+# Check to see whether a particular set of modules exists.  Similar
+# to PKG_CHECK_MODULES(), but does not set variables or print errors.
+#
+# Please remember that m4 expands AC_REQUIRE([PKG_PROG_PKG_CONFIG])
+# only at the first occurence in configure.ac, so if the first place
+# it's called might be skipped (such as if it is within an "if", you
+# have to call PKG_CHECK_EXISTS manually
+# --------------------------------------------------------------
+AC_DEFUN([PKG_CHECK_EXISTS],
+[AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl
+if test -n "$PKG_CONFIG" && \
+    AC_RUN_LOG([$PKG_CONFIG --exists --print-errors "$1"]); then
+  m4_default([$2], [:])
+m4_ifvaln([$3], [else
+  $3])dnl
+fi])
+
+# _PKG_CONFIG([VARIABLE], [COMMAND], [MODULES])
+# ---------------------------------------------
+m4_define([_PKG_CONFIG],
+[if test -n "$$1"; then
+    pkg_cv_[]$1="$$1"
+ elif test -n "$PKG_CONFIG"; then
+    PKG_CHECK_EXISTS([$3],
+                     [pkg_cv_[]$1=`$PKG_CONFIG --[]$2 "$3" 2>/dev/null`
+		      test "x$?" != "x0" && pkg_failed=yes ],
+		     [pkg_failed=yes])
+ else
+    pkg_failed=untried
+fi[]dnl
+])# _PKG_CONFIG
+
+# _PKG_SHORT_ERRORS_SUPPORTED
+# -----------------------------
+AC_DEFUN([_PKG_SHORT_ERRORS_SUPPORTED],
+[AC_REQUIRE([PKG_PROG_PKG_CONFIG])
+if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then
+        _pkg_short_errors_supported=yes
+else
+        _pkg_short_errors_supported=no
+fi[]dnl
+])# _PKG_SHORT_ERRORS_SUPPORTED
+
+
+# PKG_CHECK_MODULES(VARIABLE-PREFIX, MODULES, [ACTION-IF-FOUND],
+# [ACTION-IF-NOT-FOUND])
+#
+#
+# Note that if there is a possibility the first call to
+# PKG_CHECK_MODULES might not happen, you should be sure to include an
+# explicit call to PKG_PROG_PKG_CONFIG in your configure.ac
+#
+#
+# --------------------------------------------------------------
+AC_DEFUN([PKG_CHECK_MODULES],
+[AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl
+AC_ARG_VAR([$1][_CFLAGS], [C compiler flags for $1, overriding pkg-config])dnl
+AC_ARG_VAR([$1][_LIBS], [linker flags for $1, overriding pkg-config])dnl
+
+pkg_failed=no
+AC_MSG_CHECKING([for $1])
+
+_PKG_CONFIG([$1][_CFLAGS], [cflags], [$2])
+_PKG_CONFIG([$1][_LIBS], [libs], [$2])
+
+m4_define([_PKG_TEXT], [Alternatively, you may set the environment variables $1[]_CFLAGS
+and $1[]_LIBS to avoid the need to call pkg-config.
+See the pkg-config man page for more details.])
+
+if test $pkg_failed = yes; then
+   	AC_MSG_RESULT([no])
+        _PKG_SHORT_ERRORS_SUPPORTED
+        if test $_pkg_short_errors_supported = yes; then
+	        $1[]_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "$2" 2>&1`
+        else 
+	        $1[]_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "$2" 2>&1`
+        fi
+	# Put the nasty error message in config.log where it belongs
+	echo "$$1[]_PKG_ERRORS" >&AS_MESSAGE_LOG_FD
+
+	m4_default([$4], [AC_MSG_ERROR(
+[Package requirements ($2) were not met:
+
+$$1_PKG_ERRORS
+
+Consider adjusting the PKG_CONFIG_PATH environment variable if you
+installed software in a non-standard prefix.
+
+_PKG_TEXT])[]dnl
+        ])
+elif test $pkg_failed = untried; then
+     	AC_MSG_RESULT([no])
+	m4_default([$4], [AC_MSG_FAILURE(
+[The pkg-config script could not be found or is too old.  Make sure it
+is in your PATH or set the PKG_CONFIG environment variable to the full
+path to pkg-config.
+
+_PKG_TEXT
+
+To get pkg-config, see <http://pkg-config.freedesktop.org/>.])[]dnl
+        ])
+else
+	$1[]_CFLAGS=$pkg_cv_[]$1[]_CFLAGS
+	$1[]_LIBS=$pkg_cv_[]$1[]_LIBS
+        AC_MSG_RESULT([yes])
+	$3
+fi[]dnl
+])# PKG_CHECK_MODULES
+
+# Copyright (C) 2002-2013 Free Software Foundation, Inc.
+#
+# This file is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# AM_AUTOMAKE_VERSION(VERSION)
+# ----------------------------
+# Automake X.Y traces this macro to ensure aclocal.m4 has been
+# generated from the m4 files accompanying Automake X.Y.
+# (This private macro should not be called outside this file.)
+AC_DEFUN([AM_AUTOMAKE_VERSION],
+[am__api_version='1.13'
+dnl Some users find AM_AUTOMAKE_VERSION and mistake it for a way to
+dnl require some minimum version.  Point them to the right macro.
+m4_if([$1], [1.13.3], [],
+      [AC_FATAL([Do not call $0, use AM_INIT_AUTOMAKE([$1]).])])dnl
+])
+
+# _AM_AUTOCONF_VERSION(VERSION)
+# -----------------------------
+# aclocal traces this macro to find the Autoconf version.
+# This is a private macro too.  Using m4_define simplifies
+# the logic in aclocal, which can simply ignore this definition.
+m4_define([_AM_AUTOCONF_VERSION], [])
+
+# AM_SET_CURRENT_AUTOMAKE_VERSION
+# -------------------------------
+# Call AM_AUTOMAKE_VERSION and AM_AUTOMAKE_VERSION so they can be traced.
+# This function is AC_REQUIREd by AM_INIT_AUTOMAKE.
+AC_DEFUN([AM_SET_CURRENT_AUTOMAKE_VERSION],
+[AM_AUTOMAKE_VERSION([1.13.3])dnl
+m4_ifndef([AC_AUTOCONF_VERSION],
+  [m4_copy([m4_PACKAGE_VERSION], [AC_AUTOCONF_VERSION])])dnl
+_AM_AUTOCONF_VERSION(m4_defn([AC_AUTOCONF_VERSION]))])
+
+# AM_AUX_DIR_EXPAND                                         -*- Autoconf -*-
+
+# Copyright (C) 2001-2013 Free Software Foundation, Inc.
+#
+# This file is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# For projects using AC_CONFIG_AUX_DIR([foo]), Autoconf sets
+# $ac_aux_dir to '$srcdir/foo'.  In other projects, it is set to
+# '$srcdir', '$srcdir/..', or '$srcdir/../..'.
+#
+# Of course, Automake must honor this variable whenever it calls a
+# tool from the auxiliary directory.  The problem is that $srcdir (and
+# therefore $ac_aux_dir as well) can be either absolute or relative,
+# depending on how configure is run.  This is pretty annoying, since
+# it makes $ac_aux_dir quite unusable in subdirectories: in the top
+# source directory, any form will work fine, but in subdirectories a
+# relative path needs to be adjusted first.
+#
+# $ac_aux_dir/missing
+#    fails when called from a subdirectory if $ac_aux_dir is relative
+# $top_srcdir/$ac_aux_dir/missing
+#    fails if $ac_aux_dir is absolute,
+#    fails when called from a subdirectory in a VPATH build with
+#          a relative $ac_aux_dir
+#
+# The reason of the latter failure is that $top_srcdir and $ac_aux_dir
+# are both prefixed by $srcdir.  In an in-source build this is usually
+# harmless because $srcdir is '.', but things will broke when you
+# start a VPATH build or use an absolute $srcdir.
+#
+# So we could use something similar to $top_srcdir/$ac_aux_dir/missing,
+# iff we strip the leading $srcdir from $ac_aux_dir.  That would be:
+#   am_aux_dir='\$(top_srcdir)/'`expr "$ac_aux_dir" : "$srcdir//*\(.*\)"`
+# and then we would define $MISSING as
+#   MISSING="\${SHELL} $am_aux_dir/missing"
+# This will work as long as MISSING is not called from configure, because
+# unfortunately $(top_srcdir) has no meaning in configure.
+# However there are other variables, like CC, which are often used in
+# configure, and could therefore not use this "fixed" $ac_aux_dir.
+#
+# Another solution, used here, is to always expand $ac_aux_dir to an
+# absolute PATH.  The drawback is that using absolute paths prevent a
+# configured tree to be moved without reconfiguration.
+
+AC_DEFUN([AM_AUX_DIR_EXPAND],
+[dnl Rely on autoconf to set up CDPATH properly.
+AC_PREREQ([2.50])dnl
+# expand $ac_aux_dir to an absolute path
+am_aux_dir=`cd $ac_aux_dir && pwd`
+])
+
+# AM_CONDITIONAL                                            -*- Autoconf -*-
+
+# Copyright (C) 1997-2013 Free Software Foundation, Inc.
+#
+# This file is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# AM_CONDITIONAL(NAME, SHELL-CONDITION)
+# -------------------------------------
+# Define a conditional.
+AC_DEFUN([AM_CONDITIONAL],
+[AC_PREREQ([2.52])dnl
+ m4_if([$1], [TRUE],  [AC_FATAL([$0: invalid condition: $1])],
+       [$1], [FALSE], [AC_FATAL([$0: invalid condition: $1])])dnl
+AC_SUBST([$1_TRUE])dnl
+AC_SUBST([$1_FALSE])dnl
+_AM_SUBST_NOTMAKE([$1_TRUE])dnl
+_AM_SUBST_NOTMAKE([$1_FALSE])dnl
+m4_define([_AM_COND_VALUE_$1], [$2])dnl
+if $2; then
+  $1_TRUE=
+  $1_FALSE='#'
+else
+  $1_TRUE='#'
+  $1_FALSE=
+fi
+AC_CONFIG_COMMANDS_PRE(
+[if test -z "${$1_TRUE}" && test -z "${$1_FALSE}"; then
+  AC_MSG_ERROR([[conditional "$1" was never defined.
+Usually this means the macro was only invoked conditionally.]])
+fi])])
+
+# Copyright (C) 1999-2013 Free Software Foundation, Inc.
+#
+# This file is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+
+# There are a few dirty hacks below to avoid letting 'AC_PROG_CC' be
+# written in clear, in which case automake, when reading aclocal.m4,
+# will think it sees a *use*, and therefore will trigger all it's
+# C support machinery.  Also note that it means that autoscan, seeing
+# CC etc. in the Makefile, will ask for an AC_PROG_CC use...
+
+
+# _AM_DEPENDENCIES(NAME)
+# ----------------------
+# See how the compiler implements dependency checking.
+# NAME is "CC", "CXX", "OBJC", "OBJCXX", "UPC", or "GJC".
+# We try a few techniques and use that to set a single cache variable.
+#
+# We don't AC_REQUIRE the corresponding AC_PROG_CC since the latter was
+# modified to invoke _AM_DEPENDENCIES(CC); we would have a circular
+# dependency, and given that the user is not expected to run this macro,
+# just rely on AC_PROG_CC.
+AC_DEFUN([_AM_DEPENDENCIES],
+[AC_REQUIRE([AM_SET_DEPDIR])dnl
+AC_REQUIRE([AM_OUTPUT_DEPENDENCY_COMMANDS])dnl
+AC_REQUIRE([AM_MAKE_INCLUDE])dnl
+AC_REQUIRE([AM_DEP_TRACK])dnl
+
+m4_if([$1], [CC],   [depcc="$CC"   am_compiler_list=],
+      [$1], [CXX],  [depcc="$CXX"  am_compiler_list=],
+      [$1], [OBJC], [depcc="$OBJC" am_compiler_list='gcc3 gcc'],
+      [$1], [OBJCXX], [depcc="$OBJCXX" am_compiler_list='gcc3 gcc'],
+      [$1], [UPC],  [depcc="$UPC"  am_compiler_list=],
+      [$1], [GCJ],  [depcc="$GCJ"  am_compiler_list='gcc3 gcc'],
+                    [depcc="$$1"   am_compiler_list=])
+
+AC_CACHE_CHECK([dependency style of $depcc],
+               [am_cv_$1_dependencies_compiler_type],
+[if test -z "$AMDEP_TRUE" && test -f "$am_depcomp"; then
+  # We make a subdir and do the tests there.  Otherwise we can end up
+  # making bogus files that we don't know about and never remove.  For
+  # instance it was reported that on HP-UX the gcc test will end up
+  # making a dummy file named 'D' -- because '-MD' means "put the output
+  # in D".
+  rm -rf conftest.dir
+  mkdir conftest.dir
+  # Copy depcomp to subdir because otherwise we won't find it if we're
+  # using a relative directory.
+  cp "$am_depcomp" conftest.dir
+  cd conftest.dir
+  # We will build objects and dependencies in a subdirectory because
+  # it helps to detect inapplicable dependency modes.  For instance
+  # both Tru64's cc and ICC support -MD to output dependencies as a
+  # side effect of compilation, but ICC will put the dependencies in
+  # the current directory while Tru64 will put them in the object
+  # directory.
+  mkdir sub
+
+  am_cv_$1_dependencies_compiler_type=none
+  if test "$am_compiler_list" = ""; then
+     am_compiler_list=`sed -n ['s/^#*\([a-zA-Z0-9]*\))$/\1/p'] < ./depcomp`
+  fi
+  am__universal=false
+  m4_case([$1], [CC],
+    [case " $depcc " in #(
+     *\ -arch\ *\ -arch\ *) am__universal=true ;;
+     esac],
+    [CXX],
+    [case " $depcc " in #(
+     *\ -arch\ *\ -arch\ *) am__universal=true ;;
+     esac])
+
+  for depmode in $am_compiler_list; do
+    # Setup a source with many dependencies, because some compilers
+    # like to wrap large dependency lists on column 80 (with \), and
+    # we should not choose a depcomp mode which is confused by this.
+    #
+    # We need to recreate these files for each test, as the compiler may
+    # overwrite some of them when testing with obscure command lines.
+    # This happens at least with the AIX C compiler.
+    : > sub/conftest.c
+    for i in 1 2 3 4 5 6; do
+      echo '#include "conftst'$i'.h"' >> sub/conftest.c
+      # Using ": > sub/conftst$i.h" creates only sub/conftst1.h with
+      # Solaris 10 /bin/sh.
+      echo '/* dummy */' > sub/conftst$i.h
+    done
+    echo "${am__include} ${am__quote}sub/conftest.Po${am__quote}" > confmf
+
+    # We check with '-c' and '-o' for the sake of the "dashmstdout"
+    # mode.  It turns out that the SunPro C++ compiler does not properly
+    # handle '-M -o', and we need to detect this.  Also, some Intel
+    # versions had trouble with output in subdirs.
+    am__obj=sub/conftest.${OBJEXT-o}
+    am__minus_obj="-o $am__obj"
+    case $depmode in
+    gcc)
+      # This depmode causes a compiler race in universal mode.
+      test "$am__universal" = false || continue
+      ;;
+    nosideeffect)
+      # After this tag, mechanisms are not by side-effect, so they'll
+      # only be used when explicitly requested.
+      if test "x$enable_dependency_tracking" = xyes; then
+	continue
+      else
+	break
+      fi
+      ;;
+    msvc7 | msvc7msys | msvisualcpp | msvcmsys)
+      # This compiler won't grok '-c -o', but also, the minuso test has
+      # not run yet.  These depmodes are late enough in the game, and
+      # so weak that their functioning should not be impacted.
+      am__obj=conftest.${OBJEXT-o}
+      am__minus_obj=
+      ;;
+    none) break ;;
+    esac
+    if depmode=$depmode \
+       source=sub/conftest.c object=$am__obj \
+       depfile=sub/conftest.Po tmpdepfile=sub/conftest.TPo \
+       $SHELL ./depcomp $depcc -c $am__minus_obj sub/conftest.c \
+         >/dev/null 2>conftest.err &&
+       grep sub/conftst1.h sub/conftest.Po > /dev/null 2>&1 &&
+       grep sub/conftst6.h sub/conftest.Po > /dev/null 2>&1 &&
+       grep $am__obj sub/conftest.Po > /dev/null 2>&1 &&
+       ${MAKE-make} -s -f confmf > /dev/null 2>&1; then
+      # icc doesn't choke on unknown options, it will just issue warnings
+      # or remarks (even with -Werror).  So we grep stderr for any message
+      # that says an option was ignored or not supported.
+      # When given -MP, icc 7.0 and 7.1 complain thusly:
+      #   icc: Command line warning: ignoring option '-M'; no argument required
+      # The diagnosis changed in icc 8.0:
+      #   icc: Command line remark: option '-MP' not supported
+      if (grep 'ignoring option' conftest.err ||
+          grep 'not supported' conftest.err) >/dev/null 2>&1; then :; else
+        am_cv_$1_dependencies_compiler_type=$depmode
+        break
+      fi
+    fi
+  done
+
+  cd ..
+  rm -rf conftest.dir
+else
+  am_cv_$1_dependencies_compiler_type=none
+fi
+])
+AC_SUBST([$1DEPMODE], [depmode=$am_cv_$1_dependencies_compiler_type])
+AM_CONDITIONAL([am__fastdep$1], [
+  test "x$enable_dependency_tracking" != xno \
+  && test "$am_cv_$1_dependencies_compiler_type" = gcc3])
+])
+
+
+# AM_SET_DEPDIR
+# -------------
+# Choose a directory name for dependency files.
+# This macro is AC_REQUIREd in _AM_DEPENDENCIES.
+AC_DEFUN([AM_SET_DEPDIR],
+[AC_REQUIRE([AM_SET_LEADING_DOT])dnl
+AC_SUBST([DEPDIR], ["${am__leading_dot}deps"])dnl
+])
+
+
+# AM_DEP_TRACK
+# ------------
+AC_DEFUN([AM_DEP_TRACK],
+[AC_ARG_ENABLE([dependency-tracking], [dnl
+AS_HELP_STRING(
+  [--enable-dependency-tracking],
+  [do not reject slow dependency extractors])
+AS_HELP_STRING(
+  [--disable-dependency-tracking],
+  [speeds up one-time build])])
+if test "x$enable_dependency_tracking" != xno; then
+  am_depcomp="$ac_aux_dir/depcomp"
+  AMDEPBACKSLASH='\'
+  am__nodep='_no'
+fi
+AM_CONDITIONAL([AMDEP], [test "x$enable_dependency_tracking" != xno])
+AC_SUBST([AMDEPBACKSLASH])dnl
+_AM_SUBST_NOTMAKE([AMDEPBACKSLASH])dnl
+AC_SUBST([am__nodep])dnl
+_AM_SUBST_NOTMAKE([am__nodep])dnl
+])
+
+# Generate code to set up dependency tracking.              -*- Autoconf -*-
+
+# Copyright (C) 1999-2013 Free Software Foundation, Inc.
+#
+# This file is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+
+# _AM_OUTPUT_DEPENDENCY_COMMANDS
+# ------------------------------
+AC_DEFUN([_AM_OUTPUT_DEPENDENCY_COMMANDS],
+[{
+  # Older Autoconf quotes --file arguments for eval, but not when files
+  # are listed without --file.  Let's play safe and only enable the eval
+  # if we detect the quoting.
+  case $CONFIG_FILES in
+  *\'*) eval set x "$CONFIG_FILES" ;;
+  *)   set x $CONFIG_FILES ;;
+  esac
+  shift
+  for mf
+  do
+    # Strip MF so we end up with the name of the file.
+    mf=`echo "$mf" | sed -e 's/:.*$//'`
+    # Check whether this is an Automake generated Makefile or not.
+    # We used to match only the files named 'Makefile.in', but
+    # some people rename them; so instead we look at the file content.
+    # Grep'ing the first line is not enough: some people post-process
+    # each Makefile.in and add a new line on top of each file to say so.
+    # Grep'ing the whole file is not good either: AIX grep has a line
+    # limit of 2048, but all sed's we know have understand at least 4000.
+    if sed -n 's,^#.*generated by automake.*,X,p' "$mf" | grep X >/dev/null 2>&1; then
+      dirpart=`AS_DIRNAME("$mf")`
+    else
+      continue
+    fi
+    # Extract the definition of DEPDIR, am__include, and am__quote
+    # from the Makefile without running 'make'.
+    DEPDIR=`sed -n 's/^DEPDIR = //p' < "$mf"`
+    test -z "$DEPDIR" && continue
+    am__include=`sed -n 's/^am__include = //p' < "$mf"`
+    test -z "$am__include" && continue
+    am__quote=`sed -n 's/^am__quote = //p' < "$mf"`
+    # Find all dependency output files, they are included files with
+    # $(DEPDIR) in their names.  We invoke sed twice because it is the
+    # simplest approach to changing $(DEPDIR) to its actual value in the
+    # expansion.
+    for file in `sed -n "
+      s/^$am__include $am__quote\(.*(DEPDIR).*\)$am__quote"'$/\1/p' <"$mf" | \
+	 sed -e 's/\$(DEPDIR)/'"$DEPDIR"'/g'`; do
+      # Make sure the directory exists.
+      test -f "$dirpart/$file" && continue
+      fdir=`AS_DIRNAME(["$file"])`
+      AS_MKDIR_P([$dirpart/$fdir])
+      # echo "creating $dirpart/$file"
+      echo '# dummy' > "$dirpart/$file"
+    done
+  done
+}
+])# _AM_OUTPUT_DEPENDENCY_COMMANDS
+
+
+# AM_OUTPUT_DEPENDENCY_COMMANDS
+# -----------------------------
+# This macro should only be invoked once -- use via AC_REQUIRE.
+#
+# This code is only required when automatic dependency tracking
+# is enabled.  FIXME.  This creates each '.P' file that we will
+# need in order to bootstrap the dependency handling code.
+AC_DEFUN([AM_OUTPUT_DEPENDENCY_COMMANDS],
+[AC_CONFIG_COMMANDS([depfiles],
+     [test x"$AMDEP_TRUE" != x"" || _AM_OUTPUT_DEPENDENCY_COMMANDS],
+     [AMDEP_TRUE="$AMDEP_TRUE" ac_aux_dir="$ac_aux_dir"])
+])
+
+# Do all the work for Automake.                             -*- Autoconf -*-
+
+# Copyright (C) 1996-2013 Free Software Foundation, Inc.
+#
+# This file is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This macro actually does too much.  Some checks are only needed if
+# your package does certain things.  But this isn't really a big deal.
+
+# AM_INIT_AUTOMAKE(PACKAGE, VERSION, [NO-DEFINE])
+# AM_INIT_AUTOMAKE([OPTIONS])
+# -----------------------------------------------
+# The call with PACKAGE and VERSION arguments is the old style
+# call (pre autoconf-2.50), which is being phased out.  PACKAGE
+# and VERSION should now be passed to AC_INIT and removed from
+# the call to AM_INIT_AUTOMAKE.
+# We support both call styles for the transition.  After
+# the next Automake release, Autoconf can make the AC_INIT
+# arguments mandatory, and then we can depend on a new Autoconf
+# release and drop the old call support.
+AC_DEFUN([AM_INIT_AUTOMAKE],
+[AC_PREREQ([2.65])dnl
+dnl Autoconf wants to disallow AM_ names.  We explicitly allow
+dnl the ones we care about.
+m4_pattern_allow([^AM_[A-Z]+FLAGS$])dnl
+AC_REQUIRE([AM_SET_CURRENT_AUTOMAKE_VERSION])dnl
+AC_REQUIRE([AC_PROG_INSTALL])dnl
+if test "`cd $srcdir && pwd`" != "`pwd`"; then
+  # Use -I$(srcdir) only when $(srcdir) != ., so that make's output
+  # is not polluted with repeated "-I."
+  AC_SUBST([am__isrc], [' -I$(srcdir)'])_AM_SUBST_NOTMAKE([am__isrc])dnl
+  # test to see if srcdir already configured
+  if test -f $srcdir/config.status; then
+    AC_MSG_ERROR([source directory already configured; run "make distclean" there first])
+  fi
+fi
+
+# test whether we have cygpath
+if test -z "$CYGPATH_W"; then
+  if (cygpath --version) >/dev/null 2>/dev/null; then
+    CYGPATH_W='cygpath -w'
+  else
+    CYGPATH_W=echo
+  fi
+fi
+AC_SUBST([CYGPATH_W])
+
+# Define the identity of the package.
+dnl Distinguish between old-style and new-style calls.
+m4_ifval([$2],
+[AC_DIAGNOSE([obsolete],
+             [$0: two- and three-arguments forms are deprecated.])
+m4_ifval([$3], [_AM_SET_OPTION([no-define])])dnl
+ AC_SUBST([PACKAGE], [$1])dnl
+ AC_SUBST([VERSION], [$2])],
+[_AM_SET_OPTIONS([$1])dnl
+dnl Diagnose old-style AC_INIT with new-style AM_AUTOMAKE_INIT.
+m4_if(
+  m4_ifdef([AC_PACKAGE_NAME], [ok]):m4_ifdef([AC_PACKAGE_VERSION], [ok]),
+  [ok:ok],,
+  [m4_fatal([AC_INIT should be called with package and version arguments])])dnl
+ AC_SUBST([PACKAGE], ['AC_PACKAGE_TARNAME'])dnl
+ AC_SUBST([VERSION], ['AC_PACKAGE_VERSION'])])dnl
+
+_AM_IF_OPTION([no-define],,
+[AC_DEFINE_UNQUOTED([PACKAGE], ["$PACKAGE"], [Name of package])
+ AC_DEFINE_UNQUOTED([VERSION], ["$VERSION"], [Version number of package])])dnl
+
+# Some tools Automake needs.
+AC_REQUIRE([AM_SANITY_CHECK])dnl
+AC_REQUIRE([AC_ARG_PROGRAM])dnl
+AM_MISSING_PROG([ACLOCAL], [aclocal-${am__api_version}])
+AM_MISSING_PROG([AUTOCONF], [autoconf])
+AM_MISSING_PROG([AUTOMAKE], [automake-${am__api_version}])
+AM_MISSING_PROG([AUTOHEADER], [autoheader])
+AM_MISSING_PROG([MAKEINFO], [makeinfo])
+AC_REQUIRE([AM_PROG_INSTALL_SH])dnl
+AC_REQUIRE([AM_PROG_INSTALL_STRIP])dnl
+AC_REQUIRE([AC_PROG_MKDIR_P])dnl
+# For better backward compatibility.  To be removed once Automake 1.9.x
+# dies out for good.  For more background, see:
+# <http://lists.gnu.org/archive/html/automake/2012-07/msg00001.html>
+# <http://lists.gnu.org/archive/html/automake/2012-07/msg00014.html>
+AC_SUBST([mkdir_p], ['$(MKDIR_P)'])
+# We need awk for the "check" target.  The system "awk" is bad on
+# some platforms.
+AC_REQUIRE([AC_PROG_AWK])dnl
+AC_REQUIRE([AC_PROG_MAKE_SET])dnl
+AC_REQUIRE([AM_SET_LEADING_DOT])dnl
+_AM_IF_OPTION([tar-ustar], [_AM_PROG_TAR([ustar])],
+	      [_AM_IF_OPTION([tar-pax], [_AM_PROG_TAR([pax])],
+			     [_AM_PROG_TAR([v7])])])
+_AM_IF_OPTION([no-dependencies],,
+[AC_PROVIDE_IFELSE([AC_PROG_CC],
+		  [_AM_DEPENDENCIES([CC])],
+		  [m4_define([AC_PROG_CC],
+			     m4_defn([AC_PROG_CC])[_AM_DEPENDENCIES([CC])])])dnl
+AC_PROVIDE_IFELSE([AC_PROG_CXX],
+		  [_AM_DEPENDENCIES([CXX])],
+		  [m4_define([AC_PROG_CXX],
+			     m4_defn([AC_PROG_CXX])[_AM_DEPENDENCIES([CXX])])])dnl
+AC_PROVIDE_IFELSE([AC_PROG_OBJC],
+		  [_AM_DEPENDENCIES([OBJC])],
+		  [m4_define([AC_PROG_OBJC],
+			     m4_defn([AC_PROG_OBJC])[_AM_DEPENDENCIES([OBJC])])])dnl
+AC_PROVIDE_IFELSE([AC_PROG_OBJCXX],
+		  [_AM_DEPENDENCIES([OBJCXX])],
+		  [m4_define([AC_PROG_OBJCXX],
+			     m4_defn([AC_PROG_OBJCXX])[_AM_DEPENDENCIES([OBJCXX])])])dnl
+])
+AC_REQUIRE([AM_SILENT_RULES])dnl
+dnl The testsuite driver may need to know about EXEEXT, so add the
+dnl 'am__EXEEXT' conditional if _AM_COMPILER_EXEEXT was seen.  This
+dnl macro is hooked onto _AC_COMPILER_EXEEXT early, see below.
+AC_CONFIG_COMMANDS_PRE(dnl
+[m4_provide_if([_AM_COMPILER_EXEEXT],
+  [AM_CONDITIONAL([am__EXEEXT], [test -n "$EXEEXT"])])])dnl
+])
+
+dnl Hook into '_AC_COMPILER_EXEEXT' early to learn its expansion.  Do not
+dnl add the conditional right here, as _AC_COMPILER_EXEEXT may be further
+dnl mangled by Autoconf and run in a shell conditional statement.
+m4_define([_AC_COMPILER_EXEEXT],
+m4_defn([_AC_COMPILER_EXEEXT])[m4_provide([_AM_COMPILER_EXEEXT])])
+
+
+# When config.status generates a header, we must update the stamp-h file.
+# This file resides in the same directory as the config header
+# that is generated.  The stamp files are numbered to have different names.
+
+# Autoconf calls _AC_AM_CONFIG_HEADER_HOOK (when defined) in the
+# loop where config.status creates the headers, so we can generate
+# our stamp files there.
+AC_DEFUN([_AC_AM_CONFIG_HEADER_HOOK],
+[# Compute $1's index in $config_headers.
+_am_arg=$1
+_am_stamp_count=1
+for _am_header in $config_headers :; do
+  case $_am_header in
+    $_am_arg | $_am_arg:* )
+      break ;;
+    * )
+      _am_stamp_count=`expr $_am_stamp_count + 1` ;;
+  esac
+done
+echo "timestamp for $_am_arg" >`AS_DIRNAME(["$_am_arg"])`/stamp-h[]$_am_stamp_count])
+
+# Copyright (C) 2001-2013 Free Software Foundation, Inc.
+#
+# This file is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# AM_PROG_INSTALL_SH
+# ------------------
+# Define $install_sh.
+AC_DEFUN([AM_PROG_INSTALL_SH],
+[AC_REQUIRE([AM_AUX_DIR_EXPAND])dnl
+if test x"${install_sh}" != xset; then
+  case $am_aux_dir in
+  *\ * | *\	*)
+    install_sh="\${SHELL} '$am_aux_dir/install-sh'" ;;
+  *)
+    install_sh="\${SHELL} $am_aux_dir/install-sh"
+  esac
+fi
+AC_SUBST([install_sh])])
+
+# Copyright (C) 2003-2013 Free Software Foundation, Inc.
+#
+# This file is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# Check whether the underlying file-system supports filenames
+# with a leading dot.  For instance MS-DOS doesn't.
+AC_DEFUN([AM_SET_LEADING_DOT],
+[rm -rf .tst 2>/dev/null
+mkdir .tst 2>/dev/null
+if test -d .tst; then
+  am__leading_dot=.
+else
+  am__leading_dot=_
+fi
+rmdir .tst 2>/dev/null
+AC_SUBST([am__leading_dot])])
+
+# Add --enable-maintainer-mode option to configure.         -*- Autoconf -*-
+# From Jim Meyering
+
+# Copyright (C) 1996-2013 Free Software Foundation, Inc.
+#
+# This file is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# AM_MAINTAINER_MODE([DEFAULT-MODE])
+# ----------------------------------
+# Control maintainer-specific portions of Makefiles.
+# Default is to disable them, unless 'enable' is passed literally.
+# For symmetry, 'disable' may be passed as well.  Anyway, the user
+# can override the default with the --enable/--disable switch.
+AC_DEFUN([AM_MAINTAINER_MODE],
+[m4_case(m4_default([$1], [disable]),
+       [enable], [m4_define([am_maintainer_other], [disable])],
+       [disable], [m4_define([am_maintainer_other], [enable])],
+       [m4_define([am_maintainer_other], [enable])
+        m4_warn([syntax], [unexpected argument to AM@&t@_MAINTAINER_MODE: $1])])
+AC_MSG_CHECKING([whether to enable maintainer-specific portions of Makefiles])
+  dnl maintainer-mode's default is 'disable' unless 'enable' is passed
+  AC_ARG_ENABLE([maintainer-mode],
+    [AS_HELP_STRING([--]am_maintainer_other[-maintainer-mode],
+      am_maintainer_other[ make rules and dependencies not useful
+      (and sometimes confusing) to the casual installer])],
+    [USE_MAINTAINER_MODE=$enableval],
+    [USE_MAINTAINER_MODE=]m4_if(am_maintainer_other, [enable], [no], [yes]))
+  AC_MSG_RESULT([$USE_MAINTAINER_MODE])
+  AM_CONDITIONAL([MAINTAINER_MODE], [test $USE_MAINTAINER_MODE = yes])
+  MAINT=$MAINTAINER_MODE_TRUE
+  AC_SUBST([MAINT])dnl
+]
+)
+
+# Check to see how 'make' treats includes.	            -*- Autoconf -*-
+
+# Copyright (C) 2001-2013 Free Software Foundation, Inc.
+#
+# This file is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# AM_MAKE_INCLUDE()
+# -----------------
+# Check to see how make treats includes.
+AC_DEFUN([AM_MAKE_INCLUDE],
+[am_make=${MAKE-make}
+cat > confinc << 'END'
+am__doit:
+	@echo this is the am__doit target
+.PHONY: am__doit
+END
+# If we don't find an include directive, just comment out the code.
+AC_MSG_CHECKING([for style of include used by $am_make])
+am__include="#"
+am__quote=
+_am_result=none
+# First try GNU make style include.
+echo "include confinc" > confmf
+# Ignore all kinds of additional output from 'make'.
+case `$am_make -s -f confmf 2> /dev/null` in #(
+*the\ am__doit\ target*)
+  am__include=include
+  am__quote=
+  _am_result=GNU
+  ;;
+esac
+# Now try BSD make style include.
+if test "$am__include" = "#"; then
+   echo '.include "confinc"' > confmf
+   case `$am_make -s -f confmf 2> /dev/null` in #(
+   *the\ am__doit\ target*)
+     am__include=.include
+     am__quote="\""
+     _am_result=BSD
+     ;;
+   esac
+fi
+AC_SUBST([am__include])
+AC_SUBST([am__quote])
+AC_MSG_RESULT([$_am_result])
+rm -f confinc confmf
+])
+
+# Copyright (C) 1999-2013 Free Software Foundation, Inc.
+#
+# This file is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# AM_PROG_CC_C_O
+# --------------
+# Like AC_PROG_CC_C_O, but changed for automake.
+AC_DEFUN([AM_PROG_CC_C_O],
+[AC_REQUIRE([AC_PROG_CC_C_O])dnl
+AC_REQUIRE([AM_AUX_DIR_EXPAND])dnl
+AC_REQUIRE_AUX_FILE([compile])dnl
+# FIXME: we rely on the cache variable name because
+# there is no other way.
+set dummy $CC
+am_cc=`echo $[2] | sed ['s/[^a-zA-Z0-9_]/_/g;s/^[0-9]/_/']`
+eval am_t=\$ac_cv_prog_cc_${am_cc}_c_o
+if test "$am_t" != yes; then
+   # Losing compiler, so override with the script.
+   # FIXME: It is wrong to rewrite CC.
+   # But if we don't then we get into trouble of one sort or another.
+   # A longer-term fix would be to have automake use am__CC in this case,
+   # and then we could set am__CC="\$(top_srcdir)/compile \$(CC)"
+   CC="$am_aux_dir/compile $CC"
+fi
+dnl Make sure AC_PROG_CC is never called again, or it will override our
+dnl setting of CC.
+m4_define([AC_PROG_CC],
+          [m4_fatal([AC_PROG_CC cannot be called after AM_PROG_CC_C_O])])
+])
+
+# Fake the existence of programs that GNU maintainers use.  -*- Autoconf -*-
+
+# Copyright (C) 1997-2013 Free Software Foundation, Inc.
+#
+# This file is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# AM_MISSING_PROG(NAME, PROGRAM)
+# ------------------------------
+AC_DEFUN([AM_MISSING_PROG],
+[AC_REQUIRE([AM_MISSING_HAS_RUN])
+$1=${$1-"${am_missing_run}$2"}
+AC_SUBST($1)])
+
+# AM_MISSING_HAS_RUN
+# ------------------
+# Define MISSING if not defined so far and test if it is modern enough.
+# If it is, set am_missing_run to use it, otherwise, to nothing.
+AC_DEFUN([AM_MISSING_HAS_RUN],
+[AC_REQUIRE([AM_AUX_DIR_EXPAND])dnl
+AC_REQUIRE_AUX_FILE([missing])dnl
+if test x"${MISSING+set}" != xset; then
+  case $am_aux_dir in
+  *\ * | *\	*)
+    MISSING="\${SHELL} \"$am_aux_dir/missing\"" ;;
+  *)
+    MISSING="\${SHELL} $am_aux_dir/missing" ;;
+  esac
+fi
+# Use eval to expand $SHELL
+if eval "$MISSING --is-lightweight"; then
+  am_missing_run="$MISSING "
+else
+  am_missing_run=
+  AC_MSG_WARN(['missing' script is too old or missing])
+fi
+])
+
+# Helper functions for option handling.                     -*- Autoconf -*-
+
+# Copyright (C) 2001-2013 Free Software Foundation, Inc.
+#
+# This file is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# _AM_MANGLE_OPTION(NAME)
+# -----------------------
+AC_DEFUN([_AM_MANGLE_OPTION],
+[[_AM_OPTION_]m4_bpatsubst($1, [[^a-zA-Z0-9_]], [_])])
+
+# _AM_SET_OPTION(NAME)
+# --------------------
+# Set option NAME.  Presently that only means defining a flag for this option.
+AC_DEFUN([_AM_SET_OPTION],
+[m4_define(_AM_MANGLE_OPTION([$1]), [1])])
+
+# _AM_SET_OPTIONS(OPTIONS)
+# ------------------------
+# OPTIONS is a space-separated list of Automake options.
+AC_DEFUN([_AM_SET_OPTIONS],
+[m4_foreach_w([_AM_Option], [$1], [_AM_SET_OPTION(_AM_Option)])])
+
+# _AM_IF_OPTION(OPTION, IF-SET, [IF-NOT-SET])
+# -------------------------------------------
+# Execute IF-SET if OPTION is set, IF-NOT-SET otherwise.
+AC_DEFUN([_AM_IF_OPTION],
+[m4_ifset(_AM_MANGLE_OPTION([$1]), [$2], [$3])])
+
+# Copyright (C) 2001-2013 Free Software Foundation, Inc.
+#
+# This file is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# AM_RUN_LOG(COMMAND)
+# -------------------
+# Run COMMAND, save the exit status in ac_status, and log it.
+# (This has been adapted from Autoconf's _AC_RUN_LOG macro.)
+AC_DEFUN([AM_RUN_LOG],
+[{ echo "$as_me:$LINENO: $1" >&AS_MESSAGE_LOG_FD
+   ($1) >&AS_MESSAGE_LOG_FD 2>&AS_MESSAGE_LOG_FD
+   ac_status=$?
+   echo "$as_me:$LINENO: \$? = $ac_status" >&AS_MESSAGE_LOG_FD
+   (exit $ac_status); }])
+
+# Check to make sure that the build environment is sane.    -*- Autoconf -*-
+
+# Copyright (C) 1996-2013 Free Software Foundation, Inc.
+#
+# This file is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# AM_SANITY_CHECK
+# ---------------
+AC_DEFUN([AM_SANITY_CHECK],
+[AC_MSG_CHECKING([whether build environment is sane])
+# Reject unsafe characters in $srcdir or the absolute working directory
+# name.  Accept space and tab only in the latter.
+am_lf='
+'
+case `pwd` in
+  *[[\\\"\#\$\&\'\`$am_lf]]*)
+    AC_MSG_ERROR([unsafe absolute working directory name]);;
+esac
+case $srcdir in
+  *[[\\\"\#\$\&\'\`$am_lf\ \	]]*)
+    AC_MSG_ERROR([unsafe srcdir value: '$srcdir']);;
+esac
+
+# Do 'set' in a subshell so we don't clobber the current shell's
+# arguments.  Must try -L first in case configure is actually a
+# symlink; some systems play weird games with the mod time of symlinks
+# (eg FreeBSD returns the mod time of the symlink's containing
+# directory).
+if (
+   am_has_slept=no
+   for am_try in 1 2; do
+     echo "timestamp, slept: $am_has_slept" > conftest.file
+     set X `ls -Lt "$srcdir/configure" conftest.file 2> /dev/null`
+     if test "$[*]" = "X"; then
+	# -L didn't work.
+	set X `ls -t "$srcdir/configure" conftest.file`
+     fi
+     if test "$[*]" != "X $srcdir/configure conftest.file" \
+	&& test "$[*]" != "X conftest.file $srcdir/configure"; then
+
+	# If neither matched, then we have a broken ls.  This can happen
+	# if, for instance, CONFIG_SHELL is bash and it inherits a
+	# broken ls alias from the environment.  This has actually
+	# happened.  Such a system could not be considered "sane".
+	AC_MSG_ERROR([ls -t appears to fail.  Make sure there is not a broken
+  alias in your environment])
+     fi
+     if test "$[2]" = conftest.file || test $am_try -eq 2; then
+       break
+     fi
+     # Just in case.
+     sleep 1
+     am_has_slept=yes
+   done
+   test "$[2]" = conftest.file
+   )
+then
+   # Ok.
+   :
+else
+   AC_MSG_ERROR([newly created file is older than distributed files!
+Check your system clock])
+fi
+AC_MSG_RESULT([yes])
+# If we didn't sleep, we still need to ensure time stamps of config.status and
+# generated files are strictly newer.
+am_sleep_pid=
+if grep 'slept: no' conftest.file >/dev/null 2>&1; then
+  ( sleep 1 ) &
+  am_sleep_pid=$!
+fi
+AC_CONFIG_COMMANDS_PRE(
+  [AC_MSG_CHECKING([that generated files are newer than configure])
+   if test -n "$am_sleep_pid"; then
+     # Hide warnings about reused PIDs.
+     wait $am_sleep_pid 2>/dev/null
+   fi
+   AC_MSG_RESULT([done])])
+rm -f conftest.file
+])
+
+# Copyright (C) 2009-2013 Free Software Foundation, Inc.
+#
+# This file is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# AM_SILENT_RULES([DEFAULT])
+# --------------------------
+# Enable less verbose build rules; with the default set to DEFAULT
+# ("yes" being less verbose, "no" or empty being verbose).
+AC_DEFUN([AM_SILENT_RULES],
+[AC_ARG_ENABLE([silent-rules], [dnl
+AS_HELP_STRING(
+  [--enable-silent-rules],
+  [less verbose build output (undo: "make V=1")])
+AS_HELP_STRING(
+  [--disable-silent-rules],
+  [verbose build output (undo: "make V=0")])dnl
+])
+case $enable_silent_rules in @%:@ (((
+  yes) AM_DEFAULT_VERBOSITY=0;;
+   no) AM_DEFAULT_VERBOSITY=1;;
+    *) AM_DEFAULT_VERBOSITY=m4_if([$1], [yes], [0], [1]);;
+esac
+dnl
+dnl A few 'make' implementations (e.g., NonStop OS and NextStep)
+dnl do not support nested variable expansions.
+dnl See automake bug#9928 and bug#10237.
+am_make=${MAKE-make}
+AC_CACHE_CHECK([whether $am_make supports nested variables],
+   [am_cv_make_support_nested_variables],
+   [if AS_ECHO([['TRUE=$(BAR$(V))
+BAR0=false
+BAR1=true
+V=1
+am__doit:
+	@$(TRUE)
+.PHONY: am__doit']]) | $am_make -f - >/dev/null 2>&1; then
+  am_cv_make_support_nested_variables=yes
+else
+  am_cv_make_support_nested_variables=no
+fi])
+if test $am_cv_make_support_nested_variables = yes; then
+  dnl Using '$V' instead of '$(V)' breaks IRIX make.
+  AM_V='$(V)'
+  AM_DEFAULT_V='$(AM_DEFAULT_VERBOSITY)'
+else
+  AM_V=$AM_DEFAULT_VERBOSITY
+  AM_DEFAULT_V=$AM_DEFAULT_VERBOSITY
+fi
+AC_SUBST([AM_V])dnl
+AM_SUBST_NOTMAKE([AM_V])dnl
+AC_SUBST([AM_DEFAULT_V])dnl
+AM_SUBST_NOTMAKE([AM_DEFAULT_V])dnl
+AC_SUBST([AM_DEFAULT_VERBOSITY])dnl
+AM_BACKSLASH='\'
+AC_SUBST([AM_BACKSLASH])dnl
+_AM_SUBST_NOTMAKE([AM_BACKSLASH])dnl
+])
+
+# Copyright (C) 2001-2013 Free Software Foundation, Inc.
+#
+# This file is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# AM_PROG_INSTALL_STRIP
+# ---------------------
+# One issue with vendor 'install' (even GNU) is that you can't
+# specify the program used to strip binaries.  This is especially
+# annoying in cross-compiling environments, where the build's strip
+# is unlikely to handle the host's binaries.
+# Fortunately install-sh will honor a STRIPPROG variable, so we
+# always use install-sh in "make install-strip", and initialize
+# STRIPPROG with the value of the STRIP variable (set by the user).
+AC_DEFUN([AM_PROG_INSTALL_STRIP],
+[AC_REQUIRE([AM_PROG_INSTALL_SH])dnl
+# Installed binaries are usually stripped using 'strip' when the user
+# run "make install-strip".  However 'strip' might not be the right
+# tool to use in cross-compilation environments, therefore Automake
+# will honor the 'STRIP' environment variable to overrule this program.
+dnl Don't test for $cross_compiling = yes, because it might be 'maybe'.
+if test "$cross_compiling" != no; then
+  AC_CHECK_TOOL([STRIP], [strip], :)
+fi
+INSTALL_STRIP_PROGRAM="\$(install_sh) -c -s"
+AC_SUBST([INSTALL_STRIP_PROGRAM])])
+
+# Copyright (C) 2006-2013 Free Software Foundation, Inc.
+#
+# This file is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# _AM_SUBST_NOTMAKE(VARIABLE)
+# ---------------------------
+# Prevent Automake from outputting VARIABLE = @VARIABLE@ in Makefile.in.
+# This macro is traced by Automake.
+AC_DEFUN([_AM_SUBST_NOTMAKE])
+
+# AM_SUBST_NOTMAKE(VARIABLE)
+# --------------------------
+# Public sister of _AM_SUBST_NOTMAKE.
+AC_DEFUN([AM_SUBST_NOTMAKE], [_AM_SUBST_NOTMAKE($@)])
+
+# Check how to create a tarball.                            -*- Autoconf -*-
+
+# Copyright (C) 2004-2013 Free Software Foundation, Inc.
+#
+# This file is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# _AM_PROG_TAR(FORMAT)
+# --------------------
+# Check how to create a tarball in format FORMAT.
+# FORMAT should be one of 'v7', 'ustar', or 'pax'.
+#
+# Substitute a variable $(am__tar) that is a command
+# writing to stdout a FORMAT-tarball containing the directory
+# $tardir.
+#     tardir=directory && $(am__tar) > result.tar
+#
+# Substitute a variable $(am__untar) that extract such
+# a tarball read from stdin.
+#     $(am__untar) < result.tar
+#
+AC_DEFUN([_AM_PROG_TAR],
+[# Always define AMTAR for backward compatibility.  Yes, it's still used
+# in the wild :-(  We should find a proper way to deprecate it ...
+AC_SUBST([AMTAR], ['$${TAR-tar}'])
+
+# We'll loop over all known methods to create a tar archive until one works.
+_am_tools='gnutar m4_if([$1], [ustar], [plaintar]) pax cpio none'
+
+m4_if([$1], [v7],
+  [am__tar='$${TAR-tar} chof - "$$tardir"' am__untar='$${TAR-tar} xf -'],
+
+  [m4_case([$1],
+    [ustar],
+     [# The POSIX 1988 'ustar' format is defined with fixed-size fields.
+      # There is notably a 21 bits limit for the UID and the GID.  In fact,
+      # the 'pax' utility can hang on bigger UID/GID (see automake bug#8343
+      # and bug#13588).
+      am_max_uid=2097151 # 2^21 - 1
+      am_max_gid=$am_max_uid
+      # The $UID and $GID variables are not portable, so we need to resort
+      # to the POSIX-mandated id(1) utility.  Errors in the 'id' calls
+      # below are definitely unexpected, so allow the users to see them
+      # (that is, avoid stderr redirection).
+      am_uid=`id -u || echo unknown`
+      am_gid=`id -g || echo unknown`
+      AC_MSG_CHECKING([whether UID '$am_uid' is supported by ustar format])
+      if test $am_uid -le $am_max_uid; then
+         AC_MSG_RESULT([yes])
+      else
+         AC_MSG_RESULT([no])
+         _am_tools=none
+      fi
+      AC_MSG_CHECKING([whether GID '$am_gid' is supported by ustar format])
+      if test $am_gid -le $am_max_gid; then
+         AC_MSG_RESULT([yes])
+      else
+        AC_MSG_RESULT([no])
+        _am_tools=none
+      fi],
+
+  [pax],
+    [],
+
+  [m4_fatal([Unknown tar format])])
+
+  AC_MSG_CHECKING([how to create a $1 tar archive])
+
+  # Go ahead even if we have the value already cached.  We do so because we
+  # need to set the values for the 'am__tar' and 'am__untar' variables.
+  _am_tools=${am_cv_prog_tar_$1-$_am_tools}
+
+  for _am_tool in $_am_tools; do
+    case $_am_tool in
+    gnutar)
+      for _am_tar in tar gnutar gtar; do
+        AM_RUN_LOG([$_am_tar --version]) && break
+      done
+      am__tar="$_am_tar --format=m4_if([$1], [pax], [posix], [$1]) -chf - "'"$$tardir"'
+      am__tar_="$_am_tar --format=m4_if([$1], [pax], [posix], [$1]) -chf - "'"$tardir"'
+      am__untar="$_am_tar -xf -"
+      ;;
+    plaintar)
+      # Must skip GNU tar: if it does not support --format= it doesn't create
+      # ustar tarball either.
+      (tar --version) >/dev/null 2>&1 && continue
+      am__tar='tar chf - "$$tardir"'
+      am__tar_='tar chf - "$tardir"'
+      am__untar='tar xf -'
+      ;;
+    pax)
+      am__tar='pax -L -x $1 -w "$$tardir"'
+      am__tar_='pax -L -x $1 -w "$tardir"'
+      am__untar='pax -r'
+      ;;
+    cpio)
+      am__tar='find "$$tardir" -print | cpio -o -H $1 -L'
+      am__tar_='find "$tardir" -print | cpio -o -H $1 -L'
+      am__untar='cpio -i -H $1 -d'
+      ;;
+    none)
+      am__tar=false
+      am__tar_=false
+      am__untar=false
+      ;;
+    esac
+
+    # If the value was cached, stop now.  We just wanted to have am__tar
+    # and am__untar set.
+    test -n "${am_cv_prog_tar_$1}" && break
+
+    # tar/untar a dummy directory, and stop if the command works.
+    rm -rf conftest.dir
+    mkdir conftest.dir
+    echo GrepMe > conftest.dir/file
+    AM_RUN_LOG([tardir=conftest.dir && eval $am__tar_ >conftest.tar])
+    rm -rf conftest.dir
+    if test -s conftest.tar; then
+      AM_RUN_LOG([$am__untar <conftest.tar])
+      AM_RUN_LOG([cat conftest.dir/file])
+      grep GrepMe conftest.dir/file >/dev/null 2>&1 && break
+    fi
+  done
+  rm -rf conftest.dir
+
+  AC_CACHE_VAL([am_cv_prog_tar_$1], [am_cv_prog_tar_$1=$_am_tool])
+  AC_MSG_RESULT([$am_cv_prog_tar_$1])])
+
+AC_SUBST([am__tar])
+AC_SUBST([am__untar])
+]) # _AM_PROG_TAR
+
+m4_include([acinclude.m4])
diff --git a/bluez/android/Android.mk b/bluez/android/Android.mk
new file mode 100644
index 0000000..0e0932d
--- /dev/null
+++ b/bluez/android/Android.mk
@@ -0,0 +1,468 @@
+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/"`
+
+# 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_STORAGEDIR=\"/data/misc/bluetooth\" \
+
+# 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/android/hidhost.c \
+	bluez/android/socket.c \
+	bluez/android/ipc.c \
+	bluez/android/avdtp.c \
+	bluez/android/a2dp.c \
+	bluez/android/avctp.c \
+	bluez/android/avrcp.c \
+	bluez/android/avrcp-lib.c \
+	bluez/android/pan.c \
+	bluez/android/handsfree.c \
+	bluez/android/gatt.c \
+	bluez/android/health.c \
+	bluez/src/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/io-glib.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_PATH)/bluez/lib \
+
+LOCAL_CFLAGS := $(BLUEZ_COMMON_CFLAGS)
+
+LOCAL_SHARED_LIBRARIES := \
+	libglib \
+
+lib_headers := \
+	bluetooth.h \
+	hci.h \
+	hci_lib.h \
+	l2cap.h \
+	sdp_lib.h \
+	sdp.h \
+	rfcomm.h \
+	sco.h \
+	bnep.h \
+
+$(shell mkdir -p $(LOCAL_PATH)/bluez/lib/bluetooth)
+
+$(foreach file,$(lib_headers), $(shell ln -sf ../$(file) $(LOCAL_PATH)/bluez/lib/bluetooth/$(file)))
+
+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
+
+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 \
+
+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_PATH := $(TARGET_OUT_SHARED_LIBRARIES)/hw
+LOCAL_MODULE_TAGS := optional
+LOCAL_MODULE_CLASS := SHARED_LIBRARIES
+LOCAL_REQUIRED_MODULES := bluetoothd bluetoothd-snoop init.bluetooth.rc
+
+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-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-sock.c \
+	bluez/android/client/if-gatt.c \
+	bluez/android/hal-utils.c \
+
+LOCAL_C_INCLUDES += \
+	$(call include-path-for, system-core) \
+	$(call include-path-for, libhardware) \
+
+LOCAL_CFLAGS := $(BLUEZ_COMMON_CFLAGS)
+
+LOCAL_SHARED_LIBRARIES := libhardware
+
+LOCAL_MODULE_PATH := $(TARGET_OUT_OPTIONAL_EXECUTABLES)
+LOCAL_MODULE_TAGS := debug
+LOCAL_MODULE := haltest
+
+include $(BUILD_EXECUTABLE)
+
+#
+# btmon
+#
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := \
+	bluez/monitor/main.c \
+	bluez/monitor/mainloop.c \
+	bluez/monitor/display.c \
+	bluez/monitor/hcidump.c \
+	bluez/monitor/control.c \
+	bluez/monitor/packet.c \
+	bluez/monitor/l2cap.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/src/shared/util.c \
+	bluez/src/shared/queue.c \
+	bluez/src/shared/crypto.c \
+	bluez/src/shared/btsnoop.c \
+	bluez/lib/hci.c \
+	bluez/lib/bluetooth.c \
+
+LOCAL_C_INCLUDES := \
+	$(LOCAL_PATH)/bluez \
+	$(LOCAL_PATH)/bluez/lib \
+
+LOCAL_CFLAGS := $(BLUEZ_COMMON_CFLAGS)
+
+LOCAL_MODULE_PATH := $(TARGET_OUT_OPTIONAL_EXECUTABLES)
+LOCAL_MODULE_TAGS := debug
+LOCAL_MODULE := btmon
+
+include $(BUILD_EXECUTABLE)
+
+#
+# btproxy
+#
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := \
+	bluez/tools/btproxy.c \
+	bluez/monitor/mainloop.c \
+	bluez/src/shared/util.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
+
+include $(BUILD_EXECUTABLE)
+
+#
+# A2DP audio
+#
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := bluez/android/hal-audio.c
+
+LOCAL_C_INCLUDES = \
+	$(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)
+
+LOCAL_MODULE_PATH := $(TARGET_OUT_SHARED_LIBRARIES)/hw
+LOCAL_MODULE_TAGS := optional
+LOCAL_MODULE := audio.a2dp.default
+
+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_PATH)/bluez/lib \
+
+LOCAL_CFLAGS := $(BLUEZ_COMMON_CFLAGS)
+
+LOCAL_MODULE_PATH := $(TARGET_OUT_OPTIONAL_EXECUTABLES)
+LOCAL_MODULE_TAGS := debug
+LOCAL_MODULE := l2test
+
+include $(BUILD_EXECUTABLE)
+
+#
+# bluetoothd-snoop
+#
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := \
+	bluez/android/bluetoothd-snoop.c \
+	bluez/monitor/mainloop.c \
+	bluez/src/shared/btsnoop.c \
+
+LOCAL_C_INCLUDES := \
+	$(LOCAL_PATH)/bluez \
+	$(LOCAL_PATH)/bluez/lib \
+
+LOCAL_CFLAGS := $(BLUEZ_COMMON_CFLAGS)
+
+LOCAL_MODULE_TAGS := optional
+LOCAL_MODULE := bluetoothd-snoop
+
+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/sdp.c \
+	bluez/monitor/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/uuid-helper.c \
+
+LOCAL_C_INCLUDES := \
+	$(LOCAL_PATH)/bluez \
+	$(LOCAL_PATH)/bluez/lib \
+
+LOCAL_CFLAGS := $(BLUEZ_COMMON_CFLAGS)
+
+LOCAL_MODULE_PATH := $(TARGET_OUT_OPTIONAL_EXECUTABLES)
+LOCAL_MODULE_TAGS := debug
+LOCAL_MODULE := btmgmt
+
+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/lib \
+
+LOCAL_CFLAGS := $(BLUEZ_COMMON_CFLAGS)
+
+LOCAL_MODULE_PATH := $(TARGET_OUT_OPTIONAL_EXECUTABLES)
+LOCAL_MODULE_TAGS := debug
+LOCAL_MODULE := l2ping
+
+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/lib \
+
+LOCAL_CFLAGS := $(BLUEZ_COMMON_CFLAGS)
+
+LOCAL_MODULE_PATH := $(TARGET_OUT_OPTIONAL_EXECUTABLES)
+LOCAL_MODULE_TAGS := debug
+LOCAL_MODULE := avtest
+
+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
+
+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
diff --git a/bluez/android/Makefile.am b/bluez/android/Makefile.am
new file mode 100644
index 0000000..58ee3c9
--- /dev/null
+++ b/bluez/android/Makefile.am
@@ -0,0 +1,233 @@
+if ANDROID
+android_plugindir = $(abs_top_srcdir)/android/.libs
+
+noinst_PROGRAMS += android/system-emulator
+
+android_system_emulator_SOURCES = android/system-emulator.c \
+					monitor/mainloop.h monitor/mainloop.c
+
+noinst_PROGRAMS += android/bluetoothd-snoop
+
+android_bluetoothd_snoop_SOURCES = android/bluetoothd-snoop.c \
+				monitor/mainloop.h monitor/mainloop.c \
+				src/shared/btsnoop.h src/shared/btsnoop.c
+
+noinst_PROGRAMS += android/bluetoothd
+
+android_bluetoothd_SOURCES = android/main.c \
+				src/log.c \
+				android/hal-msg.h \
+				android/audio-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 \
+				src/shared/io.h src/shared/io-glib.c \
+				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/ringbuf.h src/shared/ringbuf.c \
+				src/shared/hfp.h src/shared/hfp.c \
+				android/bluetooth.h android/bluetooth.c \
+				android/hidhost.h android/hidhost.c \
+				android/ipc-common.h \
+				android/ipc.h android/ipc.c \
+				android/avdtp.h android/avdtp.c \
+				android/a2dp.h android/a2dp.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/pan.h android/pan.c \
+				android/handsfree.h android/handsfree.c \
+				android/gatt.h android/gatt.c \
+				android/health.h android/health.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 @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-avrcp.c \
+					android/hal-handsfree.c \
+					android/hal-gatt.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/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/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-rc.c \
+				android/client/if-bt.c \
+				android/client/if-gatt.c \
+				android/client/if-hf.c \
+				android/client/if-hh.c \
+				android/client/if-pan.c \
+				android/client/if-sock.c \
+				android/client/if-audio.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/btdev.h emulator/btdev.c \
+				emulator/bthost.h emulator/bthost.c \
+				emulator/smp.c \
+				src/shared/crypto.h src/shared/crypto.c \
+				src/shared/io.h src/shared/io-glib.c \
+				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/hciemu.h src/shared/hciemu.c \
+				src/shared/tester.h src/shared/tester.c \
+				src/shared/timeout.h src/shared/timeout-glib.c \
+				monitor/rfcomm.h \
+				android/hardware/hardware.c \
+				android/android-tester.c
+
+android_android_tester_CFLAGS = $(AM_CFLAGS) -I$(srcdir)/android \
+				-DPLUGINDIR=\""$(android_plugindir)"\"
+
+android_android_tester_LDADD = lib/libbluetooth-internal.la @GLIB_LIBS@
+
+android_android_tester_LDFLAGS = -pthread -ldl
+
+noinst_PROGRAMS += android/ipc-tester
+
+android_ipc_tester_SOURCES = emulator/btdev.h emulator/btdev.c \
+				emulator/bthost.h emulator/bthost.c \
+				emulator/smp.c \
+				src/shared/crypto.h src/shared/crypto.c \
+				src/shared/io.h src/shared/io-glib.c \
+				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/hciemu.h src/shared/hciemu.c \
+				src/shared/tester.h src/shared/tester.c \
+				src/shared/timeout.h src/shared/timeout-glib.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 @GLIB_LIBS@
+
+android_audio_a2dp_default_la_SOURCES = android/audio-msg.h \
+					android/hal-msg.h \
+					android/hal-audio.c \
+					android/hardware/audio.h \
+					android/hardware/audio_effect.h \
+					android/hardware/hardware.h \
+					android/system/audio.h
+
+unit_tests += android/test-ipc
+
+android_test_ipc_SOURCES = android/test-ipc.c \
+				src/shared/util.h src/shared/util.c \
+				src/log.h src/log.c \
+				android/ipc-common.h \
+				android/ipc.c android/ipc.h
+android_test_ipc_LDADD = @GLIB_LIBS@
+
+plugin_LTLIBRARIES += android/audio.a2dp.default.la
+
+android_audio_a2dp_default_la_CFLAGS = $(AM_CFLAGS) -I$(srcdir)/android
+
+android_audio_a2dp_default_la_LIBADD = @SBC_LIBS@
+
+android_audio_a2dp_default_la_LDFLAGS = $(AM_LDFLAGS) -module -avoid-version \
+					-no-undefined -pthread -lrt
+
+endif
+
+EXTRA_DIST += android/Android.mk android/README \
+				android/init.bluetooth.rc \
+				android/hal-ipc-api.txt \
+				android/audio-ipc-api.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/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/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-hsp.txt
diff --git a/bluez/android/README b/bluez/android/README
new file mode 100644
index 0000000..5fd4cfa
--- /dev/null
+++ b/bluez/android/README
@@ -0,0 +1,316 @@
+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
+
+
+Building and running on Android
+===============================
+
+Steps needed to build and run Android Open Source Project 4.4.2 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://code.google.com/p/aosp-bluez.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 - Currently only 'master' branch available at
+https://android.googlesource.com/platform/bionic provides all required
+functionality and running BlueZ on release branch requires backporting missing
+features (currently only epoll_create1 call for Android 4.4.2). Sample
+Bionic for Android 4.4.2 with all required features backported is available at
+https://code.google.com/p/aosp-bluez.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://code.google.com/p/aosp-bluez.device-lge-mako/    (Nexus 4)
+https://code.google.com/p/aosp-bluez.device-asus-flo/    (Nexus 7 2013)
+
+
+Downloading and building
+------------------------
+
+Building for Android requires full Android AOSP source tree. Sample Android
+4.4.2 tree with all required components present is available at
+http://code.google.com/p/aosp-bluez/
+
+This tree provides support for Nexus4 (target aosp_mako-userdebug) and
+Nexus 7 2013 (target aosp_flo-userdebug). 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:
+repo init -u https://code.google.com/p/aosp-bluez.platform-manifest -b kitkat
+repo sync
+
+Building:
+source build/envsetup.sh
+lunch aosp_mako-userdebug    or    lunch aosp_flo-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.
+
+
+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
+
+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 subsytem 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://code.google.com/p/aosp-bluez.
+
+
+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://code.google.com/p/aosp-bluez.glib/ for an example.
+
+
+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 procced 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." followed by specific HAL name e.g.
+"persist.sys.bluetooth.handsfree". This section list available customization
+options.
+
+Property	Value		Description
+-------------------------------------------
+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)
+
+
+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         Status
+---------------------------------------
+core          bluetooth.h        complete
+a2dp          bt_av.h            complete
+gatt          bt_gatt.h          initial
+              bt_gatt_client.h   initial
+              bt_gatt_server.h   not started
+handsfree     bt_hf.h            complete
+hidhost       bt_hh.h            complete
+health        bt_hl.h            initial
+pan           bt_pan.h           complete
+avrcp         bt_rc.h            complete
+socket        bt_sock.h          complete
+
+
+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
+get_remote_service_record          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 and device, for
+                                   device this property is to be returned as
+                                   response to get_remote_service_record,
+                                   not sure what to return on get_property
+                                   calls (records of all services?)
+
+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
+
+
+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.
+
+https://android-review.googlesource.com/82757
+https://android-review.googlesource.com/87670
+https://android-review.googlesource.com/88384
+
+
+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
diff --git a/bluez/android/a2dp.c b/bluez/android/a2dp.c
new file mode 100644
index 0000000..4ea16e2
--- /dev/null
+++ b/bluez/android/a2dp.c
@@ -0,0 +1,1645 @@
+/*
+ *
+ *  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/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 L2CAP_PSM_AVDTP 0x19
+#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;
+
+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(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, L2CAP_PSM_AVDTP,
+					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 check_capabilities(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_check_config(codec->data, codec_len, preset->data,
+								preset->len);
+	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 *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);
+	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;
+	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 */
+	dev->session = avdtp_new(fd, imtu, omtu, 0x0100);
+	if (!dev->session)
+		goto failed;
+
+	avdtp_add_disconnect_cb(dev->session, disconnect_cb, dev);
+
+	if (dev->io) {
+		g_io_channel_unref(dev->io);
+		dev->io = NULL;
+	}
+
+	/* Proceed to stream setup if initiator */
+	if (dev->state == HAL_A2DP_STATE_CONNECTING) {
+		int perr;
+
+		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 src, dst;
+	char address[18];
+	GError *gerr = NULL;
+	GSList *l;
+
+	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_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);
+	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(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));
+
+	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("");
+
+	setup = find_setup(cmd->id);
+	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->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;
+}
+
+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);
+
+	server = bt_io_listen(connect_cb, NULL, NULL, NULL, &err,
+				BT_IO_OPT_SOURCE_BDADDR, &adapter_addr,
+				BT_IO_OPT_PSM, L2CAP_PSM_AVDTP,
+				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 = 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/android/a2dp.h b/bluez/android/a2dp.h
new file mode 100644
index 0000000..8a70407
--- /dev/null
+++ b/bluez/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/android/android-tester.c b/bluez/android/android-tester.c
new file mode 100644
index 0000000..6d2edc7
--- /dev/null
+++ b/bluez/android/android-tester.c
@@ -0,0 +1,4856 @@
+/*
+ * 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 <unistd.h>
+#include <fcntl.h>
+
+#include <glib.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <sys/un.h>
+#include <sys/signalfd.h>
+#include <libgen.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/hciemu.h"
+
+#include "emulator/bthost.h"
+#include "monitor/bt.h"
+
+#include <hardware/hardware.h>
+#include <hardware/bluetooth.h>
+#include <hardware/bt_sock.h>
+#include <hardware/bt_hh.h>
+
+#include "utils.h"
+
+struct priority_property {
+	bt_property_t prop;
+	int prio;
+};
+
+struct generic_data {
+	int expected_adapter_status;
+	uint32_t expect_settings_set;
+	int expected_cb_count;
+	bt_property_t set_property;
+	bt_callbacks_t expected_hal_cb;
+	struct priority_property *expected_properties;
+	uint8_t expected_properties_num;
+};
+
+struct socket_data {
+	btsock_type_t sock_type;
+	const char *service_name;
+	const uint8_t *service_uuid;
+	const bt_bdaddr_t *bdaddr;
+	int channel;
+	int flags;
+	bt_status_t expected_status;
+	bool test_channel;
+};
+
+struct hidhost_generic_data {
+	bthh_status_t expected_status;
+	int expected_conn_state;
+	int expected_cb_count;
+	bthh_protocol_mode_t expected_protocol_mode;
+	int expected_report;
+	bthh_callbacks_t expected_hal_cb;
+	int expected_report_size;
+};
+
+#define WAIT_FOR_SIGNAL_TIME 2 /* in seconds */
+#define EMULATOR_SIGNAL "emulator_started"
+
+#define BT_STATUS_NOT_EXPECTED	-1
+
+struct test_data {
+	struct mgmt *mgmt;
+	uint16_t mgmt_index;
+	unsigned int mgmt_settings_id;
+	struct hciemu *hciemu;
+	enum hciemu_type hciemu_type;
+	const void *test_data;
+	pid_t bluetoothd_pid;
+	guint signalfd;
+
+	struct hw_device_t *device;
+	const bt_interface_t *if_bluetooth;
+	const btsock_interface_t *if_sock;
+	const bthh_interface_t *if_hid;
+
+	int conditions_left;
+
+	/* Set to true if test conditions should be verified */
+	bool test_checks_valid;
+
+	bool test_result_set;
+
+	int cb_count;
+	GSList *expected_properties_list;
+
+	/* hidhost */
+	uint16_t sdp_handle;
+	uint16_t sdp_cid;
+	uint16_t ctrl_handle;
+	uint16_t ctrl_cid;
+	uint16_t intr_handle;
+	uint16_t intr_cid;
+};
+
+struct bt_cb_data {
+	bt_state_t state;
+	bt_status_t status;
+
+	bt_bdaddr_t bdaddr;
+	bt_bdname_t bdname;
+	uint32_t cod;
+
+	bt_ssp_variant_t ssp_variant;
+	uint32_t passkey;
+
+	int num;
+	bt_property_t *props;
+};
+
+struct hh_cb_data {
+	bt_bdaddr_t bdaddr;
+
+	bthh_status_t status;
+	bthh_hid_info_t hid_info;
+	bthh_protocol_mode_t mode;
+	bthh_connection_state_t state;
+
+	uint8_t *report;
+	int size;
+};
+
+static char exec_dir[PATH_MAX + 1];
+
+static gint scheduled_cbacks_num = 0;
+
+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;
+	}
+
+	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 mgmt_debug(const char *str, void *user_data)
+{
+	const char *prefix = user_data;
+
+	tester_print("%s%s", prefix, str);
+}
+
+static void test_update_state(void)
+{
+	struct test_data *data = tester_get_data();
+
+	if (data->conditions_left == 0 && !data->test_result_set) {
+		data->test_result_set = true;
+		tester_test_passed();
+	}
+}
+
+static void test_mgmt_settings_set(struct test_data *data)
+{
+	data->conditions_left--;
+
+	test_update_state();
+}
+
+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();
+	const struct generic_data *test_data = 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);
+
+	if ((settings & test_data->expect_settings_set) !=
+					test_data->expect_settings_set)
+		return;
+
+	test_mgmt_settings_set(data);
+	mgmt_unregister(data->mgmt, data->mgmt_settings_id);
+}
+
+static void check_cb_count(void)
+{
+	struct test_data *data = tester_get_data();
+
+	if (!data->test_checks_valid)
+		return;
+
+	if (data->cb_count == 0) {
+		data->conditions_left--;
+		test_update_state();
+	}
+}
+
+static void expected_cb_count_init(struct test_data *data)
+{
+	const struct generic_data *test_data = data->test_data;
+
+	data->cb_count = test_data->expected_cb_count;
+
+	check_cb_count();
+}
+
+static void mgmt_cb_init(struct test_data *data)
+{
+	const struct generic_data *test_data = data->test_data;
+
+	if (!test_data->expect_settings_set)
+		test_mgmt_settings_set(data);
+	else
+		data->mgmt_settings_id = mgmt_register(data->mgmt,
+				MGMT_EV_NEW_SETTINGS, data->mgmt_index,
+				command_generic_new_settings, NULL, NULL);
+}
+
+static void expected_status_init(struct test_data *data)
+{
+	const struct generic_data *test_data = data->test_data;
+
+	if (test_data->expected_adapter_status == BT_STATUS_NOT_EXPECTED)
+		data->conditions_left--;
+}
+
+static void test_property_init(struct test_data *data)
+{
+	const struct generic_data *test_data = data->test_data;
+	GSList *l = data->expected_properties_list;
+	int i;
+
+	if (!test_data->expected_properties_num) {
+		data->conditions_left--;
+		return;
+	}
+
+	for (i = 0; i < test_data->expected_properties_num; i++)
+		l = g_slist_prepend(l, &(test_data->expected_properties[i]));
+
+	data->expected_properties_list = l;
+}
+
+static void init_test_conditions(struct test_data *data)
+{
+	data->test_checks_valid = true;
+
+	data->conditions_left = 4;
+
+	expected_cb_count_init(data);
+	mgmt_cb_init(data);
+	expected_status_init(data);
+	test_property_init(data);
+}
+
+static void check_expected_status(uint8_t status)
+{
+	struct test_data *data = tester_get_data();
+	const struct generic_data *test_data = data->test_data;
+
+	if (test_data->expected_adapter_status == status) {
+		data->conditions_left--;
+		test_update_state();
+	} else
+		tester_test_failed();
+}
+
+static int locate_property(gconstpointer expected_data,
+						gconstpointer received_prop)
+{
+	bt_property_t rec_prop = *((bt_property_t *)received_prop);
+	bt_property_t exp_prop =
+			((struct priority_property *)expected_data)->prop;
+
+	if (exp_prop.type && (exp_prop.type != rec_prop.type))
+		return 1;
+	if (exp_prop.len && (exp_prop.len != rec_prop.len))
+		return 1;
+	if (exp_prop.val && memcmp(exp_prop.val, rec_prop.val, exp_prop.len))
+		return 1;
+
+	return 0;
+}
+
+static int compare_priorities(gconstpointer prop_list, gconstpointer priority)
+{
+	int prio = GPOINTER_TO_INT(priority);
+	int comp_prio = ((struct priority_property *)prop_list)->prio;
+
+	if (prio > comp_prio)
+		return 0;
+
+	return 1;
+}
+
+static bool check_prop_priority(int rec_prop_prio)
+{
+	struct test_data *data = tester_get_data();
+	GSList *l = data->expected_properties_list;
+
+	if (!rec_prop_prio || !g_slist_length(l))
+		return true;
+
+	if (g_slist_find_custom(l, GINT_TO_POINTER(rec_prop_prio),
+							&compare_priorities))
+		return false;
+
+	return true;
+}
+
+static void check_expected_property(bt_property_t received_prop)
+{
+	struct test_data *data = tester_get_data();
+	int rec_prio;
+	GSList *l = data->expected_properties_list;
+	GSList *found_exp_prop;
+
+	if (!g_slist_length(l))
+		return;
+
+	found_exp_prop = g_slist_find_custom(l, &received_prop,
+							&locate_property);
+
+	if (found_exp_prop) {
+		rec_prio = ((struct priority_property *)
+						(found_exp_prop->data))->prio;
+		if (check_prop_priority(rec_prio))
+			l = g_slist_remove(l, found_exp_prop->data);
+	}
+
+	data->expected_properties_list = l;
+
+	if (g_slist_length(l))
+		return;
+
+	data->conditions_left--;
+	test_update_state();
+}
+
+static bool check_test_property(bt_property_t received_prop,
+						bt_property_t expected_prop)
+{
+	if (expected_prop.type && (expected_prop.type != received_prop.type))
+		return false;
+	if (expected_prop.len && (expected_prop.len != received_prop.len))
+		return false;
+	if (expected_prop.val && memcmp(expected_prop.val, received_prop.val,
+							expected_prop.len))
+		return false;
+
+	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;
+	}
+
+	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 void test_post_teardown(const void *test_data)
+{
+	struct test_data *data = tester_get_data();
+
+	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 = 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, "bluetooth.start=daemon"))
+		goto failed;
+
+	close(pipe);
+	close(fd);
+	return bluetoothd_start(hci_index);
+
+failed:
+	close(pipe);
+
+	if (fd >= 0)
+		close(fd);
+}
+
+static void emu_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:
+	case BT_HCI_CMD_LE_SET_ADV_ENABLE:
+		break;
+	default:
+		return;
+	}
+
+	tester_print("Emulated remote set connectable status 0x%02x", status);
+
+	if (status)
+		tester_setup_failed();
+	else
+		tester_setup_complete();
+}
+
+static void setup_powered_emulated_remote(void)
+{
+	struct test_data *data = tester_get_data();
+	struct bthost *bthost;
+
+	tester_print("Controller powered on");
+
+	bthost = hciemu_client_get_host(data->hciemu);
+	bthost_set_cmd_complete_cb(bthost, emu_connectable_complete, data);
+
+	if (data->hciemu_type == HCIEMU_TYPE_LE)
+		bthost_set_adv_enable(bthost, 0x01);
+	else
+		bthost_write_scan_enable(bthost, 0x03);
+}
+
+static void enable_success_cb(bt_state_t state)
+{
+	struct test_data *data = tester_get_data();
+
+	if (state == BT_STATE_ON) {
+		setup_powered_emulated_remote();
+		data->cb_count--;
+		check_cb_count();
+	}
+}
+
+static void disable_success_cb(bt_state_t state)
+{
+	struct test_data *data = tester_get_data();
+
+	if (state == BT_STATE_OFF) {
+		data->cb_count--;
+		check_cb_count();
+	}
+}
+
+static gboolean adapter_state_changed(gpointer user_data)
+{
+	struct test_data *data = tester_get_data();
+	const struct generic_data *test = data->test_data;
+	struct bt_cb_data *cb_data = user_data;
+
+	if (data->test_checks_valid &&
+			test->expected_hal_cb.adapter_state_changed_cb) {
+		test->expected_hal_cb.adapter_state_changed_cb(cb_data->state);
+		goto cleanup;
+	}
+
+	if (!data->test_checks_valid && cb_data->state == BT_STATE_ON)
+		setup_powered_emulated_remote();
+
+cleanup:
+	g_free(cb_data);
+
+	g_atomic_int_dec_and_test(&scheduled_cbacks_num);
+	return FALSE;
+}
+
+static void adapter_state_changed_cb(bt_state_t state)
+{
+	struct bt_cb_data *cb_data = g_new0(struct bt_cb_data, 1);
+
+	cb_data->state = state;
+
+	g_atomic_int_inc(&scheduled_cbacks_num);
+	g_idle_add(adapter_state_changed, cb_data);
+}
+
+static void discovery_start_success_cb(bt_discovery_state_t state)
+{
+	struct test_data *data = tester_get_data();
+
+	if (state == BT_DISCOVERY_STARTED) {
+		data->cb_count--;
+		check_cb_count();
+	}
+}
+
+static void discovery_start_done_cb(bt_discovery_state_t state)
+{
+	struct test_data *data = tester_get_data();
+	bt_status_t status;
+
+	status = data->if_bluetooth->start_discovery();
+	data->cb_count--;
+
+	check_cb_count();
+	check_expected_status(status);
+}
+
+static void discovery_stop_success_cb(bt_discovery_state_t state)
+{
+	struct test_data *data = tester_get_data();
+	bt_status_t status;
+
+	if (state == BT_DISCOVERY_STARTED && data->cb_count == 2) {
+		status = data->if_bluetooth->cancel_discovery();
+		check_expected_status(status);
+		data->cb_count--;
+		return;
+	}
+	if (state == BT_DISCOVERY_STOPPED && data->cb_count == 1) {
+		data->cb_count--;
+		check_cb_count();
+	}
+}
+
+static void discovery_device_found_state_changed_cb(bt_discovery_state_t state)
+{
+	struct test_data *data = tester_get_data();
+
+	if (state == BT_DISCOVERY_STARTED && data->cb_count == 3) {
+		data->cb_count--;
+		return;
+	}
+	if (state == BT_DISCOVERY_STOPPED && data->cb_count == 1) {
+		data->cb_count--;
+		check_cb_count();
+	}
+}
+
+static void remote_discovery_state_changed_cb(bt_discovery_state_t state)
+{
+	struct test_data *data = tester_get_data();
+
+	if (state == BT_DISCOVERY_STARTED && data->cb_count == 3) {
+		data->cb_count--;
+		return;
+	}
+	if (state == BT_DISCOVERY_STOPPED && data->cb_count == 1) {
+		data->cb_count--;
+		check_cb_count();
+	}
+}
+
+static void remote_setprop_disc_state_changed_cb(bt_discovery_state_t state)
+{
+	struct test_data *data = tester_get_data();
+
+	if (state == BT_DISCOVERY_STARTED && data->cb_count == 4) {
+		data->cb_count--;
+		return;
+	}
+	if (state == BT_DISCOVERY_STOPPED) {
+		data->cb_count--;
+		check_cb_count();
+	}
+}
+
+static gboolean discovery_state_changed(gpointer user_data)
+{
+	struct test_data *data = tester_get_data();
+	const struct generic_data *test = data->test_data;
+	struct bt_cb_data *cb_data = user_data;
+
+	if (test && test->expected_hal_cb.discovery_state_changed_cb)
+		test->expected_hal_cb.discovery_state_changed_cb(
+								cb_data->state);
+
+	g_free(cb_data);
+
+	g_atomic_int_dec_and_test(&scheduled_cbacks_num);
+	return FALSE;
+}
+
+static void discovery_state_changed_cb(bt_discovery_state_t state)
+{
+	struct bt_cb_data *cb_data = g_new0(struct bt_cb_data, 1);
+
+	cb_data->state = state;
+	g_atomic_int_inc(&scheduled_cbacks_num);
+	g_idle_add(discovery_state_changed, cb_data);
+}
+
+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 void free_properties(int num_properties, bt_property_t *properties)
+{
+	int i;
+
+	for (i = 0; i < num_properties; i++)
+		g_free(properties[i].val);
+
+	g_free(properties);
+}
+
+static void discovery_device_found_cb(int num_properties,
+						bt_property_t *properties)
+{
+	struct test_data *data = tester_get_data();
+	uint8_t *remote_bdaddr =
+			(uint8_t *)hciemu_get_client_bdaddr(data->hciemu);
+	uint32_t emu_remote_type = BT_DEVICE_DEVTYPE_BREDR;
+	int32_t emu_remote_rssi = -60;
+	bt_bdaddr_t emu_remote_bdaddr;
+	int i;
+	bt_property_t expected_prop;
+	bt_property_t received_prop;
+
+	data->cb_count--;
+	check_cb_count();
+
+	if (num_properties < 1) {
+		tester_test_failed();
+		return;
+	}
+
+	bdaddr2android((const bdaddr_t *) remote_bdaddr, &emu_remote_bdaddr);
+
+	for (i = 0; i < num_properties; i++) {
+		received_prop = properties[i];
+
+		switch (properties[i].type) {
+		case BT_PROPERTY_BDADDR:
+			expected_prop.type = BT_PROPERTY_BDADDR;
+			expected_prop.len = sizeof(emu_remote_bdaddr);
+			expected_prop.val = &emu_remote_bdaddr;
+			break;
+
+		case BT_PROPERTY_TYPE_OF_DEVICE:
+			expected_prop.type = BT_PROPERTY_TYPE_OF_DEVICE;
+			expected_prop.len = sizeof(emu_remote_type);
+			expected_prop.val = &emu_remote_type;
+			break;
+
+		case BT_PROPERTY_REMOTE_RSSI:
+			expected_prop.type = BT_PROPERTY_REMOTE_RSSI;
+			expected_prop.len = sizeof(emu_remote_rssi);
+			expected_prop.val = &emu_remote_rssi;
+			break;
+
+		default:
+			expected_prop.type = 0;
+			expected_prop.len = 0;
+			expected_prop.val = NULL;
+			break;
+		}
+
+		if (!check_test_property(received_prop, expected_prop)) {
+			data->if_bluetooth->cancel_discovery();
+			tester_test_failed();
+			return;
+		}
+	}
+
+	data->if_bluetooth->cancel_discovery();
+}
+
+static void remote_getprops_device_found_cb(int num_properties,
+						bt_property_t *properties)
+{
+	struct test_data *data = tester_get_data();
+	uint8_t *bdaddr = (uint8_t *)hciemu_get_client_bdaddr(data->hciemu);
+	bt_bdaddr_t remote_addr;
+
+	bdaddr2android((const bdaddr_t *)bdaddr, &remote_addr.address);
+
+	if (data->cb_count == 2)
+		data->cb_count--;
+
+	data->if_bluetooth->cancel_discovery();
+	data->if_bluetooth->get_remote_device_properties(&remote_addr);
+}
+
+static void remote_get_property_device_found_cb(int num_properties,
+						bt_property_t *properties)
+{
+	struct test_data *data = tester_get_data();
+	const struct generic_data *test = data->test_data;
+	bt_status_t status;
+	uint8_t *bdaddr = (uint8_t *)hciemu_get_client_bdaddr(data->hciemu);
+	bt_bdaddr_t remote_addr;
+
+	const bt_property_t prop = test->expected_properties[0].prop;
+
+	bdaddr2android((const bdaddr_t *)bdaddr, &remote_addr.address);
+
+	if (data->cb_count == 2)
+		data->cb_count--;
+
+	data->if_bluetooth->cancel_discovery();
+	status = data->if_bluetooth->get_remote_device_property(&remote_addr,
+								prop.type);
+	check_expected_status(status);
+}
+
+static void remote_setprop_device_found_cb(int num_properties,
+						bt_property_t *properties)
+{
+	struct test_data *data = tester_get_data();
+	const struct generic_data *test = data->test_data;
+	bt_status_t status;
+	uint8_t *bdaddr = (uint8_t *)hciemu_get_client_bdaddr(data->hciemu);
+	bt_bdaddr_t remote_addr;
+
+	const bt_property_t prop = test->expected_properties[0].prop;
+
+	bdaddr2android((const bdaddr_t *)bdaddr, &remote_addr.address);
+
+	if (data->cb_count == 3)
+		data->cb_count--;
+
+	data->if_bluetooth->cancel_discovery();
+	status = data->if_bluetooth->set_remote_device_property(&remote_addr,
+									&prop);
+	check_expected_status(status);
+}
+
+static void remote_setprop_fail_device_found_cb(int num_properties,
+						bt_property_t *properties)
+{
+	struct test_data *data = tester_get_data();
+	const struct generic_data *test = data->test_data;
+	bt_status_t status;
+	uint8_t *bdaddr = (uint8_t *)hciemu_get_client_bdaddr(data->hciemu);
+	bt_bdaddr_t remote_addr;
+
+	const bt_property_t prop = test->expected_properties[0].prop;
+
+	bdaddr2android((const bdaddr_t *)bdaddr, &remote_addr.address);
+
+	if (data->cb_count == 2)
+		data->cb_count--;
+
+	data->if_bluetooth->cancel_discovery();
+	status = data->if_bluetooth->set_remote_device_property(&remote_addr,
+									&prop);
+	check_expected_status(status);
+}
+
+static void bond_device_found_cb(int num_properties, bt_property_t *properties)
+{
+	struct test_data *data = tester_get_data();
+	uint8_t *bdaddr = (uint8_t *)hciemu_get_client_bdaddr(data->hciemu);
+	bt_bdaddr_t remote_addr;
+	bt_status_t status;
+
+	bdaddr2android((const bdaddr_t *)bdaddr, &remote_addr.address);
+
+	if (data->cb_count == 4) {
+		data->cb_count--;
+		status = data->if_bluetooth->create_bond(&remote_addr);
+		check_expected_status(status);
+	}
+}
+
+static void bond_nostatus_device_found_cb(int num_properties,
+						bt_property_t *properties)
+{
+	struct test_data *data = tester_get_data();
+	uint8_t *bdaddr = (uint8_t *)hciemu_get_client_bdaddr(data->hciemu);
+	bt_bdaddr_t remote_addr;
+
+	bdaddr2android((const bdaddr_t *)bdaddr, &remote_addr.address);
+
+	if (data->cb_count == 4) {
+		data->cb_count--;
+		data->if_bluetooth->create_bond(&remote_addr);
+	}
+}
+
+static gboolean device_found(gpointer user_data)
+{
+	struct test_data *data = tester_get_data();
+	const struct generic_data *test = data->test_data;
+	struct bt_cb_data *cb_data = user_data;
+
+	if (data->test_checks_valid && test->expected_hal_cb.device_found_cb)
+		test->expected_hal_cb.device_found_cb(cb_data->num,
+								cb_data->props);
+
+	free_properties(cb_data->num, cb_data->props);
+	g_free(cb_data);
+
+	g_atomic_int_dec_and_test(&scheduled_cbacks_num);
+	return FALSE;
+}
+
+static void device_found_cb(int num_properties, bt_property_t *properties)
+{
+	struct bt_cb_data *cb_data = g_new0(struct bt_cb_data, 1);
+
+	cb_data->num = num_properties;
+	cb_data->props = copy_properties(num_properties, properties);
+
+	g_atomic_int_inc(&scheduled_cbacks_num);
+	g_idle_add(device_found, cb_data);
+}
+
+static void check_count_properties_cb(bt_status_t status, int num_properties,
+						bt_property_t *properties)
+{
+	int i;
+
+	for (i = 0; i < num_properties; i++)
+		check_expected_property(properties[i]);
+}
+
+static gboolean adapter_properties(gpointer user_data)
+{
+	struct test_data *data = tester_get_data();
+	const struct generic_data *test = data->test_data;
+	struct bt_cb_data *cb_data = user_data;
+
+	if (data->test_checks_valid &&
+				test->expected_hal_cb.adapter_properties_cb)
+		test->expected_hal_cb.adapter_properties_cb(cb_data->status,
+						cb_data->num, cb_data->props);
+
+	free_properties(cb_data->num, cb_data->props);
+	g_free(cb_data);
+
+	g_atomic_int_dec_and_test(&scheduled_cbacks_num);
+	return FALSE;
+}
+
+static void adapter_properties_cb(bt_status_t status, int num_properties,
+						bt_property_t *properties)
+{
+	struct bt_cb_data *cb_data = g_new0(struct bt_cb_data, 1);
+
+	cb_data->status = status;
+	cb_data->num = num_properties;
+	cb_data->props = copy_properties(num_properties, properties);
+
+	g_atomic_int_inc(&scheduled_cbacks_num);
+	g_idle_add(adapter_properties, cb_data);
+}
+
+static void remote_test_device_properties_cb(bt_status_t status,
+				bt_bdaddr_t *bd_addr, int num_properties,
+				bt_property_t *properties)
+{
+	int i;
+
+	for (i = 0; i < num_properties; i++)
+		check_expected_property(properties[i]);
+}
+
+static void remote_setprop_device_properties_cb(bt_status_t status,
+				bt_bdaddr_t *bd_addr, int num_properties,
+				bt_property_t *properties)
+{
+	int i;
+	struct test_data *data = tester_get_data();
+	const struct generic_data *test = data->test_data;
+	uint8_t *bdaddr = (uint8_t *)hciemu_get_client_bdaddr(data->hciemu);
+	bt_bdaddr_t remote_addr;
+	const bt_property_t prop = test->expected_properties[1].prop;
+
+	for (i = 0; i < num_properties; i++)
+		check_expected_property(properties[i]);
+
+	if (g_slist_length(data->expected_properties_list) == 1) {
+		bdaddr2android((const bdaddr_t *)bdaddr, &remote_addr.address);
+		data->cb_count--;
+		check_cb_count();
+		data->if_bluetooth->get_remote_device_property(&remote_addr,
+								prop.type);
+	}
+}
+
+static gboolean remote_device_properties(gpointer user_data)
+{
+	struct test_data *data = tester_get_data();
+	const struct generic_data *test = data->test_data;
+	struct bt_cb_data *cb_data = user_data;
+
+	if (data->test_checks_valid &&
+			test->expected_hal_cb.remote_device_properties_cb)
+		test->expected_hal_cb.remote_device_properties_cb(
+					cb_data->status, &cb_data->bdaddr,
+					cb_data->num, cb_data->props);
+
+	free_properties(cb_data->num, cb_data->props);
+	g_free(cb_data);
+
+	g_atomic_int_dec_and_test(&scheduled_cbacks_num);
+	return FALSE;
+}
+
+static void remote_device_properties_cb(bt_status_t status,
+				bt_bdaddr_t *bd_addr, int num_properties,
+				bt_property_t *properties)
+{
+	struct bt_cb_data *cb_data = g_new0(struct bt_cb_data, 1);
+
+	cb_data->status = status;
+	cb_data->bdaddr = *bd_addr;
+	cb_data->num = num_properties;
+	cb_data->props = copy_properties(num_properties, properties);
+
+	g_atomic_int_inc(&scheduled_cbacks_num);
+	g_idle_add(remote_device_properties, cb_data);
+}
+
+static void bond_test_bonded_state_changed_cb(bt_status_t status,
+			bt_bdaddr_t *remote_bd_addr, bt_bond_state_t state)
+{
+	struct test_data *data = tester_get_data();
+
+	switch (state) {
+	case BT_BOND_STATE_BONDING:
+		data->cb_count--;
+		break;
+	case BT_BOND_STATE_BONDED:
+		data->cb_count--;
+		check_cb_count();
+		break;
+	default:
+		tester_test_failed();
+		break;
+	}
+}
+
+static void bond_test_none_state_changed_cb(bt_status_t status,
+			bt_bdaddr_t *remote_bd_addr, bt_bond_state_t state)
+{
+	struct test_data *data = tester_get_data();
+
+	switch (state) {
+	case BT_BOND_STATE_BONDING:
+		data->cb_count--;
+		break;
+	case BT_BOND_STATE_NONE:
+		data->cb_count--;
+		check_cb_count();
+		break;
+	default:
+		tester_test_failed();
+		break;
+	}
+}
+
+static void bond_remove_success_state_changed_cb(bt_status_t status,
+			bt_bdaddr_t *remote_bd_addr, bt_bond_state_t state)
+{
+	struct test_data *data = tester_get_data();
+	bt_status_t remove_status;
+	uint8_t *bdaddr = (uint8_t *)hciemu_get_client_bdaddr(data->hciemu);
+	bt_bdaddr_t remote_addr;
+
+	bdaddr2android((const bdaddr_t *)bdaddr, &remote_addr.address);
+
+	if (state == BT_BOND_STATE_BONDED) {
+		data->cb_count--;
+		remove_status = data->if_bluetooth->remove_bond(&remote_addr);
+		check_expected_status(remove_status);
+		return;
+	}
+
+	if (state == BT_BOND_STATE_NONE) {
+		data->cb_count--;
+		check_cb_count();
+	}
+}
+
+static gboolean bond_state_changed(gpointer user_data)
+{
+	struct test_data *data = tester_get_data();
+	const struct generic_data *test = data->test_data;
+	struct bt_cb_data *cb_data = user_data;
+
+	if (data->test_checks_valid &&
+				test->expected_hal_cb.bond_state_changed_cb)
+		test->expected_hal_cb.bond_state_changed_cb(cb_data->status,
+					&cb_data->bdaddr, cb_data->state);
+
+	g_free(cb_data);
+	return FALSE;
+}
+
+static void bond_state_changed_cb(bt_status_t status,
+			bt_bdaddr_t *remote_bd_addr, bt_bond_state_t state)
+{
+	struct bt_cb_data *cb_data = g_new0(struct bt_cb_data, 1);
+
+	cb_data->status = status;
+	cb_data->bdaddr = *remote_bd_addr;
+	cb_data->state = state;
+
+	g_idle_add(bond_state_changed, cb_data);
+}
+
+static void bond_create_pin_success_request_cb(bt_bdaddr_t *remote_bd_addr,
+					bt_bdname_t *bd_name, uint32_t cod)
+{
+	struct test_data *data = tester_get_data();
+	const bt_bdaddr_t *bdaddr = remote_bd_addr;
+	bt_pin_code_t pin_code = {
+	.pin = { 0x30, 0x30, 0x30, 0x30 },
+	};
+	uint8_t pin_len = 4;
+
+	data->cb_count--;
+
+	data->if_bluetooth->pin_reply(bdaddr, TRUE, pin_len, &pin_code);
+}
+
+static void bond_create_pin_fail_request_cb(bt_bdaddr_t *remote_bd_addr,
+					bt_bdname_t *bd_name, uint32_t cod)
+{
+	struct test_data *data = tester_get_data();
+	const bt_bdaddr_t *bdaddr = remote_bd_addr;
+	bt_pin_code_t pin_code = {
+	.pin = { 0x31, 0x31, 0x31, 0x31 },
+	};
+	uint8_t pin_len = 4;
+
+	data->cb_count--;
+
+	data->if_bluetooth->pin_reply(bdaddr, TRUE, pin_len, &pin_code);
+}
+
+static gboolean pin_request(gpointer user_data)
+{
+	struct test_data *data = tester_get_data();
+	const struct generic_data *test = data->test_data;
+	struct bt_cb_data *cb_data = user_data;
+
+	if (data->test_checks_valid && test->expected_hal_cb.pin_request_cb)
+		test->expected_hal_cb.pin_request_cb(&cb_data->bdaddr,
+						&cb_data->bdname, cb_data->cod);
+
+	g_free(cb_data);
+	return FALSE;
+}
+
+static void pin_request_cb(bt_bdaddr_t *remote_bd_addr,
+					bt_bdname_t *bd_name, uint32_t cod)
+{
+	struct bt_cb_data *cb_data = g_new0(struct bt_cb_data, 1);
+
+	cb_data->bdaddr = *remote_bd_addr;
+	cb_data->bdname = *bd_name;
+	cb_data->cod = cod;
+
+	g_idle_add(pin_request, cb_data);
+}
+
+static void bond_create_ssp_request_cb(const bt_bdaddr_t *remote_bd_addr,
+					bt_ssp_variant_t pairing_variant,
+					bool accept, uint32_t pass_key)
+{
+	struct test_data *data = tester_get_data();
+
+	data->if_bluetooth->ssp_reply(remote_bd_addr,
+					BT_SSP_VARIANT_PASSKEY_CONFIRMATION,
+					accept, pass_key);
+
+	data->cb_count--;
+}
+
+static void bond_create_ssp_success_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)
+{
+	bool accept = true;
+
+	bond_create_ssp_request_cb(remote_bd_addr, pairing_variant, accept,
+								pass_key);
+}
+
+static void bond_create_ssp_fail_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)
+{
+	bool accept = false;
+
+	bond_create_ssp_request_cb(remote_bd_addr, pairing_variant, accept,
+								pass_key);
+}
+
+static void bond_cancel_success_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 test_data *data = tester_get_data();
+	uint8_t *bdaddr = (uint8_t *)hciemu_get_client_bdaddr(data->hciemu);
+	bt_bdaddr_t remote_addr;
+	bt_status_t status;
+
+	bdaddr2android((const bdaddr_t *)bdaddr, &remote_addr.address);
+
+	data->cb_count--;
+
+	status = data->if_bluetooth->cancel_bond(&remote_addr);
+	check_expected_status(status);
+}
+
+static gboolean ssp_request(gpointer user_data)
+{
+	struct test_data *data = tester_get_data();
+	const struct generic_data *test = data->test_data;
+	struct bt_cb_data *cb_data = user_data;
+
+	if (data->test_checks_valid && test->expected_hal_cb.ssp_request_cb)
+		test->expected_hal_cb.ssp_request_cb(&cb_data->bdaddr,
+					&cb_data->bdname, cb_data->cod,
+					cb_data->ssp_variant, cb_data->passkey);
+
+	g_free(cb_data);
+	return FALSE;
+}
+
+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 bt_cb_data *cb_data = g_new0(struct bt_cb_data, 1);
+
+	cb_data->bdaddr = *remote_bd_addr;
+	cb_data->bdname = *bd_name;
+	cb_data->cod = cod;
+	cb_data->ssp_variant = pairing_variant;
+	cb_data->passkey = pass_key;
+
+	g_idle_add(ssp_request, cb_data);
+}
+
+static bt_bdaddr_t enable_done_bdaddr_val = { {0x00} };
+static const char enable_done_bdname_val[] = "BlueZ for Android";
+static bt_uuid_t enable_done_uuids_val = {
+	.uu = { 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x10, 0x00, 0x80, 0x00,
+					0x00, 0x80, 0x5f, 0x9b, 0x34, 0xfb},
+};
+static bt_device_type_t enable_done_tod_val = BT_DEVICE_DEVTYPE_DUAL;
+static bt_scan_mode_t enable_done_scanmode_val = BT_SCAN_MODE_NONE;
+static uint32_t enable_done_disctimeout_val = 120;
+
+static struct priority_property enable_done_props[] = {
+	{
+	.prop.type = BT_PROPERTY_BDADDR,
+	.prop.len = sizeof(enable_done_bdaddr_val),
+	.prop.val = &enable_done_bdaddr_val,
+	.prio = 1,
+	},
+	{
+	.prop.type = BT_PROPERTY_BDNAME,
+	.prop.len = sizeof(enable_done_bdname_val) - 1,
+	.prop.val = &enable_done_bdname_val,
+	.prio = 2,
+	},
+	{
+	.prop.type = BT_PROPERTY_UUIDS,
+	.prop.len = sizeof(enable_done_uuids_val),
+	.prop.val = &enable_done_uuids_val,
+	.prio = 3,
+	},
+	{
+	.prop.type = BT_PROPERTY_CLASS_OF_DEVICE,
+	.prop.len = sizeof(uint32_t),
+	.prop.val = NULL,
+	.prio = 4,
+	},
+	{
+	.prop.type = BT_PROPERTY_TYPE_OF_DEVICE,
+	.prop.len = sizeof(enable_done_tod_val),
+	.prop.val = &enable_done_tod_val,
+	.prio = 5,
+	},
+	{
+	.prop.type = BT_PROPERTY_ADAPTER_SCAN_MODE,
+	.prop.len = sizeof(enable_done_scanmode_val),
+	.prop.val = &enable_done_scanmode_val,
+	.prio = 6,
+	},
+	{
+	.prop.type = BT_PROPERTY_ADAPTER_BONDED_DEVICES,
+	.prop.len = 0,
+	.prop.val = NULL,
+	.prio = 7,
+	},
+	{
+	.prop.type = BT_PROPERTY_ADAPTER_DISCOVERY_TIMEOUT,
+	.prop.len = sizeof(enable_done_disctimeout_val),
+	.prop.val = &enable_done_disctimeout_val,
+	.prio = 8,
+	},
+};
+
+static const struct generic_data bluetooth_enable_success_test = {
+	.expected_hal_cb.adapter_state_changed_cb = enable_success_cb,
+	.expected_hal_cb.adapter_properties_cb = check_count_properties_cb,
+	.expected_cb_count = 1,
+	.expected_properties_num = 8,
+	.expected_properties = enable_done_props,
+	.expected_adapter_status = BT_STATUS_SUCCESS,
+};
+
+static const struct generic_data bluetooth_enable_success2_test = {
+	.expected_hal_cb.adapter_properties_cb = check_count_properties_cb,
+	.expected_adapter_status = BT_STATUS_SUCCESS,
+	.expected_properties_num = 8,
+	.expected_properties = enable_done_props,
+};
+
+static const struct generic_data bluetooth_disable_success_test = {
+	.expected_hal_cb.adapter_state_changed_cb = disable_success_cb,
+	.expected_cb_count = 1,
+	.expected_adapter_status = BT_STATUS_SUCCESS,
+};
+
+static char test_set_bdname[] = "test_bdname_set";
+
+static struct priority_property setprop_bdname_props[] = {
+	{
+	.prop.type = BT_PROPERTY_BDNAME,
+	.prop.val = test_set_bdname,
+	.prop.len = sizeof(test_set_bdname) - 1,
+	.prio = 0,
+	},
+};
+
+static const struct generic_data bluetooth_setprop_bdname_success_test = {
+	.expected_hal_cb.adapter_properties_cb = check_count_properties_cb,
+	.expected_properties_num = 1,
+	.expected_properties = setprop_bdname_props,
+};
+
+static bt_scan_mode_t test_setprop_scanmode_val =
+					BT_SCAN_MODE_CONNECTABLE_DISCOVERABLE;
+
+static struct priority_property setprop_scanmode_props[] = {
+	{
+	.prop.type = BT_PROPERTY_ADAPTER_SCAN_MODE,
+	.prop.val = &test_setprop_scanmode_val,
+	.prop.len = sizeof(bt_scan_mode_t),
+	},
+};
+
+static const struct generic_data bluetooth_setprop_scanmode_success_test = {
+	.expected_hal_cb.adapter_properties_cb = check_count_properties_cb,
+	.expected_properties_num = 1,
+	.expected_properties = setprop_scanmode_props,
+	.expected_adapter_status = BT_STATUS_SUCCESS,
+};
+
+static uint32_t test_setprop_disctimeout_val = 120;
+
+static struct priority_property setprop_disctimeout_props[] = {
+	{
+	.prop.type = BT_PROPERTY_ADAPTER_DISCOVERY_TIMEOUT,
+	.prop.val = &test_setprop_disctimeout_val,
+	.prop.len = sizeof(test_setprop_disctimeout_val),
+	},
+};
+
+static const struct generic_data bluetooth_setprop_disctimeout_success_test = {
+	.expected_hal_cb.adapter_properties_cb = check_count_properties_cb,
+	.expected_properties_num = 1,
+	.expected_properties = setprop_disctimeout_props,
+	.expected_adapter_status = BT_STATUS_SUCCESS,
+};
+
+static bt_bdaddr_t test_getprop_bdaddr_val = { {0x00} };
+
+static struct priority_property getprop_bdaddr_props[] = {
+	{
+	.prop.type = BT_PROPERTY_BDADDR,
+	.prop.val = &test_getprop_bdaddr_val,
+	.prop.len = sizeof(test_getprop_bdaddr_val),
+	},
+};
+
+static const struct generic_data bluetooth_getprop_bdaddr_success_test = {
+	.expected_hal_cb.adapter_properties_cb = check_count_properties_cb,
+	.expected_properties_num = 1,
+	.expected_properties = getprop_bdaddr_props,
+	.expected_adapter_status = BT_STATUS_SUCCESS,
+};
+
+static const char test_bdname[] = "test_bdname_setget";
+
+static struct priority_property getprop_bdname_props[] = {
+	{
+	.prop.type = BT_PROPERTY_BDNAME,
+	.prop.val = &test_bdname,
+	.prop.len = sizeof(test_bdname) - 1,
+	},
+};
+
+static const struct generic_data bluetooth_getprop_bdname_success_test = {
+	.expected_hal_cb.adapter_properties_cb = check_count_properties_cb,
+	.expected_properties_num = 1,
+	.expected_properties = getprop_bdname_props,
+	.expected_adapter_status = BT_STATUS_SUCCESS,
+};
+
+static unsigned char setprop_uuids[] = { 0xfb, 0x34, 0x9b, 0x5f, 0x80, 0x00,
+			0x00, 0x80, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00,
+			0x00, 0x00 };
+
+static struct priority_property setprop_uuid_prop[] = {
+	{
+	.prop.type = BT_PROPERTY_UUIDS,
+	.prop.val = &setprop_uuids,
+	.prop.len = sizeof(setprop_uuids),
+	},
+};
+
+static const struct generic_data bluetooth_setprop_uuid_invalid_test = {
+	.expected_adapter_status = BT_STATUS_FAIL,
+};
+
+static uint32_t setprop_class_of_device = 0;
+
+static struct priority_property setprop_cod_props[] = {
+	{
+	.prop.type = BT_PROPERTY_CLASS_OF_DEVICE,
+	.prop.val = &setprop_class_of_device,
+	.prop.len = sizeof(setprop_class_of_device),
+	},
+};
+
+static const struct generic_data bluetooth_setprop_cod_invalid_test = {
+	.expected_adapter_status = BT_STATUS_FAIL,
+};
+
+static bt_device_type_t setprop_type_of_device = BT_DEVICE_DEVTYPE_DUAL;
+
+static struct priority_property setprop_tod_props[] = {
+	{
+	.prop.type = BT_PROPERTY_TYPE_OF_DEVICE,
+	.prop.val = &setprop_type_of_device,
+	.prop.len = sizeof(setprop_type_of_device),
+	},
+};
+
+static const struct generic_data bluetooth_setprop_tod_invalid_test = {
+	.expected_adapter_status = BT_STATUS_FAIL,
+};
+
+static int32_t setprop_remote_rssi = 0;
+
+static struct priority_property setprop_remote_rssi_props[] = {
+	{
+	.prop.type = BT_PROPERTY_REMOTE_RSSI,
+	.prop.val = &setprop_remote_rssi,
+	.prop.len = sizeof(setprop_remote_rssi),
+	},
+};
+
+static const struct generic_data bluetooth_setprop_remote_rssi_invalid_test = {
+	.expected_adapter_status = BT_STATUS_FAIL,
+};
+
+static bt_service_record_t setprop_remote_service = {
+	.uuid = { {0x00} },
+	.channel = 12,
+	.name = "bt_name",
+};
+
+static struct priority_property setprop_service_record_props[] = {
+	{
+	.prop.type = BT_PROPERTY_SERVICE_RECORD,
+	.prop.val = &setprop_remote_service,
+	.prop.len = sizeof(setprop_remote_service),
+	},
+};
+
+static const struct generic_data
+			bluetooth_setprop_service_record_invalid_test = {
+	.expected_adapter_status = BT_STATUS_FAIL,
+};
+
+static bt_bdaddr_t setprop_bdaddr = {
+	.address = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
+};
+
+static struct priority_property setprop_bdaddr_props[] = {
+	{
+	.prop.type = BT_PROPERTY_BDADDR,
+	.prop.val = &setprop_bdaddr,
+	.prop.len = sizeof(setprop_bdaddr),
+	},
+};
+
+static const struct generic_data bluetooth_setprop_bdaddr_invalid_test = {
+	.expected_adapter_status = BT_STATUS_FAIL,
+};
+
+static bt_scan_mode_t setprop_scanmode_connectable = BT_SCAN_MODE_CONNECTABLE;
+
+static struct priority_property setprop_scanmode_connectable_props[] = {
+	{
+	.prop.type = BT_PROPERTY_ADAPTER_SCAN_MODE,
+	.prop.val = &setprop_scanmode_connectable,
+	.prop.len = sizeof(setprop_scanmode_connectable),
+	},
+};
+
+static const struct generic_data
+			bluetooth_setprop_scanmode_connectable_success_test = {
+	.expected_hal_cb.adapter_properties_cb = check_count_properties_cb,
+	.expected_properties_num = 1,
+	.expected_properties = setprop_scanmode_connectable_props,
+	.expected_adapter_status = BT_STATUS_SUCCESS,
+};
+
+static bt_bdaddr_t setprop_bonded_devices = {
+	.address = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05 },
+};
+
+static struct priority_property setprop_bonded_devices_props[] = {
+	{
+	.prop.type = BT_PROPERTY_ADAPTER_BONDED_DEVICES,
+	.prop.val = &setprop_bonded_devices,
+	.prop.len = sizeof(setprop_bonded_devices),
+	},
+};
+
+static const struct generic_data
+			bluetooth_setprop_bonded_devices_invalid_test = {
+	.expected_adapter_status = BT_STATUS_FAIL,
+};
+
+static uint32_t getprop_cod = 0x00020c;
+
+static struct priority_property getprop_cod_props[] = {
+	{
+	.prop.type = BT_PROPERTY_CLASS_OF_DEVICE,
+	.prop.val = &getprop_cod,
+	.prop.len = sizeof(getprop_cod),
+	},
+};
+
+static const struct generic_data bluetooth_getprop_cod_success_test = {
+	.expected_hal_cb.adapter_properties_cb = check_count_properties_cb,
+	.expected_properties_num = 1,
+	.expected_properties = getprop_cod_props,
+	.expected_adapter_status = BT_STATUS_SUCCESS,
+};
+
+static bt_device_type_t getprop_tod = BT_DEVICE_DEVTYPE_DUAL;
+
+static struct priority_property getprop_tod_props[] = {
+	{
+	.prop.type = BT_PROPERTY_TYPE_OF_DEVICE,
+	.prop.val = &getprop_tod,
+	.prop.len = sizeof(getprop_tod),
+	},
+};
+
+static const struct generic_data bluetooth_getprop_tod_success_test = {
+	.expected_hal_cb.adapter_properties_cb = check_count_properties_cb,
+	.expected_properties_num = 1,
+	.expected_properties = getprop_tod_props,
+	.expected_adapter_status = BT_STATUS_SUCCESS,
+};
+
+static bt_scan_mode_t getprop_scanmode = BT_SCAN_MODE_NONE;
+
+static struct priority_property getprop_scanmode_props[] = {
+	{
+	.prop.type = BT_PROPERTY_ADAPTER_SCAN_MODE,
+	.prop.val = &getprop_scanmode,
+	.prop.len = sizeof(getprop_scanmode),
+	},
+};
+
+static const struct generic_data bluetooth_getprop_scanmode_success_test = {
+	.expected_hal_cb.adapter_properties_cb = check_count_properties_cb,
+	.expected_properties_num = 1,
+	.expected_properties = getprop_scanmode_props,
+	.expected_adapter_status = BT_STATUS_SUCCESS,
+};
+
+static uint32_t getprop_disctimeout_val = 120;
+
+static struct priority_property getprop_disctimeout_props[] = {
+	{
+	.prop.type = BT_PROPERTY_ADAPTER_DISCOVERY_TIMEOUT,
+	.prop.val = &getprop_disctimeout_val,
+	.prop.len = sizeof(getprop_disctimeout_val),
+	},
+};
+
+static const struct generic_data bluetooth_getprop_disctimeout_success_test = {
+	.expected_hal_cb.adapter_properties_cb = check_count_properties_cb,
+	.expected_properties_num = 1,
+	.expected_properties = getprop_disctimeout_props,
+	.expected_adapter_status = BT_STATUS_SUCCESS,
+};
+
+static bt_uuid_t getprop_uuids = {
+	.uu = { 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x10, 0x00, 0x80, 0x00,
+					0x00, 0x80, 0x5F, 0x9B, 0x34, 0xFB },
+};
+
+static struct priority_property getprop_uuids_props[] = {
+	{
+	.prop.type = BT_PROPERTY_UUIDS,
+	.prop.val = &getprop_uuids,
+	.prop.len = sizeof(getprop_uuids),
+	},
+};
+
+static const struct generic_data bluetooth_getprop_uuids_success_test = {
+	.expected_hal_cb.adapter_properties_cb = check_count_properties_cb,
+	.expected_properties_num = 1,
+	.expected_properties = getprop_uuids_props,
+	.expected_adapter_status = BT_STATUS_SUCCESS,
+};
+
+static struct priority_property getprop_bondeddev_props[] = {
+	{
+	.prop.type = BT_PROPERTY_ADAPTER_BONDED_DEVICES,
+	.prop.val = NULL,
+	.prop.len = 0,
+	},
+};
+
+static const struct generic_data bluetooth_getprop_bondeddev_success_test = {
+	.expected_hal_cb.adapter_properties_cb = check_count_properties_cb,
+	.expected_properties_num = 1,
+	.expected_properties = getprop_bondeddev_props,
+	.expected_adapter_status = BT_STATUS_SUCCESS,
+};
+
+static bt_scan_mode_t setprop_scanmode_none = BT_SCAN_MODE_NONE;
+
+static struct priority_property setprop_scanmode_none_props[] = {
+	{
+	.prop.type = BT_PROPERTY_ADAPTER_SCAN_MODE,
+	.prop.val = &setprop_scanmode_none,
+	.prop.len = sizeof(setprop_scanmode_none),
+	},
+};
+
+static const struct generic_data
+			bluetooth_setprop_scanmode_none_success2_test = {
+	.expected_hal_cb.adapter_properties_cb = check_count_properties_cb,
+	.expected_properties_num = 1,
+	.expected_properties = setprop_scanmode_none_props,
+	.expected_adapter_status = BT_STATUS_SUCCESS,
+};
+
+static const struct generic_data bluetooth_discovery_start_success_test = {
+	.expected_hal_cb.discovery_state_changed_cb =
+						discovery_start_success_cb,
+	.expected_cb_count = 1,
+	.expected_adapter_status = BT_STATUS_SUCCESS,
+};
+
+static const struct generic_data bluetooth_discovery_start_success2_test = {
+	.expected_hal_cb.discovery_state_changed_cb = discovery_start_done_cb,
+	.expected_cb_count = 1,
+	.expected_adapter_status = BT_STATUS_SUCCESS,
+};
+
+static const struct generic_data bluetooth_discovery_stop_success2_test = {
+	.expected_adapter_status = BT_STATUS_SUCCESS,
+};
+
+static const struct generic_data bluetooth_discovery_stop_success_test = {
+	.expected_hal_cb.discovery_state_changed_cb = discovery_stop_success_cb,
+	.expected_cb_count = 2,
+	.expected_adapter_status = BT_STATUS_SUCCESS,
+};
+
+static const struct generic_data bluetooth_discovery_device_found_test = {
+	.expected_hal_cb.discovery_state_changed_cb =
+					discovery_device_found_state_changed_cb,
+	.expected_hal_cb.device_found_cb = discovery_device_found_cb,
+	.expected_cb_count = 3,
+	.expected_adapter_status = BT_STATUS_NOT_EXPECTED,
+};
+
+static const char remote_get_properties_bdname_val[] = "00:AA:01:01:00:00";
+static uint32_t remote_get_properties_cod_val = 0;
+static bt_device_type_t remote_get_properties_tod_val = BT_DEVICE_DEVTYPE_BREDR;
+static int32_t remote_get_properties_rssi_val = -60;
+
+static struct priority_property remote_getprops_props[] = {
+	{
+	.prop.type = BT_PROPERTY_BDNAME,
+	.prop.val = &remote_get_properties_bdname_val,
+	.prop.len = sizeof(remote_get_properties_bdname_val) - 1,
+	.prio = 1,
+	},
+	{
+	.prop.type = BT_PROPERTY_UUIDS,
+	.prop.val = NULL,
+	.prop.len = 0,
+	.prio = 2,
+	},
+	{
+	.prop.type = BT_PROPERTY_CLASS_OF_DEVICE,
+	.prop.val = &remote_get_properties_cod_val,
+	.prop.len = sizeof(remote_get_properties_cod_val),
+	.prio = 3,
+	},
+	{
+	.prop.type = BT_PROPERTY_TYPE_OF_DEVICE,
+	.prop.val = &remote_get_properties_tod_val,
+	.prop.len = sizeof(remote_get_properties_tod_val),
+	.prio = 4,
+	},
+	{
+	.prop.type = BT_PROPERTY_REMOTE_RSSI,
+	.prop.val = &remote_get_properties_rssi_val,
+	.prop.len = sizeof(remote_get_properties_rssi_val),
+	.prio = 5,
+	},
+	{
+	.prop.type = BT_PROPERTY_REMOTE_DEVICE_TIMESTAMP,
+	.prop.val = NULL,
+	.prop.len = 4,
+	.prio = 6,
+	},
+};
+
+static const struct generic_data bt_dev_getprops_success_test = {
+	.expected_hal_cb.discovery_state_changed_cb =
+					remote_discovery_state_changed_cb,
+	.expected_hal_cb.device_found_cb = remote_getprops_device_found_cb,
+	.expected_hal_cb.remote_device_properties_cb =
+					remote_test_device_properties_cb,
+	.expected_cb_count = 3,
+	.expected_properties_num = 6,
+	.expected_properties = remote_getprops_props,
+	.expected_adapter_status = BT_STATUS_NOT_EXPECTED,
+};
+
+static const char remote_getprop_bdname_val[] = "00:AA:01:01:00:00";
+
+static struct priority_property remote_getprop_bdname_props[] = {
+	{
+	.prop.type = BT_PROPERTY_BDNAME,
+	.prop.val = &remote_getprop_bdname_val,
+	.prop.len = sizeof(remote_getprop_bdname_val) - 1,
+	},
+};
+
+static const struct generic_data bt_dev_getprop_bdname_success_test = {
+	.expected_hal_cb.discovery_state_changed_cb =
+					remote_discovery_state_changed_cb,
+	.expected_hal_cb.device_found_cb = remote_get_property_device_found_cb,
+	.expected_hal_cb.remote_device_properties_cb =
+					remote_test_device_properties_cb,
+	.expected_cb_count = 3,
+	.expected_properties_num = 1,
+	.expected_properties = remote_getprop_bdname_props,
+	.expected_adapter_status = BT_STATUS_SUCCESS,
+};
+
+static struct priority_property remote_getprop_uuids_props[] = {
+	{
+	.prop.type = BT_PROPERTY_UUIDS,
+	.prop.val = NULL,
+	.prop.len = 0,
+	},
+};
+
+static const struct generic_data bt_dev_getprop_uuids_success_test = {
+	.expected_hal_cb.discovery_state_changed_cb =
+					remote_discovery_state_changed_cb,
+	.expected_hal_cb.device_found_cb = remote_get_property_device_found_cb,
+	.expected_hal_cb.remote_device_properties_cb =
+					remote_test_device_properties_cb,
+	.expected_cb_count = 3,
+	.expected_properties_num = 1,
+	.expected_properties = remote_getprop_uuids_props,
+	.expected_adapter_status = BT_STATUS_SUCCESS,
+};
+
+static uint32_t remote_getprop_cod_val = 0;
+
+static struct priority_property remote_getprop_cod_props[] = {
+	{
+	.prop.type = BT_PROPERTY_CLASS_OF_DEVICE,
+	.prop.val = &remote_getprop_cod_val,
+	.prop.len = sizeof(remote_getprop_cod_val),
+	},
+};
+
+static const struct generic_data bt_dev_getprop_cod_success_test = {
+	.expected_hal_cb.discovery_state_changed_cb =
+					remote_discovery_state_changed_cb,
+	.expected_hal_cb.device_found_cb = remote_get_property_device_found_cb,
+	.expected_hal_cb.remote_device_properties_cb =
+					remote_test_device_properties_cb,
+	.expected_cb_count = 3,
+	.expected_properties_num = 1,
+	.expected_properties = remote_getprop_cod_props,
+	.expected_adapter_status = BT_STATUS_SUCCESS,
+};
+
+static bt_device_type_t remote_getprop_tod_val = BT_DEVICE_DEVTYPE_BREDR;
+
+static struct priority_property remote_getprop_tod_props[] = {
+	{
+	.prop.type = BT_PROPERTY_TYPE_OF_DEVICE,
+	.prop.val = &remote_getprop_tod_val,
+	.prop.len = sizeof(remote_getprop_tod_val),
+	},
+};
+
+static const struct generic_data bt_dev_getprop_tod_success_test = {
+	.expected_hal_cb.discovery_state_changed_cb =
+					remote_discovery_state_changed_cb,
+	.expected_hal_cb.device_found_cb = remote_get_property_device_found_cb,
+	.expected_hal_cb.remote_device_properties_cb =
+					remote_test_device_properties_cb,
+	.expected_cb_count = 3,
+	.expected_properties_num = 1,
+	.expected_properties = remote_getprop_tod_props,
+	.expected_adapter_status = BT_STATUS_SUCCESS,
+};
+
+static int32_t remote_getprop_rssi_val = -60;
+
+static struct priority_property remote_getprop_rssi_props[] = {
+	{
+	.prop.type = BT_PROPERTY_REMOTE_RSSI,
+	.prop.val = &remote_getprop_rssi_val,
+	.prop.len = sizeof(remote_getprop_rssi_val),
+	},
+};
+
+static const struct generic_data bt_dev_getprop_rssi_success_test = {
+	.expected_hal_cb.discovery_state_changed_cb =
+					remote_discovery_state_changed_cb,
+	.expected_hal_cb.device_found_cb = remote_get_property_device_found_cb,
+	.expected_hal_cb.remote_device_properties_cb =
+					remote_test_device_properties_cb,
+	.expected_cb_count = 3,
+	.expected_properties_num = 1,
+	.expected_properties = remote_getprop_rssi_props,
+	.expected_adapter_status = BT_STATUS_SUCCESS,
+};
+
+static struct priority_property remote_getprop_timestamp_props[] = {
+	{
+	.prop.type = BT_PROPERTY_REMOTE_DEVICE_TIMESTAMP,
+	.prop.val = NULL,
+	.prop.len = 4,
+	},
+};
+
+static const struct generic_data bt_dev_getprop_timpestamp_success_test = {
+	.expected_hal_cb.discovery_state_changed_cb =
+					remote_discovery_state_changed_cb,
+	.expected_hal_cb.device_found_cb = remote_get_property_device_found_cb,
+	.expected_hal_cb.remote_device_properties_cb =
+					remote_test_device_properties_cb,
+	.expected_cb_count = 3,
+	.expected_properties_num = 1,
+	.expected_properties = remote_getprop_timestamp_props,
+	.expected_adapter_status = BT_STATUS_SUCCESS,
+};
+
+static bt_bdaddr_t remote_getprop_bdaddr_val = {
+	.address = { 0x00, 0xaa, 0x01, 0x00, 0x00, 0x00 }
+};
+
+static struct priority_property remote_getprop_bdaddr_props[] = {
+	{
+	.prop.type = BT_PROPERTY_BDADDR,
+	.prop.val = &remote_getprop_bdaddr_val,
+	.prop.len = sizeof(remote_getprop_bdaddr_val),
+	},
+};
+
+static const struct generic_data bt_dev_getprop_bdaddr_fail_test = {
+	.expected_hal_cb.discovery_state_changed_cb =
+					remote_discovery_state_changed_cb,
+	.expected_hal_cb.device_found_cb = remote_get_property_device_found_cb,
+	.expected_hal_cb.remote_device_properties_cb =
+					remote_test_device_properties_cb,
+	.expected_cb_count = 3,
+	.expected_properties = remote_getprop_bdaddr_props,
+	.expected_adapter_status = BT_STATUS_FAIL,
+};
+
+static bt_service_record_t remote_getprop_servrec_val = {
+	.uuid = { {0x00} },
+	.channel = 12,
+	.name = "bt_name",
+};
+
+static struct priority_property remote_getprop_servrec_props[] = {
+	{
+	.prop.type = BT_PROPERTY_SERVICE_RECORD,
+	.prop.val = &remote_getprop_servrec_val,
+	.prop.len = sizeof(remote_getprop_servrec_val),
+	},
+};
+
+static const struct generic_data bt_dev_getprop_servrec_fail_test = {
+	.expected_hal_cb.discovery_state_changed_cb =
+					remote_discovery_state_changed_cb,
+	.expected_hal_cb.device_found_cb = remote_get_property_device_found_cb,
+	.expected_hal_cb.remote_device_properties_cb =
+					remote_test_device_properties_cb,
+	.expected_cb_count = 3,
+	.expected_properties = remote_getprop_servrec_props,
+	.expected_adapter_status = BT_STATUS_FAIL,
+};
+
+static bt_scan_mode_t remote_getprop_scanmode_val = BT_SCAN_MODE_CONNECTABLE;
+
+static struct priority_property remote_getprop_scanmode_props[] = {
+	{
+	.prop.type = BT_PROPERTY_ADAPTER_SCAN_MODE,
+	.prop.val = &remote_getprop_scanmode_val,
+	.prop.len = sizeof(remote_getprop_scanmode_val),
+	},
+};
+
+static const struct generic_data bt_dev_getprop_scanmode_fail_test = {
+	.expected_hal_cb.discovery_state_changed_cb =
+					remote_discovery_state_changed_cb,
+	.expected_hal_cb.device_found_cb = remote_get_property_device_found_cb,
+	.expected_hal_cb.remote_device_properties_cb =
+					remote_test_device_properties_cb,
+	.expected_cb_count = 3,
+	.expected_properties = remote_getprop_scanmode_props,
+	.expected_adapter_status = BT_STATUS_FAIL,
+};
+
+static struct priority_property remote_getprop_bondeddev_props[] = {
+	{
+	.prop.type = BT_PROPERTY_ADAPTER_BONDED_DEVICES,
+	.prop.val = NULL,
+	.prop.len = 0,
+	},
+};
+
+static const struct generic_data bt_dev_getprop_bondeddev_fail_test = {
+	.expected_hal_cb.discovery_state_changed_cb =
+					remote_discovery_state_changed_cb,
+	.expected_hal_cb.device_found_cb = remote_get_property_device_found_cb,
+	.expected_hal_cb.remote_device_properties_cb =
+					remote_test_device_properties_cb,
+	.expected_cb_count = 3,
+	.expected_properties = remote_getprop_bondeddev_props,
+	.expected_adapter_status = BT_STATUS_FAIL,
+};
+
+static uint32_t remote_getprop_disctimeout_val = 120;
+
+static struct priority_property remote_getprop_disctimeout_props[] = {
+	{
+	.prop.type = BT_PROPERTY_ADAPTER_DISCOVERY_TIMEOUT,
+	.prop.val = &remote_getprop_disctimeout_val,
+	.prop.len = sizeof(remote_getprop_disctimeout_val),
+	},
+};
+
+static const struct generic_data bt_dev_getprop_disctimeout_fail_test = {
+	.expected_hal_cb.discovery_state_changed_cb =
+					remote_discovery_state_changed_cb,
+	.expected_hal_cb.device_found_cb = remote_get_property_device_found_cb,
+	.expected_hal_cb.remote_device_properties_cb =
+					remote_test_device_properties_cb,
+	.expected_cb_count = 3,
+	.expected_properties = remote_getprop_disctimeout_props,
+	.expected_adapter_status = BT_STATUS_FAIL,
+};
+
+static struct priority_property remote_getprop_verinfo_props[] = {
+	{
+	.prop.type = BT_PROPERTY_REMOTE_VERSION_INFO,
+	.prop.val = NULL,
+	.prop.len = 0,
+	},
+};
+
+static const struct generic_data bt_dev_getprop_verinfo_fail_test = {
+	.expected_hal_cb.discovery_state_changed_cb =
+					remote_discovery_state_changed_cb,
+	.expected_hal_cb.device_found_cb = remote_get_property_device_found_cb,
+	.expected_hal_cb.remote_device_properties_cb =
+					remote_test_device_properties_cb,
+	.expected_cb_count = 3,
+	.expected_properties = remote_getprop_verinfo_props,
+	.expected_adapter_status = BT_STATUS_FAIL,
+};
+
+static struct priority_property remote_getprop_fname_props[] = {
+	{
+	.prop.type = BT_PROPERTY_REMOTE_VERSION_INFO,
+	.prop.val = NULL,
+	.prop.len = 0,
+	},
+};
+
+static const struct generic_data bt_dev_getprop_fname_fail_test = {
+	.expected_hal_cb.discovery_state_changed_cb =
+					remote_discovery_state_changed_cb,
+	.expected_hal_cb.device_found_cb = remote_get_property_device_found_cb,
+	.expected_hal_cb.remote_device_properties_cb =
+					remote_test_device_properties_cb,
+	.expected_cb_count = 3,
+	.expected_properties = remote_getprop_fname_props,
+	.expected_adapter_status = BT_STATUS_FAIL,
+};
+
+static const char remote_setprop_fname_val[] = "set_fname_test";
+
+static struct priority_property remote_setprop_fname_props[] = {
+	{
+	.prop.type = BT_PROPERTY_REMOTE_FRIENDLY_NAME,
+	.prop.val = &remote_setprop_fname_val,
+	.prop.len = sizeof(remote_setprop_fname_val) - 1,
+	},
+	{
+	.prop.type = BT_PROPERTY_REMOTE_FRIENDLY_NAME,
+	.prop.val = &remote_setprop_fname_val,
+	.prop.len = sizeof(remote_setprop_fname_val) - 1,
+	},
+};
+
+static const struct generic_data bt_dev_setprop_fname_success_test = {
+	.expected_hal_cb.discovery_state_changed_cb =
+					remote_setprop_disc_state_changed_cb,
+	.expected_hal_cb.device_found_cb = remote_setprop_device_found_cb,
+	.expected_hal_cb.remote_device_properties_cb =
+					remote_setprop_device_properties_cb,
+	.expected_cb_count = 4,
+	.expected_properties_num = 2,
+	.expected_properties = remote_setprop_fname_props,
+	.expected_adapter_status = BT_STATUS_SUCCESS,
+};
+
+static const char remote_setprop_bdname_val[] = "setprop_bdname_fail";
+
+static struct priority_property remote_setprop_bdname_props[] = {
+	{
+	.prop.type = BT_PROPERTY_BDNAME,
+	.prop.val = &remote_setprop_bdname_val,
+	.prop.len = sizeof(remote_setprop_bdname_val) - 1,
+	},
+};
+
+static const struct generic_data bt_dev_setprop_bdname_fail_test = {
+	.expected_hal_cb.discovery_state_changed_cb =
+					remote_discovery_state_changed_cb,
+	.expected_hal_cb.device_found_cb = remote_setprop_fail_device_found_cb,
+	.expected_cb_count = 3,
+	.expected_properties = remote_setprop_bdname_props,
+	.expected_adapter_status = BT_STATUS_FAIL,
+};
+
+static struct priority_property remote_setprop_uuids_props[] = {
+	{
+	.prop.type = BT_PROPERTY_UUIDS,
+	.prop.val = NULL,
+	.prop.len = 0,
+	},
+};
+
+static const struct generic_data bt_dev_setprop_uuids_fail_test  = {
+	.expected_hal_cb.discovery_state_changed_cb =
+					remote_discovery_state_changed_cb,
+	.expected_hal_cb.device_found_cb = remote_setprop_fail_device_found_cb,
+	.expected_cb_count = 3,
+	.expected_properties = remote_setprop_uuids_props,
+	.expected_adapter_status = BT_STATUS_FAIL,
+};
+
+static uint32_t remote_setprop_cod_val = 0;
+
+static struct priority_property remote_setprop_cod_props[] = {
+	{
+	.prop.type = BT_PROPERTY_CLASS_OF_DEVICE,
+	.prop.val = &remote_setprop_cod_val,
+	.prop.len = sizeof(remote_setprop_cod_val),
+	},
+};
+
+static const struct generic_data bt_dev_setprop_cod_fail_test = {
+	.expected_hal_cb.discovery_state_changed_cb =
+					remote_discovery_state_changed_cb,
+	.expected_hal_cb.device_found_cb = remote_setprop_fail_device_found_cb,
+	.expected_cb_count = 3,
+	.expected_properties = remote_setprop_cod_props,
+	.expected_adapter_status = BT_STATUS_FAIL,
+};
+
+static bt_device_type_t remote_setprop_tod_val = BT_DEVICE_DEVTYPE_BREDR;
+
+static struct priority_property remote_setprop_tod_props[] = {
+	{
+	.prop.type = BT_PROPERTY_TYPE_OF_DEVICE,
+	.prop.val = &remote_setprop_tod_val,
+	.prop.len = sizeof(remote_setprop_tod_val),
+	},
+};
+
+static const struct generic_data bt_dev_setprop_tod_fail_test = {
+	.expected_hal_cb.discovery_state_changed_cb =
+					remote_discovery_state_changed_cb,
+	.expected_hal_cb.device_found_cb = remote_setprop_fail_device_found_cb,
+	.expected_cb_count = 3,
+	.expected_properties = remote_setprop_tod_props,
+	.expected_adapter_status = BT_STATUS_FAIL,
+};
+
+static int32_t remote_setprop_rssi_val = -60;
+
+static struct priority_property remote_setprop_rssi_props[] = {
+	{
+	.prop.type = BT_PROPERTY_REMOTE_RSSI,
+	.prop.val = &remote_setprop_rssi_val,
+	.prop.len = sizeof(remote_setprop_rssi_val),
+	},
+};
+
+static const struct generic_data bt_dev_setprop_rssi_fail_test = {
+	.expected_hal_cb.discovery_state_changed_cb =
+					remote_discovery_state_changed_cb,
+	.expected_hal_cb.device_found_cb = remote_setprop_fail_device_found_cb,
+	.expected_cb_count = 3,
+	.expected_properties = remote_setprop_rssi_props,
+	.expected_adapter_status = BT_STATUS_FAIL,
+};
+
+static int32_t remote_setprop_timestamp_val = 0xAB;
+
+static struct priority_property remote_setprop_timestamp_props[] = {
+	{
+	.prop.type = BT_PROPERTY_REMOTE_DEVICE_TIMESTAMP,
+	.prop.val = (&remote_setprop_timestamp_val),
+	.prop.len = sizeof(remote_setprop_timestamp_val),
+	},
+};
+
+static const struct generic_data bt_dev_setprop_timpestamp_fail_test = {
+	.expected_hal_cb.discovery_state_changed_cb =
+					remote_discovery_state_changed_cb,
+	.expected_hal_cb.device_found_cb = remote_setprop_fail_device_found_cb,
+	.expected_cb_count = 3,
+	.expected_properties = remote_setprop_timestamp_props,
+	.expected_adapter_status = BT_STATUS_FAIL,
+};
+
+static bt_bdaddr_t remote_setprop_bdaddr_val = {
+	.address = { 0x00, 0xaa, 0x01, 0x00, 0x00, 0x00 }
+};
+
+static struct priority_property remote_setprop_bdaddr_props[] = {
+	{
+	.prop.type = BT_PROPERTY_BDADDR,
+	.prop.val = &remote_setprop_bdaddr_val,
+	.prop.len = sizeof(remote_setprop_bdaddr_val),
+	},
+};
+
+static const struct generic_data bt_dev_setprop_bdaddr_fail_test = {
+	.expected_hal_cb.discovery_state_changed_cb =
+					remote_discovery_state_changed_cb,
+	.expected_hal_cb.device_found_cb = remote_setprop_fail_device_found_cb,
+	.expected_cb_count = 3,
+	.expected_properties = remote_setprop_bdaddr_props,
+	.expected_adapter_status = BT_STATUS_FAIL,
+};
+
+static bt_service_record_t remote_setprop_servrec_val = {
+	.uuid = { {0x00} },
+	.channel = 12,
+	.name = "bt_name",
+};
+
+static struct priority_property remote_setprop_servrec_props[] = {
+	{
+	.prop.type = BT_PROPERTY_SERVICE_RECORD,
+	.prop.val = &remote_setprop_servrec_val,
+	.prop.len = sizeof(remote_setprop_servrec_val),
+	},
+};
+
+static const struct generic_data bt_dev_setprop_servrec_fail_test = {
+	.expected_hal_cb.discovery_state_changed_cb =
+					remote_discovery_state_changed_cb,
+	.expected_hal_cb.device_found_cb = remote_setprop_fail_device_found_cb,
+	.expected_cb_count = 3,
+	.expected_properties = remote_setprop_servrec_props,
+	.expected_adapter_status = BT_STATUS_FAIL,
+};
+
+static bt_scan_mode_t remote_setprop_scanmode_val = BT_SCAN_MODE_CONNECTABLE;
+
+static struct priority_property remote_setprop_scanmode_props[] = {
+	{
+	.prop.type = BT_PROPERTY_ADAPTER_SCAN_MODE,
+	.prop.val = &remote_setprop_scanmode_val,
+	.prop.len = sizeof(remote_setprop_scanmode_val),
+	},
+};
+
+static const struct generic_data bt_dev_setprop_scanmode_fail_test = {
+	.expected_hal_cb.discovery_state_changed_cb =
+					remote_discovery_state_changed_cb,
+	.expected_hal_cb.device_found_cb = remote_setprop_fail_device_found_cb,
+	.expected_cb_count = 3,
+	.expected_properties = remote_setprop_scanmode_props,
+	.expected_adapter_status = BT_STATUS_FAIL,
+};
+
+static bt_bdaddr_t remote_setprop_bondeddev_val = {
+	.address = { 0x00, 0xaa, 0x01, 0x00, 0x00, 0x00 }
+};
+
+static struct priority_property remote_setprop_bondeddev_props[] = {
+	{
+	.prop.type = BT_PROPERTY_ADAPTER_BONDED_DEVICES,
+	.prop.val = &remote_setprop_bondeddev_val,
+	.prop.len = sizeof(remote_setprop_bondeddev_val),
+	},
+};
+
+static const struct generic_data bt_dev_setprop_bondeddev_fail_test = {
+	.expected_hal_cb.discovery_state_changed_cb =
+					remote_discovery_state_changed_cb,
+	.expected_hal_cb.device_found_cb = remote_setprop_fail_device_found_cb,
+	.expected_cb_count = 3,
+	.expected_properties = remote_setprop_bondeddev_props,
+	.expected_adapter_status = BT_STATUS_FAIL,
+};
+
+static uint32_t remote_setprop_disctimeout_val = 120;
+
+static struct priority_property remote_setprop_disctimeout_props[] = {
+	{
+	.prop.type = BT_PROPERTY_ADAPTER_DISCOVERY_TIMEOUT,
+	.prop.val = &remote_setprop_disctimeout_val,
+	.prop.len = sizeof(remote_setprop_disctimeout_val),
+	},
+};
+
+static const struct generic_data bt_dev_setprop_disctimeout_fail_test = {
+	.expected_hal_cb.discovery_state_changed_cb =
+					remote_discovery_state_changed_cb,
+	.expected_hal_cb.device_found_cb = remote_setprop_fail_device_found_cb,
+	.expected_cb_count = 3,
+	.expected_properties = remote_setprop_disctimeout_props,
+	.expected_adapter_status = BT_STATUS_FAIL,
+};
+
+static const struct generic_data bt_bond_create_pin_success_test = {
+	.expected_hal_cb.device_found_cb = bond_device_found_cb,
+	.expected_hal_cb.bond_state_changed_cb =
+					bond_test_bonded_state_changed_cb,
+	.expected_hal_cb.pin_request_cb = bond_create_pin_success_request_cb,
+	.expected_cb_count = 4,
+	.expected_adapter_status = BT_STATUS_SUCCESS,
+};
+
+static const struct generic_data bt_bond_create_pin_fail_test = {
+	.expected_hal_cb.device_found_cb = bond_nostatus_device_found_cb,
+	.expected_hal_cb.bond_state_changed_cb =
+						bond_test_none_state_changed_cb,
+	.expected_hal_cb.pin_request_cb = bond_create_pin_fail_request_cb,
+	.expected_cb_count = 4,
+	.expected_adapter_status = MGMT_STATUS_AUTH_FAILED,
+};
+
+static const struct generic_data bt_bond_create_ssp_success_test = {
+	.expected_hal_cb.device_found_cb = bond_device_found_cb,
+	.expected_hal_cb.bond_state_changed_cb =
+					bond_test_bonded_state_changed_cb,
+	.expected_hal_cb.ssp_request_cb = bond_create_ssp_success_request_cb,
+	.expected_cb_count = 4,
+	.expected_adapter_status = BT_STATUS_SUCCESS,
+};
+
+static const struct generic_data bt_bond_create_ssp_fail_test = {
+	.expected_hal_cb.device_found_cb = bond_nostatus_device_found_cb,
+	.expected_hal_cb.bond_state_changed_cb =
+						bond_test_none_state_changed_cb,
+	.expected_hal_cb.ssp_request_cb = bond_create_ssp_fail_request_cb,
+	.expected_cb_count = 4,
+	.expected_adapter_status = MGMT_STATUS_AUTH_FAILED,
+};
+
+static const struct generic_data bt_bond_create_no_disc_success_test = {
+	.expected_hal_cb.bond_state_changed_cb =
+					bond_test_bonded_state_changed_cb,
+	.expected_hal_cb.ssp_request_cb = bond_create_ssp_success_request_cb,
+	.expected_cb_count = 3,
+	.expected_adapter_status = BT_STATUS_SUCCESS,
+};
+
+static const struct generic_data bt_bond_create_bad_addr_success_test = {
+	.expected_adapter_status = MGMT_STATUS_CONNECT_FAILED,
+};
+
+static const struct generic_data bt_bond_cancel_success_test = {
+	.expected_hal_cb.device_found_cb = bond_nostatus_device_found_cb,
+	.expected_hal_cb.bond_state_changed_cb =
+						bond_test_none_state_changed_cb,
+	.expected_hal_cb.ssp_request_cb = bond_cancel_success_ssp_request_cb,
+	.expected_cb_count = 4,
+	.expected_adapter_status = BT_STATUS_SUCCESS,
+};
+
+static const struct generic_data bt_bond_remove_success_test = {
+	.expected_hal_cb.device_found_cb = bond_nostatus_device_found_cb,
+	.expected_hal_cb.bond_state_changed_cb =
+					bond_remove_success_state_changed_cb,
+	.expected_hal_cb.ssp_request_cb = bond_create_ssp_success_request_cb,
+	.expected_cb_count = 4,
+	.expected_adapter_status = BT_STATUS_SUCCESS,
+};
+
+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 = NULL,
+	.thread_evt_cb = NULL,
+	.dut_mode_recv_cb = NULL,
+	.le_test_mode_cb = NULL
+};
+
+static bool setup(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(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;
+
+	return true;
+}
+
+static void setup_base(const void *test_data)
+{
+	struct test_data *data = tester_get_data();
+	bt_status_t status;
+
+	if (!setup(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_enabled_adapter(const void *test_data)
+{
+	struct test_data *data = tester_get_data();
+	bt_status_t status;
+
+	if (!setup(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;
+	}
+
+	status = data->if_bluetooth->enable();
+	if (status != BT_STATUS_SUCCESS)
+		tester_setup_failed();
+}
+
+static void teardown(const void *test_data)
+{
+	struct test_data *data = tester_get_data();
+
+	if (data->if_hid) {
+		data->if_hid->cleanup();
+		data->if_hid = NULL;
+	}
+
+	if (data->if_bluetooth) {
+		data->if_bluetooth->cleanup();
+		data->if_bluetooth = NULL;
+	}
+
+	/* Test result already known, no need to check further */
+	data->test_checks_valid = false;
+
+	if (data->expected_properties_list)
+		g_slist_free(data->expected_properties_list);
+
+	data->device->close(data->device);
+
+	if (!data->bluetoothd_pid)
+		tester_teardown_complete();
+}
+
+static void test_dummy(const void *test_data)
+{
+	tester_test_passed();
+}
+
+static void test_enable(const void *test_data)
+{
+	struct test_data *data = tester_get_data();
+	bt_status_t adapter_status;
+
+	uint8_t *bdaddr = (uint8_t *)hciemu_get_master_bdaddr(data->hciemu);
+
+	init_test_conditions(data);
+
+	bdaddr2android((const bdaddr_t *)bdaddr,
+					&enable_done_bdaddr_val.address);
+
+	adapter_status = data->if_bluetooth->enable();
+	check_expected_status(adapter_status);
+}
+
+static void test_enable_done(const void *test_data)
+{
+	struct test_data *data = tester_get_data();
+	bt_status_t adapter_status;
+
+	uint8_t *bdaddr = (uint8_t *)hciemu_get_master_bdaddr(data->hciemu);
+
+	init_test_conditions(data);
+
+	bdaddr2android((const bdaddr_t *)bdaddr,
+					&enable_done_bdaddr_val.address);
+
+	adapter_status = data->if_bluetooth->enable();
+	check_expected_status(adapter_status);
+}
+
+static void test_disable(const void *test_data)
+{
+	struct test_data *data = tester_get_data();
+	bt_status_t adapter_status;
+
+	init_test_conditions(data);
+
+	adapter_status = data->if_bluetooth->disable();
+	check_expected_status(adapter_status);
+}
+
+static void test_setprop_bdname_success(const void *test_data)
+{
+	struct test_data *data = tester_get_data();
+	const bt_property_t *prop = &(setprop_bdname_props[0].prop);
+	bt_status_t adapter_status;
+
+	init_test_conditions(data);
+
+	adapter_status = data->if_bluetooth->set_adapter_property(prop);
+	check_expected_status(adapter_status);
+}
+
+static void test_setprop_scanmode_succes(const void *test_data)
+{
+	struct test_data *data = tester_get_data();
+	const bt_property_t *prop = &(setprop_scanmode_props[0].prop);
+	bt_status_t adapter_status;
+
+	init_test_conditions(data);
+
+	adapter_status = data->if_bluetooth->set_adapter_property(prop);
+	check_expected_status(adapter_status);
+}
+
+static void test_setprop_disctimeout_succes(const void *test_data)
+{
+	struct test_data *data = tester_get_data();
+	const bt_property_t *prop = &(setprop_disctimeout_props[0].prop);
+	bt_status_t adapter_status;
+
+	init_test_conditions(data);
+
+	adapter_status = data->if_bluetooth->set_adapter_property(prop);
+	check_expected_status(adapter_status);
+}
+
+static void test_getprop_bdaddr_success(const void *test_data)
+{
+	struct test_data *data = tester_get_data();
+	const bt_property_t prop = setprop_bdaddr_props[0].prop;
+	bt_status_t adapter_status;
+	uint8_t *bdaddr = (uint8_t *)hciemu_get_master_bdaddr(data->hciemu);
+
+	init_test_conditions(data);
+
+	bdaddr2android((const bdaddr_t *)bdaddr,
+					&test_getprop_bdaddr_val.address);
+
+	adapter_status = data->if_bluetooth->get_adapter_property(prop.type);
+	check_expected_status(adapter_status);
+}
+
+static void test_getprop_bdname_success(const void *test_data)
+{
+	struct test_data *data = tester_get_data();
+	const bt_property_t *prop = &(getprop_bdname_props[0].prop);
+	bt_status_t adapter_status;
+
+	init_test_conditions(data);
+
+	adapter_status = data->if_bluetooth->set_adapter_property(prop);
+	if (adapter_status != BT_STATUS_SUCCESS) {
+		tester_test_failed();
+		return;
+	}
+
+	adapter_status = data->if_bluetooth->get_adapter_property((*prop).type);
+	check_expected_status(adapter_status);
+}
+static void test_setprop_uuid_invalid(const void *test_data)
+{
+	struct test_data *data = tester_get_data();
+	const bt_property_t *prop = &(setprop_uuid_prop[0].prop);
+	bt_status_t adapter_status;
+
+	init_test_conditions(data);
+
+	adapter_status = data->if_bluetooth->set_adapter_property(prop);
+	check_expected_status(adapter_status);
+}
+
+static void test_setprop_cod_invalid(const void *test_data)
+{
+	struct test_data *data = tester_get_data();
+	const bt_property_t *prop = &(setprop_cod_props[0].prop);
+	bt_status_t adapter_status;
+
+	init_test_conditions(data);
+
+	adapter_status = data->if_bluetooth->set_adapter_property(prop);
+	check_expected_status(adapter_status);
+}
+
+static void test_setprop_tod_invalid(const void *test_data)
+{
+	struct test_data *data = tester_get_data();
+	const struct generic_data *test = data->test_data;
+	const bt_property_t *prop = &test->set_property;
+	bt_status_t adapter_status;
+
+	init_test_conditions(data);
+
+	adapter_status = data->if_bluetooth->set_adapter_property(prop);
+	check_expected_status(adapter_status);
+}
+
+static void test_setprop_rssi_invalid(const void *test_data)
+{
+	struct test_data *data = tester_get_data();
+	const bt_property_t *prop = &(setprop_remote_rssi_props[0].prop);
+	bt_status_t adapter_status;
+
+	init_test_conditions(data);
+
+	adapter_status = data->if_bluetooth->set_adapter_property(prop);
+	check_expected_status(adapter_status);
+}
+
+static void test_setprop_service_record_invalid(const void *test_data)
+{
+	struct test_data *data = tester_get_data();
+	const bt_property_t *prop = &(setprop_service_record_props[0].prop);
+	bt_status_t adapter_status;
+
+	init_test_conditions(data);
+
+	adapter_status = data->if_bluetooth->set_adapter_property(prop);
+	check_expected_status(adapter_status);
+}
+
+static void test_setprop_bdaddr_invalid(const void *test_data)
+{
+	struct test_data *data = tester_get_data();
+	const bt_property_t *prop = &(setprop_bdaddr_props[0].prop);
+	bt_status_t adapter_status;
+
+	init_test_conditions(data);
+
+	adapter_status = data->if_bluetooth->set_adapter_property(prop);
+	check_expected_status(adapter_status);
+}
+
+static void test_setprop_scanmode_connectable_success(const void *test_data)
+{
+	struct test_data *data = tester_get_data();
+	const bt_property_t *prop =
+				&(setprop_scanmode_connectable_props[0].prop);
+	bt_status_t adapter_status;
+
+	init_test_conditions(data);
+
+	adapter_status = data->if_bluetooth->set_adapter_property(prop);
+	check_expected_status(adapter_status);
+}
+
+static void test_setprop_bonded_devices_invalid(const void *test_data)
+{
+	struct test_data *data = tester_get_data();
+	const bt_property_t *prop = &(setprop_bonded_devices_props[0].prop);
+	bt_status_t adapter_status;
+
+	init_test_conditions(data);
+
+	adapter_status = data->if_bluetooth->set_adapter_property(prop);
+	check_expected_status(adapter_status);
+}
+
+static void test_getprop_cod_success(const void *test_data)
+{
+	struct test_data *data = tester_get_data();
+	const bt_property_t prop = setprop_cod_props[0].prop;
+	bt_status_t adapter_status;
+
+	init_test_conditions(data);
+
+	adapter_status = data->if_bluetooth->get_adapter_property(prop.type);
+	check_expected_status(adapter_status);
+}
+
+static void test_getprop_tod_success(const void *test_data)
+{
+	struct test_data *data = tester_get_data();
+	const bt_property_t prop = setprop_tod_props[0].prop;
+	bt_status_t adapter_status;
+
+	init_test_conditions(data);
+
+	adapter_status = data->if_bluetooth->get_adapter_property(prop.type);
+	check_expected_status(adapter_status);
+}
+
+static void test_getprop_scanmode_success(const void *test_data)
+{
+	struct test_data *data = tester_get_data();
+	const bt_property_t prop = setprop_scanmode_props[0].prop;
+	bt_status_t adapter_status;
+
+	init_test_conditions(data);
+
+	adapter_status = data->if_bluetooth->get_adapter_property(prop.type);
+	check_expected_status(adapter_status);
+}
+
+static void test_getprop_disctimeout_success(const void *test_data)
+{
+	struct test_data *data = tester_get_data();
+	const bt_property_t prop = setprop_disctimeout_props[0].prop;
+	bt_status_t adapter_status;
+
+	init_test_conditions(data);
+
+	adapter_status = data->if_bluetooth->get_adapter_property(prop.type);
+	check_expected_status(adapter_status);
+}
+
+static void test_getprop_uuids_success(const void *test_data)
+{
+	struct test_data *data = tester_get_data();
+	const bt_property_t prop = getprop_uuids_props[0].prop;
+	bt_status_t adapter_status;
+
+	init_test_conditions(data);
+
+	adapter_status = data->if_bluetooth->get_adapter_property(prop.type);
+	check_expected_status(adapter_status);
+}
+
+static void test_getprop_bondeddev_success(const void *test_data)
+{
+	struct test_data *data = tester_get_data();
+	const bt_property_t prop = getprop_bondeddev_props[0].prop;
+	bt_status_t adapter_status;
+
+	init_test_conditions(data);
+
+	adapter_status = data->if_bluetooth->get_adapter_property(prop.type);
+	check_expected_status(adapter_status);
+}
+
+static void test_setprop_scanmode_none_done(const void *test_data)
+{
+	struct test_data *data = tester_get_data();
+	const bt_property_t *prop = &(setprop_scanmode_none_props[0].prop);
+	bt_status_t adapter_status;
+
+	init_test_conditions(data);
+
+	adapter_status = data->if_bluetooth->set_adapter_property(prop);
+	check_expected_status(adapter_status);
+}
+
+static void test_discovery_start_success(const void *test_data)
+{
+	struct test_data *data = tester_get_data();
+	bt_status_t status;
+
+	init_test_conditions(data);
+
+	status = data->if_bluetooth->start_discovery();
+	check_expected_status(status);
+}
+
+static void test_discovery_stop_done(const void *test_data)
+{
+	struct test_data *data = tester_get_data();
+	bt_status_t status;
+
+	init_test_conditions(data);
+
+	status = data->if_bluetooth->cancel_discovery();
+	check_expected_status(status);
+}
+
+static void test_discovery_stop_success(const void *test_data)
+{
+	struct test_data *data = tester_get_data();
+
+	init_test_conditions(data);
+
+	data->if_bluetooth->start_discovery();
+}
+
+static void test_discovery_start_done(const void *test_data)
+{
+	struct test_data *data = tester_get_data();
+
+	init_test_conditions(data);
+
+	data->if_bluetooth->start_discovery();
+}
+
+static void test_discovery_device_found(const void *test_data)
+{
+	struct test_data *data = tester_get_data();
+
+	init_test_conditions(data);
+
+	data->if_bluetooth->start_discovery();
+}
+
+static void test_dev_getprops_success(const void *test_data)
+{
+	struct test_data *data = tester_get_data();
+
+	init_test_conditions(data);
+
+	data->if_bluetooth->start_discovery();
+}
+
+static void test_dev_getprop_bdname_success(const void *test_data)
+{
+	struct test_data *data = tester_get_data();
+
+	init_test_conditions(data);
+
+	data->if_bluetooth->start_discovery();
+}
+
+static void test_dev_getprop_uuids_success(const void *test_data)
+{
+	struct test_data *data = tester_get_data();
+
+	init_test_conditions(data);
+
+	data->if_bluetooth->start_discovery();
+}
+
+static void test_dev_getprop_cod_success(const void *test_data)
+{
+	struct test_data *data = tester_get_data();
+
+	init_test_conditions(data);
+
+	data->if_bluetooth->start_discovery();
+}
+
+static void test_dev_getprop_tod_success(const void *test_data)
+{
+	struct test_data *data = tester_get_data();
+
+	init_test_conditions(data);
+
+	data->if_bluetooth->start_discovery();
+}
+
+static void test_dev_getprop_rssi_success(const void *test_data)
+{
+	struct test_data *data = tester_get_data();
+
+	init_test_conditions(data);
+
+	data->if_bluetooth->start_discovery();
+}
+
+static void test_dev_getprop_timestamp_success(const void *test_data)
+{
+	struct test_data *data = tester_get_data();
+
+	init_test_conditions(data);
+
+	data->if_bluetooth->start_discovery();
+}
+
+static void test_dev_getprop_bdaddr_fail(const void *test_data)
+{
+	struct test_data *data = tester_get_data();
+
+	init_test_conditions(data);
+
+	data->if_bluetooth->start_discovery();
+}
+
+static void test_dev_getprop_servrec_fail(const void *test_data)
+{
+	struct test_data *data = tester_get_data();
+
+	init_test_conditions(data);
+
+	data->if_bluetooth->start_discovery();
+}
+
+static void test_dev_getprop_scanmode_fail(const void *test_data)
+{
+	struct test_data *data = tester_get_data();
+
+	init_test_conditions(data);
+
+	data->if_bluetooth->start_discovery();
+}
+
+static void test_dev_getprop_bondeddev_fail(const void *test_data)
+{
+	struct test_data *data = tester_get_data();
+
+	init_test_conditions(data);
+
+	data->if_bluetooth->start_discovery();
+}
+
+static void test_dev_getprop_disctimeout_fail(const void *test_data)
+{
+	struct test_data *data = tester_get_data();
+
+	init_test_conditions(data);
+
+	data->if_bluetooth->start_discovery();
+}
+
+static void test_dev_getprop_verinfo_fail(const void *test_data)
+{
+	struct test_data *data = tester_get_data();
+
+	init_test_conditions(data);
+
+	data->if_bluetooth->start_discovery();
+}
+
+static void test_dev_getprop_fname_fail(const void *test_data)
+{
+	struct test_data *data = tester_get_data();
+
+	init_test_conditions(data);
+
+	data->if_bluetooth->start_discovery();
+}
+
+static void test_dev_setprop_fname_success(const void *test_data)
+{
+	struct test_data *data = tester_get_data();
+
+	init_test_conditions(data);
+
+	data->if_bluetooth->start_discovery();
+}
+
+static void test_dev_setprop_bdname_fail(const void *test_data)
+{
+	struct test_data *data = tester_get_data();
+
+	init_test_conditions(data);
+
+	data->if_bluetooth->start_discovery();
+}
+
+static void test_dev_setprop_uuids_fail(const void *test_data)
+{
+	struct test_data *data = tester_get_data();
+
+	init_test_conditions(data);
+
+	data->if_bluetooth->start_discovery();
+}
+
+static void test_dev_setprop_cod_fail(const void *test_data)
+{
+	struct test_data *data = tester_get_data();
+
+	init_test_conditions(data);
+
+	data->if_bluetooth->start_discovery();
+}
+
+static void test_dev_setprop_tod_fail(const void *test_data)
+{
+	struct test_data *data = tester_get_data();
+
+	init_test_conditions(data);
+
+	data->if_bluetooth->start_discovery();
+}
+
+static void test_dev_setprop_rssi_fail(const void *test_data)
+{
+	struct test_data *data = tester_get_data();
+
+	init_test_conditions(data);
+
+	data->if_bluetooth->start_discovery();
+}
+
+static void test_dev_setprop_timestamp_fail(const void *test_data)
+{
+	struct test_data *data = tester_get_data();
+
+	init_test_conditions(data);
+
+	data->if_bluetooth->start_discovery();
+}
+
+static void test_dev_setprop_bdaddr_fail(const void *test_data)
+{
+	struct test_data *data = tester_get_data();
+
+	init_test_conditions(data);
+
+	data->if_bluetooth->start_discovery();
+}
+
+static void test_dev_setprop_servrec_fail(const void *test_data)
+{
+	struct test_data *data = tester_get_data();
+
+	init_test_conditions(data);
+
+	data->if_bluetooth->start_discovery();
+}
+
+static void test_dev_setprop_scanmode_fail(const void *test_data)
+{
+	struct test_data *data = tester_get_data();
+
+	init_test_conditions(data);
+
+	data->if_bluetooth->start_discovery();
+}
+
+static void test_dev_setprop_bondeddev_fail(const void *test_data)
+{
+	struct test_data *data = tester_get_data();
+
+	init_test_conditions(data);
+
+	data->if_bluetooth->start_discovery();
+}
+
+static void test_dev_setprop_disctimeout_fail(const void *test_data)
+{
+	struct test_data *data = tester_get_data();
+
+	init_test_conditions(data);
+
+	data->if_bluetooth->start_discovery();
+}
+
+static void bond_device_auth_fail_callback(uint16_t index, uint16_t length,
+							const void *param,
+							void *user_data)
+{
+	const struct mgmt_ev_auth_failed *ev = param;
+
+	check_expected_status(ev->status);
+}
+
+static void test_bond_create_pin_success(const void *test_data)
+{
+	struct test_data *data = tester_get_data();
+	struct bthost *bthost = hciemu_client_get_host(data->hciemu);
+
+	static uint8_t pair_device_pin[] = { 0x30, 0x30, 0x30, 0x30 };
+	const void *pin = pair_device_pin;
+	uint8_t pin_len = 4;
+
+	init_test_conditions(data);
+
+	bthost_set_pin_code(bthost, pin, pin_len);
+
+	data->if_bluetooth->start_discovery();
+}
+
+static void test_bond_create_pin_fail(const void *test_data)
+{
+	struct test_data *data = tester_get_data();
+	struct bthost *bthost = hciemu_client_get_host(data->hciemu);
+
+	static uint8_t pair_device_pin[] = { 0x30, 0x30, 0x30, 0x30 };
+	const void *pin = pair_device_pin;
+	uint8_t pin_len = 4;
+
+	init_test_conditions(data);
+
+	mgmt_register(data->mgmt, MGMT_EV_AUTH_FAILED, data->mgmt_index,
+					bond_device_auth_fail_callback, data,
+					NULL);
+
+	bthost_set_pin_code(bthost, pin, pin_len);
+
+	data->if_bluetooth->start_discovery();
+}
+
+static void test_bond_create_ssp_success(const void *test_data)
+{
+	struct test_data *data = tester_get_data();
+	struct bthost *bthost = hciemu_client_get_host(data->hciemu);
+
+	init_test_conditions(data);
+
+	bthost_write_ssp_mode(bthost, 0x01);
+
+	data->if_bluetooth->start_discovery();
+}
+
+static void test_bond_create_ssp_fail(const void *test_data)
+{
+	struct test_data *data = tester_get_data();
+	struct bthost *bthost = hciemu_client_get_host(data->hciemu);
+
+	init_test_conditions(data);
+
+	mgmt_register(data->mgmt, MGMT_EV_AUTH_FAILED, data->mgmt_index,
+					bond_device_auth_fail_callback, data,
+					NULL);
+
+	bthost_write_ssp_mode(bthost, 0x01);
+
+	data->if_bluetooth->start_discovery();
+}
+
+static void test_bond_create_no_disc_success(const void *test_data)
+{
+	struct test_data *data = tester_get_data();
+	struct bthost *bthost = hciemu_client_get_host(data->hciemu);
+
+	uint8_t *bdaddr = (uint8_t *)hciemu_get_client_bdaddr(data->hciemu);
+	bt_bdaddr_t remote_addr;
+	bt_status_t status;
+
+	init_test_conditions(data);
+
+	bdaddr2android((const bdaddr_t *)bdaddr, &remote_addr.address);
+
+	bthost_write_ssp_mode(bthost, 0x01);
+
+	status = data->if_bluetooth->create_bond(&remote_addr);
+	check_expected_status(status);
+}
+
+static void test_bond_create_bad_addr_success(const void *test_data)
+{
+	struct test_data *data = tester_get_data();
+	bt_bdaddr_t bad_addr = {
+		.address = { 0x12, 0x34, 0x56, 0x78, 0x90, 0x12 }
+	};
+
+	init_test_conditions(data);
+
+	mgmt_register(data->mgmt, MGMT_EV_CONNECT_FAILED, data->mgmt_index,
+					bond_device_auth_fail_callback, data,
+					NULL);
+
+	data->if_bluetooth->create_bond(&bad_addr);
+}
+
+static void test_bond_cancel_success(const void *test_data)
+{
+	struct test_data *data = tester_get_data();
+	struct bthost *bthost = hciemu_client_get_host(data->hciemu);
+
+	init_test_conditions(data);
+
+	bthost_write_ssp_mode(bthost, 0x01);
+
+	data->if_bluetooth->start_discovery();
+}
+
+static void test_bond_remove_success(const void *test_data)
+{
+	struct test_data *data = tester_get_data();
+	struct bthost *bthost = hciemu_client_get_host(data->hciemu);
+
+	init_test_conditions(data);
+
+	bthost_write_ssp_mode(bthost, 0x01);
+
+	data->if_bluetooth->start_discovery();
+}
+
+/* Test Socket HAL */
+
+static gboolean adapter_socket_state_changed(gpointer user_data)
+{
+	struct bt_cb_data *cb_data = user_data;
+
+	switch (cb_data->state) {
+	case BT_STATE_ON:
+		setup_powered_emulated_remote();
+		break;
+	case BT_STATE_OFF:
+		tester_setup_failed();
+		break;
+	default:
+		break;
+	}
+
+	g_free(cb_data);
+
+	g_atomic_int_dec_and_test(&scheduled_cbacks_num);
+	return FALSE;
+}
+
+static void adapter_socket_state_changed_cb(bt_state_t state)
+{
+	struct bt_cb_data *cb_data = g_new0(struct bt_cb_data, 1);
+
+	cb_data->state = state;
+
+	g_atomic_int_inc(&scheduled_cbacks_num);
+	g_idle_add(adapter_socket_state_changed, cb_data);
+}
+
+const bt_bdaddr_t bdaddr_dummy = {
+	.address = { 0x00, 0x11, 0x22, 0x33, 0x44, 0x55}
+};
+
+static const struct socket_data btsock_inv_param_socktype = {
+	.bdaddr = &bdaddr_dummy,
+	.sock_type = 0,
+	.channel = 1,
+	.service_uuid = NULL,
+	.service_name = "Test service",
+	.flags = 0,
+	.expected_status = BT_STATUS_PARM_INVALID,
+};
+
+static const struct socket_data btsock_inv_param_socktype_l2cap = {
+	.bdaddr = &bdaddr_dummy,
+	.sock_type = BTSOCK_L2CAP,
+	.channel = 1,
+	.service_uuid = NULL,
+	.service_name = "Test service",
+	.flags = 0,
+	.expected_status = BT_STATUS_UNSUPPORTED,
+};
+
+/* Test invalid: channel & uuid are both zeroes */
+static const struct socket_data btsock_inv_params_chan_uuid = {
+	.bdaddr = &bdaddr_dummy,
+	.sock_type = BTSOCK_RFCOMM,
+	.channel = 0,
+	.service_uuid = NULL,
+	.service_name = "Test service",
+	.flags = 0,
+	.expected_status = BT_STATUS_PARM_INVALID,
+};
+
+static const struct socket_data btsock_success = {
+	.bdaddr = &bdaddr_dummy,
+	.sock_type = BTSOCK_RFCOMM,
+	.channel = 1,
+	.service_uuid = NULL,
+	.service_name = "Test service",
+	.flags = 0,
+	.expected_status = BT_STATUS_SUCCESS,
+	.test_channel = false
+};
+
+static const struct socket_data btsock_success_check_chan = {
+	.sock_type = BTSOCK_RFCOMM,
+	.channel = 1,
+	.service_uuid = NULL,
+	.service_name = "Test service",
+	.flags = 0,
+	.expected_status = BT_STATUS_SUCCESS,
+	.test_channel = true,
+};
+
+static const struct socket_data btsock_inv_param_bdaddr = {
+	.bdaddr = NULL,
+	.sock_type = BTSOCK_RFCOMM,
+	.channel = 1,
+	.service_uuid = NULL,
+	.service_name = "Test service",
+	.flags = 0,
+	.expected_status = BT_STATUS_PARM_INVALID,
+};
+
+static const struct socket_data btsock_inv_listen_listen = {
+	.sock_type = BTSOCK_RFCOMM,
+	.channel = 1,
+	.service_uuid = NULL,
+	.service_name = "Test service",
+	.flags = 0,
+	.expected_status = BT_STATUS_BUSY,
+	.test_channel = true,
+};
+
+static bt_callbacks_t bt_socket_callbacks = {
+	.size = sizeof(bt_callbacks),
+	.adapter_state_changed_cb = adapter_socket_state_changed_cb,
+	.adapter_properties_cb = NULL,
+	.remote_device_properties_cb = NULL,
+	.device_found_cb = NULL,
+	.discovery_state_changed_cb = NULL,
+	.pin_request_cb = NULL,
+	.ssp_request_cb = NULL,
+	.bond_state_changed_cb = NULL,
+	.acl_state_changed_cb = NULL,
+	.thread_evt_cb = NULL,
+	.dut_mode_recv_cb = NULL,
+	.le_test_mode_cb = NULL
+};
+
+static void setup_socket_interface(const void *test_data)
+{
+	struct test_data *data = tester_get_data();
+	bt_status_t status;
+	const void *sock;
+
+	if (!setup(data)) {
+		tester_setup_failed();
+		return;
+	}
+
+	status = data->if_bluetooth->init(&bt_socket_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_socket_interface_enabled(const void *test_data)
+{
+	struct test_data *data = tester_get_data();
+	bt_status_t status;
+	const void *sock;
+
+	if (!setup(data)) {
+		tester_setup_failed();
+		return;
+	}
+
+	status = data->if_bluetooth->init(&bt_socket_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;
+
+	status = data->if_bluetooth->enable();
+	if (status != BT_STATUS_SUCCESS)
+		tester_setup_failed();
+}
+
+static void test_generic_listen(const void *test_data)
+{
+	struct test_data *data = tester_get_data();
+	const struct socket_data *test = data->test_data;
+	bt_status_t status;
+	int sock_fd = -1;
+
+	status = data->if_sock->listen(test->sock_type,
+					test->service_name, test->service_uuid,
+					test->channel, &sock_fd, test->flags);
+	if (status != test->expected_status) {
+		tester_test_failed();
+		goto clean;
+	}
+
+	/* Check that file descriptor is valid */
+	if (status == BT_STATUS_SUCCESS && fcntl(sock_fd, F_GETFD) < 0) {
+		tester_test_failed();
+		return;
+	}
+
+	if (status == BT_STATUS_SUCCESS && test->test_channel) {
+		int channel, len;
+
+		len = read(sock_fd, &channel, sizeof(channel));
+		if (len != sizeof(channel) || channel != test->channel) {
+			tester_test_failed();
+			goto clean;
+		}
+
+		tester_print("read correct channel: %d", channel);
+	}
+
+	tester_test_passed();
+
+clean:
+	if (sock_fd >= 0)
+		close(sock_fd);
+}
+
+static void test_listen_close(const void *test_data)
+{
+	struct test_data *data = tester_get_data();
+	const struct socket_data *test = data->test_data;
+	bt_status_t status;
+	int sock_fd = -1;
+
+	status = data->if_sock->listen(test->sock_type,
+					test->service_name, test->service_uuid,
+					test->channel, &sock_fd, test->flags);
+	if (status != test->expected_status) {
+		tester_warn("sock->listen() failed");
+		tester_test_failed();
+		goto clean;
+	}
+
+	/* Check that file descriptor is valid */
+	if (status == BT_STATUS_SUCCESS && fcntl(sock_fd, F_GETFD) < 0) {
+		tester_warn("sock_fd %d is not valid", sock_fd);
+		tester_test_failed();
+		return;
+	}
+
+	tester_print("Got valid sock_fd: %d", sock_fd);
+
+	/* Now close sock_fd */
+	close(sock_fd);
+	sock_fd = -1;
+
+	/* Try to listen again */
+	status = data->if_sock->listen(test->sock_type,
+					test->service_name, test->service_uuid,
+					test->channel, &sock_fd, test->flags);
+	if (status != test->expected_status) {
+		tester_warn("sock->listen() failed");
+		tester_test_failed();
+		goto clean;
+	}
+
+	/* Check that file descriptor is valid */
+	if (status == BT_STATUS_SUCCESS && fcntl(sock_fd, F_GETFD) < 0) {
+		tester_warn("sock_fd %d is not valid", sock_fd);
+		tester_test_failed();
+		return;
+	}
+
+	tester_print("Got valid sock_fd: %d", sock_fd);
+
+	tester_test_passed();
+
+clean:
+	if (sock_fd >= 0)
+		close(sock_fd);
+}
+
+static void test_listen_listen(const void *test_data)
+{
+	struct test_data *data = tester_get_data();
+	const struct socket_data *test = data->test_data;
+	bt_status_t status;
+	int sock_fd1 = -1, sock_fd2 = -1;
+
+	status = data->if_sock->listen(test->sock_type,
+					test->service_name, test->service_uuid,
+					test->channel, &sock_fd1, test->flags);
+	if (status != BT_STATUS_SUCCESS) {
+		tester_warn("sock->listen() failed");
+		tester_test_failed();
+		goto clean;
+	}
+
+	status = data->if_sock->listen(test->sock_type,
+					test->service_name, test->service_uuid,
+					test->channel, &sock_fd2, test->flags);
+	if (status != test->expected_status) {
+		tester_warn("sock->listen() failed, status %d", status);
+		tester_test_failed();
+		goto clean;
+	}
+
+	tester_print("status after second listen(): %d", status);
+
+	tester_test_passed();
+
+clean:
+	if (sock_fd1 >= 0)
+		close(sock_fd1);
+
+	if (sock_fd2 >= 0)
+		close(sock_fd2);
+}
+
+static void test_generic_connect(const void *test_data)
+{
+	struct test_data *data = tester_get_data();
+	const struct socket_data *test = data->test_data;
+	bt_status_t status;
+	int sock_fd = -1;
+
+	status = data->if_sock->connect(test->bdaddr, test->sock_type,
+					test->service_uuid, test->channel,
+					&sock_fd, test->flags);
+	if (status != test->expected_status) {
+		tester_test_failed();
+		goto clean;
+	}
+
+	/* Check that file descriptor is valid */
+	if (status == BT_STATUS_SUCCESS && fcntl(sock_fd, F_GETFD) < 0) {
+		tester_test_failed();
+		return;
+	}
+
+	tester_test_passed();
+
+clean:
+	if (sock_fd >= 0)
+		close(sock_fd);
+}
+
+static gboolean socket_chan_cb(GIOChannel *io, GIOCondition cond,
+							gpointer user_data)
+{
+	int sock_fd = g_io_channel_unix_get_fd(io);
+	struct test_data *data = tester_get_data();
+	const struct socket_data *test = data->test_data;
+	int channel, len;
+
+	tester_print("%s", __func__);
+
+	if (cond & G_IO_HUP) {
+		tester_warn("Socket %d hang up", sock_fd);
+		goto failed;
+	}
+
+	if (cond & (G_IO_ERR | G_IO_NVAL)) {
+		tester_warn("Socket error: sock %d cond %d", sock_fd, cond);
+		goto failed;
+	}
+
+	if (test->test_channel) {
+		len = read(sock_fd, &channel, sizeof(channel));
+		if (len != sizeof(channel) || channel != test->channel)
+			goto failed;
+
+		tester_print("read correct channel: %d", channel);
+		tester_test_passed();
+		return FALSE;
+	}
+
+failed:
+	tester_test_failed();
+	return FALSE;
+}
+
+static void test_socket_real_connect(const void *test_data)
+{
+	struct test_data *data = tester_get_data();
+	const struct socket_data *test = data->test_data;
+	struct bthost *bthost = hciemu_client_get_host(data->hciemu);
+	const uint8_t *client_bdaddr;
+	bt_bdaddr_t emu_bdaddr;
+	bt_status_t status;
+	int sock_fd = -1;
+
+	client_bdaddr = hciemu_get_client_bdaddr(data->hciemu);
+	if (!client_bdaddr) {
+		tester_warn("No client bdaddr");
+		tester_test_failed();
+		return;
+	}
+
+	bdaddr2android((bdaddr_t *) client_bdaddr, &emu_bdaddr);
+
+	bthost_add_l2cap_server(bthost, 0x0003, NULL, NULL);
+
+	status = data->if_sock->connect(&emu_bdaddr, test->sock_type,
+					test->service_uuid, test->channel,
+					&sock_fd, test->flags);
+	if (status != test->expected_status) {
+		tester_test_failed();
+		goto clean;
+	}
+
+	/* Check that file descriptor is valid */
+	if (status == BT_STATUS_SUCCESS && fcntl(sock_fd, F_GETFD) < 0) {
+		tester_test_failed();
+		return;
+	}
+
+	tester_print("status %d sock_fd %d", status, sock_fd);
+
+	if (status == BT_STATUS_SUCCESS) {
+		GIOChannel *io;
+
+		io = g_io_channel_unix_new(sock_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);
+	}
+
+	return;
+
+clean:
+	if (sock_fd >= 0)
+		close(sock_fd);
+}
+
+static gboolean hidhost_connection_state(gpointer user_data)
+{
+	struct test_data *data = tester_get_data();
+	const struct hidhost_generic_data *test = data->test_data;
+	struct hh_cb_data *cb_data = user_data;
+
+	data->cb_count++;
+
+	if (cb_data->state == BTHH_CONN_STATE_CONNECTED)
+		tester_setup_complete();
+
+	if (test && test->expected_hal_cb.connection_state_cb)
+		test->expected_hal_cb.connection_state_cb(&cb_data->bdaddr,
+								cb_data->state);
+
+	g_free(cb_data);
+
+	g_atomic_int_dec_and_test(&scheduled_cbacks_num);
+	return FALSE;
+}
+
+static void hidhost_connection_state_cb(bt_bdaddr_t *bd_addr,
+						bthh_connection_state_t state)
+{
+	struct hh_cb_data *cb_data = g_new0(struct hh_cb_data, 1);
+
+	cb_data->state = state;
+	cb_data->bdaddr = *bd_addr;
+
+	g_atomic_int_inc(&scheduled_cbacks_num);
+	g_idle_add(hidhost_connection_state, cb_data);
+}
+
+static gboolean hidhost_virual_unplug(gpointer user_data)
+{
+	struct test_data *data = tester_get_data();
+	const struct hidhost_generic_data *test = data->test_data;
+	struct hh_cb_data *cb_data = user_data;
+
+	data->cb_count++;
+
+	if (test && test->expected_hal_cb.virtual_unplug_cb)
+		test->expected_hal_cb.virtual_unplug_cb(&cb_data->bdaddr,
+							cb_data->status);
+
+	g_free(cb_data);
+
+	g_atomic_int_dec_and_test(&scheduled_cbacks_num);
+	return FALSE;
+}
+
+static void hidhost_virual_unplug_cb(bt_bdaddr_t *bd_addr, bthh_status_t status)
+{
+	struct hh_cb_data *cb_data = g_new0(struct hh_cb_data, 1);
+
+	cb_data->bdaddr = *bd_addr;
+	cb_data->status = status;
+
+	g_atomic_int_inc(&scheduled_cbacks_num);
+	g_idle_add(hidhost_virual_unplug, cb_data);
+}
+
+static gboolean hidhost_hid_info(gpointer user_data)
+{
+	struct test_data *data = tester_get_data();
+	const struct hidhost_generic_data *test = data->test_data;
+	struct hh_cb_data *cb_data = user_data;
+
+	data->cb_count++;
+
+	if (test && test->expected_hal_cb.hid_info_cb)
+		test->expected_hal_cb.hid_info_cb(&cb_data->bdaddr,
+							cb_data->hid_info);
+
+	g_free(cb_data);
+
+	g_atomic_int_dec_and_test(&scheduled_cbacks_num);
+	return FALSE;
+}
+
+static void hidhost_hid_info_cb(bt_bdaddr_t *bd_addr, bthh_hid_info_t hid)
+{
+	struct hh_cb_data *cb_data = g_new0(struct hh_cb_data, 1);
+
+	cb_data->bdaddr = *bd_addr;
+	cb_data->hid_info = hid;
+
+	g_atomic_int_inc(&scheduled_cbacks_num);
+	g_idle_add(hidhost_hid_info, cb_data);
+}
+
+static gboolean hidhost_protocol_mode(gpointer user_data)
+{
+	struct test_data *data = tester_get_data();
+	const struct hidhost_generic_data *test = data->test_data;
+	struct hh_cb_data *cb_data = user_data;
+
+	data->cb_count++;
+
+	if (test && test->expected_hal_cb.protocol_mode_cb)
+		test->expected_hal_cb.protocol_mode_cb(&cb_data->bdaddr,
+						cb_data->status, cb_data->mode);
+
+	g_free(cb_data);
+
+	g_atomic_int_dec_and_test(&scheduled_cbacks_num);
+	return FALSE;
+}
+
+static void hidhost_protocol_mode_cb(bt_bdaddr_t *bd_addr,
+						bthh_status_t status,
+						bthh_protocol_mode_t mode)
+{
+	struct hh_cb_data *cb_data = g_new0(struct hh_cb_data, 1);
+
+	cb_data->bdaddr = *bd_addr;
+	cb_data->status = status;
+	cb_data->mode = mode;
+
+	g_atomic_int_inc(&scheduled_cbacks_num);
+	g_idle_add(hidhost_protocol_mode, cb_data);
+}
+
+static gboolean hidhost_get_report(gpointer user_data)
+{
+	struct test_data *data = tester_get_data();
+	const struct hidhost_generic_data *test = data->test_data;
+	struct hh_cb_data *cb_data = user_data;
+
+	data->cb_count++;
+
+	if (test && test->expected_hal_cb.get_report_cb)
+		test->expected_hal_cb.get_report_cb(&cb_data->bdaddr,
+			cb_data->status, cb_data->report, cb_data->size);
+
+	g_free(cb_data->report);
+	g_free(cb_data);
+
+	g_atomic_int_dec_and_test(&scheduled_cbacks_num);
+	return FALSE;
+}
+
+static void hidhost_get_report_cb(bt_bdaddr_t *bd_addr, bthh_status_t status,
+						uint8_t *report, int size)
+{
+	struct hh_cb_data *cb_data = g_new0(struct hh_cb_data, 1);
+
+	cb_data->bdaddr = *bd_addr;
+	cb_data->status = status;
+	cb_data->report = g_memdup(report, size);
+	cb_data->size = size;
+
+	g_atomic_int_inc(&scheduled_cbacks_num);
+	g_idle_add(hidhost_get_report, cb_data);
+}
+
+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_virual_unplug_cb
+};
+
+static bool setup_hidhost(const void *test_data)
+{
+	struct test_data *data = tester_get_data();
+	bt_status_t status;
+	const void *hid;
+
+	if (!setup(data))
+		return false;
+
+	status = data->if_bluetooth->init(&bt_callbacks);
+	if (status != BT_STATUS_SUCCESS) {
+		data->if_bluetooth = NULL;
+		return false;
+	}
+
+	hid = data->if_bluetooth->get_profile_interface(BT_PROFILE_HIDHOST_ID);
+	if (!hid)
+		return false;
+
+	data->if_hid = hid;
+
+	status = data->if_hid->init(&bthh_callbacks);
+	if (status != BT_STATUS_SUCCESS) {
+		data->if_hid = NULL;
+		return false;
+	}
+
+	return true;
+}
+
+static void setup_hidhost_interface(const void *test_data)
+{
+	if (setup_hidhost(test_data))
+		tester_setup_complete();
+	else
+		tester_setup_failed();
+}
+
+#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
+
+static void hid_prepare_reply_protocol_mode(const void *data, uint16_t len)
+{
+	struct test_data *t_data = tester_get_data();
+	struct bthost *bthost = hciemu_client_get_host(t_data->hciemu);
+	uint8_t pdu[2] = { 0, 0 };
+	uint16_t pdu_len = 0;
+
+	pdu_len = 2;
+	pdu[0] = 0xa0;
+	pdu[1] = 0x00;
+
+	bthost_send_cid(bthost, t_data->ctrl_handle, t_data->ctrl_cid,
+						(void *)pdu, pdu_len);
+}
+
+static void hid_prepare_reply_report(const void *data, uint16_t len)
+{
+	struct test_data *t_data = tester_get_data();
+	struct bthost *bthost = hciemu_client_get_host(t_data->hciemu);
+	uint8_t pdu[3] = { 0, 0, 0 };
+	uint16_t pdu_len = 0;
+
+	pdu_len = 3;
+	pdu[0] = 0xa2;
+	pdu[1] = 0x01;
+	pdu[2] = 0x00;
+
+	bthost_send_cid(bthost, t_data->ctrl_handle, t_data->ctrl_cid,
+						(void *)pdu, pdu_len);
+}
+
+static void hid_intr_cid_hook_cb(const void *data, uint16_t len,
+							void *user_data)
+{
+	uint8_t header = ((uint8_t *) data)[0];
+
+	switch (header) {
+	case HID_SEND_DATA:
+		tester_test_passed();
+		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);
+
+	data->intr_handle = handle;
+	data->intr_cid = cid;
+
+	bthost_add_cid_hook(bthost, handle, cid, hid_intr_cid_hook_cb, NULL);
+}
+
+static void hid_ctrl_cid_hook_cb(const void *data, uint16_t len,
+							void *user_data)
+{
+	uint8_t header = ((uint8_t *) data)[0];
+
+	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(data, len);
+		break;
+	case HID_GET_INPUT_REPORT:
+	case HID_GET_OUTPUT_REPORT:
+	case HID_GET_FEATURE_REPORT:
+		hid_prepare_reply_report(data, len);
+		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:
+		tester_test_passed();
+		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);
+
+	data->ctrl_handle = handle;
+	data->ctrl_cid = cid;
+
+	bthost_add_cid_hook(bthost, handle, cid, hid_ctrl_cid_hook_cb, NULL);
+}
+
+static const uint8_t did_req_pdu[] = { 0x06, /* PDU id */
+			0x00, 0x00, /* Transaction id */
+			0x00, 0x0f, /* Req length */
+			0x35, 0x03, /* Attributes length */
+			0x19, 0x12, 0x00, 0xff, 0xff, 0x35, 0x05, 0x0a, 0x00,
+			0x00, 0xff, 0xff, 0x00 }; /* no continuation */
+
+static const uint8_t did_rsp_pdu[] = { 0x07, /* PDU id */
+			0x00, 0x00, /* Transaction id */
+			0x00, 0x4f, /* Response length */
+			0x00, 0x4c, /* Attributes length */
+			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 }; /* no continuation */
+
+static const uint8_t hid_rsp_pdu[] = { 0x07, /* PDU id */
+			0x00, 0x01, /* Transaction id */
+			0x01, 0x71, /* Response length */
+			0x01, 0x6E, /* Attributes length */
+			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 }; /* no continuation */
+
+static void hid_sdp_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);
+
+	if (!memcmp(did_req_pdu, data, len)) {
+		bthost_send_cid(bthost, t_data->sdp_handle, t_data->sdp_cid,
+					did_rsp_pdu, sizeof(did_rsp_pdu));
+		return;
+	}
+
+	bthost_send_cid(bthost, t_data->sdp_handle, t_data->sdp_cid,
+					hid_rsp_pdu, sizeof(hid_rsp_pdu));
+}
+
+static void hid_sdp_search_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);
+
+	data->sdp_handle = handle;
+	data->sdp_cid = cid;
+
+	bthost_add_cid_hook(bthost, handle, cid, hid_sdp_cid_hook_cb, NULL);
+}
+
+static void emu_powered_complete(uint16_t opcode, uint8_t status,
+					const void *param, uint8_t len,
+					void *user_data)
+{
+	struct test_data *data = tester_get_data();
+	const uint8_t *hid_addr = hciemu_get_client_bdaddr(data->hciemu);
+	bt_bdaddr_t bdaddr;
+	bt_status_t bt_status;
+
+	switch (opcode) {
+	case BT_HCI_CMD_WRITE_SCAN_ENABLE:
+	case BT_HCI_CMD_LE_SET_ADV_ENABLE:
+		break;
+	default:
+		return;
+	}
+
+	if (status) {
+		tester_setup_failed();
+		return;
+	}
+
+	data->cb_count = 0;
+	bdaddr2android((const bdaddr_t *) hid_addr, &bdaddr);
+	bt_status = data->if_hid->connect(&bdaddr);
+	if (bt_status != BT_STATUS_SUCCESS)
+		tester_setup_failed();
+}
+
+static void setup_hidhost_connect(const void *test_data)
+{
+	struct test_data *data = tester_get_data();
+	struct bthost *bthost;
+
+	if (!setup_hidhost(test_data)) {
+		tester_setup_failed();
+		return;
+	}
+
+	bthost = hciemu_client_get_host(data->hciemu);
+
+	/* Emulate SDP (PSM = 1) */
+	bthost_add_l2cap_server(bthost, 1, hid_sdp_search_cb, NULL);
+	/* Emulate Control Channel (PSM = 17) */
+	bthost_add_l2cap_server(bthost, 17, hid_ctrl_connect_cb, NULL);
+	/* Emulate Interrupt Channel (PSM = 19) */
+	bthost_add_l2cap_server(bthost, 19, hid_intr_connect_cb, NULL);
+
+	bthost_set_cmd_complete_cb(bthost, emu_powered_complete, data);
+	bthost_write_scan_enable(bthost, 0x03);
+}
+
+static void hid_discon_cb(bt_bdaddr_t *bd_addr, bthh_connection_state_t state)
+{
+	if (state == BTHH_CONN_STATE_DISCONNECTED)
+		tester_test_passed();
+}
+
+static const struct hidhost_generic_data hidhost_test_disconnect = {
+	.expected_hal_cb.connection_state_cb = hid_discon_cb,
+};
+
+static void test_hidhost_disconnect(const void *test_data)
+{
+	struct test_data *data = tester_get_data();
+	const uint8_t *hid_addr = hciemu_get_client_bdaddr(data->hciemu);
+	bt_bdaddr_t bdaddr;
+	bt_status_t bt_status;
+
+	data->cb_count = 0;
+	bdaddr2android((const bdaddr_t *) hid_addr, &bdaddr);
+	bt_status = data->if_hid->disconnect(&bdaddr);
+	if (bt_status != BT_STATUS_SUCCESS)
+		tester_test_failed();
+}
+
+static void test_hidhost_virtual_unplug(const void *test_data)
+{
+	struct test_data *data = tester_get_data();
+	const uint8_t *hid_addr = hciemu_get_client_bdaddr(data->hciemu);
+	bt_bdaddr_t bdaddr;
+	bt_status_t bt_status;
+
+	data->cb_count = 0;
+	bdaddr2android((const bdaddr_t *) hid_addr, &bdaddr);
+	bt_status = data->if_hid->virtual_unplug(&bdaddr);
+	if (bt_status != BT_STATUS_SUCCESS)
+		tester_test_failed();
+}
+
+static void hid_protocol_mode_cb(bt_bdaddr_t *bd_addr, bthh_status_t status,
+						bthh_protocol_mode_t mode)
+{
+	struct test_data *data = tester_get_data();
+	const struct hidhost_generic_data *test = data->test_data;
+
+	if (data->cb_count == test->expected_cb_count &&
+					status == test->expected_status &&
+					mode == test->expected_protocol_mode)
+		tester_test_passed();
+	else
+		tester_test_failed();
+}
+
+static const struct hidhost_generic_data hidhost_test_get_protocol = {
+	.expected_hal_cb.protocol_mode_cb = hid_protocol_mode_cb,
+	.expected_cb_count = 1,
+	.expected_protocol_mode = BTHH_BOOT_MODE,
+	.expected_status = BTHH_OK,
+};
+
+static void test_hidhost_get_protocol(const void *test_data)
+{
+	struct test_data *data = tester_get_data();
+	const uint8_t *hid_addr = hciemu_get_client_bdaddr(data->hciemu);
+	bt_bdaddr_t bdaddr;
+	bt_status_t bt_status;
+
+	data->cb_count = 0;
+	bdaddr2android((const bdaddr_t *) hid_addr, &bdaddr);
+	bt_status = data->if_hid->get_protocol(&bdaddr, BTHH_REPORT_MODE);
+	if (bt_status != BT_STATUS_SUCCESS)
+		tester_test_failed();
+}
+
+static void test_hidhost_set_protocol(const void *test_data)
+{
+	struct test_data *data = tester_get_data();
+	const uint8_t *hid_addr = hciemu_get_client_bdaddr(data->hciemu);
+	bt_bdaddr_t bdaddr;
+	bt_status_t bt_status;
+
+	data->cb_count = 0;
+	bdaddr2android((const bdaddr_t *) hid_addr, &bdaddr);
+	bt_status = data->if_hid->set_protocol(&bdaddr, BTHH_REPORT_MODE);
+	if (bt_status != BT_STATUS_SUCCESS)
+		tester_test_failed();
+}
+
+static void test_hidhost_set_report(const void *test_data)
+{
+	struct test_data *data = tester_get_data();
+	const uint8_t *hid_addr = hciemu_get_client_bdaddr(data->hciemu);
+	bt_bdaddr_t bdaddr;
+	bt_status_t bt_status;
+	char *buf = "010101";
+
+	data->cb_count = 0;
+	bdaddr2android((const bdaddr_t *) hid_addr, &bdaddr);
+	bt_status = data->if_hid->set_report(&bdaddr, BTHH_INPUT_REPORT, buf);
+	if (bt_status != BT_STATUS_SUCCESS)
+		tester_test_failed();
+}
+
+static void test_hidhost_send_data(const void *test_data)
+{
+	struct test_data *data = tester_get_data();
+	const uint8_t *hid_addr = hciemu_get_client_bdaddr(data->hciemu);
+	bt_bdaddr_t bdaddr;
+	bt_status_t bt_status;
+	char *buf = "fe0201";
+
+	data->cb_count = 0;
+	bdaddr2android((const bdaddr_t *) hid_addr, &bdaddr);
+	bt_status = data->if_hid->send_data(&bdaddr, buf);
+	if (bt_status != BT_STATUS_SUCCESS)
+		tester_test_failed();
+}
+
+static void hid_get_report_cb(bt_bdaddr_t *bd_addr, bthh_status_t status,
+						uint8_t *report, int size)
+{
+	struct test_data *data = tester_get_data();
+	const struct hidhost_generic_data *test = data->test_data;
+
+	if (data->cb_count == test->expected_cb_count &&
+					status == test->expected_status &&
+					size == test->expected_report_size)
+		tester_test_passed();
+	else
+		tester_test_failed();
+}
+
+static const struct hidhost_generic_data hidhost_test_get_report = {
+	.expected_hal_cb.get_report_cb = hid_get_report_cb,
+	.expected_cb_count = 1,
+	.expected_status = BTHH_OK,
+	.expected_report_size = 2,
+};
+
+static void test_hidhost_get_report(const void *test_data)
+{
+	struct test_data *data = tester_get_data();
+	const uint8_t *hid_addr = hciemu_get_client_bdaddr(data->hciemu);
+	bt_bdaddr_t bdaddr;
+	bt_status_t bt_status;
+
+	data->cb_count = 0;
+	bdaddr2android((const bdaddr_t *) hid_addr, &bdaddr);
+	bt_status = data->if_hid->get_report(&bdaddr, BTHH_INPUT_REPORT, 1, 20);
+	if (bt_status != BT_STATUS_SUCCESS)
+		tester_test_failed();
+}
+
+#define test_bredr(name, data, test_setup, test, test_teardown) \
+	do { \
+		struct test_data *user; \
+		user = g_malloc0(sizeof(struct test_data)); \
+		if (!user) \
+			break; \
+		user->hciemu_type = HCIEMU_TYPE_BREDR; \
+		user->test_data = data; \
+		tester_add_full(name, data, test_pre_setup, test_setup, \
+				test, test_teardown, test_post_teardown, \
+							1, user, g_free); \
+	} while (0)
+
+#define test_bredrle(name, data, test_setup, test, test_teardown) \
+	do { \
+		struct test_data *user; \
+		user = g_malloc0(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, test_setup, \
+				test, test_teardown, test_post_teardown, \
+							3, user, g_free); \
+	} while (0)
+
+int main(int argc, char *argv[])
+{
+	snprintf(exec_dir, sizeof(exec_dir), "%s", dirname(argv[0]));
+
+	tester_init(&argc, &argv);
+
+	test_bredrle("Bluetooth Init", NULL, setup_base, test_dummy, teardown);
+
+	test_bredrle("Bluetooth Enable - Success",
+						&bluetooth_enable_success_test,
+						setup_base, test_enable,
+						teardown);
+
+	test_bredrle("Bluetooth Enable - Success 2",
+						&bluetooth_enable_success2_test,
+						setup_enabled_adapter,
+						test_enable_done, teardown);
+
+	test_bredrle("Bluetooth Disable - Success",
+						&bluetooth_disable_success_test,
+						setup_enabled_adapter,
+						test_disable, teardown);
+
+	test_bredrle("Bluetooth Set BDNAME - Success",
+					&bluetooth_setprop_bdname_success_test,
+					setup_enabled_adapter,
+					test_setprop_bdname_success, teardown);
+
+	test_bredrle("Bluetooth Set SCAN_MODE - Success",
+				&bluetooth_setprop_scanmode_success_test,
+				setup_enabled_adapter,
+				test_setprop_scanmode_succes, teardown);
+
+	test_bredrle("Bluetooth Set DISCOVERY_TIMEOUT - Success",
+				&bluetooth_setprop_disctimeout_success_test,
+				setup_enabled_adapter,
+				test_setprop_disctimeout_succes, teardown);
+
+	test_bredrle("Bluetooth Get BDADDR - Success",
+					&bluetooth_getprop_bdaddr_success_test,
+					setup_enabled_adapter,
+					test_getprop_bdaddr_success, teardown);
+
+	test_bredrle("Bluetooth Get BDNAME - Success",
+					&bluetooth_getprop_bdname_success_test,
+					setup_enabled_adapter,
+					test_getprop_bdname_success, teardown);
+
+	test_bredrle("Bluetooth Set UUID - Invalid",
+					&bluetooth_setprop_uuid_invalid_test,
+					setup_enabled_adapter,
+					test_setprop_uuid_invalid, teardown);
+
+	test_bredrle("Bluetooth Set CLASS_OF_DEVICE - Invalid",
+					&bluetooth_setprop_cod_invalid_test,
+					setup_enabled_adapter,
+					test_setprop_cod_invalid, teardown);
+
+	test_bredrle("Bluetooth Set TYPE_OF_DEVICE - Invalid",
+					&bluetooth_setprop_tod_invalid_test,
+					setup_enabled_adapter,
+					test_setprop_tod_invalid, teardown);
+
+	test_bredrle("Bluetooth Set REMOTE_RSSI - Invalid",
+				&bluetooth_setprop_remote_rssi_invalid_test,
+				setup_enabled_adapter,
+				test_setprop_rssi_invalid, teardown);
+
+	test_bredrle("Bluetooth Set SERVICE_RECORD - Invalid",
+				&bluetooth_setprop_service_record_invalid_test,
+				setup_enabled_adapter,
+				test_setprop_service_record_invalid, teardown);
+
+	test_bredrle("Bluetooth Set BDADDR - Invalid",
+					&bluetooth_setprop_bdaddr_invalid_test,
+					setup_enabled_adapter,
+					test_setprop_bdaddr_invalid, teardown);
+
+	test_bredrle("Bluetooth Set SCAN_MODE CONNECTABLE - Success",
+			&bluetooth_setprop_scanmode_connectable_success_test,
+			setup_enabled_adapter,
+			test_setprop_scanmode_connectable_success, teardown);
+
+	test_bredrle("Bluetooth Set BONDED_DEVICES - Invalid",
+				&bluetooth_setprop_bonded_devices_invalid_test,
+				setup_enabled_adapter,
+				test_setprop_bonded_devices_invalid, teardown);
+
+	test_bredrle("Bluetooth Get CLASS_OF_DEVICE - Success",
+					&bluetooth_getprop_cod_success_test,
+					setup_enabled_adapter,
+					test_getprop_cod_success, teardown);
+
+	test_bredrle("Bluetooth Get TYPE_OF_DEVICE - Success",
+					&bluetooth_getprop_tod_success_test,
+					setup_enabled_adapter,
+					test_getprop_tod_success, teardown);
+
+	test_bredrle("Bluetooth Get SCAN_MODE - Success",
+				&bluetooth_getprop_scanmode_success_test,
+				setup_enabled_adapter,
+				test_getprop_scanmode_success, teardown);
+
+	test_bredrle("Bluetooth Get DISCOVERY_TIMEOUT - Success",
+				&bluetooth_getprop_disctimeout_success_test,
+				setup_enabled_adapter,
+				test_getprop_disctimeout_success, teardown);
+
+	test_bredrle("Bluetooth Get UUIDS - Success",
+					&bluetooth_getprop_uuids_success_test,
+					setup_enabled_adapter,
+					test_getprop_uuids_success, teardown);
+
+	test_bredrle("Bluetooth Get BONDED_DEVICES - Success",
+				&bluetooth_getprop_bondeddev_success_test,
+				setup_enabled_adapter,
+				test_getprop_bondeddev_success, teardown);
+
+	test_bredrle("Bluetooth Set SCAN_MODE NONE - Success 2",
+				&bluetooth_setprop_scanmode_none_success2_test,
+				setup_enabled_adapter,
+				test_setprop_scanmode_none_done, teardown);
+
+	test_bredrle("Bluetooth BR/EDR Discovery Start - Success",
+				&bluetooth_discovery_start_success_test,
+				setup_enabled_adapter,
+				test_discovery_start_success, teardown);
+
+	test_bredrle("Bluetooth BR/EDR Discovery Start - Success 2",
+				&bluetooth_discovery_start_success2_test,
+				setup_enabled_adapter,
+				test_discovery_start_done, teardown);
+
+	test_bredrle("Bluetooth BR/EDR Discovery Stop - Success",
+				&bluetooth_discovery_stop_success_test,
+				setup_enabled_adapter,
+				test_discovery_stop_success, teardown);
+
+	test_bredrle("Bluetooth BR/EDR Discovery Stop - Success 2",
+				&bluetooth_discovery_stop_success2_test,
+				setup_enabled_adapter,
+				test_discovery_stop_done, teardown);
+
+	test_bredr("Bluetooth BR/EDR Discovery Device Found",
+				&bluetooth_discovery_device_found_test,
+				setup_enabled_adapter,
+				test_discovery_device_found, teardown);
+
+	test_bredr("Bluetooth Device Get Props - Success",
+					&bt_dev_getprops_success_test,
+					setup_enabled_adapter,
+					test_dev_getprops_success, teardown);
+
+	test_bredr("Bluetooth Device Get BDNAME - Success",
+				&bt_dev_getprop_bdname_success_test,
+				setup_enabled_adapter,
+				test_dev_getprop_bdname_success, teardown);
+
+	test_bredr("Bluetooth Device Get UUIDS - Success",
+				&bt_dev_getprop_uuids_success_test,
+				setup_enabled_adapter,
+				test_dev_getprop_uuids_success, teardown);
+
+	test_bredr("Bluetooth Device Get COD - Success",
+					&bt_dev_getprop_cod_success_test,
+					setup_enabled_adapter,
+					test_dev_getprop_cod_success, teardown);
+
+	test_bredr("Bluetooth Device Get TOD - Success",
+					&bt_dev_getprop_tod_success_test,
+					setup_enabled_adapter,
+					test_dev_getprop_tod_success, teardown);
+
+	test_bredr("Bluetooth Device Get RSSI - Success",
+				&bt_dev_getprop_rssi_success_test,
+				setup_enabled_adapter,
+				test_dev_getprop_rssi_success, teardown);
+
+	test_bredr("Bluetooth Device Get TIMESTAMP - Success",
+				&bt_dev_getprop_timpestamp_success_test,
+				setup_enabled_adapter,
+				test_dev_getprop_timestamp_success, teardown);
+
+	test_bredr("Bluetooth Device Get BDADDR - Fail",
+				&bt_dev_getprop_bdaddr_fail_test,
+				setup_enabled_adapter,
+				test_dev_getprop_bdaddr_fail, teardown);
+
+	test_bredr("Bluetooth Device Get SERVICE_RECORD - Fail",
+				&bt_dev_getprop_servrec_fail_test,
+				setup_enabled_adapter,
+				test_dev_getprop_servrec_fail, teardown);
+
+	test_bredr("Bluetooth Device Get SCAN_MODE - Fail",
+				&bt_dev_getprop_scanmode_fail_test,
+				setup_enabled_adapter,
+				test_dev_getprop_scanmode_fail, teardown);
+
+	test_bredr("Bluetooth Device Get BONDED_DEVICES - Fail",
+				&bt_dev_getprop_bondeddev_fail_test,
+				setup_enabled_adapter,
+				test_dev_getprop_bondeddev_fail, teardown);
+
+	test_bredr("Bluetooth Device Get DISCOVERY_TIMEOUT - Fail",
+				&bt_dev_getprop_disctimeout_fail_test,
+				setup_enabled_adapter,
+				test_dev_getprop_disctimeout_fail, teardown);
+
+	test_bredr("Bluetooth Device Get VERSION_INFO - Fail",
+				&bt_dev_getprop_verinfo_fail_test,
+				setup_enabled_adapter,
+				test_dev_getprop_verinfo_fail, teardown);
+
+	test_bredr("Bluetooth Device Get FRIENDLY_NAME - Fail",
+					&bt_dev_getprop_fname_fail_test,
+					setup_enabled_adapter,
+					test_dev_getprop_fname_fail, teardown);
+
+	test_bredr("Bluetooth Device Set FRIENDLY_NAME - Success",
+				&bt_dev_setprop_fname_success_test,
+				setup_enabled_adapter,
+				test_dev_setprop_fname_success, teardown);
+
+	test_bredr("Bluetooth Device Set BDNAME - Fail",
+					&bt_dev_setprop_bdname_fail_test,
+					setup_enabled_adapter,
+					test_dev_setprop_bdname_fail, teardown);
+
+	test_bredr("Bluetooth Device Set UUIDS - Fail",
+					&bt_dev_setprop_uuids_fail_test,
+					setup_enabled_adapter,
+					test_dev_setprop_uuids_fail, teardown);
+
+	test_bredr("Bluetooth Device Set COD - Fail",
+					&bt_dev_setprop_cod_fail_test,
+					setup_enabled_adapter,
+					test_dev_setprop_cod_fail, teardown);
+
+	test_bredr("Bluetooth Device Set TOD - Fail",
+					&bt_dev_setprop_tod_fail_test,
+					setup_enabled_adapter,
+					test_dev_setprop_tod_fail, teardown);
+
+	test_bredr("Bluetooth Device Set RSSI - Fail",
+				&bt_dev_setprop_rssi_fail_test,
+				setup_enabled_adapter,
+				test_dev_setprop_rssi_fail, teardown);
+
+	test_bredr("Bluetooth Device Set TIMESTAMP - Fail",
+				&bt_dev_setprop_timpestamp_fail_test,
+				setup_enabled_adapter,
+				test_dev_setprop_timestamp_fail, teardown);
+
+	test_bredr("Bluetooth Device Set BDADDR - Fail",
+				&bt_dev_setprop_bdaddr_fail_test,
+				setup_enabled_adapter,
+				test_dev_setprop_bdaddr_fail, teardown);
+
+	test_bredr("Bluetooth Device Set SERVICE_RECORD - Fail",
+				&bt_dev_setprop_servrec_fail_test,
+				setup_enabled_adapter,
+				test_dev_setprop_servrec_fail, teardown);
+
+	test_bredr("Bluetooth Device Set SCAN_MODE - Fail",
+				&bt_dev_setprop_scanmode_fail_test,
+				setup_enabled_adapter,
+				test_dev_setprop_scanmode_fail, teardown);
+
+	test_bredr("Bluetooth Device Set BONDED_DEVICES - Fail",
+				&bt_dev_setprop_bondeddev_fail_test,
+				setup_enabled_adapter,
+				test_dev_setprop_bondeddev_fail, teardown);
+
+	test_bredr("Bluetooth Device Set DISCOVERY_TIMEOUT - Fail",
+				&bt_dev_setprop_disctimeout_fail_test,
+				setup_enabled_adapter,
+				test_dev_setprop_disctimeout_fail, teardown);
+
+	test_bredr("Bluetooth Create Bond PIN - Success",
+					&bt_bond_create_pin_success_test,
+					setup_enabled_adapter,
+					test_bond_create_pin_success, teardown);
+
+	test_bredr("Bluetooth Create Bond PIN - Bad PIN",
+					&bt_bond_create_pin_fail_test,
+					setup_enabled_adapter,
+					test_bond_create_pin_fail, teardown);
+
+	test_bredr("Bluetooth Create Bond SSP - Success",
+					&bt_bond_create_ssp_success_test,
+					setup_enabled_adapter,
+					test_bond_create_ssp_success, teardown);
+
+	test_bredr("Bluetooth Create Bond SSP - Negative reply",
+					&bt_bond_create_ssp_fail_test,
+					setup_enabled_adapter,
+					test_bond_create_ssp_fail, teardown);
+
+	test_bredrle("Bluetooth Create Bond - No Discovery",
+				&bt_bond_create_no_disc_success_test,
+				setup_enabled_adapter,
+				test_bond_create_no_disc_success, teardown);
+
+	test_bredrle("Bluetooth Create Bond - Bad Address",
+				&bt_bond_create_bad_addr_success_test,
+				setup_enabled_adapter,
+				test_bond_create_bad_addr_success, teardown);
+
+	test_bredr("Bluetooth Cancel Bonding - Success",
+					&bt_bond_cancel_success_test,
+					setup_enabled_adapter,
+					test_bond_cancel_success, teardown);
+
+	test_bredr("Bluetooth Remove Bond - Success",
+					&bt_bond_remove_success_test,
+					setup_enabled_adapter,
+					test_bond_remove_success, teardown);
+
+	test_bredrle("Socket Init", NULL, setup_socket_interface,
+						test_dummy, teardown);
+
+	test_bredrle("Socket Listen - Invalid: sock_type 0",
+			&btsock_inv_param_socktype, setup_socket_interface,
+			test_generic_listen, teardown);
+
+	test_bredrle("Socket Listen - Invalid: sock_type L2CAP",
+			&btsock_inv_param_socktype_l2cap,
+			setup_socket_interface, test_generic_listen, teardown);
+
+	test_bredrle("Socket Listen - Invalid: chan, uuid",
+			&btsock_inv_params_chan_uuid,
+			setup_socket_interface, test_generic_listen, teardown);
+
+	test_bredrle("Socket Listen - Check returned fd valid",
+			&btsock_success,
+			setup_socket_interface, test_generic_listen, teardown);
+
+	test_bredrle("Socket Listen - Check returned channel",
+			&btsock_success_check_chan,
+			setup_socket_interface, test_generic_listen, teardown);
+
+	test_bredrle("Socket Listen - Close and Listen again",
+			&btsock_success_check_chan,
+			setup_socket_interface, test_listen_close, teardown);
+
+	test_bredrle("Socket Listen - Invalid: double Listen",
+			&btsock_inv_listen_listen,
+			setup_socket_interface, test_listen_listen, teardown);
+
+	test_bredrle("Socket Connect - Check returned fd valid",
+			&btsock_success, setup_socket_interface,
+			test_generic_connect, teardown);
+
+	test_bredrle("Socket Connect - Invalid: sock_type 0",
+			&btsock_inv_param_socktype, setup_socket_interface,
+			test_generic_connect, teardown);
+
+	test_bredrle("Socket Connect - Invalid: sock_type L2CAP",
+			&btsock_inv_param_socktype_l2cap,
+			setup_socket_interface, test_generic_connect, teardown);
+
+	test_bredrle("Socket Connect - Invalid: chan, uuid",
+			&btsock_inv_params_chan_uuid,
+			setup_socket_interface, test_generic_connect, teardown);
+
+	test_bredrle("Socket Connect - Invalid: bdaddr",
+			&btsock_inv_param_bdaddr,
+			setup_socket_interface, test_generic_connect, teardown);
+
+	test_bredrle("Socket Connect - Check returned chan",
+			&btsock_success_check_chan,
+			setup_socket_interface_enabled,
+			test_socket_real_connect, teardown);
+
+	test_bredrle("HIDHost Init", NULL, setup_hidhost_interface,
+						test_dummy, teardown);
+
+	test_bredrle("HIDHost Connect Success",
+				NULL, setup_hidhost_connect,
+				test_dummy, teardown);
+
+	test_bredrle("HIDHost Disconnect Success",
+				&hidhost_test_disconnect, setup_hidhost_connect,
+				test_hidhost_disconnect, teardown);
+
+	test_bredrle("HIDHost VirtualUnplug Success",
+				&hidhost_test_disconnect, setup_hidhost_connect,
+				test_hidhost_virtual_unplug, teardown);
+
+	test_bredrle("HIDHost GetProtocol Success",
+			&hidhost_test_get_protocol, setup_hidhost_connect,
+				test_hidhost_get_protocol, teardown);
+
+	test_bredrle("HIDHost SetProtocol Success",
+			&hidhost_test_get_protocol, setup_hidhost_connect,
+				test_hidhost_set_protocol, teardown);
+
+	test_bredrle("HIDHost GetReport Success",
+			&hidhost_test_get_report, setup_hidhost_connect,
+				test_hidhost_get_report, teardown);
+
+	test_bredrle("HIDHost SetReport Success",
+				NULL, setup_hidhost_connect,
+				test_hidhost_set_report, teardown);
+
+	test_bredrle("HIDHost SendData Success",
+				NULL, setup_hidhost_connect,
+				test_hidhost_send_data, teardown);
+	return tester_run();
+}
diff --git a/bluez/android/audio-ipc-api.txt b/bluez/android/audio-ipc-api.txt
new file mode 100644
index 0000000..f4a497d
--- /dev/null
+++ b/bluez/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/android/audio-msg.h b/bluez/android/audio-msg.h
new file mode 100644
index 0000000..5981355
--- /dev/null
+++ b/bluez/android/audio-msg.h
@@ -0,0 +1,81 @@
+/*
+ *
+ *  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 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/android/avctp.c b/bluez/android/avctp.c
new file mode 100644
index 0000000..ed91810
--- /dev/null
+++ b/bluez/android/avctp.c
@@ -0,0 +1,1556 @@
+/*
+ *
+ *  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 <bluetooth/sdp.h>
+
+#include <glib.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));
+#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));
+#define AVC_HEADER_LENGTH 3
+
+#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));
+#define AVC_HEADER_LENGTH 3
+
+#else
+#error "Unknown byte order"
+#endif
+
+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;
+	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 {
+	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,
+				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");
+		/* FIXME: Add general reject */
+		/* 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");
+
+	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,
+				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 *params, size_t params_len)
+{
+	uint8_t operands[7];
+	size_t len;
+
+	DBG("op 0x%02x %s params_len %zd", op, op2str(op), params_len);
+
+	/* Button pressed */
+	operands[0] = op & 0x7f;
+
+	if (op == AVC_VENDOR_UNIQUE && params &&
+				params_len == 5) {
+		memcpy(&operands[2], params, params_len);
+		len = params_len + 2;
+		operands[1] = params_len;
+	} else {
+		len = 2;
+		operands[1] = 0;
+	}
+
+	return avctp_send_req(session, AVC_CTYPE_CONTROL,
+				AVC_SUBUNIT_PANEL, AVC_OP_PASSTHROUGH,
+				operands, len,
+				avctp_passthrough_rsp, NULL);
+}
+
+static int avctp_passthrough_release(struct avctp *session, uint8_t op,
+					uint8_t *params, size_t params_len)
+{
+	uint8_t operands[7];
+	size_t len;
+
+	DBG("%s", op2str(op));
+
+	/* Button released */
+	operands[0] = op | 0x80;
+	operands[1] = 0;
+
+	if (op == AVC_VENDOR_UNIQUE && params &&
+				params_len > sizeof(operands) - 2) {
+		memcpy(&operands[2], params, params_len);
+		len = params_len;
+	} else
+		len = 2;
+
+	return avctp_send_req(session, AVC_CTYPE_CONTROL,
+				AVC_SUBUNIT_PANEL, AVC_OP_PASSTHROUGH,
+				operands, len,
+				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_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_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 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;
+
+	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);
+}
diff --git a/bluez/android/avctp.h b/bluez/android/avctp.h
new file mode 100644
index 0000000..98c1142
--- /dev/null
+++ b/bluez/android/avctp.h
@@ -0,0 +1,178 @@
+/*
+ *
+ *  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
+
+/* 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 size_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_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/android/avdtp.c b/bluez/android/avdtp.c
new file mode 100644
index 0000000..2c49dcb
--- /dev/null
+++ b/bluez/android/avdtp.c
@@ -0,0 +1,3421 @@
+/*
+ *
+ *  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 "src/log.h"
+#include "avdtp.h"
+
+#define AVDTP_PSM 25
+
+#define MAX_SEID 0x3E
+
+#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 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;
+
+	struct avdtp_server *server;
+
+	guint auth_id;
+
+	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;
+
+	GSList *disconnect;
+
+	bool shutdown;
+};
+
+static GSList *lseps = 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 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->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);
+
+	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 struct avdtp_local_sep *find_local_sep_by_seid(uint8_t seid)
+{
+	GSList *l;
+
+	for (l = lseps; l != NULL; l = g_slist_next(l)) {
+		struct avdtp_local_sep *sep = l->data;
+
+		if (sep->info.seid == seid)
+			return sep;
+	}
+
+	return NULL;
+}
+
+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 (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 gboolean avdtp_discover_cmd(struct avdtp *session, uint8_t transaction,
+							void *buf, int size)
+{
+	GSList *l;
+	unsigned int rsp_size, sep_count, i;
+	struct seid_info *seps;
+	gboolean ret;
+
+	sep_count = g_slist_length(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);
+
+	for (l = lseps, i = 0; l != NULL; l = l->next, i++) {
+		struct avdtp_local_sep *sep = l->data;
+
+		memcpy(&seps[i], &sep->info, sizeof(struct seid_info));
+	}
+
+	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(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(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(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(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(req->first_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(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(req->first_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(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(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;
+	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)
+		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 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 avdtp *session;
+	GIOCondition cond = G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL;
+	int new_fd;
+
+	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);
+
+	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_abort(session, stream) == 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;
+
+	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;
+
+	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) {
+			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;
+
+	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,
+							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->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 (!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(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;
+
+	if (g_slist_length(lseps) > MAX_SEID)
+		return NULL;
+
+	sep = g_new0(struct avdtp_local_sep, 1);
+
+	sep->state = AVDTP_STATE_IDLE;
+	sep->info.seid = g_slist_length(lseps) + 1;
+	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);
+	lseps = g_slist_append(lseps, sep);
+
+	return sep;
+}
+
+int avdtp_unregister_sep(struct avdtp_local_sep *sep)
+{
+	if (!sep)
+		return -EINVAL;
+
+	lseps = g_slist_remove(lseps, sep);
+
+	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);
+
+	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/android/avdtp.h b/bluez/android/avdtp.h
new file mode 100644
index 0000000..e8d1907
--- /dev/null
+++ b/bluez/android/avdtp.h
@@ -0,0 +1,282 @@
+/*
+ *
+ *  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;
+};
+
+/* 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);
+
+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,
+							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(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 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/android/avrcp-lib.c b/bluez/android/avrcp-lib.c
new file mode 100644
index 0000000..c7b8b6a
--- /dev/null
+++ b/bluez/android/avrcp-lib.c
@@ -0,0 +1,1023 @@
+/*
+ *
+ *  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 "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			106
+
+#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 {
+	struct avctp *conn;
+	struct avrcp_player *player;
+
+	size_t tx_mtu;
+	uint8_t *tx_buf;
+
+	const struct avrcp_control_handler *control_handlers;
+	void *control_data;
+	unsigned int control_id;
+
+	const struct avrcp_passthrough_handler *passthrough_handlers;
+	void *passthrough_data;
+	unsigned int passthrough_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);
+}
+
+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);
+
+		/* 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);
+
+	g_free(session->player);
+	g_free(session->tx_buf);
+	g_free(session);
+}
+
+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 = (void *) operands;
+	uint32_t company_id = ntoh24(pdu->company_id);
+	uint16_t params_len = ntohs(pdu->params_len);
+	ssize_t ret;
+
+	if (company_id != IEEEID_BTSIG) {
+		*code = AVC_CTYPE_NOT_IMPLEMENTED;
+		return 0;
+	}
+
+	DBG("AVRCP PDU 0x%02X, len 0x%04X", pdu->pdu_id, params_len);
+
+	pdu->packet_type = 0;
+	pdu->rsvd = 0;
+
+	if (operand_count < AVRCP_HEADER_LENGTH) {
+		pdu->params[0] = AVRCP_STATUS_INVALID_COMMAND;
+		goto reject;
+	}
+
+	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, params_len, pdu->params,
+							session->control_data);
+	if (ret < 0) {
+		switch (ret) {
+		case -EAGAIN:
+			return ret;
+		case -ENOSYS:
+			pdu->params[0] = AVRCP_STATUS_INVALID_COMMAND;
+			goto reject;
+		case -EINVAL:
+			pdu->params[0] = AVRCP_STATUS_INVALID_PARAM;
+			goto reject;
+		default:
+			pdu->params[0] = AVRCP_STATUS_INTERNAL_ERROR;
+			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->tx_mtu = omtu;
+	session->tx_buf = g_malloc(omtu);
+
+	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);
+
+	avctp_set_destroy_cb(session->conn, disconnect_cb, session);
+
+	return session;
+}
+
+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;
+
+	if (!params || params_len != 1)
+		return -EINVAL;
+
+	switch (params[0]) {
+	case CAP_COMPANY_ID:
+		params[1] = 1;
+		hton24(&params[2], 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;
+
+	DBG("");
+
+	if (!params || params_len != 1 + params[0])
+		return -EINVAL;
+
+	if (!check_attributes(params[0], &params[1]))
+		return -EINVAL;
+
+	if (!player->ind || !player->ind->get_attribute_text)
+		return -ENOSYS;
+
+	return player->ind->get_attribute_text(session, transaction, params[0],
+						&params[1], 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;
+
+	DBG("");
+
+	if (!params || params_len != 1)
+		return -EINVAL;
+
+	if (params[0] > AVRCP_ATTRIBUTE_LAST ||
+					params[0] == AVRCP_ATTRIBUTE_ILEGAL)
+		return -EINVAL;
+
+	if (!player->ind || !player->ind->list_values)
+		return -ENOSYS;
+
+	return player->ind->list_values(session, transaction, params[0],
+							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;
+		case AVRCP_ATTRIBUTE_REPEAT_MODE:
+			if (values[i] < AVRCP_REPEAT_MODE_OFF ||
+					values[i] > AVRCP_REPEAT_MODE_GROUP)
+				return false;
+		case AVRCP_ATTRIBUTE_SHUFFLE:
+			if (values[i] < AVRCP_SHUFFLE_OFF ||
+					values[i] > AVRCP_SHUFFLE_GROUP)
+				return false;
+		case AVRCP_ATTRIBUTE_SCAN:
+			if (values[i] < AVRCP_SCAN_OFF ||
+					values[i] > AVRCP_SCAN_GROUP)
+				return false;
+		}
+	}
+
+	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;
+
+	DBG("");
+
+	if (params_len != 2 + params[1])
+		return -EINVAL;
+
+	if (params[0] > AVRCP_ATTRIBUTE_LAST ||
+					params[0] == AVRCP_ATTRIBUTE_ILEGAL)
+		return -EINVAL;
+
+	if (!check_value(params[0], params[1], &params[2]))
+		return -EINVAL;
+
+	if (!player->ind || !player->ind->get_value_text)
+		return -ENOSYS;
+
+	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;
+
+	DBG("");
+
+	if (!params || params_len < 1 + params[0])
+		return -EINVAL;
+
+	if (!check_attributes(params[0], &params[1]))
+		return -EINVAL;
+
+	if (!player->ind || !player->ind->get_value)
+		return -ENOSYS;
+
+	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;
+	int i;
+
+	DBG("");
+
+	if (!params || params_len != params[0] * 2 + 1)
+		return -EINVAL;
+
+	for (i = 0; i < params[0]; i++) {
+		uint8_t attr = params[i * 2 + 1];
+		uint8_t val = params[i * 2 + 2];
+
+		if (!check_value(attr, 1, &val))
+			return -EINVAL;
+	}
+
+	if (!player->ind || !player->ind->set_value)
+		return -ENOSYS;
+
+	return player->ind->set_value(session, transaction, params[0],
+					&params[1], 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 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;
+	uint64_t uid;
+	uint8_t number;
+	uint32_t attrs[AVRCP_MEDIA_ATTRIBUTE_LAST];
+	int i;
+
+	DBG("");
+
+	if (!params || params_len != 9 + params[8] * 4)
+		return -EINVAL;
+
+	uid = get_be64(params);
+	number = params[8];
+
+	for (i = 0; i < number; i++) {
+		attrs[i] = get_be32(&params[9 + i * 4]);
+
+		if (attrs[i] == AVRCP_MEDIA_ATTRIBUTE_ILLEGAL ||
+				attrs[i] > AVRCP_MEDIA_ATTRIBUTE_LAST)
+			return -EINVAL;
+	}
+
+	if (!player->ind || !player->ind->get_element_attributes)
+		return -ENOSYS;
+
+	return player->ind->get_element_attributes(session, transaction, uid,
+							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;
+	uint32_t interval;
+
+	DBG("");
+
+	if (!params || params_len != 5)
+		return -EINVAL;
+
+	if (!player->ind || !player->ind->register_notification)
+		return -ENOSYS;
+
+	interval = get_be32(&params[1]);
+
+	return player->ind->register_notification(session, transaction,
+							params[0], interval,
+							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;
+	uint16_t id;
+
+	DBG("");
+
+	if (!params || params_len != 2)
+		return -EINVAL;
+
+	if (!player->ind || !player->ind->set_addressed)
+		return -ENOSYS;
+
+	id = get_be16(params);
+
+	return player->ind->set_addressed(session, transaction, id,
+							player->user_data);
+}
+
+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_ADDRESSED_PLAYER,
+					AVC_CTYPE_CONTROL, AVC_CTYPE_STABLE,
+					set_addressed },
+		{ },
+};
+
+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;
+}
+
+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);
+	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,
+					uint8_t *params, size_t params_len)
+{
+	struct avrcp_header *pdu = (void *) session->tx_buf;
+	size_t len = sizeof(*pdu);
+
+	memset(pdu, 0, len);
+
+	hton24(pdu->company_id, IEEEID_BTSIG);
+	pdu->pdu_id = pdu_id;
+	pdu->packet_type = AVRCP_PACKET_TYPE_SINGLE;
+
+	if (params_len > 0) {
+		len += params_len;
+
+		if (len > session->tx_mtu)
+			return -ENOBUFS;
+
+		memcpy(pdu->params, params, params_len);
+		pdu->params_len = htons(params_len);
+	}
+
+	return avctp_send_vendordep(session->conn, transaction, code, subunit,
+							session->tx_buf, len);
+}
+
+static int avrcp_send_req(struct avrcp *session, uint8_t code, uint8_t subunit,
+					uint8_t pdu_id, uint8_t *params,
+					size_t params_len, avctp_rsp_cb func,
+					void *user_data)
+{
+	struct avrcp_header *pdu = (void *) session->tx_buf;
+	size_t len = sizeof(*pdu);
+
+	memset(pdu, 0, len);
+
+	hton24(pdu->company_id, IEEEID_BTSIG);
+	pdu->pdu_id = pdu_id;
+	pdu->packet_type = AVRCP_PACKET_TYPE_SINGLE;
+
+	if (params_len > 0) {
+		len += params_len;
+
+		if (len > session->tx_mtu)
+			return -ENOBUFS;
+
+		memcpy(pdu->params, params, params_len);
+		pdu->params_len = htons(params_len);
+	}
+
+	return avctp_send_vendordep_req(session->conn, code, subunit,
+					session->tx_buf, len, func, user_data);
+}
+
+int avrcp_get_capabilities(struct avrcp *session, uint8_t param,
+					avctp_rsp_cb func, void *user_data)
+{
+	return avrcp_send_req(session, AVC_CTYPE_STATUS, AVC_SUBUNIT_PANEL,
+				AVRCP_GET_CAPABILITIES, &param, sizeof(param),
+				func, user_data);
+}
+
+int avrcp_register_notification(struct avrcp *session, uint8_t event,
+					uint32_t interval, avctp_rsp_cb func,
+					void *user_data)
+{
+	uint8_t params[5];
+
+	params[0] = event;
+	put_be32(interval, &params[1]);
+
+	return avrcp_send_req(session, AVC_CTYPE_NOTIFY, AVC_SUBUNIT_PANEL,
+					AVRCP_REGISTER_NOTIFICATION,
+					params, sizeof(params),
+					func, user_data);
+}
+
+int avrcp_list_player_attributes(struct avrcp *session, avctp_rsp_cb func,
+								void *user_data)
+{
+	return avrcp_send_req(session, AVC_CTYPE_STATUS, AVC_SUBUNIT_PANEL,
+				AVRCP_LIST_PLAYER_ATTRIBUTES, NULL, 0,
+				func, user_data);
+}
+
+int avrcp_get_player_attribute_text(struct avrcp *session, uint8_t *attributes,
+					uint8_t attr_len, avctp_rsp_cb func,
+					void *user_data)
+{
+	return avrcp_send_req(session, AVC_CTYPE_STATUS, AVC_SUBUNIT_PANEL,
+				AVRCP_GET_PLAYER_ATTRIBUTE_TEXT, attributes,
+				attr_len, func, user_data);
+}
+
+int avrcp_get_current_player_value(struct avrcp *session, uint8_t *attrs,
+					uint8_t attr_count, avctp_rsp_cb func,
+					void *user_data)
+
+{
+	uint8_t buf[AVRCP_ATTRIBUTE_LAST + 1];
+
+	if (attr_count > AVRCP_ATTRIBUTE_LAST)
+		return -EINVAL;
+
+	if (attrs && attr_count) {
+		buf[0] = attr_count;
+		memcpy(buf + 1, attrs, attr_count);
+	}
+
+	return avrcp_send_req(session, AVC_CTYPE_STATUS, AVC_SUBUNIT_PANEL,
+				AVRCP_GET_CURRENT_PLAYER_VALUE, buf,
+				attr_count + 1, func, user_data);
+}
+
+int avrcp_set_player_value(struct avrcp *session, uint8_t *attributes,
+					uint8_t attr_count, uint8_t *values,
+					avctp_rsp_cb func, void *user_data)
+{
+	uint8_t buf[2 * AVRCP_ATTRIBUTE_LAST + 1];
+	int i;
+
+	if (attr_count > AVRCP_ATTRIBUTE_LAST)
+		return -EINVAL;
+
+	buf[0] = attr_count;
+
+	for (i = 0; i < attr_count; i++) {
+		buf[i * 2 + 1] = attributes[i];
+		buf[i * 2 + 2] = values[i];
+	}
+
+	return avrcp_send_req(session, AVC_CTYPE_CONTROL, AVC_SUBUNIT_PANEL,
+				AVRCP_SET_PLAYER_VALUE, buf, 2 * attr_count + 1,
+				func, user_data);
+}
+
+int avrcp_get_play_status(struct avrcp *session, avctp_rsp_cb func,
+								void *user_data)
+{
+	return avrcp_send_req(session, AVC_CTYPE_STATUS, AVC_SUBUNIT_PANEL,
+				AVRCP_GET_PLAY_STATUS, NULL, 0, func,
+				user_data);
+}
+
+int avrcp_set_volume(struct avrcp *session, uint8_t volume, avctp_rsp_cb func,
+							void *user_data)
+{
+	return avrcp_send_req(session, AVC_CTYPE_CONTROL, AVC_SUBUNIT_PANEL,
+						AVRCP_SET_ABSOLUTE_VOLUME,
+						&volume, sizeof(volume),
+						func, user_data);
+}
+
+int avrcp_get_element_attributes(struct avrcp *session, avctp_rsp_cb func,
+								void *user_data)
+{
+	uint8_t buf[9];
+
+	/* This returns all attributes */
+	memset(buf, 0, sizeof(buf));
+
+	return avrcp_send_req(session, AVC_CTYPE_STATUS, AVC_SUBUNIT_PANEL,
+				AVRCP_GET_ELEMENT_ATTRIBUTES, buf, sizeof(buf),
+				func, user_data);
+}
+
+int avrcp_set_addressed_player(struct avrcp *session, uint16_t player_id,
+					avctp_rsp_cb func, void *user_data)
+{
+	uint8_t params[2];
+
+	put_be16(player_id, params);
+
+	return avrcp_send_req(session, AVC_CTYPE_CONTROL, AVC_SUBUNIT_PANEL,
+				AVRCP_SET_ADDRESSED_PLAYER, params,
+				sizeof(params), func, user_data);
+}
+
+int avrcp_get_capabilities_rsp(struct avrcp *session, uint8_t transaction,
+						uint8_t number, uint8_t *events)
+{
+	uint8_t pdu[AVRCP_EVENT_LAST + 1];
+
+	if (number > AVRCP_EVENT_LAST)
+		return -EINVAL;
+
+	pdu[0] = number;
+	memcpy(&pdu[1], events, number);
+
+	return avrcp_send(session, transaction, AVC_CTYPE_STABLE,
+				AVC_SUBUNIT_PANEL, AVRCP_GET_CAPABILITIES,
+				pdu, number + 1);
+}
+
+int avrcp_list_player_attributes_rsp(struct avrcp *session, uint8_t transaction,
+					uint8_t number, uint8_t *attrs)
+{
+	uint8_t pdu[AVRCP_ATTRIBUTE_LAST + 1];
+
+	if (number > AVRCP_ATTRIBUTE_LAST)
+		return -EINVAL;
+
+	pdu[0] = number;
+
+	if (number > 0)
+		memcpy(&pdu[1], attrs, number);
+
+	return avrcp_send(session, transaction, AVC_CTYPE_STABLE,
+				AVC_SUBUNIT_PANEL, AVRCP_LIST_PLAYER_ATTRIBUTES,
+				pdu, number + 1);
+}
+
+int avrcp_get_player_attribute_text_rsp(struct avrcp *session,
+					uint8_t transaction, uint8_t number,
+					uint8_t *attrs, const char **text)
+{
+	uint8_t pdu[AVRCP_ATTRIBUTE_LAST * (4 + 255)];
+	uint8_t *ptr;
+	uint16_t length;
+	int i;
+
+	if (number > AVRCP_ATTRIBUTE_LAST)
+		return -EINVAL;
+
+	pdu[0] = number;
+	length = 1;
+	for (i = 0, ptr = &pdu[1]; 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]);
+
+		ptr[0] = attrs[i];
+		put_be16(AVRCP_CHARSET_UTF8, &ptr[1]);
+		ptr[3] = len;
+		memcpy(&ptr[4], text[i], len);
+		ptr += 4 + len;
+		length += 4 + len;
+	}
+
+	return avrcp_send(session, transaction, AVC_CTYPE_STABLE,
+			AVC_SUBUNIT_PANEL, AVRCP_GET_PLAYER_ATTRIBUTE_TEXT,
+			pdu, length);
+}
+
+int avrcp_list_player_values_rsp(struct avrcp *session, uint8_t transaction,
+					uint8_t number, uint8_t *values)
+{
+	uint8_t pdu[AVRCP_ATTRIBUTE_LAST + 1];
+
+	if (number > AVRCP_ATTRIBUTE_LAST)
+		return -EINVAL;
+
+	pdu[0] = number;
+	memcpy(&pdu[1], values, number);
+
+	return avrcp_send(session, transaction, AVC_CTYPE_STABLE,
+			AVC_SUBUNIT_PANEL, AVRCP_LIST_PLAYER_VALUES,
+			pdu, number + 1);
+}
+
+int avrcp_get_play_status_rsp(struct avrcp *session, uint8_t transaction,
+				uint32_t position, uint32_t duration,
+				uint8_t status)
+{
+	uint8_t pdu[9];
+
+	put_be32(position, &pdu[0]);
+	put_be32(duration, &pdu[4]);
+	pdu[8] = status;
+
+	return avrcp_send(session, transaction, AVC_CTYPE_STABLE,
+				AVC_SUBUNIT_PANEL, AVRCP_GET_PLAY_STATUS,
+				pdu, sizeof(pdu));
+}
+
+int avrcp_get_player_values_text_rsp(struct avrcp *session,
+					uint8_t transaction, uint8_t number,
+					uint8_t *values, const char **text)
+{
+	uint8_t pdu[AVRCP_ATTRIBUTE_LAST * (4 + 255)];
+	uint8_t *ptr;
+	uint16_t length;
+	int i;
+
+	if (number > AVRCP_ATTRIBUTE_LAST)
+		return -EINVAL;
+
+	pdu[0] = number;
+	length = 1;
+	for (i = 0, ptr = &pdu[1]; i < number; i++) {
+		uint8_t len = 0;
+
+		if (text[i])
+			len = strlen(text[i]);
+
+		ptr[0] = values[i];
+		put_be16(AVRCP_CHARSET_UTF8, &ptr[1]);
+		ptr[3] = len;
+		memcpy(&ptr[4], text[i], len);
+		ptr += 4 + len;
+		length += 4 + len;
+	}
+
+	return avrcp_send(session, transaction, AVC_CTYPE_STABLE,
+			AVC_SUBUNIT_PANEL, AVRCP_GET_PLAYER_VALUE_TEXT,
+			pdu, length);
+}
+
+int avrcp_get_current_player_value_rsp(struct avrcp *session,
+					uint8_t transaction, uint8_t number,
+					uint8_t *attrs, uint8_t *values)
+{
+	uint8_t pdu[AVRCP_ATTRIBUTE_LAST * 2  + 1];
+	uint8_t *ptr;
+	uint16_t length;
+	int i;
+
+	if (number > AVRCP_ATTRIBUTE_LAST)
+		return -EINVAL;
+
+	pdu[0] = number;
+	length = 1;
+	for (i = 0, ptr = &pdu[1]; i < number; i++) {
+		ptr[0] = attrs[i];
+		ptr[1] = values[i];
+		ptr += 2;
+		length += 2;
+	}
+
+	return avrcp_send(session, transaction, AVC_CTYPE_STABLE,
+			AVC_SUBUNIT_PANEL, AVRCP_GET_CURRENT_PLAYER_VALUE,
+			pdu, length);
+}
+
+int avrcp_get_element_attrs_rsp(struct avrcp *session, uint8_t transaction,
+					uint8_t *params, size_t params_len)
+{
+	return avrcp_send(session, transaction, AVC_CTYPE_STABLE,
+				AVC_SUBUNIT_PANEL, AVRCP_GET_ELEMENT_ATTRIBUTES,
+				params, params_len);
+}
+
+int avrcp_register_notification_rsp(struct avrcp *session, uint8_t transaction,
+					uint8_t code, uint8_t *params,
+					size_t params_len)
+{
+	return avrcp_send(session, transaction, code,
+				AVC_SUBUNIT_PANEL, AVRCP_REGISTER_NOTIFICATION,
+				params, params_len);
+}
+
+int avrcp_set_addressed_player_rsp(struct avrcp *session, uint8_t transaction,
+							uint8_t status)
+{
+	return avrcp_send(session, transaction, AVC_CTYPE_STABLE,
+				AVC_SUBUNIT_PANEL, AVRCP_SET_ADDRESSED_PLAYER,
+				&status, sizeof(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/android/avrcp-lib.h b/bluez/android/avrcp-lib.h
new file mode 100644
index 0000000..d9acb7d
--- /dev/null
+++ b/bluez/android/avrcp-lib.h
@@ -0,0 +1,243 @@
+/*
+ *
+ *  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_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_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
+
+/* Company IDs for vendor dependent commands */
+#define IEEEID_BTSIG		0x001958
+
+/* Parameters legths */
+#define AVRCP_REGISTER_NOTIFICATION_PARAM_LENGTH       5
+
+struct avrcp;
+
+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_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,
+					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_addressed) (struct avrcp *session, uint8_t transaction,
+					uint16_t id, void *user_data);
+};
+
+struct avrcp_control_cfm {
+};
+
+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);
+
+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,
+					uint8_t *params, size_t params_len);
+int avrcp_get_capabilities(struct avrcp *session, uint8_t param,
+					avctp_rsp_cb func, void *user_data);
+int avrcp_register_notification(struct avrcp *session, uint8_t event,
+					uint32_t interval, avctp_rsp_cb func,
+					void *user_data);
+int avrcp_list_player_attributes(struct avrcp *session, avctp_rsp_cb func,
+							void *user_data);
+int avrcp_get_player_attribute_text(struct avrcp *session, uint8_t *attributes,
+					uint8_t attr_len, avctp_rsp_cb func,
+					void *user_data);
+int avrcp_set_player_value(struct avrcp *session, uint8_t *attributes,
+					uint8_t attr_count, uint8_t *values,
+					avctp_rsp_cb func, void *user_data);
+int avrcp_get_current_player_value(struct avrcp *session, uint8_t *attrs,
+					uint8_t attr_count, avctp_rsp_cb func,
+					void *user_data);
+int avrcp_get_play_status(struct avrcp *session, avctp_rsp_cb func,
+							void *user_data);
+int avrcp_set_volume(struct avrcp *session, uint8_t volume, avctp_rsp_cb func,
+							void *user_data);
+int avrcp_get_element_attributes(struct avrcp *session, avctp_rsp_cb func,
+							void *user_data);
+int avrcp_set_addressed_player(struct avrcp *session, uint16_t player_id,
+					avctp_rsp_cb func, void *user_data);
+
+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_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 *params,
+					size_t params_len);
+int avrcp_set_addressed_player_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/android/avrcp.c b/bluez/android/avrcp.c
new file mode 100644
index 0000000..ec98139
--- /dev/null
+++ b/bluez/android/avrcp.c
@@ -0,0 +1,1112 @@
+/*
+ *
+ *  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
+
+#define AVRCP_FEATURE_CATEGORY_1	0x0001
+#define AVRCP_FEATURE_CATEGORY_2	0x0002
+#define AVRCP_FEATURE_CATEGORY_3	0x0004
+#define AVRCP_FEATURE_CATEGORY_4	0x0008
+
+static bdaddr_t adapter_addr;
+static uint32_t record_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 pdu[IPC_MTU];
+	size_t pdu_len;
+	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;
+	}
+
+	pdu[0] = cmd->event;
+	pdu_len = 1;
+
+	switch (cmd->event) {
+	case AVRCP_EVENT_STATUS_CHANGED:
+	case AVRCP_EVENT_TRACK_CHANGED:
+	case AVRCP_EVENT_PLAYBACK_POS_CHANGED:
+		memcpy(&pdu[1], cmd->data, cmd->len);
+		pdu_len += cmd->len;
+		break;
+	default:
+		status = HAL_STATUS_FAILED;
+		goto done;
+	}
+
+	ret = avrcp_register_notification_rsp(req->dev->session,
+						req->transaction, code,
+						pdu, pdu_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 gboolean set_volume_rsp(struct avctp *conn,
+					uint8_t code, uint8_t subunit,
+					uint8_t *operands, size_t operand_count,
+					void *user_data)
+{
+	struct hal_ev_avrcp_volume_changed ev;
+	uint8_t *params;
+
+	if (code != AVC_CTYPE_ACCEPTED) {
+		ev.volume = 0;
+		ev.type = code;
+		goto done;
+	}
+
+	if (operands == NULL || operand_count < 7)
+		return FALSE;
+
+	params = &operands[7];
+
+	ev.volume = params[0] & 0x7F;
+	ev.type = code;
+
+done:
+	ipc_send_notif(hal_ipc, HAL_SERVICE_ID_AVRCP,
+					HAL_EV_AVRCP_VOLUME_CHANGED,
+					sizeof(ev), &ev);
+
+	return FALSE;
+}
+
+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, set_volume_rsp,
+									dev);
+	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_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 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 -EAGAIN;
+}
+
+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 -EAGAIN;
+}
+
+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 -EAGAIN;
+
+}
+
+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 -EAGAIN;
+}
+
+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 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_device *dev = user_data;
+	struct hal_ev_avrcp_volume_changed ev;
+	uint8_t *params;
+
+	if (code != AVC_CTYPE_INTERIM && code != AVC_CTYPE_CHANGED)
+		return FALSE;
+
+	if (operands == NULL || operand_count < 7)
+		return FALSE;
+
+	params = &operands[7];
+
+	if (params == NULL || params[0] != AVRCP_EVENT_VOLUME_CHANGED)
+		return FALSE;
+
+	ev.type = code;
+	ev.volume = params[1] & 0x7F;
+
+	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, params[0], 0,
+					register_notification_rsp, dev);
+	return FALSE;
+}
+
+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_device *dev = user_data;
+	uint8_t *params;
+	uint8_t count;
+
+	if (operands == NULL || operand_count < 7)
+		return FALSE;
+
+	params = &operands[7];
+
+	if (params == NULL || params[0] != CAP_EVENTS_SUPPORTED)
+		return FALSE;
+
+	for (count = params[1]; count > 0; count--) {
+		uint8_t event = params[1 + count];
+
+		if (event != AVRCP_EVENT_VOLUME_CHANGED)
+			continue;
+
+		avrcp_register_notification(dev->session, event, 0,
+						register_notification_rsp,
+						dev);
+		return FALSE;
+	}
+
+	return FALSE;
+}
+
+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, NULL, 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,
+						get_capabilities_rsp, dev);
+
+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 (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 src, dst;
+	GError *err = NULL;
+
+	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);
+		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_record();
+	if (!rec) {
+		error("Failed to allocate AVRCP record");
+		goto fail;
+	}
+
+	if (bt_adapter_add_record(rec, 0) < 0) {
+		error("Failed to register AVRCP record");
+		sdp_record_free(rec);
+		goto fail;
+	}
+	record_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_id);
+	record_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/android/avrcp.h b/bluez/android/avrcp.h
new file mode 100644
index 0000000..11e79b7
--- /dev/null
+++ b/bluez/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/android/bluetooth.c b/bluez/android/bluetooth.c
new file mode 100644
index 0000000..adf7c56
--- /dev/null
+++ b/bluez/android/bluetooth.c
@@ -0,0 +1,3749 @@
+/*
+ *
+ *  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 "src/shared/util.h"
+#include "src/shared/mgmt.h"
+#include "src/uuid-helper.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 DEFAULT_ADAPTER_NAME "BlueZ for Android"
+
+#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 DEVICE_ID_SOURCE	0x0002	/* USB */
+#define DEVICE_ID_VENDOR	0x1d6b	/* Linux Foundation */
+#define DEVICE_ID_PRODUCT	0x0247	/* BlueZ for Android */
+
+#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;
+
+	bool le;
+	bool bredr;
+
+	int bond_state;
+
+	char *name;
+	char *friendly_name;
+
+	uint32_t class;
+	int32_t rssi;
+
+	uint32_t timestamp;
+
+	GSList *uuids;
+
+	bool found; /* if device is found in current discovery session */
+	unsigned int confirm_id; /* mgtm command id if command pending */
+
+};
+
+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;
+
+	uint32_t current_settings;
+
+	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,
+	.current_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 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);
+	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_error_free(gerr);
+		gerr = NULL;
+	}
+
+	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);
+
+	g_key_file_set_integer(key_file, addr, "Timestamp", dev->timestamp);
+
+	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;
+
+	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);
+	new_dev->timestamp = time(NULL);
+	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;
+	} else {
+		dev->le = true;
+		dev->bdaddr_type = bdaddr_type;
+	}
+
+	dev->bond_state = HAL_BOND_STATE_NONE;
+	dev->timestamp = 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  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*/
+}
+
+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(const bdaddr_t *addr, uint8_t status,
+								uint8_t state)
+{
+	struct hal_ev_bond_state_changed ev;
+
+	ev.status = status;
+	ev.state = state;
+	bdaddr2android(addr, ev.bdaddr);
+
+	ipc_send_notif(hal_ipc, HAL_SERVICE_ID_BLUETOOTH,
+				HAL_EV_BOND_STATE_CHANGED, sizeof(ev), &ev);
+}
+
+static void set_device_bond_state(const bdaddr_t *addr, uint8_t status,
+								int state)
+{
+	struct device *dev;
+
+	dev = find_device(addr);
+	if (!dev)
+		return;
+
+	if (dev->bond_state == state)
+		return;
+
+	switch (state) {
+	case HAL_BOND_STATE_NONE:
+		if (dev->bond_state == HAL_BOND_STATE_BONDED) {
+			bonded_devices = g_slist_remove(bonded_devices, dev);
+			remove_device_info(dev, DEVICES_FILE);
+			cache_device(dev);
+		}
+		break;
+	case HAL_BOND_STATE_BONDED:
+		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);
+		break;
+	case HAL_BOND_STATE_BONDING:
+	default:
+		break;
+	}
+
+	dev->bond_state = state;
+
+	send_bond_state_change(&dev->bdaddr, status, state);
+}
+
+static  void send_device_property(const bdaddr_t *bdaddr, 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;
+	bdaddr2android(bdaddr, 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->bdaddr, 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->bond_state == HAL_BOND_STATE_BONDED)
+		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 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;
+	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;
+	}
+
+	set_device_bond_state(&addr->bdaddr, HAL_STATUS_SUCCESS,
+							HAL_BOND_STATE_BONDED);
+
+	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->bdaddr, 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);
+
+	set_device_bond_state(&ev->addr.bdaddr, HAL_STATUS_SUCCESS,
+						HAL_BOND_STATE_BONDING);
+
+	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));
+	bdaddr2android(&ev->addr.bdaddr, 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(const bdaddr_t *addr, uint8_t variant,
+							uint32_t passkey)
+{
+	struct hal_ev_ssp_request ev;
+
+	/* It is ok to have empty name and CoD of remote devices here since
+	* those information has been already provided on device_connected event
+	* or during device scaning. Android will use that instead.
+	*/
+	memset(&ev, 0, sizeof(ev));
+	bdaddr2android(addr, ev.bdaddr);
+	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;
+	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);
+
+	set_device_bond_state(&ev->addr.bdaddr, HAL_STATUS_SUCCESS,
+						HAL_BOND_STATE_BONDING);
+
+	if (ev->confirm_hint)
+		send_ssp_request(&ev->addr.bdaddr, HAL_SSP_VARIANT_CONSENT, 0);
+	else
+		send_ssp_request(&ev->addr.bdaddr, 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;
+	char dst[18];
+
+	if (length < sizeof(*ev)) {
+		error("Too small passkey request event");
+		return;
+	}
+
+	ba2str(&ev->addr.bdaddr, dst);
+	DBG("%s", dst);
+
+	set_device_bond_state(&ev->addr.bdaddr, HAL_STATUS_SUCCESS,
+						HAL_BOND_STATE_BONDING);
+
+	send_ssp_request(&ev->addr.bdaddr, 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;
+	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;
+
+	set_device_bond_state(&ev->addr.bdaddr, HAL_STATUS_SUCCESS,
+						HAL_BOND_STATE_BONDING);
+
+	send_ssp_request(&ev->addr.bdaddr, 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_adapter_discovering_type(void)
+{
+	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 bool start_discovery(uint8_t type)
+{
+	struct mgmt_cp_start_discovery cp;
+
+	cp.type = get_adapter_discovering_type() & type;
+
+	DBG("type=0x%x", cp.type);
+
+	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;
+}
+
+static void mgmt_discovering_event(uint16_t index, uint16_t length,
+					const void *param, void *user_data)
+{
+	const struct mgmt_ev_discovering *ev = param;
+	struct hal_ev_discovery_state_changed cp;
+	bool is_discovering = adapter.cur_discovery_type;
+
+	if (length < sizeof(*ev)) {
+		error("Too small discovering event");
+		return;
+	}
+
+	DBG("hci%u type %u discovering %u", index, ev->type,
+							ev->discovering);
+
+	if (is_discovering == !!ev->discovering)
+		return;
+
+	adapter.cur_discovery_type = ev->discovering ?
+						ev->type : SCAN_TYPE_NONE;
+
+	DBG("new discovering state %u", ev->discovering);
+
+	if (adapter.cur_discovery_type != SCAN_TYPE_NONE) {
+		cp.state = HAL_DISCOVERY_STATE_STARTED;
+	} else {
+		g_slist_foreach(bonded_devices, clear_device_found, NULL);
+		g_slist_foreach(cached_devices, clear_device_found, NULL);
+		cp.state = HAL_DISCOVERY_STATE_STOPPED;
+	}
+
+	ipc_send_notif(hal_ipc, HAL_SERVICE_ID_BLUETOOTH,
+			HAL_EV_DISCOVERY_STATE_CHANGED, sizeof(cp), &cp);
+
+	if (gatt_discovery_stopped_cb &&
+			(adapter.cur_discovery_type == SCAN_TYPE_NONE)) {
+		/* One shot notification about discovery stopped send to gatt*/
+		gatt_discovery_stopped_cb();
+		gatt_discovery_stopped_cb = NULL;
+	}
+
+	/* If discovery is ON or there is no expected next discovery session
+	 * then just return
+	 */
+	if ((adapter.cur_discovery_type != SCAN_TYPE_NONE) ||
+		(adapter.exp_discovery_type == SCAN_TYPE_NONE))
+		return;
+
+	start_discovery(adapter.exp_discovery_type);
+
+	/* Maintain expected discovery type if there is gatt client
+	 * registered
+	 */
+	adapter.exp_discovery_type = gatt_device_found_cb ?
+						SCAN_TYPE_LE : SCAN_TYPE_NONE;
+}
+
+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;
+	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;
+}
+
+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;
+	bdaddr_t android_bdaddr;
+	uint8_t android_type;
+	int size;
+
+	memset(buf, 0, sizeof(buf));
+
+	if (adapter.cur_discovery_type)
+		dev->found = true;
+
+	size = sizeof(*ev);
+
+	bdaddr2android(&dev->bdaddr, &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++;
+	}
+
+	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 bdaddr_type)
+{
+	uint8_t buf[IPC_MTU];
+	struct hal_ev_remote_device_props *ev = (void *) buf;
+	uint8_t android_type;
+	int size;
+
+	memset(buf, 0, sizeof(buf));
+
+	size = sizeof(*ev);
+
+	ev->status = HAL_STATUS_SUCCESS;
+	bdaddr2android(&dev->bdaddr, ev->bdaddr);
+
+	if (dev->bdaddr_type != bdaddr_type) {
+		bool type_changed = false;
+
+		dev->bdaddr_type = bdaddr_type;
+		if (bdaddr_type == BDADDR_BREDR) {
+			type_changed = !dev->bredr;
+			dev->bredr = true;
+		} else {
+			type_changed = !dev->le;
+			dev->le = true;
+		}
+
+		if (type_changed) {
+			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) {
+		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++;
+	}
+
+	if (ev->num_props)
+		ipc_send_notif(hal_ipc, HAL_SERVICE_ID_BLUETOOTH,
+					HAL_EV_REMOTE_DEVICE_PROPS, size, buf);
+}
+
+static void update_found_device(const bdaddr_t *bdaddr, uint8_t bdaddr_type,
+					int8_t rssi, bool confirm,
+					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 = find_device(bdaddr);
+
+	if (bdaddr_type != BDADDR_BREDR) {
+		/* Notify Gatt if its registered for LE events */
+		if (gatt_device_found_cb)
+			gatt_device_found_cb(bdaddr, bdaddr_type, rssi,
+							sizeof(eir), &eir);
+
+		if (!dev && adapter.cur_discovery_type != SCAN_TYPE_NONE &&
+				!(eir.flags & (EIR_LIM_DISC | EIR_GEN_DISC))) {
+			eir_data_free(&eir);
+			return;
+		}
+	}
+
+	/* Device found event needs to be send also for known device if this is
+	 * new discovery session. Otherwise framework will ignore it.
+	 */
+	if (!dev || !dev->found) {
+		if (!dev)
+			dev = create_device(bdaddr, bdaddr_type);
+
+		update_new_device(dev, rssi, &eir);
+	} else {
+		update_device(dev, rssi, &eir, bdaddr_type);
+	}
+
+	eir_data_free(&eir);
+
+	if (dev->bond_state != HAL_BOND_STATE_BONDED)
+		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;
+	char addr[18];
+
+	if (length < sizeof(*ev)) {
+		error("Too short device found event (%u bytes)", length);
+		return;
+	}
+
+	eir_len = btohs(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 = 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;
+
+	update_found_device(&ev->addr.bdaddr, ev->addr.type, ev->rssi,
+						confirm_name, 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;
+
+	if (length < sizeof(*ev)) {
+		error("Too short device connected event (%u bytes)", length);
+		return;
+	}
+
+	update_found_device(&ev->addr.bdaddr, ev->addr.type, 0, false,
+					&ev->eir[0], btohs(ev->eir_len));
+
+	hal_ev.status = HAL_STATUS_SUCCESS;
+	hal_ev.state = HAL_ACL_STATE_CONNECTED;
+	bdaddr2android(&ev->addr.bdaddr, hal_ev.bdaddr);
+
+	ipc_send_notif(hal_ipc, HAL_SERVICE_ID_BLUETOOTH,
+			HAL_EV_ACL_STATE_CHANGED, sizeof(hal_ev), &hal_ev);
+}
+
+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;
+
+	if (length < sizeof(*ev)) {
+		error("Too short device disconnected event (%u bytes)", length);
+		return;
+	}
+
+	hal_ev.status = HAL_STATUS_SUCCESS;
+	hal_ev.state = HAL_ACL_STATE_DISCONNECTED;
+	bdaddr2android(&ev->addr.bdaddr, hal_ev.bdaddr);
+
+	ipc_send_notif(hal_ipc, HAL_SERVICE_ID_BLUETOOTH,
+			HAL_EV_ACL_STATE_CHANGED, sizeof(hal_ev), &hal_ev);
+}
+
+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);
+
+	/* 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 && dev->bond_state == HAL_BOND_STATE_BONDING)
+		set_device_bond_state(&ev->addr.bdaddr,
+						status_mgmt2hal(ev->status),
+						HAL_BOND_STATE_NONE);
+}
+
+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 && dev->bond_state == HAL_BOND_STATE_BONDING)
+		set_device_bond_state(&ev->addr.bdaddr,
+						status_mgmt2hal(ev->status),
+						HAL_BOND_STATE_NONE);
+}
+
+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;
+
+	if (length < sizeof(*ev)) {
+		error("Too small device unpaired event (%u bytes)", length);
+		return;
+	}
+
+	DBG("");
+
+	/* TODO should device be disconnected ? */
+
+	set_device_bond_state(&ev->addr.bdaddr, HAL_STATUS_SUCCESS,
+							HAL_BOND_STATE_NONE);
+}
+
+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;
+	const struct mgmt_addr_info *addr = &ev->key.addr;
+	char dst[18];
+
+	if (length < sizeof(*ev)) {
+		error("Too small long term key event (%u bytes)", length);
+		return;
+	}
+
+	ba2str(&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);
+
+	set_device_bond_state(&addr->bdaddr, HAL_STATUS_SUCCESS,
+							HAL_BOND_STATE_BONDED);
+
+	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 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);
+}
+
+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 = htobs(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_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 = htobs(ltk_count);
+
+	for (l = ltks, ltk = cp->keys; l != NULL; l = g_slist_next(l), ltk++)
+		memcpy(ltk, ltks->data, sizeof(*ltk));
+
+	if (mgmt_send(mgmt_if, MGMT_OP_LOAD_LONG_TERM_KEYS, adapter.index,
+					cp_size, cp, NULL, NULL, NULL) == 0)
+		error("Failed to load LTKs");
+
+	g_free(cp);
+}
+
+static uint8_t get_adapter_uuids(void)
+{
+	struct hal_ev_adapter_props_changed *ev;
+	GSList *list = adapter.uuids;
+	unsigned int uuid_count = g_slist_length(list);
+	int 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;
+	uint8_t major, minor;
+	uint16_t version;
+
+	if (sscanf(VERSION, "%hhu.%hhu", &major, &minor) != 2)
+		return;
+
+	version = major << 8 | minor;
+
+	memset(&cp, 0, sizeof(cp));
+	cp.source = htobs(DEVICE_ID_SOURCE);
+	cp.vendor = htobs(DEVICE_ID_VENDOR);
+	cp.product = htobs(DEVICE_ID_PRODUCT);
+	cp.version = htobs(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(DEVICE_ID_SOURCE, DEVICE_ID_VENDOR,
+						DEVICE_ID_PRODUCT, 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;
+
+	DBG("%s", peer);
+
+	/* 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, "LinkKey", NULL);
+	if (str) {
+		g_free(str);
+		dev->bond_state = HAL_BOND_STATE_BONDED;
+	}
+
+	str = g_key_file_get_string(key_file, peer, "LongTermKey", NULL);
+	if (str) {
+		g_free(str);
+		dev->bond_state = HAL_BOND_STATE_BONDED;
+	}
+
+	str = g_key_file_get_string(key_file, peer, "SlaveLongTermKey", NULL);
+	if (str) {
+		g_free(str);
+		dev->bond_state = HAL_BOND_STATE_BONDED;
+	}
+
+	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);
+
+	dev->timestamp = 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);
+
+	info->addr.type = g_key_file_get_integer(key_file, peer, "Type", NULL);
+
+	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, "Type", 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 int device_timestamp_cmp(gconstpointer  a, gconstpointer  b)
+{
+	const struct device *deva = a;
+	const struct device *devb = b;
+
+	return deva->timestamp < devb->timestamp;
+}
+
+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;
+
+	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_ltk_info *slave_ltk_info;
+		struct device *dev;
+
+		key_info = get_key_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);
+
+		if (!key_info && !ltk_info && !slave_ltk_info) {
+			error("Failed to load keys for %s, skipping", devs[i]);
+
+			continue;
+		}
+
+		if (key_info)
+			keys = g_slist_prepend(keys, key_info);
+
+		if (ltk_info)
+			ltks = g_slist_prepend(ltks, ltk_info);
+
+		if (slave_ltk_info)
+			ltks = g_slist_prepend(ltks, slave_ltk_info);
+
+		dev = create_device_from_info(key_file, devs[i]);
+
+		bonded_devices = g_slist_prepend(bonded_devices, dev);
+	}
+
+	load_ltks(ltks);
+	g_slist_free_full(ltks, 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 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, supported_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);
+		adapter.name = g_strdup(DEFAULT_ADAPTER_NAME);
+		store_adapter_config();
+	} else if (bacmp(&adapter.bdaddr, &rp->bdaddr)) {
+		error("Bluetooth address mismatch");
+		err = -ENODEV;
+		goto failed;
+	}
+
+	if (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);
+
+	supported_settings = btohs(rp->supported_settings);
+	adapter.current_settings = btohs(rp->current_settings);
+
+	/* TODO: Register all event notification handlers */
+	register_mgmt_handlers();
+
+	clear_uuids();
+
+	set_io_capability();
+	set_device_id();
+
+	missing_settings = adapter.current_settings ^ supported_settings;
+
+	if (missing_settings & MGMT_SETTING_SSP)
+		set_mode(MGMT_OP_SET_SSP, 0x01);
+
+	if (missing_settings & MGMT_SETTING_SECURE_CONN)
+		set_mode(MGMT_OP_SET_SECURE_CONN, 0x01);
+
+	if (missing_settings & MGMT_SETTING_PAIRABLE)
+		set_mode(MGMT_OP_SET_PAIRABLE, 0x01);
+
+	if (missing_settings & MGMT_SETTING_LE)
+		set_mode(MGMT_OP_SET_LE, 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 = btohs(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 = btohs(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 = btohs(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;
+	}
+
+	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 = htobs(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;
+
+		bdaddr2android(&dev->bdaddr, 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 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;
+	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)
+{
+	get_adapter_address();
+	get_adapter_name();
+	get_adapter_uuids();
+	get_adapter_class();
+	get_adapter_type();
+	get_adapter_service_rec();
+	get_adapter_scan_mode();
+	get_adapter_bonded_devices();
+	get_adapter_discoverable_timeout();
+}
+
+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_adapter_discovering_type() & type;
+
+	DBG("type=0x%x", cp.type);
+
+	/* 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;
+}
+
+bool bt_le_discovery_stop(bt_le_discovery_stopped cb)
+{
+	if (!adapter.cur_discovery_type) {
+		if (cb)
+			cb();
+		return true;
+	}
+
+	gatt_discovery_stopped_cb = cb;
+	/* Remove device found callback */
+	gatt_device_found_cb = NULL;
+	adapter.exp_discovery_type &= ~SCAN_TYPE_LE;
+
+	return stop_discovery(adapter.cur_discovery_type);
+}
+
+bool bt_le_discovery_start(bt_le_device_found cb)
+{
+	if (!(adapter.current_settings & MGMT_SETTING_POWERED))
+		return false;
+
+	gatt_device_found_cb = cb;
+
+	adapter.exp_discovery_type |= SCAN_TYPE_LE;
+
+	/* If core is discovering, don't bother */
+	if (adapter.cur_discovery_type)
+		return true;
+
+	return start_discovery(adapter.exp_discovery_type);
+}
+
+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;
+
+	DBG("status %u", status);
+
+	/* On success bond state change will be send when new link key or LTK
+	 * event is received */
+	if (status == MGMT_STATUS_SUCCESS)
+		return;
+
+	set_device_bond_state(&rp->addr.bdaddr, status_mgmt2hal(status),
+							HAL_BOND_STATE_NONE);
+}
+
+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;
+
+	cp.io_cap = DEFAULT_IO_CAPABILITY;
+	android2bdaddr(cmd->bdaddr, &cp.addr.bdaddr);
+
+	dev = find_device(&cp.addr.bdaddr);
+
+	if (dev) {
+		if (dev->bond_state != HAL_BOND_STATE_NONE) {
+			status = HAL_STATUS_FAILED;
+			goto fail;
+		}
+
+		cp.addr.type = dev->bredr ? BDADDR_BREDR : dev->bdaddr_type;
+	} else {
+		/* Fallback to BREDR if device is unknown eg. OOB */
+		cp.addr.type = BDADDR_BREDR;
+	}
+
+	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;
+
+	set_device_bond_state(&cp.addr.bdaddr, HAL_STATUS_SUCCESS,
+						HAL_BOND_STATE_BONDING);
+
+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;
+
+	android2bdaddr(cmd->bdaddr, &cp.bdaddr);
+
+	dev = find_device(&cp.bdaddr);
+	if (!dev) {
+		status = HAL_STATUS_FAILED;
+		goto failed;
+	}
+
+	cp.type = dev->bredr ? BDADDR_BREDR : dev->bdaddr_type;
+
+
+	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 unpair_device_complete(uint8_t status, uint16_t length,
+					const void *param, void *user_data)
+{
+	const struct mgmt_rp_unpair_device *rp = param;
+
+	DBG("status %u", status);
+
+	if (status != MGMT_STATUS_SUCCESS && status != MGMT_STATUS_NOT_PAIRED)
+		return;
+
+	set_device_bond_state(&rp->addr.bdaddr, HAL_STATUS_SUCCESS,
+							HAL_BOND_STATE_NONE);
+}
+
+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;
+
+	cp.disconnect = 1;
+	android2bdaddr(cmd->bdaddr, &cp.addr.bdaddr);
+
+	dev = find_device(&cp.addr.bdaddr);
+	if (!dev) {
+		status = HAL_STATUS_FAILED;
+		goto failed;
+	}
+
+	cp.addr.type = dev->bredr ? BDADDR_BREDR : 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;
+	}
+
+	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, 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 = BDADDR_BREDR;
+
+	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, 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 = BDADDR_BREDR;
+		cp.passkey = htobl(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 = BDADDR_BREDR;
+
+		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;
+	bdaddr_t bdaddr;
+	uint8_t status;
+	char addr[18];
+
+	/* TODO should parameters sanity be verified here? */
+
+	android2bdaddr(cmd->bdaddr, &bdaddr);
+	ba2str(&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(&bdaddr, cmd->accept);
+		break;
+	case HAL_SSP_VARIANT_ENTRY:
+		status = user_passkey_reply(&bdaddr, 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->bdaddr, 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->bdaddr, 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->bdaddr, 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->bdaddr, 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)
+{
+	send_device_property(&dev->bdaddr, HAL_PROP_DEVICE_TIMESTAMP,
+				sizeof(dev->timestamp), &dev->timestamp);
+
+	return HAL_STATUS_SUCCESS;
+}
+
+static void get_remote_device_props(struct device *dev)
+{
+	get_device_name(dev);
+	get_device_uuids(dev);
+	get_device_class(dev);
+	get_device_type(dev);
+	get_device_service_rec(dev);
+	get_device_friendly_name(dev);
+	get_device_rssi(dev);
+	get_device_version_info(dev);
+	get_device_timestamp(dev);
+}
+
+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;
+	bdaddr_t addr;
+
+	android2bdaddr(cmd->bdaddr, &addr);
+
+	dev = find_device(&addr);
+	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;
+	bdaddr_t addr;
+
+	android2bdaddr(cmd->bdaddr, &addr);
+
+	dev = find_device(&addr);
+	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->bond_state == HAL_BOND_STATE_BONDED)
+		store_device_info(dev, DEVICES_FILE);
+	else
+		store_device_info(dev, CACHE_FILE);
+
+	send_device_property(&dev->bdaddr, HAL_PROP_DEVICE_FRIENDLY_NAME,
+				strlen(dev->friendly_name), dev->friendly_name);
+
+	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;
+	bdaddr_t addr;
+
+	if (len != sizeof(*cmd) + cmd->len) {
+		error("Invalid set remote device prop cmd (0x%x), terminating",
+								cmd->type);
+		raise(SIGTERM);
+		return;
+	}
+
+	android2bdaddr(cmd->bdaddr, &addr);
+
+	dev = find_device(&addr);
+	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)
+{
+	/* TODO */
+
+	error("get_remote_service_record not supported");
+
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_BLUETOOTH,
+			HAL_OP_GET_REMOTE_SERVICE_REC, HAL_STATUS_FAILED);
+}
+
+static void handle_start_discovery_cmd(const void *buf, uint16_t len)
+{
+	uint8_t status;
+
+	/* Check if there is discovery with BREDR type */
+	if (adapter.cur_discovery_type & SCAN_TYPE_BREDR) {
+		status = HAL_STATUS_SUCCESS;
+		goto reply;
+	}
+
+	if (!(adapter.current_settings & MGMT_SETTING_POWERED)) {
+		status = HAL_STATUS_NOT_READY;
+		goto reply;
+	}
+
+	adapter.exp_discovery_type |= SCAN_TYPE_DUAL;
+
+	/* If there is no discovery ongoing, try to start discovery */
+	if (!adapter.cur_discovery_type) {
+		if (!start_discovery(adapter.exp_discovery_type))
+			status = HAL_STATUS_FAILED;
+		else
+			status = HAL_STATUS_SUCCESS;
+
+		goto reply;
+	}
+
+	/* Stop discovery here. Once it is stop we will restart it
+	 * with exp_discovery_settings */
+	if (!stop_discovery(adapter.cur_discovery_type)) {
+		status = HAL_STATUS_FAILED;
+		goto reply;
+	}
+
+	status = HAL_STATUS_SUCCESS;
+
+reply:
+	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.cur_discovery_type) {
+		status = HAL_STATUS_SUCCESS;
+		goto reply;
+	}
+
+	if (!(adapter.current_settings & MGMT_SETTING_POWERED)) {
+		status = HAL_STATUS_NOT_READY;
+		goto reply;
+	}
+
+	/* Take into account that gatt might want to keep discover */
+	adapter.exp_discovery_type = gatt_device_found_cb ? SCAN_TYPE_LE : 0;
+
+	if (!stop_discovery(adapter.cur_discovery_type)) {
+		status = HAL_STATUS_FAILED;
+		goto reply;
+	}
+
+	status = HAL_STATUS_SUCCESS;
+
+reply:
+	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 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) },
+};
+
+void bt_bluetooth_register(struct ipc *ipc, uint8_t mode)
+{
+	DBG("");
+
+	hal_ipc = ipc;
+
+	ipc_register(hal_ipc, HAL_SERVICE_ID_BLUETOOTH, cmd_handlers,
+						G_N_ELEMENTS(cmd_handlers));
+}
+
+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;
+}
diff --git a/bluez/android/bluetooth.h b/bluez/android/bluetooth.h
new file mode 100644
index 0000000..8dbc623
--- /dev/null
+++ b/bluez/android/bluetooth.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
+ *
+ */
+
+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);
+
+void 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, uint8_t addr_type,
+					int rssi, uint16_t eir_len,
+					const void *eir);
+bool bt_le_discovery_start(bt_le_device_found cb);
+
+typedef void (*bt_le_discovery_stopped)(void);
+bool bt_le_discovery_stop(bt_le_discovery_stopped cb);
diff --git a/bluez/android/bluetoothd-snoop.c b/bluez/android/bluetoothd-snoop.c
new file mode 100644
index 0000000..844de83
--- /dev/null
+++ b/bluez/android/bluetoothd-snoop.c
@@ -0,0 +1,250 @@
+/*
+ *
+ *  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 <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 "monitor/mainloop.h"
+#include "src/shared/btsnoop.h"
+
+#define DEFAULT_SNOOP_FILE "/sdcard/btsnoop_hci.log"
+
+#define MAX_PACKET_SIZE (1486 + 4)
+
+static struct btsnoop *snoop = NULL;
+static uint8_t monitor_buf[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;
+	}
+
+	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_TYPE_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;
+
+	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) {
+		printf("Failed to start bluetoothd_snoop\n");
+		return EXIT_FAILURE;
+	}
+
+	mainloop_run();
+
+	close_monitor();
+
+	return EXIT_SUCCESS;
+}
diff --git a/bluez/android/client/haltest.c b/bluez/android/client/haltest.c
new file mode 100644
index 0000000..114fe31
--- /dev/null
+++ b/bluez/android/client/haltest.c
@@ -0,0 +1,440 @@
+/*
+ * 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 <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"
+
+const struct interface *interfaces[] = {
+	&audio_if,
+	&bluetooth_if,
+	&av_if,
+	&rc_if,
+	&gatt_if,
+	&gatt_client_if,
+	&gatt_server_if,
+	&hf_if,
+	&hh_if,
+	&pan_if,
+	&sock_if,
+	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)
+{
+	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[10];
+	int argc;
+	int i = 0;
+	struct method *m;
+
+	argc = command_line_to_argv(line_buffer, argv, 10);
+	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-n, --no-init          Don't call init for interfaces\n"
+		"\t    --version          Print version\n"
+		"\t-h, --help             Show help options\n");
+}
+
+enum {
+	PRINT_VERSION = 1000
+};
+
+int version = 1;
+int revision = 0;
+
+static void print_version(void)
+{
+	printf("haltest version %d.%d\n", version, revision);
+}
+
+static const struct option main_options[] = {
+	{ "no-init", no_argument, NULL, 'n' },
+	{ "help",    no_argument, NULL, 'h' },
+	{ "version", no_argument, NULL, PRINT_VERSION },
+	{ NULL }
+};
+
+static bool no_init = false;
+
+static void parse_command_line(int argc, char *argv[])
+{
+	for (;;) {
+		int opt;
+
+		opt = getopt_long(argc, argv, "nh", main_options, NULL);
+		if (opt < 0)
+			break;
+
+		switch (opt) {
+		case 'n':
+			no_init = true;
+			break;
+		case 'h':
+			usage();
+			exit(0);
+		case PRINT_VERSION:
+			print_version();
+			exit(0);
+		default:
+			putchar('\n');
+			exit(-1);
+			break;
+		}
+	}
+}
+
+static void init(void)
+{
+	static const char * const inames[] = {
+		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
+	};
+	const struct method *m;
+	const char *argv[4];
+	char init_audio[] = "audio init";
+	char init_bt[] = "bluetooth init";
+	uint32_t i;
+
+	process_line(init_audio);
+	process_line(init_bt);
+
+	m = get_interface_method("bluetooth", "get_profile_interface");
+
+	for (i = 0; i < NELEM(inames); ++i) {
+		argv[2] = inames[i];
+		m->func(3, argv);
+	}
+
+	/* Init what is available to init */
+	for (i = 2; 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)
+		init();
+
+	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/android/client/history.c b/bluez/android/client/history.c
new file mode 100644
index 0000000..ee285da
--- /dev/null
+++ b/bluez/android/client/history.c
@@ -0,0 +1,100 @@
+/*
+ * 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 20
+#define LINE_SIZE 100
+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/android/client/history.h b/bluez/android/client/history.h
new file mode 100644
index 0000000..26085b5
--- /dev/null
+++ b/bluez/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/android/client/if-audio.c b/bluez/android/client/if-audio.c
new file mode 100644
index 0000000..ce9e534
--- /dev/null
+++ b/bluez/android/client/if-audio.c
@@ -0,0 +1,522 @@
+/*
+ * 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"
+#include "pthread.h"
+#include "unistd.h"
+#include <math.h>
+
+audio_hw_device_t *if_audio = NULL;
+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),
+	DELEMENT(AUDIO_CHANNEL_OUT_SURROUND),
+	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 void playthread_cleanup(void *arg)
+{
+	pthread_mutex_lock(&state_mutex);
+	current_state = STATE_STOPPED;
+	pthread_mutex_unlock(&state_mutex);
+
+	haltest_info("Done playing.\n");
+}
+
+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;
+
+	pthread_cleanup_push(playthread_cleanup, NULL);
+
+	/* 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_cleanup_pop(1);
+	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);
+
+	err = if_audio->open_output_stream(if_audio,
+						0,
+						AUDIO_DEVICE_OUT_ALL_A2DP,
+						AUDIO_OUTPUT_FLAG_NONE,
+						NULL,
+						&stream_out);
+	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/android/client/if-av.c b/bluez/android/client/if-av.c
new file mode 100644
index 0000000..8d1f69b
--- /dev/null
+++ b/bluez/android/client/if-av.c
@@ -0,0 +1,131 @@
+/*
+ * 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));
+}
+
+static btav_callbacks_t av_cbacks = {
+	.size = sizeof(av_cbacks),
+	.connection_state_cb = connection_state,
+	.audio_state_cb = audio_state
+};
+
+/* 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/android/client/if-bt.c b/bluez/android/client/if-bt.c
new file mode 100644
index 0000000..8dcffea
--- /dev/null
+++ b/bluez/android/client/if-bt.c
@@ -0,0 +1,850 @@
+/*
+ * 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 "terminal.h"
+#include "../hal-utils.h"
+
+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) {
+		remote_devices_capacity *= 2;
+		remote_devices = realloc(remote_devices, sizeof(bt_bdaddr_t) *
+						remote_devices_capacity);
+		if (remote_devices == NULL) {
+			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);
+	}
+}
+
+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);
+
+	if (pairing_variant == 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);
+	}
+}
+
+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);
+}
+
+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
+};
+
+static void init_p(int argc, const char **argv)
+{
+	int err;
+	const hw_module_t *module;
+	hw_device_t *device;
+
+	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, &device);
+	if (err) {
+		haltest_error("module->methods->open returned %d\n", err);
+		return;
+	}
+
+	if_bluetooth =
+		    ((bluetooth_device_t *) device)->get_bluetooth_interface();
+	if (!if_bluetooth) {
+		haltest_error("get_bluetooth_interface returned NULL\n");
+		return;
+	}
+
+	EXEC(if_bluetooth->init, &bt_callbacks);
+}
+
+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);
+}
+
+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;
+
+	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;
+	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;
+
+	RETURN_IF_NULL(if_bluetooth);
+	VERIFY_ADDR_ARG(2, &addr);
+
+	EXEC(if_bluetooth->create_bond, &addr);
+}
+
+/* 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,
+		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;
+	const void *dummy = 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 = &dummy; /* TODO: change when if_hl is there */
+	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;
+	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),
+	STD_METHODCH(create_bond, "<addr>"),
+	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/android/client/if-gatt.c b/bluez/android/client/if-gatt.c
new file mode 100644
index 0000000..da2f299
--- /dev/null
+++ b/bluez/android/client/if-gatt.c
@@ -0,0 +1,1753 @@
+/*
+ * 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 "../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)
+
+#define VERIFY_INT_ARG(n, v, err) \
+	do { \
+		if (n < argc) \
+			v = atoi(argv[n]); \
+		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_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)
+
+/* 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=%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);
+}
+
+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
+};
+
+/* 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=%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=%x inc_srvc_handle=%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=%x char_handle=%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=%x descr_handle=%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=%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=%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=%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=%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=%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=%x\n", __func__, status, handle);
+}
+
+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
+};
+
+static const btgatt_callbacks_t gatt_cbacks = {
+	.size = sizeof(gatt_cbacks),
+	.client = &btgatt_client_callbacks,
+	.server = &btgatt_server_callbacks
+};
+
+/* 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)
+{
+	int client_if;
+	int start = 1;
+
+	RETURN_IF_NULL(if_gatt);
+
+	VERIFY_CLIENT_IF(2, client_if);
+
+	/* start */
+	if (argc >= 4)
+		start = atoi(argv[3]);
+
+	EXEC(if_gatt->client->scan, client_if, start);
+}
+
+/* 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;
+
+	RETURN_IF_NULL(if_gatt);
+	VERIFY_CLIENT_IF(2, client_if);
+	VERIFY_ADDR_ARG(3, &bd_addr);
+
+	/* is_direct */
+	if (argc > 4)
+		is_direct = atoi(argv[4]);
+
+	EXEC(if_gatt->client->connect, client_if, &bd_addr, is_direct);
+}
+
+/* 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);
+}
+
+/* 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 = atoi(argv[5]);
+
+	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 = atoi(argv[5]);
+
+	/* value */
+	if (argc <= 6) {
+		haltest_error("No value specified\n");
+		return;
+	}
+
+	/* len in chars */
+	len = strlen(argv[6]);
+	scan_field(argv[6], len, value, sizeof(value));
+	/* len in bytes converted from ascii chars */
+	len = (len + 1) / 2;
+
+	/* auth_req */
+	if (argc > 7)
+		auth_req = atoi(argv[7]);
+
+	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 = atoi(argv[6]);
+
+	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 = atoi(argv[6]);
+
+	/* value */
+	if (argc <= 7) {
+		haltest_error("No value specified\n");
+		return;
+	}
+
+	/* len in chars */
+	len = strlen(argv[7]);
+	scan_field(argv[7], len, value, sizeof(value));
+	/* len in bytes converted from ascii chars */
+	len = (len + 1) / 2;
+
+	/* auth_req */
+	if (argc > 8)
+		auth_req = atoi(argv[8]);
+
+	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 = atoi(argv[3]);
+
+	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);
+}
+
+/* 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 = atoi(argv[2]);
+
+	VERIFY_ADDR_ARG(3, &bd_addr);
+	VERIFY_UUID(4, &uuid);
+
+	for (i = 5; i < argc; i++)
+		*u++ = atoi(argv[i]);
+
+	EXEC(if_gatt->client->test_command, command, &params);
+}
+
+static struct method client_methods[] = {
+	STD_METHODH(register_client, "[<uuid>]"),
+	STD_METHODCH(unregister_client, "<client_if>"),
+	STD_METHODCH(scan, "<client_if> [1|0]"),
+	STD_METHODCH(connect, "<client_if> <addr> [<is_direct>]"),
+	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]"),
+	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;
+
+	RETURN_IF_NULL(if_gatt);
+	VERIFY_SERVER_IF(2, server_if);
+	VERIFY_ADDR_ARG(3, &bd_addr);
+
+	/* is_direct */
+	if (argc > 4)
+		is_direct = atoi(argv[4]);
+
+	EXEC(if_gatt->server->connect, server_if, &bd_addr, is_direct);
+}
+
+/* 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 = atoi(argv[4]);
+
+	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 = atoi(argv[5]);
+
+	/* permissions */
+	if (argc <= 6) {
+		haltest_error("No permissions specified\n");
+		return;
+	}
+	permissions = atoi(argv[6]);
+
+	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 = atoi(argv[5]);
+
+	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 = atoi(argv[4]);
+
+	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 = atoi(argv[5]);
+
+	if (argc > 6) {
+		len = strlen(argv[6]);
+		scan_field(argv[6], len, (uint8_t *) data, sizeof(data));
+	}
+
+	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[])
+{
+	haltest_warn("%s is not implemented yet\n", __func__);
+}
+
+#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>"),
+	GATTS_METHODCH(connect, "<server_if> <addr> [<is_direct>]"),
+	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> <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>"),
+	END_METHOD
+};
+
+const struct interface gatt_server_if = {
+	.name = "gatts",
+	.methods = server_methods
+};
diff --git a/bluez/android/client/if-hf.c b/bluez/android/client/if-hf.c
new file mode 100644
index 0000000..d0e7a66
--- /dev/null
+++ b/bluez/android/client/if-hf.c
@@ -0,0 +1,785 @@
+/*
+ * 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
+
+/* 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
+ */
+static void vr_cmd_cb(bthf_vr_state_t state)
+{
+	haltest_info("%s: state=%s\n", __func__, bthf_vr_state_t2str(state));
+}
+
+/* Callback for answer incoming call (ATA) */
+static void answer_call_cmd_cb(void)
+{
+	haltest_info("%s\n", __func__);
+}
+
+/* Callback for disconnect call (AT+CHUP) */
+static void hangup_call_cmd_cb(void)
+{
+	haltest_info("%s\n", __func__);
+}
+
+/*
+ * Callback for disconnect call (AT+CHUP)
+ * type will denote Speaker/Mic gain (BtHfVolumeControl).
+ */
+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);
+}
+
+/*
+ * Callback for dialing an outgoing call
+ * If number is NULL, redial
+ */
+static void dial_call_cmd_cb(char *number)
+{
+	haltest_info("%s: number=%s\n", __func__, number);
+}
+
+/*
+ * Callback for sending DTMF tones
+ * tone contains the dtmf character to be sent
+ */
+static void dtmf_cmd_cb(char tone)
+{
+	haltest_info("%s: tone=%d\n", __func__, tone);
+}
+
+/*
+ * Callback for enabling/disabling noise reduction/echo cancellation
+ * value will be 1 to enable, 0 to disable
+ */
+static void nrec_cmd_cb(bthf_nrec_t nrec)
+{
+	haltest_info("%s: nrec=%s\n", __func__, bthf_nrec_t2str(nrec));
+}
+
+/*
+ * Callback for call hold handling (AT+CHLD)
+ * value will contain the call hold command (0, 1, 2, 3)
+ */
+static void chld_cmd_cb(bthf_chld_type_t chld)
+{
+	haltest_info("%s: chld=%s\n", __func__, bthf_chld_type_t2str(chld));
+}
+
+/* Callback for CNUM (subscriber number) */
+static void cnum_cmd_cb(void)
+{
+	haltest_info("%s\n", __func__);
+}
+
+/* Callback for indicators (CIND) */
+static void cind_cmd_cb(void)
+{
+	haltest_info("%s\n", __func__);
+}
+
+/* Callback for operator selection (COPS) */
+static void cops_cmd_cb(void)
+{
+	haltest_info("%s\n", __func__);
+}
+
+/* Callback for call list (AT+CLCC) */
+static void clcc_cmd_cb(void)
+{
+	haltest_info("%s\n", __func__);
+}
+
+/*
+ * Callback for unknown AT command recd from HF
+ * at_string will contain the unparsed AT string
+ */
+static void unknown_at_cmd_cb(char *at_string)
+{
+	haltest_info("%s: at_string=%s\n", __func__, at_string);
+}
+
+/* Callback for keypressed (HSP) event. */
+static void key_pressed_cmd_cb(void)
+{
+	haltest_info("%s\n", __func__);
+}
+
+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,
+	.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)
+{
+	RETURN_IF_NULL(if_hf);
+
+	EXEC(if_hf->init, &hf_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_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)
+{
+	RETURN_IF_NULL(if_hf);
+
+	EXEC(if_hf->start_voice_recognition);
+}
+
+/* stop voice recognition */
+
+static void stop_voice_recognition_p(int argc, const char **argv)
+{
+	RETURN_IF_NULL(if_hf);
+
+	EXEC(if_hf->stop_voice_recognition);
+}
+
+/* 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;
+
+	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]);
+
+	EXEC(if_hf->volume_control, type, volume);
+}
+
+/* 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)
+{
+	RETURN_IF_NULL(if_hf);
+
+	/* response */
+	if (argc <= 2) {
+		haltest_error("No cops specified\n");
+		return;
+	}
+
+	EXEC(if_hf->cops_response, argv[2]);
+}
+
+/* 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;
+
+	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]);
+
+	EXEC(if_hf->cind_response, svc, num_active, num_held, call_setup_state,
+							signal, roam, batt_chg);
+}
+
+/* Pre-formatted AT response, typically in response to unknown AT cmd */
+
+static void formatted_at_response_p(int argc, const char **argv)
+{
+	RETURN_IF_NULL(if_hf);
+
+	/* response */
+	if (argc <= 2) {
+		haltest_error("No response specified\n");
+		return;
+	}
+
+	EXEC(if_hf->formatted_at_response, argv[2]);
+}
+
+/* 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 = 0;
+
+	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 = atoi(argv[3]);
+
+	EXEC(if_hf->at_response, response_code, error_code);
+}
+
+/* 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;
+
+	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]);
+
+	EXEC(if_hf->clcc_response, index, dir, state, mode, mpty, number,
+									type);
+}
+
+/* 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[] = {
+	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, "<vol_type> <volume>"),
+	STD_METHODCH(device_status_notification,
+			"<ntk_state> <svt_type> <signal> <batt_chg>"),
+	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>"),
+	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/android/client/if-hh.c b/bluez/android/client/if-hh.c
new file mode 100644
index 0000000..0341d25
--- /dev/null
+++ b/bluez/android/client/if-hh.c
@@ -0,0 +1,438 @@
+/*
+ * 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 <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 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
+};
+
+/* 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/android/client/if-main.h b/bluez/android/client/if-main.h
new file mode 100644
index 0000000..b628464
--- /dev/null
+++ b/bluez/android/client/if-main.h
@@ -0,0 +1,177 @@
+/*
+ * 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 <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 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;
+
+/*
+ * 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 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;
+
+/* 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);
+
+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/android/client/if-pan.c b/bluez/android/client/if-pan.c
new file mode 100644
index 0000000..bdb36cc
--- /dev/null
+++ b/bluez/android/client/if-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 <hardware/bluetooth.h>
+
+#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
+
+static void control_state_cb(btpan_control_state_t state, bt_status_t error,
+					int local_role, const char *ifname)
+{
+	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/android/client/if-rc.c b/bluez/android/client/if-rc.c
new file mode 100644
index 0000000..31efc29
--- /dev/null
+++ b/bluez/android/client/if-rc.c
@@ -0,0 +1,321 @@
+/*
+ * 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<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
+
+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);
+}
+
+/* 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_METHOD(cleanup),
+	END_METHOD
+};
+
+const struct interface rc_if = {
+	.name = "rc",
+	.methods = methods
+};
diff --git a/bluez/android/client/if-sock.c b/bluez/android/client/if-sock.c
new file mode 100644
index 0000000..4c1af82
--- /dev/null
+++ b/bluez/android/client/if-sock.c
@@ -0,0 +1,348 @@
+/*
+ * 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 "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/android/client/pollhandler.c b/bluez/android/client/pollhandler.c
new file mode 100644
index 0000000..6160921
--- /dev/null
+++ b/bluez/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/android/client/pollhandler.h b/bluez/android/client/pollhandler.h
new file mode 100644
index 0000000..e2f22df
--- /dev/null
+++ b/bluez/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/android/client/tabcompletion.c b/bluez/android/client/tabcompletion.c
new file mode 100644
index 0000000..aceea39
--- /dev/null
+++ b/bluez/android/client/tabcompletion.c
@@ -0,0 +1,378 @@
+/*
+ * 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 "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/android/client/terminal.c b/bluez/android/client/terminal.c
new file mode 100644
index 0000000..f7b56de
--- /dev/null
+++ b/bluez/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/android/client/terminal.h b/bluez/android/client/terminal.h
new file mode 100644
index 0000000..0e63936
--- /dev/null
+++ b/bluez/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/android/cutils/properties.h b/bluez/android/cutils/properties.h
new file mode 100644
index 0000000..66a4a84
--- /dev/null
+++ b/bluez/android/cutils/properties.h
@@ -0,0 +1,71 @@
+/*
+ *
+ *  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 <stdio.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+
+#define PROPERTY_VALUE_MAX 32
+
+static inline int property_get(const char *key, char *value,
+						const char *default_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/android/gatt.c b/bluez/android/gatt.c
new file mode 100644
index 0000000..0414c4f
--- /dev/null
+++ b/bluez/android/gatt.c
@@ -0,0 +1,1236 @@
+/*
+ *
+ *  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/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 "attrib/gattrib.h"
+#include "attrib/att.h"
+#include "attrib/gatt.h"
+#include "btio/btio.h"
+
+struct gatt_client {
+	int32_t id;
+	uint8_t uuid[16];
+};
+
+struct gatt_device {
+	bdaddr_t bdaddr;
+	uint8_t bdaddr_type;
+
+	struct queue *clients;
+
+	bool connect_ready;
+	int32_t conn_id;
+
+	GAttrib *attrib;
+	GIOChannel *att_io;
+	struct queue *services;
+
+	guint watch_id;
+};
+
+static struct ipc *hal_ipc = NULL;
+static bdaddr_t adapter_addr;
+static bool scanning = false;
+
+static struct queue *gatt_clients = NULL;
+static struct queue *conn_list	= NULL;		/* Connected devices */
+static struct queue *conn_wait_queue = NULL;	/* Devs waiting to connect */
+
+static void bt_le_discovery_stop_cb(void);
+
+static bool match_client_by_uuid(const void *data, const void *user_data)
+{
+	const uint8_t *exp_uuid = user_data;
+	const struct gatt_client *client = data;
+
+	return !memcmp(exp_uuid, client->uuid, sizeof(client->uuid));
+}
+
+static bool match_client_by_id(const void *data, const void *user_data)
+{
+	int32_t exp_id = PTR_TO_INT(user_data);
+	const struct gatt_client *client = data;
+
+	return client->id == exp_id;
+}
+
+static bool match_by_value(const void *data, const void *user_data)
+{
+	return data == user_data;
+}
+
+static bool match_dev_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_dev_connect_ready(const void *data, const void *user_data)
+{
+	const struct gatt_device *dev = data;
+
+	return dev->connect_ready;
+}
+
+static bool match_dev_by_conn_id(const void *data, const void *user_data)
+{
+	const struct gatt_device *dev = data;
+	const int32_t conn_id = PTR_TO_INT(user_data);
+
+	return dev->conn_id == conn_id;
+}
+
+static void destroy_device(void *data)
+{
+	struct gatt_device *dev = data;
+
+	queue_destroy(dev->clients, NULL);
+	queue_destroy(dev->services, free);
+	free(dev);
+}
+
+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_client *client;
+	static int32_t client_cnt = 1;
+	uint8_t status;
+
+	DBG("");
+
+	if (!cmd->uuid) {
+		error("gatt: no uuid received");
+		status = HAL_STATUS_FAILED;
+		goto failed;
+	}
+
+	if (queue_find(gatt_clients, match_client_by_uuid, &cmd->uuid)) {
+		error("gatt: client uuid is already on list");
+		status = HAL_STATUS_FAILED;
+		goto failed;
+	}
+
+	client = new0(struct gatt_client, 1);
+
+	memcpy(client->uuid, cmd->uuid, sizeof(client->uuid));
+
+	client->id = client_cnt++;
+
+	queue_push_head(gatt_clients, client);
+
+	status = HAL_STATUS_SUCCESS;
+
+	ev.status = status;
+	ev.client_if = client->id;
+	memcpy(ev.app_uuid, client->uuid, sizeof(client->uuid));
+
+	ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT,
+			HAL_EV_GATT_CLIENT_REGISTER_CLIENT, sizeof(ev), &ev);
+
+failed:
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT,
+					HAL_OP_GATT_CLIENT_REGISTER, status);
+}
+
+static void handle_client_unregister(const void *buf, uint16_t len)
+{
+	const struct hal_cmd_gatt_client_unregister *cmd = buf;
+	uint8_t status;
+	struct gatt_client *cl;
+
+	DBG("");
+
+	cl = queue_remove_if(gatt_clients, match_client_by_id,
+						INT_TO_PTR(cmd->client_if));
+	if (!cl) {
+		error("gatt: client_if=%d not found", cmd->client_if);
+		status = HAL_STATUS_FAILED;
+		goto failed;
+	}
+
+	free(cl);
+	status = HAL_STATUS_SUCCESS;
+
+failed:
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT,
+					HAL_OP_GATT_CLIENT_UNREGISTER, status);
+}
+
+static void primary_cb(uint8_t status, GSList *services, void *user_data)
+{
+	struct hal_ev_gatt_client_search_complete ev;
+	struct gatt_device *dev = user_data;
+	GSList *l;
+
+	DBG("Status %d", status);
+
+	if (status) {
+		error("gatt: Discover all primary services failed: %s",
+							att_ecode2str(status));
+		ev.status = HAL_STATUS_FAILED;
+		goto done;
+	}
+
+	if (!services) {
+		info("gatt: No primary services found");
+		ev.status = HAL_STATUS_SUCCESS;
+		goto done;
+	}
+
+	for (l = services; l; l = l->next) {
+		struct hal_ev_gatt_client_search_result ev_res;
+		struct gatt_primary *prim = l->data;
+		struct gatt_primary *p;
+		bt_uuid_t uuid;
+
+		p = new0(struct gatt_primary, 1);
+		if (!p) {
+			error("gatt: Cannot allocate memory for gatt_primary");
+			continue;
+		}
+
+		memset(&ev_res, 0, sizeof(ev_res));
+
+		/* Put primary service to our local list */
+		memcpy(p, prim, sizeof(*p));
+		if (!queue_push_tail(dev->services, p)) {
+			error("gatt: Cannot push primary service to the list");
+			free(p);
+			continue;
+		}
+
+		DBG("attr handle = 0x%04x, end grp handle = 0x%04x uuid: %s",
+				prim->range.start, prim->range.end, prim->uuid);
+
+		/* Set event data */
+		ev_res.conn_id  = dev->conn_id;
+		ev_res.srvc_id.is_primary = 1;
+		ev_res.srvc_id.inst_id = 0;
+
+		if (bt_string_to_uuid(&uuid, prim->uuid) < 0) {
+			error("gatt: Cannot convert string to uuid");
+			continue;
+		}
+
+		memcpy(&ev_res.srvc_id.uuid, &uuid.value, sizeof(uuid.value));
+
+		ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT ,
+					HAL_EV_GATT_CLIENT_SEARCH_RESULT,
+					sizeof(ev_res), &ev_res);
+	}
+
+	ev.status = HAL_STATUS_SUCCESS;
+
+done:
+	ev.conn_id = dev->conn_id;
+	ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT,
+			HAL_EV_GATT_CLIENT_SEARCH_COMPLETE, sizeof(ev), &ev);
+}
+
+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;
+		device->attrib = NULL;
+		g_attrib_cancel_all(attrib);
+		g_attrib_unref(attrib);
+	}
+}
+
+static void send_client_disconnect_notify(int32_t id, struct gatt_device *dev,
+								uint8_t status)
+{
+	struct hal_ev_gatt_client_disconnect ev;
+
+	ev.client_if = id;
+	ev.conn_id = dev->conn_id;
+	ev.status = status;
+	bdaddr2android(&dev->bdaddr, &ev.bda);
+
+	ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT,
+			HAL_EV_GATT_CLIENT_DISCONNECT, sizeof(ev), &ev);
+}
+
+static void client_disconnect_notify(void *data, void *user_data)
+{
+	struct gatt_device *dev = user_data;
+	int32_t id = PTR_TO_INT(data);
+
+	send_client_disconnect_notify(id, dev, HAL_STATUS_SUCCESS);
+}
+
+static bool is_device_wating_for_connect(const bdaddr_t *addr,
+							uint8_t addr_type)
+{
+	struct gatt_device *dev;
+
+	DBG("");
+
+	dev = queue_find(conn_wait_queue, match_dev_by_bdaddr, (void *)addr);
+	if (!dev)
+		return false;
+
+	dev->bdaddr_type = addr_type;
+
+	/* Mark that this device is ready for connect.
+	 * Need it because will continue with connect after scan is stopped
+	 */
+	dev->connect_ready = true;
+
+	return true;
+}
+
+static void le_device_found_handler(const bdaddr_t *addr, uint8_t addr_type,
+						int rssi, uint16_t eir_len,
+							const void *eir)
+{
+	uint8_t buf[IPC_MTU];
+	struct hal_ev_gatt_client_scan_result *ev = (void *) buf;
+	char bda[18];
+
+	if (!scanning)
+		goto connect;
+
+	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);
+
+connect:
+	if (!is_device_wating_for_connect(addr, addr_type))
+		return;
+
+	/* 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 gboolean disconnected_cb(GIOChannel *io, GIOCondition cond,
+							gpointer user_data)
+{
+	bdaddr_t *addr = user_data;
+	struct gatt_device *dev;
+	int sock, err = 0;
+	socklen_t len;
+
+	dev = queue_remove_if(conn_list, match_dev_by_bdaddr, addr);
+
+	sock = g_io_channel_unix_get_fd(io);
+	len = sizeof(err);
+	if (getsockopt(sock, SOL_SOCKET, SO_ERROR, &err, &len) < 0)
+		goto done;
+
+	DBG("%s (%d)", strerror(err), err);
+
+	/* 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) {
+		if (!queue_push_tail(conn_wait_queue, dev)) {
+			error("gatt: Cannot push data");
+		} else {
+			bt_le_discovery_start(le_device_found_handler);
+			return FALSE;
+		}
+	}
+
+done:
+	connection_cleanup(dev);
+
+	queue_foreach(dev->clients, client_disconnect_notify, dev);
+	destroy_device(dev);
+
+	return FALSE;
+}
+
+static void send_client_connect_notify(void *data, void *user_data)
+{
+	struct hal_ev_gatt_client_connect *ev = user_data;
+	int32_t id = PTR_TO_INT(data);
+
+	ev->client_if = id;
+
+	ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT,
+				HAL_EV_GATT_CLIENT_CONNECT, sizeof(*ev), ev);
+
+}
+
+static void connect_cb(GIOChannel *io, GError *gerr, gpointer user_data)
+{
+	bdaddr_t *addr = user_data;
+	struct gatt_device *dev;
+	struct hal_ev_gatt_client_connect ev;
+	GAttrib *attrib;
+	static uint32_t conn_id = 0;
+	uint8_t status;
+
+	/* Take device from conn waiting queue */
+	dev = queue_remove_if(conn_wait_queue, match_dev_by_bdaddr, addr);
+	if (!dev) {
+		error("gatt: Device not on the connect wait queue!?");
+		g_io_channel_shutdown(io, TRUE, NULL);
+		return;
+	}
+
+	g_io_channel_unref(dev->att_io);
+	dev->att_io = NULL;
+
+	/* Set address and client id in the event */
+	bdaddr2android(&dev->bdaddr, &ev.bda);
+
+	if (gerr) {
+		error("gatt: connection failed %s", gerr->message);
+		status = HAL_STATUS_FAILED;
+		goto reply;
+	}
+
+	attrib = g_attrib_new(io);
+	if (!attrib) {
+		error("gatt: unable to create new GAttrib instance");
+		status = HAL_STATUS_FAILED;
+		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->conn_id = ++conn_id;
+
+	/* Move gatt device from connect queue to conn_list */
+	if (!queue_push_tail(conn_list, dev)) {
+		error("gatt: Cannot push dev on conn_list");
+		connection_cleanup(dev);
+		status = HAL_STATUS_FAILED;
+		goto reply;
+	}
+
+	status = HAL_STATUS_SUCCESS;
+	goto reply;
+
+reply:
+	ev.conn_id = dev ? dev->conn_id : 0;
+	ev.status = status;
+
+	queue_foreach(dev->clients, send_client_connect_notify, &ev);
+
+	/* If connection did not succeed, destroy device */
+	if (status)
+		destroy_device(dev);
+
+	/* Check if we should restart scan */
+	if (scanning)
+		bt_le_discovery_start(le_device_found_handler);
+
+	/*FIXME: What to do if discovery won't start here. */
+}
+
+static int connect_le(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);
+
+	/*TODO: If we are bonded then we should use higier sec level */
+	sec_level = BT_IO_SEC_LOW;
+
+	/*
+	 * This connection will help us catch any PDUs that comes before
+	 * pairing finishes
+	 */
+	io = bt_io_connect(connect_cb, dev, NULL, &gerr,
+			BT_IO_OPT_SOURCE_BDADDR,
+			&adapter_addr,
+			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) {
+		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;
+
+	return 0;
+}
+
+static void handle_client_scan(const void *buf, uint16_t len)
+{
+	const struct hal_cmd_gatt_client_scan *cmd = buf;
+	uint8_t status;
+	void *registered;
+
+	DBG("new state %d", cmd->start);
+
+	registered = queue_find(gatt_clients, match_client_by_id,
+						INT_TO_PTR(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(le_device_found_handler)) {
+		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_next_dev(void)
+{
+	struct gatt_device *dev;
+
+	DBG("");
+
+	if (queue_isempty(conn_wait_queue))
+		return 0;
+
+	/* Discovery has been stopped because there is connection waiting */
+	dev = queue_find(conn_wait_queue, match_dev_connect_ready, NULL);
+	if (!dev)
+		/* Lets try again. */
+		return -1;
+
+	dev->connect_ready = false;
+
+	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(le_device_found_handler);
+}
+
+static struct gatt_device *find_device(bdaddr_t *addr)
+{
+	struct gatt_device *dev;
+
+	dev = queue_find(conn_list, match_dev_by_bdaddr, addr);
+	if (dev)
+		return dev;
+
+	dev = queue_find(conn_wait_queue, match_dev_by_bdaddr, addr);
+	if (dev)
+		return dev;
+
+	return NULL;
+}
+
+static void handle_client_connect(const void *buf, uint16_t len)
+{
+	const struct hal_cmd_gatt_client_connect *cmd = buf;
+	struct gatt_device *dev = NULL;
+	void *l;
+	bdaddr_t addr;
+	uint8_t status;
+	bool send_notify = false;
+
+	DBG("");
+
+	/* Check if client is registered */
+	l = queue_find(gatt_clients, match_client_by_id,
+						INT_TO_PTR(cmd->client_if));
+	if (!l) {
+		error("gatt: Client id %d not found", cmd->client_if);
+		status = HAL_STATUS_FAILED;
+		goto reply;
+	}
+
+	android2bdaddr(&cmd->bdaddr, &addr);
+
+	/* We do support many clients for one device connection so lets check
+	  * If device is connected or in connecting state just update list of
+	  * clients
+	  */
+	dev = find_device(&addr);
+	if (dev) {
+		/* Remeber to send dummy notification event  if we area
+		 * connected
+		 */
+		if (dev->conn_id)
+			send_notify = true;
+
+		if (queue_find(dev->clients, match_by_value,
+						INT_TO_PTR(cmd->client_if))) {
+			status = HAL_STATUS_SUCCESS;
+			goto reply;
+		}
+
+		/* Store another client */
+		if (!queue_push_tail(dev->clients,
+						INT_TO_PTR(cmd->client_if))) {
+			error("gatt: Cannot push client on gatt device list");
+			status = HAL_STATUS_FAILED;
+			goto reply;
+		}
+
+		status = HAL_STATUS_SUCCESS;
+		goto reply;
+	}
+
+	/* Lets create new gatt device and put it on conn_wait_queue.
+	  * Once it is connected we move it to conn_list
+	  */
+	dev = new0(struct gatt_device, 1);
+	if (!dev) {
+		status = HAL_STATUS_FAILED;
+		goto reply;
+	}
+
+	memcpy(&dev->bdaddr, &addr, sizeof(bdaddr_t));
+
+	/* Create queue to keep list of clients for given device*/
+	dev->clients = queue_new();
+	if (!dev->clients) {
+		error("gatt: Cannot create client queue");
+		status = HAL_STATUS_FAILED;
+		goto reply;
+	}
+
+	dev->services = queue_new();
+	if (!dev->services) {
+		error("gatt: Cannot create services queue");
+		queue_destroy(dev->clients, NULL);
+		status = HAL_STATUS_FAILED;
+		goto reply;
+	}
+
+	/* Update client list of device */
+	if (!queue_push_tail(dev->clients, INT_TO_PTR(cmd->client_if))) {
+		error("gatt: Cannot push client on the client queue!?");
+		status = HAL_STATUS_FAILED;
+		goto reply;
+	}
+
+	/* Start le scan if not started */
+	if (!scanning && !bt_le_discovery_start(le_device_found_handler)) {
+		error("gatt: Could not start scan");
+		status = HAL_STATUS_FAILED;
+		goto reply;
+	}
+
+	if (!queue_push_tail(conn_wait_queue, dev)) {
+		error("gatt: Cannot push device on conn_wait_queue");
+		status = HAL_STATUS_FAILED;
+		goto reply;
+	}
+
+	status = HAL_STATUS_SUCCESS;
+
+reply:
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT, HAL_OP_GATT_CLIENT_CONNECT,
+								status);
+
+	/* If there is an error here we should make sure dev is out.*/
+	if ((status != HAL_STATUS_SUCCESS) && dev) {
+		destroy_device(dev);
+		return;
+	}
+
+	/* Send dummy notification since ACL is already up*/
+	if (send_notify) {
+		struct hal_ev_gatt_client_connect ev;
+
+		ev.conn_id = dev->conn_id;
+		ev.status = HAL_STATUS_SUCCESS;
+		ev.client_if = cmd->client_if;
+		bdaddr2android(&addr, &ev.bda);
+
+		ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT,
+						HAL_EV_GATT_CLIENT_CONNECT,
+						sizeof(ev), &ev);
+	}
+}
+
+static void handle_client_disconnect(const void *buf, uint16_t len)
+{
+	const struct hal_cmd_gatt_client_disconnect *cmd = buf;
+	struct gatt_device *dev;
+	uint8_t status;
+	char addr[18];
+
+	DBG("");
+
+	ba2str((bdaddr_t *)&cmd->bdaddr, addr);
+
+	dev = queue_find(conn_list, match_dev_by_conn_id,
+						INT_TO_PTR(cmd->conn_id));
+	if (!dev) {
+		error("gatt: dev %s with conn_id=%d not found",
+							addr, cmd->conn_id);
+		status = HAL_STATUS_FAILED;
+		goto reply;
+	}
+
+	/*Check if client owns this connection */
+	if (!queue_remove_if(dev->clients, match_by_value,
+						INT_TO_PTR(cmd->client_if))) {
+		error("gatt: cannot remove conn_id=%d", cmd->client_if);
+		status = HAL_STATUS_FAILED;
+	} else {
+		status = HAL_STATUS_SUCCESS;
+	}
+
+reply:
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT,
+			HAL_OP_GATT_CLIENT_DISCONNECT, status);
+
+	if (status == HAL_STATUS_FAILED)
+		return;
+
+	/* Just send disconnect event. If there is more clients on this
+	 * device then this is what we shall to do.
+	 * If this is last client, this is still OK to do because on connect
+	 * request we do le scan and wait until remote device start
+	 * advertisement */
+	send_client_disconnect_notify(cmd->client_if, dev, HAL_STATUS_SUCCESS);
+
+	/* If there is more clients just return */
+	if (!queue_isempty(dev->clients))
+		return;
+
+	/* If this is last client do more cleaning */
+	connection_cleanup(dev);
+	dev = queue_remove_if(conn_list, match_dev_by_bdaddr, &dev->bdaddr);
+	destroy_device(dev);
+}
+
+static void handle_client_listen(const void *buf, uint16_t len)
+{
+	DBG("");
+
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT, HAL_OP_GATT_CLIENT_LISTEN,
+							HAL_STATUS_FAILED);
+}
+
+static void handle_client_refresh(const void *buf, uint16_t len)
+{
+	DBG("");
+
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT, HAL_OP_GATT_CLIENT_REFRESH,
+							HAL_STATUS_FAILED);
+}
+
+static void handle_client_search_service(const void *buf, uint16_t len)
+{
+	const struct hal_cmd_gatt_client_search_service *cmd = buf;
+	struct gatt_device *dev;
+	uint8_t status;
+
+	DBG("");
+
+	dev = queue_find(conn_list, match_dev_by_conn_id,
+						INT_TO_PTR(cmd->conn_id));
+	if (!dev) {
+		error("gatt: dev with conn_id=%d not found", cmd->conn_id);
+		status = HAL_STATUS_FAILED;
+		goto reply;
+	}
+
+	/*TODO:  Handle filter uuid */
+
+	if (!gatt_discover_primary(dev->attrib, NULL, primary_cb, dev)) {
+		status = HAL_STATUS_FAILED;
+		goto reply;
+	}
+
+	status = HAL_STATUS_SUCCESS;
+
+reply:
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT,
+			HAL_OP_GATT_CLIENT_SEARCH_SERVICE, status);
+}
+
+static void handle_client_get_included_service(const void *buf, uint16_t len)
+{
+	DBG("");
+
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT,
+					HAL_OP_GATT_CLIENT_GET_INCLUDED_SERVICE,
+					HAL_STATUS_FAILED);
+}
+
+static void handle_client_get_characteristic(const void *buf, uint16_t len)
+{
+	DBG("");
+
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT,
+					HAL_OP_GATT_CLIENT_GET_CHARACTERISTIC,
+					HAL_STATUS_FAILED);
+}
+
+static void handle_client_get_descriptor(const void *buf, uint16_t len)
+{
+	DBG("");
+
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT,
+			HAL_OP_GATT_CLIENT_GET_DESCRIPTOR, HAL_STATUS_FAILED);
+}
+
+static void handle_client_read_characteristic(const void *buf, uint16_t len)
+{
+	DBG("");
+
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT,
+					HAL_OP_GATT_CLIENT_READ_CHARACTERISTIC,
+					HAL_STATUS_FAILED);
+}
+
+static void handle_client_write_characteristic(const void *buf, uint16_t len)
+{
+	DBG("");
+
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT,
+					HAL_OP_GATT_CLIENT_WRITE_CHARACTERISTIC,
+					HAL_STATUS_FAILED);
+}
+
+static void handle_client_read_descriptor(const void *buf, uint16_t len)
+{
+	DBG("");
+
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT,
+			HAL_OP_GATT_CLIENT_READ_DESCRIPTOR, HAL_STATUS_FAILED);
+}
+
+static void handle_client_write_descriptor(const void *buf, uint16_t len)
+{
+	DBG("");
+
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT,
+			HAL_OP_GATT_CLIENT_WRITE_DESCRIPTOR, HAL_STATUS_FAILED);
+}
+
+static void handle_client_execute_write(const void *buf, uint16_t len)
+{
+	DBG("");
+
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT,
+			HAL_OP_GATT_CLIENT_EXECUTE_WRITE, HAL_STATUS_FAILED);
+}
+
+static void handle_client_register_for_notification(const void *buf,
+								uint16_t len)
+{
+	DBG("");
+
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT,
+				HAL_OP_GATT_CLIENT_REGISTER_FOR_NOTIFICATION,
+				HAL_STATUS_FAILED);
+}
+
+static void handle_client_deregister_for_notification(const void *buf,
+								uint16_t len)
+{
+	DBG("");
+
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT,
+				HAL_OP_GATT_CLIENT_DEREGISTER_FOR_NOTIFICATION,
+				HAL_STATUS_FAILED);
+}
+
+static void handle_client_read_remote_rssi(const void *buf, uint16_t len)
+{
+	DBG("");
+
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT,
+			HAL_OP_GATT_CLIENT_READ_REMOTE_RSSI, HAL_STATUS_FAILED);
+}
+
+static void handle_client_get_device_type(const void *buf, uint16_t len)
+{
+	DBG("");
+
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT,
+			HAL_OP_GATT_CLIENT_GET_DEVICE_TYPE, HAL_STATUS_FAILED);
+}
+
+static void handle_client_set_adv_data(const void *buf, uint16_t len)
+{
+	DBG("");
+
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT,
+			HAL_OP_GATT_CLIENT_SET_ADV_DATA, HAL_STATUS_FAILED);
+}
+
+static void handle_client_test_command(const void *buf, uint16_t len)
+{
+	DBG("");
+
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT,
+			HAL_OP_GATT_CLIENT_TEST_COMMAND, HAL_STATUS_FAILED);
+}
+
+static void handle_server_register(const void *buf, uint16_t len)
+{
+	DBG("");
+
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT, HAL_OP_GATT_SERVER_REGISTER,
+							HAL_STATUS_FAILED);
+}
+
+static void handle_server_unregister(const void *buf, uint16_t len)
+{
+	DBG("");
+
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT,
+			HAL_OP_GATT_SERVER_UNREGISTER, HAL_STATUS_FAILED);
+}
+
+static void handle_server_connect(const void *buf, uint16_t len)
+{
+	DBG("");
+
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT, HAL_OP_GATT_SERVER_CONNECT,
+							HAL_STATUS_FAILED);
+}
+
+static void handle_server_disconnect(const void *buf, uint16_t len)
+{
+	DBG("");
+
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT,
+			HAL_OP_GATT_SERVER_DISCONNECT, HAL_STATUS_FAILED);
+}
+
+static void handle_server_add_service(const void *buf, uint16_t len)
+{
+	DBG("");
+
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT,
+			HAL_OP_GATT_SERVER_ADD_SERVICE, HAL_STATUS_FAILED);
+}
+
+static void handle_server_add_included_service(const void *buf, uint16_t len)
+{
+	DBG("");
+
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT,
+			HAL_OP_GATT_SERVER_ADD_INC_SERVICE, HAL_STATUS_FAILED);
+}
+
+static void handle_server_add_characteristic(const void *buf, uint16_t len)
+{
+	DBG("");
+
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT,
+					HAL_OP_GATT_SERVER_ADD_CHARACTERISTIC,
+					HAL_STATUS_FAILED);
+}
+
+static void handle_server_add_descriptor(const void *buf, uint16_t len)
+{
+	DBG("");
+
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT,
+			HAL_OP_GATT_SERVER_ADD_DESCRIPTOR, HAL_STATUS_FAILED);
+}
+
+static void handle_server_start_service(const void *buf, uint16_t len)
+{
+	DBG("");
+
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT,
+			HAL_OP_GATT_SERVER_START_SERVICE, HAL_STATUS_FAILED);
+}
+
+static void handle_server_stop_service(const void *buf, uint16_t len)
+{
+	DBG("");
+
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT,
+			HAL_OP_GATT_SERVER_STOP_SERVICE, HAL_STATUS_FAILED);
+}
+
+static void handle_server_delete_service(const void *buf, uint16_t len)
+{
+	DBG("");
+
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT,
+			HAL_OP_GATT_SERVER_DELETE_SERVICE, HAL_STATUS_FAILED);
+}
+
+static void handle_server_send_indication(const void *buf, uint16_t len)
+{
+	DBG("");
+
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT,
+			HAL_OP_GATT_SERVER_SEND_INDICATION, HAL_STATUS_FAILED);
+}
+
+static void handle_server_send_response(const void *buf, uint16_t len)
+{
+	DBG("");
+
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT,
+			HAL_OP_GATT_SERVER_SEND_RESPONSE, HAL_STATUS_FAILED);
+}
+
+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)},
+};
+
+bool bt_gatt_register(struct ipc *ipc, const bdaddr_t *addr)
+{
+	DBG("");
+
+	bacpy(&adapter_addr, addr);
+
+	hal_ipc = ipc;
+
+	conn_list = queue_new();
+	if (!conn_list) {
+		error("gatt: Can not create conn queue");
+		return false;
+	}
+
+	conn_wait_queue = queue_new();
+	if (!conn_wait_queue) {
+		error("gatt: Can not create conn queue");
+		return false;
+	}
+
+	ipc_register(hal_ipc, HAL_SERVICE_ID_GATT, cmd_handlers,
+						G_N_ELEMENTS(cmd_handlers));
+
+	gatt_clients = queue_new();
+	if (!gatt_clients) {
+		error("gatt: Cannot allocate gatt_clients");
+		return false;
+	}
+
+	return true;
+}
+
+void bt_gatt_unregister(void)
+{
+	DBG("");
+
+	queue_destroy(gatt_clients, free);
+
+	ipc_unregister(hal_ipc, HAL_SERVICE_ID_GATT);
+	hal_ipc = NULL;
+
+	queue_destroy(conn_list, destroy_device);
+	conn_list = NULL;
+
+	queue_destroy(conn_wait_queue, destroy_device);
+	conn_wait_queue = NULL;
+
+}
diff --git a/bluez/android/gatt.h b/bluez/android/gatt.h
new file mode 100644
index 0000000..d4392d9
--- /dev/null
+++ b/bluez/android/gatt.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_gatt_register(struct ipc *ipc, const bdaddr_t *addr);
+void bt_gatt_unregister(void);
diff --git a/bluez/android/hal-a2dp.c b/bluez/android/hal-a2dp.c
new file mode 100644
index 0000000..ac495a1
--- /dev/null
+++ b/bluez/android/hal-a2dp.c
@@ -0,0 +1,155 @@
+/*
+ * 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)
+{
+	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)
+{
+	struct hal_ev_a2dp_audio_state *ev = buf;
+
+	if (cbs->audio_state_cb)
+		cbs->audio_state_cb(ev->state, (bt_bdaddr_t *)(ev->bdaddr));
+}
+
+/* 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 */
+		.handler = handle_conn_state,
+		.var_len = false,
+		.data_len = sizeof(struct hal_ev_a2dp_conn_state),
+	},
+	{	/* HAL_EV_A2DP_AUDIO_STATE */
+		.handler = handle_audio_state,
+		.var_len = false,
+		.data_len = sizeof(struct hal_ev_a2dp_audio_state),
+	},
+};
+
+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;
+
+	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_A2DP);
+	}
+
+	return ret;
+}
+
+static void cleanup()
+{
+	struct hal_cmd_unregister_module cmd;
+
+	DBG("");
+
+	if (!interface_ready())
+		return;
+
+	cbs = NULL;
+
+	cmd.service_id = HAL_SERVICE_ID_A2DP;
+
+	hal_ipc_cmd(HAL_SERVICE_ID_CORE, HAL_OP_UNREGISTER_MODULE,
+					sizeof(cmd), &cmd, 0, NULL, NULL);
+
+	hal_ipc_unregister(HAL_SERVICE_ID_A2DP);
+}
+
+static btav_interface_t iface = {
+	.size = sizeof(iface),
+	.init = init,
+	.connect = a2dp_connect,
+	.disconnect = disconnect,
+	.cleanup = cleanup
+};
+
+btav_interface_t *bt_get_a2dp_interface()
+{
+	return &iface;
+}
diff --git a/bluez/android/hal-audio.c b/bluez/android/hal-audio.c
new file mode 100644
index 0000000..00dde49
--- /dev/null
+++ b/bluez/android/hal-audio.c
@@ -0,0 +1,1743 @@
+/*
+ * 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 <sbc/sbc.h>
+
+#include "audio-msg.h"
+#include "ipc-common.h"
+#include "hal-log.h"
+#include "hal-msg.h"
+#include "../profiles/audio/a2dp-codecs.h"
+#include "../src/shared/util.h"
+
+#define FIXED_A2DP_PLAYBACK_LATENCY_MS 25
+
+#define FIXED_BUFFER_SIZE (20 * 512)
+
+#define MAX_FRAMES_IN_PAYLOAD 15
+
+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;
+
+#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));
+
+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_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));
+
+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 {
+	struct rtp_header hdr;
+	struct rtp_payload payload;
+	uint8_t data[0];
+};
+
+struct audio_input_config {
+	uint32_t rate;
+	uint32_t channels;
+	audio_format_t format;
+};
+
+struct sbc_data {
+	a2dp_sbc_t sbc;
+
+	sbc_t enc;
+
+	size_t in_frame_len;
+	size_t in_buf_size;
+
+	size_t out_frame_len;
+
+	unsigned frame_duration;
+	unsigned frames_per_packet;
+};
+
+static inline 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 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;
+	}
+}
+
+#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 int sbc_get_presets(struct audio_preset *preset, size_t *len);
+static int sbc_codec_init(struct audio_preset *preset, uint16_t mtu,
+							void **codec_data);
+static int sbc_cleanup(void *codec_data);
+static int sbc_get_config(void *codec_data, struct audio_input_config *config);
+static size_t sbc_get_buffer_size(void *codec_data);
+static size_t sbc_get_mediapacket_duration(void *codec_data);
+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 audio_codec {
+	uint8_t type;
+
+	int (*get_presets) (struct audio_preset *preset, size_t *len);
+
+	int (*init) (struct audio_preset *preset, uint16_t mtu,
+				void **codec_data);
+	int (*cleanup) (void *codec_data);
+	int (*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);
+};
+
+static const struct audio_codec audio_codecs[] = {
+	{
+		.type = A2DP_CODEC_SBC,
+
+		.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,
+	}
+};
+
+#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;
+};
+
+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 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 int sbc_codec_init(struct audio_preset *preset, uint16_t payload_len,
+							void **codec_data)
+{
+	struct sbc_data *sbc_data;
+	size_t in_frame_len;
+	size_t out_frame_len;
+	size_t num_frames;
+
+	if (preset->len != sizeof(a2dp_sbc_t)) {
+		error("SBC: preset size mismatch");
+		return AUDIO_STATUS_FAILED;
+	}
+
+	sbc_data = calloc(sizeof(struct sbc_data), 1);
+	if (!sbc_data)
+		return AUDIO_STATUS_FAILED;
+
+	memcpy(&sbc_data->sbc, preset->data, preset->len);
+
+	sbc_init_encoder(sbc_data);
+
+	in_frame_len = sbc_get_codesize(&sbc_data->enc);
+	out_frame_len = sbc_get_frame_length(&sbc_data->enc);
+	num_frames = payload_len / out_frame_len;
+
+	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);
+
+	*codec_data = sbc_data;
+
+	return AUDIO_STATUS_SUCCESS;
+}
+
+static int sbc_cleanup(void *codec_data)
+{
+	struct sbc_data *sbc_data = (struct sbc_data *) codec_data;
+
+	sbc_finish(&sbc_data->enc);
+	free(codec_data);
+
+	return AUDIO_STATUS_SUCCESS;
+}
+
+static int 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 AUDIO_STATUS_FAILED;
+	}
+	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 AUDIO_STATUS_SUCCESS;
+}
+
+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;
+	size_t consumed = 0;
+	size_t encoded = 0;
+	uint8_t frame_count = 0;
+
+	while (len - consumed >= sbc_data->in_frame_len &&
+			mp_data_len - encoded >= sbc_data->out_frame_len &&
+			frame_count < MAX_FRAMES_IN_PAYLOAD) {
+		ssize_t read;
+		ssize_t written = 0;
+
+		read = sbc_encode(&sbc_data->enc, buffer + consumed,
+				sbc_data->in_frame_len, mp->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;
+	mp->payload.frame_count = frame_count;
+
+	return consumed;
+}
+
+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;
+	}
+
+	if (rsp_len)
+		*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;
+		*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;
+}
+
+static int register_endpoints(void)
+{
+	struct audio_endpoint *ep = &audio_endpoints[0];
+	size_t i;
+
+	for (i = 0; i < NUM_CODECS; i++, ep++) {
+		const struct audio_codec *codec = &audio_codecs[i];
+
+		ep->id = ipc_open_cmd(codec);
+
+		if (!ep->id)
+			return AUDIO_STATUS_FAILED;
+
+		ep->codec = codec;
+		ep->codec_data = NULL;
+		ep->fd = -1;
+	}
+
+	return 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 int set_blocking(int fd)
+{
+	int flags;
+
+	flags = fcntl(fd, F_GETFL, 0);
+	if (flags < 0) {
+		int err = -errno;
+		error("fcntl(F_GETFL): %s (%d)", strerror(-err), -err);
+		return err;
+	}
+
+	if (fcntl(fd, F_SETFL, flags & ~O_NONBLOCK) < 0) {
+		int err = -errno;
+		error("fcntl(F_SETFL): %s (%d)", strerror(-err), -err);
+		return err;
+	}
+
+	return 0;
+}
+
+static bool open_endpoint(struct audio_endpoint *ep,
+						struct audio_input_config *cfg)
+{
+	struct audio_preset *preset;
+	const struct audio_codec *codec;
+	uint16_t mtu;
+	uint16_t payload_len;
+	int fd;
+
+	if (ipc_open_stream_cmd(ep->id, &mtu, &fd, &preset) !=
+							AUDIO_STATUS_SUCCESS)
+		return false;
+
+	if (set_blocking(fd) < 0)
+		goto failed;
+
+	DBG("mtu=%u", mtu);
+
+	payload_len = mtu - sizeof(*ep->mp);
+
+	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;
+	ep->mp->hdr.v = 2;
+	ep->mp->hdr.pt = 1;
+	ep->mp->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 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;
+
+	for (i = 0; i < bytes / 2; i++) {
+		int16_t l = le16_to_cpu(get_unaligned(&input[i * 2]));
+		int16_t r = le16_to_cpu(get_unaligned(&input[i * 2 + 1]));
+
+		put_unaligned(cpu_to_le16((l + r) / 2), &output[i]);
+	}
+}
+
+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;
+	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;
+		uint64_t time_us;
+		struct timespec anchor;
+
+		time_us = ep->samples * 1000000ll / out->cfg.rate;
+
+		timespec_add(&ep->start, time_us, &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;
+			}
+		}
+
+		read = ep->codec->encode_mediapacket(ep->codec_data,
+							buffer + consumed,
+							bytes - consumed, mp,
+							free_space, &written);
+
+		/* This is non-fatal and we can just assume buffer was processed
+		 * properly and wait for next one.
+		 */
+		if (read <= 0)
+			return true;
+
+		consumed += read;
+
+		mp->hdr.sequence_number = htons(ep->seq++);
+		mp->hdr.timestamp = htonl(ep->samples);
+
+		/* AudioFlinger provides 16bit PCM, so sample size is 2 bytes
+		 * multipled 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;
+
+		while (true) {
+			ret = write(ep->fd, mp, sizeof(*mp) + written);
+
+			if (ret >= 0)
+				break;
+
+			if (errno != EINTR)
+				return false;
+		}
+	}
+
+	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 (ipc_resume_stream_cmd(out->ep->id) != AUDIO_STATUS_SUCCESS)
+			return -1;
+
+		clock_gettime(CLOCK_MONOTONIC, &out->ep->start);
+		out->ep->samples = 0;
+
+		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);
+	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(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)
+
+{
+	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;
+
+	/* TODO: for now we always use endpoint 0 */
+	out->ep = &audio_endpoints[0];
+
+	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;
+}
+
+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(struct audio_hw_device *dev,
+					audio_io_handle_t handle,
+					audio_devices_t devices,
+					struct audio_config *config,
+					struct audio_stream_in **stream_in)
+{
+	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;
+}
+
+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;
+}
+
+static int audio_close(hw_device_t *device)
+{
+	struct a2dp_audio_dev *a2dp_dev = (struct a2dp_audio_dev *)device;
+
+	DBG("");
+
+	unregister_endpoints();
+
+	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);
+
+		if (pfd.revents & (POLLHUP | POLLERR | POLLNVAL)) {
+			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;
+	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;
+
+	/* 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/android/hal-avrcp.c b/bluez/android/hal-avrcp.c
new file mode 100644
index 0000000..5f98f5b
--- /dev/null
+++ b/bluez/android/hal-avrcp.c
@@ -0,0 +1,684 @@
+/*
+ * 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-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)
+{
+	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)
+{
+	if (cbs->get_play_status_cb)
+		cbs->get_play_status_cb();
+}
+
+static void handle_list_player_attrs(void *buf, uint16_t len)
+{
+	if (cbs->list_player_app_attr_cb)
+		cbs->list_player_app_attr_cb();
+}
+
+static void handle_list_player_values(void *buf, uint16_t len)
+{
+	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)
+{
+	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)
+{
+	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)
+{
+	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)
+{
+	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)
+{
+	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)
+{
+	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)
+{
+	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)
+{
+	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;
+
+	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_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, 0, 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, 0, 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, 0, 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, 0, 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, 0, 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, 0, 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, 0, 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, 0, 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, 0, 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, 0, 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, 0, 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, 0, 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, 0, 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, 0, 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, 0, NULL, NULL);
+}
+
+static void cleanup()
+{
+	struct hal_cmd_unregister_module cmd;
+
+	DBG("");
+
+	if (!interface_ready())
+		return;
+
+	cbs = NULL;
+
+	cmd.service_id = HAL_SERVICE_ID_AVRCP;
+
+	hal_ipc_cmd(HAL_SERVICE_ID_CORE, HAL_OP_UNREGISTER_MODULE,
+					sizeof(cmd), &cmd, 0, NULL, NULL);
+
+	hal_ipc_unregister(HAL_SERVICE_ID_AVRCP);
+}
+
+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()
+{
+	return &iface;
+}
diff --git a/bluez/android/hal-bluetooth.c b/bluez/android/hal-bluetooth.c
new file mode 100644
index 0000000..48d5ea2
--- /dev/null
+++ b/bluez/android/hal-bluetooth.c
@@ -0,0 +1,937 @@
+/*
+ * 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 <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)
+{
+	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;
+	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)
+{
+	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)
+{
+	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)
+{
+	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)
+{
+	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)
+{
+	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)
+{
+	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)
+{
+	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)
+{
+	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)
+{
+	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)
+{
+	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);
+}
+
+/* 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 */
+		.handler = handle_adapter_state_changed,
+		.var_len = false,
+		.data_len = sizeof(struct hal_ev_adapter_state_changed)
+	},
+	{	/* HAL_EV_ADAPTER_PROPS_CHANGED */
+		.handler = handle_adapter_props_changed,
+		.var_len = true,
+		.data_len = sizeof(struct hal_ev_adapter_props_changed) +
+						sizeof(struct hal_property),
+	},
+	{	/* HAL_EV_REMOTE_DEVICE_PROPS */
+		.handler = handle_device_state_changed,
+		.var_len = true,
+		.data_len = sizeof(struct hal_ev_remote_device_props) +
+						sizeof(struct hal_property),
+	},
+	{	/* HAL_EV_DEVICE_FOUND */
+		.handler = handle_device_found,
+		.var_len = true,
+		.data_len = sizeof(struct hal_ev_device_found) +
+						sizeof(struct hal_property),
+	},
+	{	/* HAL_EV_DISCOVERY_STATE_CHANGED */
+		.handler = handle_discovery_state_changed,
+		.var_len = false,
+		.data_len = sizeof(struct hal_ev_discovery_state_changed),
+	},
+	{	/* HAL_EV_PIN_REQUEST */
+		.handler = handle_pin_request,
+		.var_len = false,
+		.data_len = sizeof(struct hal_ev_pin_request),
+	},
+	{	/* HAL_EV_SSP_REQUEST */
+		.handler = handle_ssp_request,
+		.var_len = false,
+		.data_len = sizeof(struct hal_ev_ssp_request),
+	},
+	{	/* HAL_EV_BOND_STATE_CHANGED */
+		.handler = handle_bond_state_change,
+		.var_len = false,
+		.data_len = sizeof(struct hal_ev_bond_state_changed),
+	},
+	{	/* HAL_EV_ACL_STATE_CHANGED */
+		.handler = handle_acl_state_changed,
+		.var_len = false,
+		.data_len = sizeof(struct hal_ev_acl_state_changed),
+	},
+	{	/* HAL_EV_DUT_MODE_RECEIVE */
+		.handler = handle_dut_mode_receive,
+		.var_len = true,
+		.data_len = sizeof(struct hal_ev_dut_mode_receive),
+	},
+	{	/* HAL_EV_LE_TEST_MODE */
+		.handler = handle_le_test_mode,
+		.var_len = false,
+		.data_len = sizeof(struct hal_ev_le_test_mode),
+	}
+};
+
+static int init(bt_callbacks_t *callbacks)
+{
+	struct hal_cmd_register_module cmd;
+	int status;
+
+	DBG("");
+
+	if (interface_ready())
+		return BT_STATUS_DONE;
+
+	bt_hal_cbacks = callbacks;
+
+	hal_ipc_register(HAL_SERVICE_ID_BLUETOOTH, ev_handlers,
+				sizeof(ev_handlers)/sizeof(ev_handlers[0]));
+
+	if (!hal_ipc_init()) {
+		bt_hal_cbacks = NULL;
+		return BT_STATUS_FAIL;
+	}
+
+	cmd.service_id = HAL_SERVICE_ID_BLUETOOTH;
+	cmd.mode = HAL_MODE_DEFAULT;
+
+	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.mode = HAL_MODE_DEFAULT;
+
+	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, 0,
+								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, 0,
+								NULL, NULL);
+}
+
+static void cleanup(void)
+{
+	DBG("");
+
+	if (!interface_ready())
+		return;
+
+	hal_ipc_cleanup();
+
+	bt_hal_cbacks = NULL;
+
+	hal_ipc_unregister(HAL_SERVICE_ID_BLUETOOTH);
+}
+
+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, 0, 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, 0, 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, 0, 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, 0, 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, 0, 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, 0, 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, 0, 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, 0,
+			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, 0,
+				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, 0,
+				NULL, NULL);
+}
+
+static int create_bond(const bt_bdaddr_t *bd_addr)
+{
+	struct hal_cmd_create_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_CREATE_BOND,
+					sizeof(cmd), &cmd, 0, NULL, NULL);
+}
+
+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, 0, 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, 0, 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, 0, 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, 0, 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();
+
+	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, 0, 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, 0, 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, 0, 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;
+}
+
+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,
+};
+
+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("");
+
+	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/android/hal-gatt.c b/bluez/android/hal-gatt.c
new file mode 100644
index 0000000..51caf8d
--- /dev/null
+++ b/bluez/android/hal-gatt.c
@@ -0,0 +1,1189 @@
+/*
+ * 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 "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)
+{
+	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)
+{
+	struct hal_ev_gatt_client_scan_result *ev = buf;
+
+	if (cbs->client->scan_result_cb)
+		cbs->client->scan_result_cb((bt_bdaddr_t *) ev->bda, ev->rssi,
+								ev->adv_data);
+}
+
+static void handle_connect(void *buf, uint16_t len)
+{
+	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)
+{
+	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)
+{
+	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)
+{
+	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)
+{
+	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)
+{
+	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)
+{
+	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)
+{
+	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)
+{
+	struct hal_ev_gatt_client_notify *ev = buf;
+	btgatt_notify_params_t params;
+
+	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)
+{
+	struct hal_ev_gatt_client_read_characteristic *ev = buf;
+	btgatt_read_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);
+
+	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)
+{
+	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)
+{
+	struct hal_ev_gatt_client_read_descriptor *ev = buf;
+	btgatt_read_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);
+
+	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)
+{
+	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)
+{
+	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)
+{
+	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)
+{
+	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)
+{
+	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)
+{
+	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)
+{
+	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)
+{
+	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)
+{
+	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)
+{
+	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)
+{
+	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)
+{
+	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)
+{
+	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)
+{
+	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)
+{
+	struct hal_ev_gatt_server_request_write *ev = buf;
+
+	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)
+{
+	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)
+{
+	struct hal_ev_gatt_server_rsp_confirmation *ev = buf;
+
+	if (cbs->server->response_confirmation_cb)
+		cbs->server->response_confirmation_cb(ev->status, ev->handle);
+}
+
+/* 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[] = {
+	/* Client Event Handlers */
+	{handle_register_client, false,
+			sizeof(struct hal_ev_gatt_client_register_client)},
+	{handle_scan_result, true,
+				sizeof(struct hal_ev_gatt_client_scan_result)},
+	{handle_connect, false, sizeof(struct hal_ev_gatt_client_connect)},
+	{handle_disconnect, false,
+			sizeof(struct hal_ev_gatt_client_disconnect)},
+	{handle_search_complete, false,
+			sizeof(struct hal_ev_gatt_client_search_complete)},
+	{handle_search_result, false,
+			sizeof(struct hal_ev_gatt_client_search_result)},
+	{handle_get_characteristic, false,
+			sizeof(struct hal_ev_gatt_client_get_characteristic)},
+	{handle_get_descriptor, false,
+			sizeof(struct hal_ev_gatt_client_get_descriptor)},
+	{handle_get_included_service, false,
+			sizeof(struct hal_ev_gatt_client_get_inc_service)},
+	{handle_register_for_notification, false,
+			sizeof(struct hal_ev_gatt_client_reg_for_notif)},
+	{handle_notify, true, sizeof(struct hal_ev_gatt_client_notify)},
+	{handle_read_characteristic, true,
+			sizeof(struct hal_ev_gatt_client_read_characteristic)},
+	{handle_write_characteristic, false,
+			sizeof(struct hal_ev_gatt_client_write_characteristic)},
+	{handle_read_descriptor, true,
+			sizeof(struct hal_ev_gatt_client_read_descriptor)},
+	{handle_write_descriptor, false,
+			sizeof(struct hal_ev_gatt_client_write_descriptor)},
+	{handle_execute_write, false,
+				sizeof(struct hal_ev_gatt_client_exec_write)},
+	{handle_read_remote_rssi, false,
+			sizeof(struct hal_ev_gatt_client_read_remote_rssi)},
+	{handle_listen, false, sizeof(struct hal_ev_gatt_client_listen)},
+
+	/* Server Event Handlers */
+	{handle_register_server, false,
+				sizeof(struct hal_ev_gatt_server_register)},
+	{handle_connection, false,
+				sizeof(struct hal_ev_gatt_server_connection)},
+	{handle_service_added, false,
+			sizeof(struct hal_ev_gatt_server_service_added)},
+	{handle_included_service_added, false,
+			sizeof(struct hal_ev_gatt_server_service_added)},
+	{handle_characteristic_added, false,
+			sizeof(struct hal_ev_gatt_server_characteristic_added)},
+	{handle_descriptor_added, false,
+			sizeof(struct hal_ev_gatt_server_descriptor_added)},
+	{handle_service_started, false,
+			sizeof(struct hal_ev_gatt_server_service_started)},
+	{handle_service_stopped, false,
+			sizeof(struct hal_ev_gatt_server_service_stopped)},
+	{handle_service_deleted, false,
+			sizeof(struct hal_ev_gatt_server_service_deleted)},
+	{handle_request_read, false,
+			sizeof(struct hal_ev_gatt_server_request_read)},
+	{handle_request_write, true,
+			sizeof(struct hal_ev_gatt_server_request_write)},
+	{handle_request_exec_write, false,
+			sizeof(struct hal_ev_gatt_server_request_exec_write)},
+	{handle_response_confirmation, false,
+			sizeof(struct hal_ev_gatt_server_rsp_confirmation)},
+};
+
+/* Client API */
+
+static bt_status_t register_client(bt_uuid_t *uuid)
+{
+	struct hal_cmd_gatt_client_register cmd;
+
+	memcpy(cmd.uuid, uuid, sizeof(*uuid));
+
+	return hal_ipc_cmd(HAL_SERVICE_ID_GATT, HAL_OP_GATT_CLIENT_REGISTER,
+					sizeof(cmd), &cmd, 0, NULL, NULL);
+}
+
+static bt_status_t unregister_client(int client_if)
+{
+	struct hal_cmd_gatt_client_unregister cmd;
+
+	cmd.client_if = client_if;
+
+	return hal_ipc_cmd(HAL_SERVICE_ID_GATT, HAL_OP_GATT_CLIENT_UNREGISTER,
+					sizeof(cmd), &cmd, 0, NULL, NULL);
+}
+
+static bt_status_t scan(int client_if, bool start)
+{
+	struct hal_cmd_gatt_client_scan cmd;
+
+	cmd.client_if = client_if;
+	cmd.start = start;
+
+	return hal_ipc_cmd(HAL_SERVICE_ID_GATT, HAL_OP_GATT_CLIENT_SCAN,
+					sizeof(cmd), &cmd, 0, NULL, NULL);
+}
+
+static bt_status_t connect(int client_if, const bt_bdaddr_t *bd_addr,
+								bool is_direct)
+{
+	struct hal_cmd_gatt_client_connect cmd;
+
+	cmd.client_if = client_if;
+	cmd.is_direct = is_direct;
+
+	memcpy(cmd.bdaddr, bd_addr, sizeof(*bd_addr));
+
+	return hal_ipc_cmd(HAL_SERVICE_ID_GATT, HAL_OP_GATT_CLIENT_CONNECT,
+					sizeof(cmd), &cmd, 0, NULL, NULL);
+}
+
+static bt_status_t disconnect(int client_if, const bt_bdaddr_t *bd_addr,
+								int conn_id)
+{
+	struct hal_cmd_gatt_client_disconnect cmd;
+
+	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, 0, NULL, NULL);
+}
+
+static bt_status_t listen(int client_if, bool start)
+{
+	struct hal_cmd_gatt_client_listen cmd;
+
+	cmd.client_if = client_if;
+	cmd.start = start;
+
+	return hal_ipc_cmd(HAL_SERVICE_ID_GATT, HAL_OP_GATT_CLIENT_LISTEN,
+					sizeof(cmd), &cmd, 0, NULL, NULL);
+}
+
+static bt_status_t refresh(int client_if, const bt_bdaddr_t *bd_addr)
+{
+	struct hal_cmd_gatt_client_refresh cmd;
+
+	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, 0, 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);
+
+	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->number = 1;
+	}
+
+	return hal_ipc_cmd(HAL_SERVICE_ID_GATT,
+					HAL_OP_GATT_CLIENT_SEARCH_SERVICE,
+					len, cmd, 0, 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);
+
+	cmd->conn_id = conn_id;
+
+	srvc_id_to_hal(&cmd->srvc_id[0], srvc_id);
+	len += sizeof(cmd->srvc_id[0]);
+	cmd->number = 1;
+
+	if (start_incl_srvc_id) {
+		srvc_id_to_hal(&cmd->srvc_id[1], start_incl_srvc_id);
+		len += sizeof(cmd->srvc_id[1]);
+		cmd->number++;
+	}
+
+	return hal_ipc_cmd(HAL_SERVICE_ID_GATT,
+					HAL_OP_GATT_CLIENT_GET_INCLUDED_SERVICE,
+					len, cmd, 0, 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);
+
+	cmd->conn_id = conn_id;
+
+	srvc_id_to_hal(&cmd->srvc_id, srvc_id);
+	cmd->number = 0;
+
+	if (start_char_id) {
+		gatt_id_to_hal(&cmd->gatt_id[0], start_char_id);
+		len += sizeof(cmd->gatt_id[0]);
+		cmd->number = 1;
+	}
+
+	return hal_ipc_cmd(HAL_SERVICE_ID_GATT,
+					HAL_OP_GATT_CLIENT_GET_CHARACTERISTIC,
+					len, cmd, 0, 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);
+
+	cmd->conn_id = conn_id;
+
+	srvc_id_to_hal(&cmd->srvc_id, srvc_id);
+
+	gatt_id_to_hal(&cmd->gatt_id[0], char_id);
+	len += sizeof(cmd->gatt_id[0]);
+	cmd->number = 1;
+
+	if (start_descr_id) {
+		gatt_id_to_hal(&cmd->gatt_id[1], start_descr_id);
+		len += sizeof(cmd->gatt_id[1]);
+		cmd->number++;
+	}
+
+	return hal_ipc_cmd(HAL_SERVICE_ID_GATT,
+					HAL_OP_GATT_CLIENT_GET_DESCRIPTOR,
+					len, cmd, 0 , 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;
+
+	cmd.conn_id = conn_id;
+	cmd.auth_req = auth_req;
+
+	srvc_id_to_hal(&cmd.srvc_id, srvc_id);
+	gatt_id_to_hal(&cmd.gatt_id, char_id);
+
+	return hal_ipc_cmd(HAL_SERVICE_ID_GATT,
+					HAL_OP_GATT_CLIENT_READ_CHARACTERISTIC,
+					sizeof(cmd), &cmd, 0, 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;
+
+	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->gatt_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, 0, 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;
+
+	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, 0, 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;
+
+	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, 0, NULL, NULL);
+}
+
+static bt_status_t execute_write(int conn_id, int execute)
+{
+	struct hal_cmd_gatt_client_execute_write cmd;
+
+	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, 0, 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;
+
+	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, 0, 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;
+
+	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, 0, 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;
+
+	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, 0, 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;
+
+	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(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)
+{
+	char buf[IPC_MTU];
+	struct hal_cmd_gatt_client_set_adv_data *cmd = (void *) buf;
+	size_t cmd_len = sizeof(*cmd) + manufacturer_len;
+
+	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;
+
+	memcpy(cmd->manufacturer_data, manufacturer_data, manufacturer_len);
+
+	return hal_ipc_cmd(HAL_SERVICE_ID_GATT, HAL_OP_GATT_CLIENT_SET_ADV_DATA,
+						cmd_len, cmd, 0, NULL, NULL);
+}
+
+static bt_status_t test_command(int command, btgatt_test_params_t *params)
+{
+	struct hal_cmd_gatt_client_test_command cmd;
+
+	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, 0, NULL, NULL);
+}
+
+/* Server API */
+
+static bt_status_t register_server(bt_uuid_t *uuid)
+{
+	struct hal_cmd_gatt_server_register cmd;
+
+	memcpy(cmd.uuid, uuid, sizeof(*uuid));
+
+	return hal_ipc_cmd(HAL_SERVICE_ID_GATT, HAL_OP_GATT_SERVER_REGISTER,
+					sizeof(cmd), &cmd, 0, NULL, NULL);
+}
+
+static bt_status_t unregister_server(int server_if)
+{
+	struct hal_cmd_gatt_server_unregister cmd;
+
+	cmd.server_if = server_if;
+
+	return hal_ipc_cmd(HAL_SERVICE_ID_GATT, HAL_OP_GATT_SERVER_UNREGISTER,
+					sizeof(cmd), &cmd, 0, NULL, NULL);
+}
+
+static bt_status_t server_connect(int server_if, const bt_bdaddr_t *bd_addr,
+								bool is_direct)
+{
+	struct hal_cmd_gatt_server_connect cmd;
+
+	cmd.server_if = server_if;
+	cmd.is_direct = is_direct;
+
+	memcpy(cmd.bdaddr, bd_addr, sizeof(*bd_addr));
+
+	return hal_ipc_cmd(HAL_SERVICE_ID_GATT, HAL_OP_GATT_SERVER_CONNECT,
+					sizeof(cmd), &cmd, 0, NULL, NULL);
+}
+
+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;
+
+	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, 0, 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;
+
+	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, 0, 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;
+
+	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, 0, 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;
+
+	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, 0, 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;
+
+	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, 0, NULL, NULL);
+}
+
+static bt_status_t start_service(int server_if, int service_handle,
+								int transport)
+{
+	struct hal_cmd_gatt_server_start_service cmd;
+
+	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, 0, NULL, NULL);
+}
+
+static bt_status_t stop_service(int server_if, int service_handle)
+{
+	struct hal_cmd_gatt_server_stop_service cmd;
+
+	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, 0, NULL, NULL);
+}
+
+static bt_status_t delete_service(int server_if, int service_handle)
+{
+	struct hal_cmd_gatt_server_delete_service cmd;
+
+	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, 0, 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;
+
+	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, 0, 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);
+
+	cmd->conn_id = conn_id;
+	cmd->trans_id = trans_id;
+	cmd->status = status;
+	cmd->len = sizeof(*response);
+
+	memcpy(cmd->data, response, sizeof(*response));
+
+	return hal_ipc_cmd(HAL_SERVICE_ID_GATT,
+					HAL_OP_GATT_SERVER_SEND_RESPONSE,
+					cmd_len, cmd, 0, 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;
+
+	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_GATT);
+	}
+
+	return ret;
+}
+
+static void cleanup(void)
+{
+	struct hal_cmd_unregister_module cmd;
+
+	DBG("");
+
+	if (!interface_ready())
+		return;
+
+	cbs = NULL;
+
+	cmd.service_id = HAL_SERVICE_ID_GATT;
+
+	hal_ipc_cmd(HAL_SERVICE_ID_CORE, HAL_OP_UNREGISTER_MODULE,
+					sizeof(cmd), &cmd, 0, NULL, NULL);
+
+	hal_ipc_unregister(HAL_SERVICE_ID_GATT);
+}
+
+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,
+	.get_device_type = get_device_type,
+	.set_adv_data = set_adv_data,
+	.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/android/hal-handsfree.c b/bluez/android/hal-handsfree.c
new file mode 100644
index 0000000..5a05975
--- /dev/null
+++ b/bluez/android/hal-handsfree.c
@@ -0,0 +1,591 @@
+/*
+ * 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"
+
+#define MODE_PROPERTY_NAME "persist.sys.bluetooth.handsfree"
+
+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)
+{
+	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)
+{
+	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)
+{
+	struct hal_ev_handsfree_vr_state *ev = buf;
+
+	if (cbs->vr_cmd_cb)
+		cbs->vr_cmd_cb(ev->state);
+}
+
+static void handle_answer(void *buf, uint16_t len)
+{
+	if (cbs->answer_call_cmd_cb)
+		cbs->answer_call_cmd_cb();
+}
+
+static void handle_hangup(void *buf, uint16_t len)
+{
+	if (cbs->hangup_call_cmd_cb)
+		cbs->hangup_call_cmd_cb();
+}
+
+static void handle_volume(void *buf, uint16_t len)
+{
+	struct hal_ev_handsfree_volume *ev = buf;
+
+	if (cbs->volume_cmd_cb)
+		cbs->volume_cmd_cb(ev->type, ev->volume);
+}
+
+static void handle_dial(void *buf, uint16_t len)
+{
+	struct hal_ev_handsfree_dial *ev = buf;
+	uint16_t num_len = ev->number_len;
+
+	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)
+		cbs->dial_call_cmd_cb((char *) ev->number);
+	else
+		cbs->dial_call_cmd_cb(NULL);
+}
+
+static void handle_dtmf(void *buf, uint16_t len)
+{
+	struct hal_ev_handsfree_dtmf *ev = buf;
+
+	if (cbs->dtmf_cmd_cb)
+		cbs->dtmf_cmd_cb(ev->tone);
+}
+
+static void handle_nrec(void *buf, uint16_t len)
+{
+	struct hal_ev_handsfree_nrec *ev = buf;
+
+	if (cbs->nrec_cmd_cb)
+		cbs->nrec_cmd_cb(ev->nrec);
+}
+
+static void handle_chld(void *buf, uint16_t len)
+{
+	struct hal_ev_handsfree_chld *ev = buf;
+
+	if (cbs->chld_cmd_cb)
+		cbs->chld_cmd_cb(ev->chld);
+}
+
+static void handle_cnum(void *buf, uint16_t len)
+{
+	if (cbs->cnum_cmd_cb)
+		cbs->cnum_cmd_cb();
+}
+
+static void handle_cind(void *buf, uint16_t len)
+{
+	if (cbs->cind_cmd_cb)
+		cbs->cind_cmd_cb();
+}
+
+static void handle_cops(void *buf, uint16_t len)
+{
+	if (cbs->cops_cmd_cb)
+		cbs->cops_cmd_cb();
+}
+
+static void handle_clcc(void *buf, uint16_t len)
+{
+	if (cbs->clcc_cmd_cb)
+		cbs->clcc_cmd_cb();
+}
+
+static void handle_unknown_at(void *buf, uint16_t len)
+{
+	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)
+		cbs->unknown_at_cmd_cb((char *) ev->buf);
+}
+
+static void handle_hsp_key_press(void *buf, uint16_t len)
+{
+	if (cbs->key_pressed_cmd_cb)
+		cbs->key_pressed_cmd_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_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, 0},
+	/*HAL_EV_HANDSFREE_HANGUP */
+	{handle_hangup, false, 0},
+	/* 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, 0},
+	/* HAL_EV_HANDSFREE_CIND */
+	{handle_cind, false, 0},
+	/* HAL_EV_HANDSFREE_COPS */
+	{handle_cops, false, 0},
+	/* HAL_EV_HANDSFREE_CLCC */
+	{handle_clcc, false, 0},
+	/* 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, 0},
+};
+
+static uint8_t get_mode(void)
+{
+	char value[PROPERTY_VALUE_MAX];
+
+	if (property_get(MODE_PROPERTY_NAME, value, "") > 0 &&
+					(!strcasecmp(value, "hfp")))
+		return HAL_MODE_HANDSFREE_HFP;
+
+	if (property_get(MODE_PROPERTY_NAME, value, "") > 0 &&
+					(!strcasecmp(value, "hfp_wbs")))
+		return HAL_MODE_HANDSFREE_HFP_WBS;
+
+	return HAL_MODE_HANDSFREE_HSP_ONLY;
+}
+
+static bt_status_t init(bthf_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, ev_handlers,
+				sizeof(ev_handlers)/sizeof(ev_handlers[0]));
+
+	cmd.service_id = HAL_SERVICE_ID_HANDSFREE;
+	cmd.mode = get_mode();
+
+	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_HANDSFREE);
+	}
+
+	return ret;
+}
+
+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, 0, 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,
+				0, 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, 0, 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, 0, 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, HAL_OP_HANDSFREE_START_VR,
+							0, NULL, 0, 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, HAL_OP_HANDSFREE_STOP_VR,
+							0, NULL, 0, NULL, NULL);
+}
+
+static bt_status_t volume_control(bthf_volume_type_t type, int volume)
+{
+	struct hal_cmd_handsfree_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,
+				HAL_OP_HANDSFREE_VOLUME_CONTROL, sizeof(cmd),
+				&cmd, 0, NULL, NULL);
+}
+
+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, 0, NULL, NULL);
+}
+
+static bt_status_t cops_response(const char *cops)
+{
+	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;
+
+	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, 0, NULL, NULL);
+}
+
+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)
+{
+	struct hal_cmd_handsfree_cind_response cmd;
+
+	DBG("");
+
+	if (!interface_ready())
+		return BT_STATUS_NOT_READY;
+
+	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, 0, NULL, NULL);
+}
+
+static bt_status_t formatted_at_response(const char *rsp)
+{
+	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;
+
+	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, 0, NULL, NULL);
+}
+
+static bt_status_t at_response(bthf_at_response_t response, int error)
+{
+	struct hal_cmd_handsfree_at_response cmd;
+
+	DBG("");
+
+	if (!interface_ready())
+		return BT_STATUS_NOT_READY;
+
+	cmd.response = response;
+	cmd.error = error;
+
+	return hal_ipc_cmd(HAL_SERVICE_ID_HANDSFREE,
+					HAL_OP_HANDSFREE_AT_RESPONSE,
+					sizeof(cmd), &cmd, 0, NULL, NULL);
+}
+
+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)
+{
+	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;
+
+	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, 0, NULL, NULL);
+}
+
+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, 0, NULL, NULL);
+}
+
+static void cleanup(void)
+{
+	struct hal_cmd_unregister_module cmd;
+
+	DBG("");
+
+	if (!interface_ready())
+		return;
+
+	cbs = NULL;
+
+	cmd.service_id = HAL_SERVICE_ID_HANDSFREE;
+
+	hal_ipc_cmd(HAL_SERVICE_ID_CORE, HAL_OP_UNREGISTER_MODULE,
+					sizeof(cmd), &cmd, 0, NULL, NULL);
+
+	hal_ipc_unregister(HAL_SERVICE_ID_HANDSFREE);
+}
+
+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
+};
+
+bthf_interface_t *bt_get_handsfree_interface(void)
+{
+	return &iface;
+}
diff --git a/bluez/android/hal-health.c b/bluez/android/hal-health.c
new file mode 100644
index 0000000..e6fe65a
--- /dev/null
+++ b/bluez/android/hal-health.c
@@ -0,0 +1,162 @@
+/*
+ * 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-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;
+}
+
+/* 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[] = {
+};
+
+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, 0, 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;
+
+	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 == HAL_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, 0, 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;
+
+	ret = hal_ipc_cmd(HAL_SERVICE_ID_CORE, HAL_OP_REGISTER_MODULE,
+					sizeof(cmd), &cmd, 0, 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;
+
+	cbacks = NULL;
+
+	cmd.service_id = HAL_SERVICE_ID_HEALTH;
+
+	hal_ipc_cmd(HAL_SERVICE_ID_CORE, HAL_OP_UNREGISTER_MODULE,
+					sizeof(cmd), &cmd, 0, NULL, NULL);
+
+	hal_ipc_unregister(HAL_SERVICE_ID_HEALTH);
+}
+
+static bthl_interface_t health_if = {
+	.size = sizeof(health_if),
+	.init = init,
+	.register_application = NULL,
+	.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/android/hal-hidhost.c b/bluez/android/hal-hidhost.c
new file mode 100644
index 0000000..16253e3
--- /dev/null
+++ b/bluez/android/hal-hidhost.c
@@ -0,0 +1,407 @@
+/*
+ * 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)
+{
+	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)
+{
+	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)
+{
+	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)
+{
+	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)
+{
+	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)
+{
+	struct hal_ev_hidhost_virtual_unplug *ev = buf;
+
+	if (cbacks->virtual_unplug_cb)
+		cbacks->virtual_unplug_cb((bt_bdaddr_t *) ev->bdaddr,
+								ev->status);
+}
+
+/* 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 */
+		.handler = handle_conn_state,
+		.var_len = false,
+		.data_len = sizeof(struct hal_ev_hidhost_conn_state)
+	},
+	{	/* HAL_EV_HIDHOST_INFO */
+		.handler = handle_info,
+		.var_len = false,
+		.data_len = sizeof(struct hal_ev_hidhost_info),
+	},
+	{	/* HAL_EV_HIDHOST_PROTO_MODE */
+		.handler = handle_proto_mode,
+		.var_len = false,
+		.data_len = sizeof(struct hal_ev_hidhost_proto_mode),
+	},
+	{	/* HAL_EV_HIDHOST_IDLE_TIME */
+		.handler = handle_idle_time,
+		.var_len = false,
+		.data_len = sizeof(struct hal_ev_hidhost_idle_time),
+	},
+	{	/* HAL_EV_HIDHOST_GET_REPORT */
+		.handler = handle_get_report,
+		.var_len = true,
+		.data_len = sizeof(struct hal_ev_hidhost_get_report),
+	},
+	{	/* HAL_EV_HIDHOST_VIRTUAL_UNPLUG */
+		.handler = handle_virtual_unplug,
+		.var_len = false,
+		.data_len = sizeof(struct hal_ev_hidhost_virtual_unplug),
+	},
+};
+
+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, 0, 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, 0, 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, 0, 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, 0, 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, 0, 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, 0, 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, 0, 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, 0, 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, 0, 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;
+
+	ret = hal_ipc_cmd(HAL_SERVICE_ID_CORE, HAL_OP_REGISTER_MODULE,
+					sizeof(cmd), &cmd, 0, 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;
+
+	cbacks = NULL;
+
+	cmd.service_id = HAL_SERVICE_ID_HIDHOST;
+
+	hal_ipc_cmd(HAL_SERVICE_ID_CORE, HAL_OP_UNREGISTER_MODULE,
+					sizeof(cmd), &cmd, 0, NULL, NULL);
+
+	hal_ipc_unregister(HAL_SERVICE_ID_HIDHOST);
+}
+
+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/android/hal-ipc-api.txt b/bluez/android/hal-ipc-api.txt
new file mode 100644
index 0000000..5613e85
--- /dev/null
+++ b/bluez/android/hal-ipc-api.txt
@@ -0,0 +1,2136 @@
+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 0x80.
+
+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.
+
+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
+
+
+Core Service (ID 0)
+===================
+
+	Opcode 0x00 - Error response
+
+		Response parameters: Status (1 octet)
+
+		Valid status values: 0x01	Failed
+
+	Opcode 0x01 - Register module command/response
+
+		Command parameters: Service id (1 octet)
+		                    Mode (1 octet)
+		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.
+
+
+Bluetooth Core HAL (ID 1)
+=========================
+
+Android HAL name: "bluetooth" (BT_HARDWARE_MODULE_ID)
+
+Commands and responses:
+
+	Opcode 0x00 - Error response
+
+		Response parameters: Status (1 octet)
+
+	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)
+		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
+
+		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
+
+	Opcode 0x01 - Listen command/response
+
+		Command parameters: Socket type (1 octet)
+		                    Service name (256 octets)
+		                    Service UUID (16 octets)
+		                    Channel (2 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 (2 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
+
+		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
+
+	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:
+
+	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)
+
+		Valid 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
+
+
+Bluetooth PAN HAL (ID 4)
+========================
+
+Android HAL name: "pan" (BT_PROFILE_PAN_ID)
+
+Commands and responses:
+
+	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
+
+	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
+
+		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
+
+	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: <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 - 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)
+		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)
+		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)
+		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)
+		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)
+		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.
+
+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)
+
+		Valid voice recognition states: 0x00 = Stopped
+		                                0x01 = Started
+
+	Opcode 0x84 - Answer Call Command notification
+
+		Notification parameters: <none>
+
+	Opcode 0x85 - Hangup Call Command notification
+
+		Notification parameters: <none>
+
+	Opcode 0x86 - Volume Command notification
+
+		Notification parameters: Volume type (1 octet)
+		                         Volume (1 octet)
+
+		Valid volume types: 0x00 = Speaker
+		                    0x01 = Microphone
+
+	Opcode 0x87 - Dial Call Command notification
+
+		Notification parameters: Number (string)
+
+	Opcode 0x88 - DTMF Command notification
+
+		Notification parameters: Tone (1 octet)
+
+	Opcode 0x89 - NREC Command notification
+
+		Notification parameters: NREC types (1 octet)
+
+		Valid NREC types: 0x00 = Stop
+		                  0x01 = Start
+
+	Opcode 0x8a - CHLD Command notification
+
+		Notification parameters: NREC types (1 octet)
+
+		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: <none>
+
+	Opcode 0x8c - CIND Command notification
+
+		Notification parameters: <none>
+
+	Opcode 0x8d - COPS Command notification
+
+		Notification parameters: <none>
+
+	Opcode 0x8e - CLCC Command notification
+
+		Notification parameters: <none>
+
+	Opcode 0x8f - Unknown AT Command notification
+
+		Notification parameters: AT command (string)
+
+	Opcode 0x90 - Key Pressed Command notification
+
+		Notification parameters: <none>
+
+
+Bluetooth Advanced Audio HAL (ID 6)
+===================================
+
+Android HAL name: "a2dp" (BT_PROFILE_ADVANCED_AUDIO_ID)
+
+Commands and responses:
+
+	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
+
+	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
+
+
+Bluetooth Health HAL (ID 7)
+===========================
+
+Android HAL name: "health" (BT_PROFILE_HEALTH_ID)
+
+Commands and responses:
+
+	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
+
+	Opcode 0x01 - Register Application command/response
+
+		Command parameters: Application name (string)
+		                    Provider name (string)
+		                    Service name (string)
+		                    Service description (string)
+		                    Number of MDEP (1 octet)
+		                    MDEP Role # (1 octet)
+		                    Data type # (1 octet)
+		                    Channel type # (1 octet)
+		                    MDEP description # (string)
+		                    ...
+		Response parameters: Application ID (2 octets)
+
+		In case of an error, the error response will be returned.
+
+	Opcode 0x02 - 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 0x03 - 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 0x04 - 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 HAL (ID 8)
+===================================
+
+Android HAL name: "avrcp" (BT_PROFILE_AV_RC_ID)
+
+Commands and responses:
+
+	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
+
+	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)
+
+Commands and responses:
+
+	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
+
+	Opcode 0x01 - Register Client command/response
+
+		Command parameters: Service UUID (16 octets)
+		Response parameters: <none>
+
+		In case of an error, the error response will be returned.
+
+	Opcode 0x02 - Unregister Client command/response
+
+		Command parameters: Client Interface (4 octets)
+		Response parameters: <none>
+
+		In case of an error, the error response will be returned.
+
+	Opcode 0x03 - 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 - Connect Device command/response
+
+		Command parameters: Client Interface (4 octets)
+		                    Remote address (6 octets)
+		                    Is Direct (1 octet)
+		Response parameters: <none>
+
+		In case of an error, the error response will be returned.
+
+	Opcode 0x05 - 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 - 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 - 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 - Search Service command/response
+
+		Command parameters: Connection ID (4 octets)
+		                    Number of UUID Filters (1 octet)
+		                    UUID Filter # (16 octets)
+		                    ...
+		Response parameters: <none>
+
+		Valid Number of UUID Filters: 0x00
+		                              0x01
+
+		In case of an error, the error response will be returned.
+
+	Opcode 0x09 - Get Included Service command/response
+
+		Command parameters: Connection ID (4 octets)
+		                    Number of GATT Service ID Elements (1 octet)
+		                    GATT Service ID # UUID (16 octets)
+		                    GATT Service ID # Instance ID (1 octet)
+		                    GATT Service ID # Is Primary (1 octet)
+		                    ...
+		Response parameters: <none>
+
+		Valid Number of GATT Service ID Elements: 0x01
+		                                          0x02
+
+		In case of an error, the error response will be returned.
+
+	Opcode 0x0a - Get Characteristic command/response
+
+		Command parameters: Connection ID (4 octets)
+		                    GATT Service ID (18 octets)
+		                    Number of GATT ID Elements (1 octet)
+		                    GATT ID # UUID (16 octets)
+		                    GATT ID # Instance ID (1 octet)
+		                    ...
+		Response parameters: <none>
+
+		Valid GATT Service ID: UUID (16 octets)
+		                       Instance ID (1 octet)
+		                       Is Primary (1 octet)
+
+		Valid Number of GATT ID Elements: 0x00
+		                                  0x01
+
+		In case of an error, the error response will be returned.
+
+	Opcode 0x0b - Get Descriptor command/response
+
+		Command parameters: Connection ID (4 octets)
+		                    GATT Service ID (18 octets)
+		                    Number of GATT ID Elements (1 octet)
+		                    GATT ID # UUID (16 octets)
+		                    GATT ID # Instance ID (1 octet)
+		                    ...
+		Response parameters: <none>
+
+		Valid GATT Service ID: UUID (16 octets)
+		                       Instance ID (1 octet)
+		                       Is Primary (1 octet)
+
+		Valid Number of GATT ID Elements: 0x01
+		                                  0x02
+
+		In case of an error, the error response will be returned.
+
+	Opcode 0x0c - Read Characteristic command/response
+
+		Command parameters: Connection ID (4 octets)
+		                    GATT Service ID (18 octets)
+		                    GATT ID (17 octets)
+		                    Authorization (4 octets)
+		Response parameters: <none>
+
+		Valid GATT Service ID: UUID (16 octets)
+		                       Instance ID (1 octet)
+		                       Is Primary (1 octet)
+
+		Valid GATT ID: UUID (16 octets)
+		               Instance ID (1 octet)
+
+		In case of an error, the error response will be returned.
+
+	Opcode 0x0d - Write Characteristic command/response
+
+		Command parameters: Connection ID (4 octets)
+		                    GATT Service ID (18 octets)
+		                    GATT ID (17 octets)
+		                    Write Type (4 octets)
+		                    Length (4 octets)
+		                    Authorization Req. (4 octets)
+		                    Value (variable)
+		Response parameters: <none>
+
+		Valid GATT Service ID: UUID (16 octets)
+		                       Instance ID (1 octet)
+		                       Is Primary (1 octet)
+
+		Valid GATT ID: UUID (16 octets)
+		               Instance ID (1 octet)
+
+		In case of an error, the error response will be returned.
+
+	Opcode 0x0e - Read Descriptor command/response
+
+		Command parameters: Connection ID (4 octets)
+		                    GATT Service ID (18 octets)
+		                    GATT ID (17 octets)
+		                    Descr. GATT ID (17 octets)
+		                    Authorization Req. (4 octets)
+		Response parameters: <none>
+
+		Valid GATT Service ID: UUID (16 octets)
+		                       Instance ID (1 octet)
+		                       Is Primary (1 octet)
+
+		Valid GATT ID: UUID (16 octets)
+		               Instance ID (1 octet)
+
+		Valid Descr. GATT ID: UUID (16 octets)
+		                      Instance ID (1 octet)
+
+		In case of an error, the error response will be returned.
+
+	Opcode 0x0f - Write Descriptor command/response
+
+		Command parameters: Connection ID (4 octets)
+		                    GATT Service ID (18 octets)
+		                    GATT ID (17 octets)
+		                    Descr. GATT ID (17 octets)
+		                    Write Type (4 octets)
+		                    Length (4 octets)
+		                    Authorization Req. (4 octets)
+		                    Value (variable)
+		Response parameters: <none>
+
+		Valid GATT Service ID: UUID (16 octets)
+		                       Instance ID (1 octet)
+		                       Is Primary (1 octet)
+
+		Valid GATT ID: UUID (16 octets)
+		               Instance ID (1 octet)
+
+		Valid Descr. GATT ID: UUID (16 octets)
+		                      Instance ID (1 octet)
+
+		In case of an error, the error response will be returned.
+
+	Opcode 0x10 - 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 - Register For Notification command/response
+
+		Command parameters: Client Interface (4 octets)
+		                    Remote address (6 octets)
+		                    GATT Service ID (18 octets)
+		                    GATT ID (17 octets)
+		Response parameters: <none>
+
+		Valid GATT Service ID: UUID (16 octets)
+		                       Instance ID (1 octet)
+		                       Is Primary (1 octet)
+
+		Valid GATT ID: UUID (16 octets)
+		               Instance ID (1 octet)
+
+		In case of an error, the error response will be returned.
+
+	Opcode 0x12 - Deregister For Notification command/response
+
+		Command parameters: Client Interface (4 octets)
+		                    Remote address (6 octets)
+		                    GATT Service ID (18 octets)
+		                    GATT ID (17 octets)
+		Response parameters: <none>
+
+		Valid GATT Service ID: UUID (16 octets)
+		                       Instance ID (1 octet)
+		                       Is Primary (1 octet)
+
+		Valid GATT ID: UUID (16 octets)
+		               Instance ID (1 octet)
+
+		In case of an error, the error response will be returned.
+
+	Opcode 0x13 - 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 - 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 - 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 - 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 - Register Server command/response
+
+		Command parameters: UUID (16 octets)
+		Response parameters: <none>
+
+		In case of an error, the error response will be returned.
+
+	Opcode 0x18 - Unregister Server command/response
+
+		Command parameters: Server (4 octets)
+		Response parameters: <none>
+
+		In case of an error, the error response will be returned.
+
+	Opcode 0x19 - Connect Peripheral command/response
+
+		Command parameters: Server (4 octets)
+		                    Remote address (6 octes)
+		                    Is Direct (1 octet)
+		Response parameters: <none>
+
+		In case of an error, the error response will be returned.
+
+	Opcode 0x1a - Disconnect Peripheral command/response
+
+		Command parameters: Server (4 octets)
+		                    Remote address (6 octes)
+		                    Connection ID (1 octet)
+		Response parameters: <none>
+
+		In case of an error, the error response will be returned.
+
+	Opcode 0x1b - 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 - 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 - 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 - 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 - 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 - 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 - 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 - 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 - Send Response command/response
+
+		Command parameters: Connection ID (4 octets)
+		                    Transaction ID (4 octets)
+		                    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.
+
+Notifications:
+
+	Opcode 0x81 - Register Client notification
+
+		Notification parameters: Status (4 octets)
+		                         Client Interface (4 octets)
+		                         UUID (16 octets)
+
+	Opcode 0x82 - Scan Result notification
+
+		Notification parameters: Address (6 octets)
+		                         RSSI (4 octets)
+		                         Length (2 octets)
+		                         Data (variable)
+
+	Opcode 0x83 - Connect Device notification
+
+		Notification parameters: Connection ID (4 octets)
+		                         Status (4 octets)
+		                         Client Interface (4 octets)
+		                         Address (6 octets)
+
+	Opcode 0x84 - Disconnect Device notification
+
+		Notification parameters: Connection ID (4 octets)
+		                         Status (4 octets)
+		                         Client Interface (4 octets)
+		                         Address (6 octets)
+
+	Opcode 0x85 - Search Complete notification
+
+		Notification parameters: Connection ID (4 octets)
+		                         Status (4 octets)
+
+	Opcode 0x86 - Search Result notification
+
+		Notification parameters: Connection ID (4 octets)
+		                         GATT Service ID (18 octets)
+
+		Valid GATT Service ID: UUID (16 octets)
+		                       Instance ID (1 octets)
+		                       Is Primary (1 octet)
+
+	Opcode 0x87 - Get Characteristic notification
+
+		Notification parameters: Connection ID (4 octets)
+		                         Status (4 octets)
+		                         GATT Service ID (18 octets)
+		                         GATT Char. ID (17 octets)
+		                         Char Prop. (4 octets)
+
+		Valid GATT Service: As described in Search Result
+
+		Valid GATT Char. ID: UUID (16 octets)
+		                     Instance ID (1 octet)
+
+	Opcode 0x88 - Get Descriptor notification
+
+		Notification parameters: Connection ID (4 octets)
+		                         Status (4 octets)
+		                         GATT Service ID (18 octets)
+		                         GATT Char. ID (17 octets)
+		                         GATT Descr. ID (17 octets)
+
+		Valid GATT Service & Char. ID: As described in Get Characteristic
+
+		Valid GATT Descr. ID: UUID (16 octets)
+		                      Instance ID (1 octet)
+
+	Opcode 0x89 - Get Included Service notification
+
+		Notification parameters: Connection ID (4 octets)
+		                         Status (4 octets)
+		                         GATT Service ID (18 octets)
+		                         GATT Incl. Service ID (18 octets)
+
+		Valid GATT Service & Incl. Service ID: As described in Search Result
+
+	Opcode 0x8a - Register For Notification notification
+
+		Notification parameters: Connection ID (4 octets)
+		                         Registered (4 octets)
+		                         Status (4 octets)
+		                         GATT Service ID (18 octets)
+		                         GATT Char. ID (17 octets)
+
+		Valid GATT Service ID: As described in Get Characteristic
+
+		Valid GATT Char. ID: As described in Get Characteristic
+
+	Opcode 0x8b - Notify notification
+
+		Notification parameters: Connection ID (4 octets)
+		                         Address (6 octets)
+		                         GATT Service ID (18 octets)
+		                         GATT Char. ID (17 octets)
+		                         Is Notify (1 octet)
+		                         Length (2 octets)
+		                         Value (variable)
+
+		Valid GATT Service ID: As described in Get Characteristic
+
+		Valid GATT Char. ID: As described in Get Characteristic
+
+	Opcode 0x8c - 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 Char. ID (17 octets)
+		                            GATT Descr. ID (17 octets)
+		                            Value Type (4 octets)
+		                            Status (1 octet)
+		                            Length (2 octets)
+		                            Value (variable)
+
+		Valid GATT Service ID: As described in Get Characteristic
+
+		Valid GATT Char. & Decr. ID: As described in Get Descriptor
+
+	Opcode 0x8d - 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)
+
+		Valid GATT Service ID: As described in Get Descriptor
+
+		Valid GATT Char. & Decr. ID: As described in Get Descriptor
+
+	Opcode 0x8e - 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 - 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 - Execute Write notification
+
+		Notification parameters: Connection ID (4 octets)
+		                         Status (4 octets)
+
+	Opcode 0x91 - Read Remote RSSI notification
+
+		Notification parameters: Client (4 octets)
+		                         Address (6 octets)
+		                         RSSI (4 octets)
+		                         Status (4 octets)
+
+	Opcode 0x92 - Listen notification
+
+		Notification parameters: Status (4 octets)
+		                         Server Interface (4 octets)
+
+	Opcode 0x93 - Register Server notification
+
+		Notification parameters: Status (4 octets)
+		                         Server (4 octets)
+		                         UUID (16 octets)
+
+	Opcode 0x94 - Connection notification
+
+		Notification parameters: Connection ID (4 octets)
+		                         Server (4 octets)
+		                         Connected (4 octets)
+		                         Address (6 octets)
+
+	Opcode 0x95 - Service Added notification
+
+		Notification parameters: Status (4 octets)
+		                         Server (4 octets)
+		                         GATT Service ID (18 octets)
+		                         Service Handle (4 octets)
+
+		Valid GATT Service ID: UUID (16 octets)
+		                       Instance ID (1 octet)
+		                       Is Primary (1 octet)
+
+	Opcode 0x96 - Included Service Added notification
+
+		Notification patemeters: Status (4 octets)
+		                         Server (4 octets)
+		                         Service Handle (4 octets)
+		                         Included Service Handle (4 octets)
+
+	Opcode 0x97 - Characteristic Added notification
+
+		Notification parameters: Status (4 octets)
+		                         Server (4 octets)
+		                         UUID (16 octets)
+		                         Service Handle (4 octets)
+		                         Characteristic Handle (4 octets)
+
+	Opcode 0x98 - Descriptor Added notification
+
+		Notification parameters: Status (4 octets)
+		                         Server (4 octets)
+		                         UUID (6 octets)
+		                         Service Handle (4 octets)
+		                         Descriptor Handle (4 octets)
+
+	Opcode 0x99 - Service Started notification
+
+		Notification parameters: Status (4 octets)
+		                         Server (4 octets)
+		                         Service Handle (4 octets)
+
+	Opcode 0x9a - Service Stopped notification
+
+		Notification parameters: Status (4 octets)
+		                         Server (4 octets)
+		                         Service Handle (4 octets)
+
+	Opcode 0x9b - Service Deleted notification
+
+		Notification parameters: Status (4 octets)
+		                         Server (4 octets)
+		                         Service Handle (4 octets)
+
+	Opcode 0x9c - 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 - 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 - Request Execute Write notification
+
+		Notification parameters: Connection ID (4 octets)
+		                         Trans ID (4 octets)
+		                         Address (6 octets)
+		                         Execute Write (4 octets)
+
+	Opcode 0x9f - Response Confirmation notification
+
+		Notification parameters: Status (4 octets)
+		                         Handle (4 octets)
diff --git a/bluez/android/hal-ipc.c b/bluez/android/hal-ipc.c
new file mode 100644
index 0000000..5bef281
--- /dev/null
+++ b/bluez/android/hal-ipc.c
@@ -0,0 +1,456 @@
+/*
+ * 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 (5 * 1000)
+
+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 void handle_msg(void *buf, ssize_t len)
+{
+	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), aborting", len);
+		exit(EXIT_FAILURE);
+	}
+
+	if (len != (ssize_t) (sizeof(*msg) + msg->len)) {
+		error("IPC: message malformed (%zd bytes), aborting", len);
+		exit(EXIT_FAILURE);
+	}
+
+	/* if service is valid */
+	if (msg->service_id > HAL_SERVICE_ID_MAX) {
+		error("IPC: unknown service (0x%x), aborting",
+							msg->service_id);
+		exit(EXIT_FAILURE);
+	}
+
+	/* if service is registered */
+	if (!services[msg->service_id].handler) {
+		error("IPC: unregistered service (0x%x), aborting",
+							msg->service_id);
+		exit(EXIT_FAILURE);
+	}
+
+	/* if opcode fit valid range */
+	if (msg->opcode < HAL_MINIMUM_EVENT) {
+		error("IPC: invalid opcode for service 0x%x (0x%x), aborting",
+						msg->service_id, msg->opcode);
+		exit(EXIT_FAILURE);
+	}
+
+	/* 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), aborting",
+						msg->service_id, msg->opcode);
+		exit(EXIT_FAILURE);
+	}
+
+	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), aborting",
+				msg->service_id, msg->opcode, msg->len);
+		exit(EXIT_FAILURE);
+	}
+
+	handler->handler(msg->payload, msg->len);
+}
+
+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, aborting :%s",
+							strerror(errno));
+			exit(EXIT_FAILURE);
+		}
+
+		/* 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, aborting");
+			exit(EXIT_FAILURE);
+		}
+
+		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;
+			}
+		}
+
+		handle_msg(buf, ret);
+	}
+
+	close(notif_sk);
+	notif_sk = -1;
+
+	bt_thread_disassociate();
+
+	DBG("exit");
+
+	return NULL;
+}
+
+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_init(void)
+{
+	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, BLUEZ_HAL_SK_PATH, sizeof(BLUEZ_HAL_SK_PATH));
+
+	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;
+	}
+
+	/* Start Android Bluetooth daemon service */
+	if (property_set("bluetooth.start", "daemon") < 0) {
+		error("Failed to set bluetooth.start=daemon");
+		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;
+	}
+
+	info("bluetoothd connected");
+
+	close(sk);
+
+	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;
+	}
+
+	return true;
+}
+
+void hal_ipc_cleanup(void)
+{
+	pthread_mutex_lock(&cmd_sk_mutex);
+	close(cmd_sk);
+	cmd_sk = -1;
+	pthread_mutex_unlock(&cmd_sk_mutex);
+
+	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, aborting");
+		exit(EXIT_FAILURE);
+	}
+
+	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, aborting :%s", strerror(errno));
+		pthread_mutex_unlock(&cmd_sk_mutex);
+		exit(EXIT_FAILURE);
+	}
+
+	/* socket was shutdown */
+	if (ret == 0) {
+		error("Command socket closed, aborting");
+		exit(EXIT_FAILURE);
+	}
+
+	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);
+	if (ret < 0) {
+		error("Receiving command response failed, aborting :%s",
+							strerror(errno));
+		pthread_mutex_unlock(&cmd_sk_mutex);
+		exit(EXIT_FAILURE);
+	}
+
+	pthread_mutex_unlock(&cmd_sk_mutex);
+
+	if (ret < (ssize_t) sizeof(cmd)) {
+		error("Too small response received(%zd bytes), aborting", ret);
+		exit(EXIT_FAILURE);
+	}
+
+	if (cmd.service_id != service_id) {
+		error("Invalid service id (0x%x vs 0x%x), aborting",
+						cmd.service_id, service_id);
+		exit(EXIT_FAILURE);
+	}
+
+	if (ret != (ssize_t) (sizeof(cmd) + cmd.len)) {
+		error("Malformed response received(%zd bytes), aborting", ret);
+		exit(EXIT_FAILURE);
+	}
+
+	if (cmd.opcode != opcode && cmd.opcode != HAL_OP_STATUS) {
+		error("Invalid opcode received (0x%x vs 0x%x), aborting",
+						cmd.opcode, opcode);
+		exit(EXIT_FAILURE);
+	}
+
+	if (cmd.opcode == HAL_OP_STATUS) {
+		struct ipc_status *s = rsp;
+
+		if (sizeof(*s) != cmd.len) {
+			error("Invalid status length, aborting");
+			exit(EXIT_FAILURE);
+		}
+
+		if (s->code == HAL_STATUS_SUCCESS) {
+			error("Invalid success status response, aborting");
+			exit(EXIT_FAILURE);
+		}
+
+		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;
+			}
+		}
+	}
+
+	if (rsp_len)
+		*rsp_len = cmd.len;
+
+	return BT_STATUS_SUCCESS;
+}
diff --git a/bluez/android/hal-ipc.h b/bluez/android/hal-ipc.h
new file mode 100644
index 0000000..2fbf30f
--- /dev/null
+++ b/bluez/android/hal-ipc.h
@@ -0,0 +1,32 @@
+/*
+ * 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);
+	bool var_len;
+	size_t data_len;
+};
+
+bool hal_ipc_init(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/android/hal-log.h b/bluez/android/hal-log.h
new file mode 100644
index 0000000..63ff61b
--- /dev/null
+++ b/bluez/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/android/hal-msg.h b/bluez/android/hal-msg.h
new file mode 100644
index 0000000..b7fa8fc
--- /dev/null
+++ b/bluez/android/hal-msg.h
@@ -0,0 +1,1570 @@
+/*
+ *
+ *  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_MAX HAL_SERVICE_ID_GATT
+
+/* 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_OP_REGISTER_MODULE		0x01
+struct hal_cmd_register_module {
+	uint8_t service_id;
+	uint8_t mode;
+} __attribute__((packed));
+
+#define HAL_OP_UNREGISTER_MODULE	0x02
+struct hal_cmd_unregister_module {
+	uint8_t service_id;
+} __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_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 HAL_OP_CREATE_BOND		0x0d
+struct hal_cmd_create_bond {
+	uint8_t bdaddr[6];
+} __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));
+
+/* Bluetooth Socket HAL api */
+
+#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 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));
+
+struct hal_string {
+	uint8_t len;
+	uint8_t data[0];
+};
+
+#define HAL_OP_HEALTH_REG_APP		0x01
+struct hal_cmd_health_reg_app {
+	struct hal_string app_name;
+	struct hal_string provider_name;
+	struct hal_string service_name;
+	struct hal_string service_descr;
+	uint8_t num_of_mdep;
+
+	struct {
+		uint8_t role;
+		uint8_t data_type;
+		uint8_t channel_type;
+		struct  hal_string descr;
+	} mdep_cfg[0];
+} __attribute__((packed));
+
+struct hal_rsp_health_reg_app {
+	uint16_t app_id;
+} __attribute__((packed));
+
+#define HAL_OP_HEALTH_UNREG_APP		0x02
+struct hal_cmd_health_unreg_app {
+	uint16_t app_id;
+} __attribute__((packed));
+
+#define HAL_OP_HEALTH_CONNECT_CHANNEL	0x03
+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	0x04
+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
+
+#define HAL_OP_HANDSFREE_STOP_VR		0x06
+
+#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;
+} __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 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;
+} __attribute__((packed));
+
+#define HAL_OP_HANDSFREE_FORMATTED_AT_RESPONSE	0x0B
+struct hal_cmd_handsfree_formatted_at_response {
+	uint16_t len;
+	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;
+} __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;
+	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));
+
+/* 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;
+} __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 number;
+	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;
+	uint8_t number;
+	struct hal_gatt_srvc_id 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 number;
+	struct hal_gatt_gatt_id gatt_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;
+	uint8_t number;
+	struct hal_gatt_gatt_id gatt_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 gatt_id;
+	int32_t auth_req;
+} __attribute__((packed));
+
+#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 gatt_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));
+
+#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;
+	uint8_t  manufacturer_data[0];
+} __attribute__((packed));
+
+#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;
+} __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 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;
+	int32_t status;
+	uint16_t len;
+	uint8_t data[0];
+} __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_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_GENERAL_ERROR	0x06
+
+#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_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_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;
+} __attribute__((packed));
+
+#define HAL_EV_HANDSFREE_ANSWER		0x84
+
+#define HAL_EV_HANDSFREE_HANGUP		0x85
+
+#define HAL_EV_HANDSFREE_VOLUME		0x86
+struct hal_ev_handsfree_volume {
+	uint8_t type;
+	uint8_t volume;
+} __attribute__((packed));
+
+#define HAL_EV_HANDSFREE_DIAL		0x87
+struct hal_ev_handsfree_dial {
+	uint16_t number_len;
+	uint8_t number[0];
+} __attribute__((packed));
+
+#define HAL_EV_HANDSFREE_DTMF		0x88
+struct hal_ev_handsfree_dtmf {
+	uint8_t tone;
+} __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;
+} __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;
+} __attribute__((packed));
+
+#define HAL_EV_HANDSFREE_CNUM		0x8B
+
+#define HAL_EV_HANDSFREE_CIND		0x8C
+
+#define HAL_EV_HANDSFREE_COPS		0x8D
+
+#define HAL_EV_HANDSFREE_CLCC		0x8E
+
+#define HAL_EV_HANDSFREE_UNKNOWN_AT	0x8F
+struct hal_ev_handsfree_unknown_at {
+	uint16_t len;
+	uint8_t buf[0];
+} __attribute__((packed));
+
+#define HAL_EV_HANDSFREE_HSP_KEY_PRESS	0x90
+
+/* AVRCP 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));
+
+#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_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));
diff --git a/bluez/android/hal-pan.c b/bluez/android/hal-pan.c
new file mode 100644
index 0000000..f383081
--- /dev/null
+++ b/bluez/android/hal-pan.c
@@ -0,0 +1,205 @@
+/*
+ * 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 btpan_callbacks_t *cbs = NULL;
+
+static bool interface_ready(void)
+{
+	return cbs != NULL;
+}
+
+static void handle_conn_state(void *buf, uint16_t len)
+{
+	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)
+{
+	struct hal_ev_pan_ctrl_state *ev = buf;
+
+	/* FIXME: 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, fix this if issue fixed in PanService.Java.
+	 */
+	if (cbs->control_state_cb)
+		cbs->control_state_cb(ev->local_role, ev->state, ev->status,
+							(char *)ev->name);
+}
+
+/* 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 */
+		.handler = handle_ctrl_state,
+		.var_len = false,
+		.data_len = sizeof(struct hal_ev_pan_ctrl_state),
+	},
+	{	/* HAL_EV_PAN_CONN_STATE */
+		.handler = handle_conn_state,
+		.var_len = false,
+		.data_len = 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, 0, 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, 0, 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, 0, 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;
+
+	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_PAN);
+	}
+
+	return ret;
+}
+
+static void pan_cleanup(void)
+{
+	struct hal_cmd_unregister_module cmd;
+
+	DBG("");
+
+	if (!interface_ready())
+		return;
+
+	cbs = NULL;
+
+	cmd.service_id = HAL_SERVICE_ID_PAN;
+
+	hal_ipc_cmd(HAL_SERVICE_ID_CORE, HAL_OP_UNREGISTER_MODULE,
+					sizeof(cmd), &cmd, 0, NULL, NULL);
+
+	hal_ipc_unregister(HAL_SERVICE_ID_PAN);
+}
+
+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()
+{
+	return &pan_if;
+}
diff --git a/bluez/android/hal-socket.c b/bluez/android/hal-socket.c
new file mode 100644
index 0000000..cfd50d1
--- /dev/null
+++ b/bluez/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/android/hal-utils.c b/bluez/android/hal-utils.c
new file mode 100644
index 0000000..ceefefc
--- /dev/null
+++ b/bluez/android/hal-utils.c
@@ -0,0 +1,330 @@
+/*
+ * 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 "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
+
+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
+
+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 = bd_addr->address;
+
+	if (!bd_addr)
+		return strcpy(buf, "NULL");
+
+	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);
+}
+
+const char *btproperty2str(const bt_property_t *property)
+{
+	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:
+		{
+			int count = property->len / sizeof(bt_bdaddr_t);
+			char *ptr = property->val;
+
+			strcat(p, "{");
+
+			while (count--) {
+				strcat(p, bdaddr2str((bt_bdaddr_t *) ptr));
+				if (count)
+					strcat(p, ", ");
+				ptr += sizeof(bt_bdaddr_t);
+			}
+
+			strcat(p, "}");
+
+		}
+		break;
+
+	case BT_PROPERTY_UUIDS:
+		{
+			int count = property->len / sizeof(bt_uuid_t);
+			uint8_t *ptr = property->val;
+
+			strcat(p, "{");
+
+			while (count--) {
+				strcat(p, btuuid2str(ptr));
+				if (count)
+					strcat(p, ", ");
+				ptr += sizeof(bt_uuid_t);
+			}
+
+			strcat(p, "}");
+
+		}
+		break;
+
+	case BT_PROPERTY_SERVICE_RECORD:
+		{
+			bt_service_record_t *rec = property->val;
+
+			sprintf(p, "{%s, %d, %s}", btuuid2str(rec->uuid.uu),
+						rec->channel, rec->name);
+		}
+		break;
+
+	default:
+		sprintf(p, "%p", property->val);
+	}
+
+	return buf;
+}
diff --git a/bluez/android/hal-utils.h b/bluez/android/hal-utils.h
new file mode 100644
index 0000000..75de7e9
--- /dev/null
+++ b/bluez/android/hal-utils.h
@@ -0,0 +1,130 @@
+/*
+ * 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>
+
+#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);
+
+/**
+ * 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);
diff --git a/bluez/android/hal.h b/bluez/android/hal.h
new file mode 100644
index 0000000..6998e9a
--- /dev/null
+++ b/bluez/android/hal.h
@@ -0,0 +1,40 @@
+/*
+ * 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>
+
+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);
+
+void bt_thread_associate(void);
+void bt_thread_disassociate(void);
diff --git a/bluez/android/handsfree.c b/bluez/android/handsfree.c
new file mode 100644
index 0000000..7dfc6dc
--- /dev/null
+++ b/bluez/android/handsfree.c
@@ -0,0 +1,2600 @@
+/*
+ *
+ *  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 "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"
+
+#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;
+};
+
+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 {
+	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;
+	bool call_hanging_up;
+
+	uint8_t negotiated_codec;
+	uint8_t proposed_codec;
+	struct hfp_codec codecs[CODECS_COUNT];
+
+	guint ring;
+	bool hsp;
+
+	struct hfp_gw *gw;
+
+	GIOChannel *sco;
+	guint sco_watch;
+} device;
+
+static uint32_t hfp_ag_features = 0;
+
+static bdaddr_t adapter_addr;
+static struct ipc *hal_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 GIOChannel *sco_server = NULL;
+
+static void device_set_state(uint8_t state)
+{
+	struct hal_ev_handsfree_conn_state ev;
+	char address[18];
+
+	if (device.state == state)
+		return;
+
+	device.state = state;
+
+	ba2str(&device.bdaddr, address);
+	DBG("device %s state %u", address, state);
+
+	bdaddr2android(&device.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 device_set_audio_state(uint8_t state)
+{
+	struct hal_ev_handsfree_audio_state ev;
+	char address[18];
+
+	if (device.audio_state == state)
+		return;
+
+	device.audio_state = state;
+
+	ba2str(&device.bdaddr, address);
+	DBG("device %s audio state %u", address, state);
+
+	bdaddr2android(&device.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(void)
+{
+	memcpy(device.codecs, codecs_defaults, sizeof(device.codecs));
+
+	if (hfp_ag_features & HFP_AG_FEAT_CODEC)
+		device.codecs[MSBC_OFFSET].local_supported = true;
+}
+
+static void device_init(const bdaddr_t *bdaddr)
+{
+	bacpy(&device.bdaddr, bdaddr);
+
+	device.setup_state = HAL_HANDSFREE_CALL_STATE_IDLE;
+
+	memcpy(device.inds, inds_defaults, sizeof(device.inds));
+
+	init_codecs();
+
+	device_set_state(HAL_EV_HANDSFREE_CONN_STATE_CONNECTING);
+}
+
+static void device_cleanup(void)
+{
+	if (device.gw) {
+		hfp_gw_unref(device.gw);
+		device.gw = NULL;
+	}
+
+	device_set_state(HAL_EV_HANDSFREE_CONN_STATE_DISCONNECTED);
+
+	if (device.sco_watch) {
+		g_source_remove(device.sco_watch);
+		device.sco_watch = 0;
+	}
+
+	if (device.sco) {
+		g_io_channel_shutdown(device.sco, TRUE, NULL);
+		g_io_channel_unref(device.sco);
+		device.sco = NULL;
+	}
+
+	if (device.ring) {
+		g_source_remove(device.ring);
+		device.ring = 0;
+	}
+
+	device_set_audio_state(HAL_EV_HANDSFREE_AUDIO_STATE_DISCONNECTED);
+
+	memset(&device, 0, sizeof(device));
+}
+
+static void disconnect_watch(void *user_data)
+{
+	DBG("");
+
+	device_cleanup();
+}
+
+static void at_cmd_unknown(const char *command, void *user_data)
+{
+	uint8_t buf[IPC_MTU];
+	struct hal_ev_handsfree_unknown_at *ev = (void *) buf;
+
+	if (device.state != HAL_EV_HANDSFREE_CONN_STATE_SLC_CONNECTED) {
+		hfp_gw_send_result(device.gw, HFP_RESULT_ERROR);
+		hfp_gw_disconnect(device.gw);
+		return;
+	}
+
+	/* copy while string including terminating NULL */
+	ev->len = strlen(command) + 1;
+	memcpy(ev->buf, command, ev->len);
+
+	if (ev->len > IPC_MTU - sizeof(*ev)) {
+		hfp_gw_send_result(device.gw, HFP_RESULT_ERROR);
+		return;
+	}
+
+	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_gw_result *result, enum hfp_gw_cmd_type type,
+								void *user_data)
+{
+	struct hal_ev_handsfree_volume ev;
+	unsigned int val;
+
+	DBG("");
+
+	switch (type) {
+	case HFP_GW_CMD_TYPE_SET:
+		if (!hfp_gw_result_get_number(result, &val) || val > 15)
+			break;
+
+		if (hfp_gw_result_has_next(result))
+			break;
+
+		ev.type = HAL_HANDSFREE_VOLUME_TYPE_MIC;
+		ev.volume = val;
+
+		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(device.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(device.gw, HFP_RESULT_ERROR);
+}
+
+static void at_cmd_vgs(struct hfp_gw_result *result, enum hfp_gw_cmd_type type,
+								void *user_data)
+{
+	struct hal_ev_handsfree_volume ev;
+	unsigned int val;
+
+	DBG("");
+
+	switch (type) {
+	case HFP_GW_CMD_TYPE_SET:
+		if (!hfp_gw_result_get_number(result, &val) || val > 15)
+			break;
+
+		if (hfp_gw_result_has_next(result))
+			break;
+
+		ev.type = HAL_HANDSFREE_VOLUME_TYPE_SPEAKER;
+		ev.volume = val;
+
+		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(device.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(device.gw, HFP_RESULT_ERROR);
+}
+
+static void at_cmd_cops(struct hfp_gw_result *result, enum hfp_gw_cmd_type type,
+							void *user_data)
+{
+	unsigned int val;
+
+	switch (type) {
+	case HFP_GW_CMD_TYPE_SET:
+		if (!hfp_gw_result_get_number(result, &val) || val != 3)
+			break;
+
+		if (!hfp_gw_result_get_number(result, &val) || val != 0)
+			break;
+
+		if (hfp_gw_result_has_next(result))
+			break;
+
+		hfp_gw_send_result(device.gw, HFP_RESULT_OK);
+		return;
+	case HFP_GW_CMD_TYPE_READ:
+		ipc_send_notif(hal_ipc, HAL_SERVICE_ID_HANDSFREE,
+						HAL_EV_HANDSFREE_COPS, 0, NULL);
+		return;
+	case HFP_GW_CMD_TYPE_TEST:
+	case HFP_GW_CMD_TYPE_COMMAND:
+		break;
+	}
+
+	hfp_gw_send_result(device.gw, HFP_RESULT_ERROR);
+}
+
+static void at_cmd_bia(struct hfp_gw_result *result, enum hfp_gw_cmd_type type,
+							void *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] = device.inds[i].active;
+
+		i = 0;
+
+		do {
+			def = (i < IND_COUNT) ? device.inds[i].active : 0;
+
+			if (!hfp_gw_result_get_number_default(result, &val, def))
+				goto failed;
+
+			if (val > 1)
+				goto failed;
+
+			if (i < IND_COUNT) {
+				tmp[i] = val || device.inds[i].always_active;
+				i++;
+			}
+		} while (hfp_gw_result_has_next(result));
+
+		for (i = 0; i < IND_COUNT; i++)
+			device.inds[i].active = tmp[i];
+
+		hfp_gw_send_result(device.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(device.gw, HFP_RESULT_ERROR);
+}
+
+static void at_cmd_a(struct hfp_gw_result *result, enum hfp_gw_cmd_type type,
+							void *user_data)
+{
+	DBG("");
+
+	switch (type) {
+	case HFP_GW_CMD_TYPE_COMMAND:
+		if (hfp_gw_result_has_next(result))
+			break;
+
+		ipc_send_notif(hal_ipc, HAL_SERVICE_ID_HANDSFREE,
+					HAL_EV_HANDSFREE_ANSWER, 0, NULL);
+
+		/* Framework is not replying with result for ATA */
+		hfp_gw_send_result(device.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(device.gw, HFP_RESULT_ERROR);
+}
+
+static void at_cmd_d(struct hfp_gw_result *result, enum hfp_gw_cmd_type type,
+							void *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_gw_result_get_unquoted_string(result,
+						(char *) ev->number, 255))
+			break;
+
+		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(device.gw, HFP_RESULT_ERROR);
+}
+
+static void at_cmd_ccwa(struct hfp_gw_result *result, enum hfp_gw_cmd_type type,
+							void *user_data)
+{
+	unsigned int val;
+
+	DBG("");
+
+	switch (type) {
+	case HFP_GW_CMD_TYPE_SET:
+		if (!hfp_gw_result_get_number(result, &val) || val > 1)
+			break;
+
+		if (hfp_gw_result_has_next(result))
+			break;
+
+		device.ccwa_enabled = val;
+
+		hfp_gw_send_result(device.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(device.gw, HFP_RESULT_ERROR);
+}
+
+static void at_cmd_chup(struct hfp_gw_result *result, enum hfp_gw_cmd_type type,
+							void *user_data)
+{
+	DBG("");
+
+	switch (type) {
+	case HFP_GW_CMD_TYPE_COMMAND:
+		if (hfp_gw_result_has_next(result))
+			break;
+
+		ipc_send_notif(hal_ipc, HAL_SERVICE_ID_HANDSFREE,
+					HAL_EV_HANDSFREE_HANGUP, 0, NULL);
+
+		/* Framework is not replying with result for AT+CHUP */
+		hfp_gw_send_result(device.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(device.gw, HFP_RESULT_ERROR);
+}
+
+static void at_cmd_clcc(struct hfp_gw_result *result, enum hfp_gw_cmd_type type,
+							void *user_data)
+{
+	DBG("");
+
+	switch (type) {
+	case HFP_GW_CMD_TYPE_COMMAND:
+		if (hfp_gw_result_has_next(result))
+			break;
+
+		ipc_send_notif(hal_ipc, HAL_SERVICE_ID_HANDSFREE,
+					HAL_EV_HANDSFREE_CLCC, 0, NULL);
+		return;
+	case HFP_GW_CMD_TYPE_READ:
+	case HFP_GW_CMD_TYPE_TEST:
+	case HFP_GW_CMD_TYPE_SET:
+		break;
+	}
+
+	hfp_gw_send_result(device.gw, HFP_RESULT_ERROR);
+}
+
+static void at_cmd_cmee(struct hfp_gw_result *result, enum hfp_gw_cmd_type type,
+							void *user_data)
+{
+	unsigned int val;
+
+	DBG("");
+
+	switch (type) {
+	case HFP_GW_CMD_TYPE_SET:
+		if (!hfp_gw_result_get_number(result, &val) || val > 1)
+			break;
+
+		if (hfp_gw_result_has_next(result))
+			break;
+
+		device.cmee_enabled = val;
+
+		hfp_gw_send_result(device.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(device.gw, HFP_RESULT_ERROR);
+}
+
+static void at_cmd_clip(struct hfp_gw_result *result, enum hfp_gw_cmd_type type,
+							void *user_data)
+{
+	unsigned int val;
+
+	DBG("");
+
+	switch (type) {
+	case HFP_GW_CMD_TYPE_SET:
+		if (!hfp_gw_result_get_number(result, &val) || val > 1)
+			break;
+
+		if (hfp_gw_result_has_next(result))
+			break;
+
+		device.clip_enabled = val;
+
+		hfp_gw_send_result(device.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(device.gw, HFP_RESULT_ERROR);
+}
+
+static void at_cmd_vts(struct hfp_gw_result *result, enum hfp_gw_cmd_type type,
+							void *user_data)
+{
+	struct hal_ev_handsfree_dtmf ev;
+	char str[2];
+
+	DBG("");
+
+	switch (type) {
+	case HFP_GW_CMD_TYPE_SET:
+		if (!hfp_gw_result_get_unquoted_string(result, str, 2))
+			break;
+
+		if (!((str[0] >= '0' && str[0] <= '9') ||
+				(str[0] >= 'A' && str[0] <= 'D') ||
+				str[0] == '*' || str[0] == '#'))
+			break;
+
+		if (hfp_gw_result_has_next(result))
+			break;
+
+		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(device.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(device.gw, HFP_RESULT_ERROR);
+}
+
+static void at_cmd_cnum(struct hfp_gw_result *result, enum hfp_gw_cmd_type type,
+							void *user_data)
+{
+	DBG("");
+
+	switch (type) {
+	case HFP_GW_CMD_TYPE_COMMAND:
+		if (hfp_gw_result_has_next(result))
+			break;
+
+		ipc_send_notif(hal_ipc, HAL_SERVICE_ID_HANDSFREE,
+						HAL_EV_HANDSFREE_CNUM, 0, NULL);
+		return;
+	case HFP_GW_CMD_TYPE_SET:
+	case HFP_GW_CMD_TYPE_READ:
+	case HFP_GW_CMD_TYPE_TEST:
+		break;
+	}
+
+	hfp_gw_send_result(device.gw, HFP_RESULT_ERROR);
+}
+
+static void at_cmd_binp(struct hfp_gw_result *result, enum hfp_gw_cmd_type type,
+							void *user_data)
+{
+	DBG("");
+
+	/* TODO */
+
+	hfp_gw_send_result(device.gw, HFP_RESULT_ERROR);
+}
+
+static void at_cmd_bldn(struct hfp_gw_result *result, enum hfp_gw_cmd_type type,
+							void *user_data)
+{
+	struct hal_ev_handsfree_dial ev;
+
+	DBG("");
+
+	switch (type) {
+	case HFP_GW_CMD_TYPE_COMMAND:
+		if (hfp_gw_result_has_next(result))
+			break;
+
+		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;
+	}
+}
+
+static void at_cmd_bvra(struct hfp_gw_result *result, enum hfp_gw_cmd_type type,
+							void *user_data)
+{
+	struct hal_ev_handsfree_vr_state ev;
+	unsigned int val;
+
+	DBG("");
+
+	switch (type) {
+	case HFP_GW_CMD_TYPE_SET:
+		if (!hfp_gw_result_get_number(result, &val) || val > 1)
+			break;
+
+		if (hfp_gw_result_has_next(result))
+			break;
+
+		if (val)
+			ev.state = HAL_HANDSFREE_VR_STARTED;
+		else
+			ev.state = HAL_HANDSFREE_VR_STOPPED;
+
+		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(device.gw, HFP_RESULT_ERROR);
+}
+
+static void at_cmd_nrec(struct hfp_gw_result *result, enum hfp_gw_cmd_type type,
+							void *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_gw_result_get_number(result, &val) || val != 0)
+			break;
+
+		if (hfp_gw_result_has_next(result))
+			break;
+
+		ev.nrec = HAL_HANDSFREE_NREC_STOP;
+
+		ipc_send_notif(hal_ipc, HAL_SERVICE_ID_HANDSFREE,
+					HAL_EV_HANDSFREE_NREC, sizeof(ev), &ev);
+
+		/* Framework is not replying with result for AT+NREC */
+		hfp_gw_send_result(device.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(device.gw, HFP_RESULT_ERROR);
+}
+
+static void at_cmd_bsir(struct hfp_gw_result *result, enum hfp_gw_cmd_type type,
+							void *user_data)
+{
+	DBG("");
+
+	/* TODO */
+
+	hfp_gw_send_result(device.gw, HFP_RESULT_ERROR);
+}
+
+static void at_cmd_btrh(struct hfp_gw_result *result, enum hfp_gw_cmd_type type,
+							void *user_data)
+{
+	DBG("");
+
+	/* TODO */
+
+	hfp_gw_send_result(device.gw, HFP_RESULT_ERROR);
+}
+
+static gboolean sco_watch_cb(GIOChannel *chan, GIOCondition cond,
+							gpointer user_data)
+{
+	g_io_channel_shutdown(device.sco, TRUE, NULL);
+	g_io_channel_unref(device.sco);
+	device.sco = NULL;
+
+	device.sco_watch = 0;
+
+	device_set_audio_state(HAL_EV_HANDSFREE_AUDIO_STATE_DISCONNECTED);
+
+	return FALSE;
+}
+
+static void select_codec(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 (!device.codecs[i].local_supported)
+			continue;
+
+		if (!device.codecs[i].remote_supported)
+			continue;
+
+		type = device.codecs[i].type;
+		break;
+	}
+
+done:
+	device.proposed_codec = type;
+
+	hfp_gw_send_info(device.gw, "+BCS: %u", type);
+}
+
+static void connect_sco_cb(GIOChannel *chan, GError *err, gpointer user_data)
+{
+	if (err) {
+		uint8_t status;
+
+		error("handsfree: audio connect failed (%s)", err->message);
+
+		status = HAL_EV_HANDSFREE_AUDIO_STATE_DISCONNECTED;
+		device_set_audio_state(status);
+
+		if (!(device.features & HFP_HF_FEAT_CODEC))
+			return;
+
+		/* If other failed, try connecting with CVSD */
+		if (device.negotiated_codec != CODEC_ID_CVSD) {
+			info("handsfree: trying fallback with CVSD");
+			select_codec(CODEC_ID_CVSD);
+		}
+
+		return;
+	}
+
+	g_io_channel_set_close_on_unref(chan, TRUE);
+
+	device.sco = g_io_channel_ref(chan);
+	device.sco_watch = g_io_add_watch(chan, G_IO_ERR | G_IO_HUP | G_IO_NVAL,
+							sco_watch_cb, NULL);
+
+	device_set_audio_state(HAL_EV_HANDSFREE_AUDIO_STATE_CONNECTED);
+}
+
+static bool connect_sco(void)
+{
+	GIOChannel *io;
+	GError *gerr = NULL;
+	uint16_t voice_settings;
+
+	if (device.sco)
+		return false;
+
+	if ((device.features & HFP_HF_FEAT_CODEC) &&
+				device.negotiated_codec != CODEC_ID_CVSD)
+		voice_settings = BT_VOICE_TRANSPARENT;
+	else
+		voice_settings = BT_VOICE_CVSD_16BIT;
+
+	io = bt_io_connect(connect_sco_cb, NULL, NULL, &gerr,
+				BT_IO_OPT_SOURCE_BDADDR, &adapter_addr,
+				BT_IO_OPT_DEST_BDADDR, &device.bdaddr,
+				BT_IO_OPT_VOICE, voice_settings,
+				BT_IO_OPT_INVALID);
+
+	if (!io) {
+		error("handsfree: unable to connect audio: %s", gerr->message);
+		g_error_free(gerr);
+		return false;
+	}
+
+	g_io_channel_unref(io);
+
+	device_set_audio_state(HAL_EV_HANDSFREE_AUDIO_STATE_CONNECTING);
+
+	return true;
+}
+
+static void at_cmd_bcc(struct hfp_gw_result *result, enum hfp_gw_cmd_type type,
+							void *user_data)
+{
+	DBG("");
+
+	switch (type) {
+	case HFP_GW_CMD_TYPE_COMMAND:
+		if (!(device.features & HFP_HF_FEAT_CODEC))
+			break;
+
+		if (hfp_gw_result_has_next(result))
+			break;
+
+		hfp_gw_send_result(device.gw, HFP_RESULT_OK);
+
+		/* we haven't negotiated codec, start selection */
+		if (!device.negotiated_codec) {
+			select_codec(0);
+			return;
+		}
+		/* we try connect to negotiated codec. If it fails, and it isn't
+		 * CVSD codec, try connect CVSD
+		 */
+		if (!connect_sco() && device.negotiated_codec != CODEC_ID_CVSD)
+			select_codec(CODEC_ID_CVSD);
+
+		return;
+	case HFP_GW_CMD_TYPE_READ:
+	case HFP_GW_CMD_TYPE_TEST:
+	case HFP_GW_CMD_TYPE_SET:
+		break;
+	}
+
+	hfp_gw_send_result(device.gw, HFP_RESULT_ERROR);
+}
+
+static void at_cmd_bcs(struct hfp_gw_result *result, enum hfp_gw_cmd_type type,
+							void *user_data)
+{
+	unsigned int val;
+
+	DBG("");
+
+	switch (type) {
+	case HFP_GW_CMD_TYPE_SET:
+		if (!hfp_gw_result_get_number(result, &val))
+			break;
+
+		if (hfp_gw_result_has_next(result))
+			break;
+
+		/* Remote replied with other codec. Reply with error */
+		if (device.proposed_codec != val) {
+			device.proposed_codec = 0;
+			break;
+		}
+
+		device.proposed_codec = 0;
+		device.negotiated_codec = val;
+
+		hfp_gw_send_result(device.gw, HFP_RESULT_OK);
+
+		/* Connect sco with negotiated parameters */
+		connect_sco();
+		return;
+	case HFP_GW_CMD_TYPE_READ:
+	case HFP_GW_CMD_TYPE_TEST:
+	case HFP_GW_CMD_TYPE_COMMAND:
+		break;
+	}
+
+	hfp_gw_send_result(device.gw, HFP_RESULT_ERROR);
+}
+
+static void at_cmd_ckpd(struct hfp_gw_result *result, enum hfp_gw_cmd_type type,
+							void *user_data)
+{
+	unsigned int val;
+
+	DBG("");
+
+	switch (type) {
+	case HFP_GW_CMD_TYPE_SET:
+		if (!hfp_gw_result_get_number(result, &val) || val != 200)
+			break;
+
+		if (hfp_gw_result_has_next(result))
+			break;
+
+		ipc_send_notif(hal_ipc, HAL_SERVICE_ID_HANDSFREE,
+				HAL_EV_HANDSFREE_HSP_KEY_PRESS, 0, NULL);
+
+		hfp_gw_send_result(device.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(device.gw, HFP_RESULT_ERROR);
+}
+
+static void register_post_slc_at(void)
+{
+	if (device.hsp) {
+		hfp_gw_register(device.gw, at_cmd_ckpd, "+CKPD", NULL, NULL);
+		hfp_gw_register(device.gw, at_cmd_vgs, "+VGS", NULL, NULL);
+		hfp_gw_register(device.gw, at_cmd_vgm, "+VGM", NULL, NULL);
+		return;
+	}
+
+	hfp_gw_register(device.gw, at_cmd_a, "A", NULL, NULL);
+	hfp_gw_register(device.gw, at_cmd_d, "D", NULL, NULL);
+	hfp_gw_register(device.gw, at_cmd_ccwa, "+CCWA", NULL, NULL);
+	hfp_gw_register(device.gw, at_cmd_chup, "+CHUP", NULL, NULL);
+	hfp_gw_register(device.gw, at_cmd_clcc, "+CLCC", NULL, NULL);
+	hfp_gw_register(device.gw, at_cmd_cops, "+COPS", NULL, NULL);
+	hfp_gw_register(device.gw, at_cmd_cmee, "+CMEE", NULL, NULL);
+	hfp_gw_register(device.gw, at_cmd_clip, "+CLIP", NULL, NULL);
+	hfp_gw_register(device.gw, at_cmd_vts, "+VTS", NULL, NULL);
+	hfp_gw_register(device.gw, at_cmd_cnum, "+CNUM", NULL, NULL);
+	hfp_gw_register(device.gw, at_cmd_bia, "+BIA", NULL, NULL);
+	hfp_gw_register(device.gw, at_cmd_binp, "+BINP", NULL, NULL);
+	hfp_gw_register(device.gw, at_cmd_bldn, "+BLDN", NULL, NULL);
+	hfp_gw_register(device.gw, at_cmd_bvra, "+BVRA", NULL, NULL);
+	hfp_gw_register(device.gw, at_cmd_nrec, "+NREC", NULL, NULL);
+	hfp_gw_register(device.gw, at_cmd_vgs, "+VGS", NULL, NULL);
+	hfp_gw_register(device.gw, at_cmd_vgm, "+VGM", NULL, NULL);
+	hfp_gw_register(device.gw, at_cmd_bsir, "+BSIR", NULL, NULL);
+	hfp_gw_register(device.gw, at_cmd_btrh, "+BTRH", NULL, NULL);
+	hfp_gw_register(device.gw, at_cmd_bcc, "+BCC", NULL, NULL);
+	hfp_gw_register(device.gw, at_cmd_bcs, "+BCS", NULL, NULL);
+}
+
+static void at_cmd_cmer(struct hfp_gw_result *result, enum hfp_gw_cmd_type type,
+							void *user_data)
+{
+	unsigned int val;
+
+	switch (type) {
+	case HFP_GW_CMD_TYPE_SET:
+		/* mode must be =3 */
+		if (!hfp_gw_result_get_number(result, &val) || val != 3)
+			break;
+
+		/* keyp is don't care */
+		if (!hfp_gw_result_get_number(result, &val))
+			break;
+
+		/* disp is don't care */
+		if (!hfp_gw_result_get_number(result, &val))
+			break;
+
+		/* ind must be 0 or 1 */
+		if (!hfp_gw_result_get_number(result, &val) || val > 1)
+			break;
+
+		if (hfp_gw_result_has_next(result))
+			break;
+
+		device.indicators_enabled = val;
+
+		hfp_gw_send_result(device.gw, HFP_RESULT_OK);
+
+		if (device.features & HFP_HF_FEAT_3WAY)
+			return;
+
+		register_post_slc_at();
+		device_set_state(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(device.gw, HFP_RESULT_ERROR);
+}
+
+static void at_cmd_cind(struct hfp_gw_result *result, enum hfp_gw_cmd_type type,
+							void *user_data)
+{
+	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 ((device.features & HFP_HF_FEAT_CODEC) &&
+				!device.codecs[CVSD_OFFSET].remote_supported)
+			break;
+
+		len = strlen("+CIND:") + 1;
+
+		for (i = 0; i < IND_COUNT; i++) {
+			len += strlen("(\"\",(X,X)),");
+			len += strlen(device.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)),",
+					device.inds[i].name,
+					device.inds[i].min,
+					device.inds[i].max == 1 ? ',' : '-',
+					device.inds[i].max);
+		}
+
+		ptr--;
+		*ptr = '\0';
+
+		hfp_gw_send_info(device.gw, "%s", buf);
+		hfp_gw_send_result(device.gw, HFP_RESULT_OK);
+
+		g_free(buf);
+		return;
+	case HFP_GW_CMD_TYPE_READ:
+		ipc_send_notif(hal_ipc, HAL_SERVICE_ID_HANDSFREE,
+						HAL_EV_HANDSFREE_CIND, 0, NULL);
+		return;
+	case HFP_GW_CMD_TYPE_SET:
+	case HFP_GW_CMD_TYPE_COMMAND:
+		break;
+	}
+
+	hfp_gw_send_result(device.gw, HFP_RESULT_ERROR);
+}
+
+static void at_cmd_brsf(struct hfp_gw_result *result, enum hfp_gw_cmd_type type,
+							void *user_data)
+{
+	unsigned int feat;
+
+	switch (type) {
+	case HFP_GW_CMD_TYPE_SET:
+		if (!hfp_gw_result_get_number(result, &feat))
+			break;
+
+		if (hfp_gw_result_has_next(result))
+			break;
+
+		/* TODO verify features */
+		device.features = feat;
+
+		hfp_gw_send_info(device.gw, "+BRSF: %u", hfp_ag_features);
+		hfp_gw_send_result(device.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(device.gw, HFP_RESULT_ERROR);
+}
+
+static void at_cmd_chld(struct hfp_gw_result *result, enum hfp_gw_cmd_type type,
+							void *user_data)
+{
+	struct hal_ev_handsfree_chld ev;
+	unsigned int val;
+
+	DBG("");
+
+	switch (type) {
+	case HFP_GW_CMD_TYPE_SET:
+		if (!hfp_gw_result_get_number(result, &val) || val > 3)
+			break;
+
+		/* No ECC support */
+		if (hfp_gw_result_has_next(result))
+			break;
+
+		/* value match HAL type */
+		ev.chld = val;
+
+		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(device.gw, "+CHLD: (%s)", HFP_AG_CHLD);
+		hfp_gw_send_result(device.gw, HFP_RESULT_OK);
+
+		register_post_slc_at();
+		device_set_state(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(device.gw, HFP_RESULT_ERROR);
+}
+
+static struct hfp_codec *find_codec_by_type(uint8_t type)
+{
+	int i;
+
+	for (i = 0; i < CODECS_COUNT; i++)
+		if (type == device.codecs[i].type)
+			return &device.codecs[i];
+
+	return NULL;
+}
+
+static void at_cmd_bac(struct hfp_gw_result *result, enum hfp_gw_cmd_type type,
+							void *user_data)
+{
+	unsigned int val;
+
+	DBG("");
+
+	switch (type) {
+	case HFP_GW_CMD_TYPE_SET:
+		if (!(device.features & HFP_HF_FEAT_CODEC))
+			goto failed;
+
+		/* set codecs to defaults */
+		init_codecs();
+		device.negotiated_codec = 0;
+
+		/* At least CVSD mandatory codec must exist
+		 * HFP V1.6 4.34.1
+		 */
+		if (!hfp_gw_result_get_number(result, &val) ||
+							val != CODEC_ID_CVSD)
+			goto failed;
+
+		device.codecs[CVSD_OFFSET].remote_supported = true;
+
+		if (hfp_gw_result_get_number(result, &val)) {
+			if (val != CODEC_ID_MSBC)
+				goto failed;
+
+			device.codecs[MSBC_OFFSET].remote_supported = true;
+		}
+
+		while (hfp_gw_result_has_next(result)) {
+			struct hfp_codec *codec;
+
+			if (!hfp_gw_result_get_number(result, &val))
+				goto failed;
+
+			codec = find_codec_by_type(val);
+			if (!codec)
+				continue;
+
+			codec->remote_supported = true;
+		}
+
+		hfp_gw_send_result(device.gw, HFP_RESULT_OK);
+
+		if (device.proposed_codec)
+			select_codec(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(device.gw, HFP_RESULT_ERROR);
+}
+
+static void register_slc_at(void)
+{
+	hfp_gw_register(device.gw, at_cmd_brsf, "+BRSF", NULL, NULL);
+	hfp_gw_register(device.gw, at_cmd_cind, "+CIND", NULL, NULL);
+	hfp_gw_register(device.gw, at_cmd_cmer, "+CMER", NULL, NULL);
+	hfp_gw_register(device.gw, at_cmd_chld, "+CHLD", NULL, NULL);
+	hfp_gw_register(device.gw, at_cmd_bac, "+BAC", NULL, NULL);
+}
+
+static void connect_cb(GIOChannel *chan, GError *err, gpointer user_data)
+{
+	DBG("");
+
+	if (err) {
+		error("handsfree: connect failed (%s)", err->message);
+		goto failed;
+	}
+
+	device.gw = hfp_gw_new(g_io_channel_unix_get_fd(chan));
+	if (!device.gw)
+		goto failed;
+
+	g_io_channel_set_close_on_unref(chan, FALSE);
+
+	hfp_gw_set_close_on_unref(device.gw, true);
+	hfp_gw_set_command_handler(device.gw, at_cmd_unknown, NULL, NULL);
+	hfp_gw_set_disconnect_handler(device.gw, disconnect_watch, NULL, NULL);
+
+	if (device.hsp) {
+		register_post_slc_at();
+		device_set_state(HAL_EV_HANDSFREE_CONN_STATE_CONNECTED);
+		device_set_state(HAL_EV_HANDSFREE_CONN_STATE_SLC_CONNECTED);
+		return;
+	}
+
+	register_slc_at();
+	device_set_state(HAL_EV_HANDSFREE_CONN_STATE_CONNECTED);
+	return;
+
+failed:
+	g_io_channel_shutdown(chan, TRUE, NULL);
+	device_cleanup();
+}
+
+static void confirm_cb(GIOChannel *chan, gpointer data)
+{
+	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("handsfree: confirm failed (%s)", err->message);
+		g_error_free(err);
+		goto drop;
+	}
+
+	DBG("incoming connect from %s", address);
+
+	if (device.state != HAL_EV_HANDSFREE_CONN_STATE_DISCONNECTED) {
+		info("handsfree: refusing connection from %s", address);
+		goto drop;
+	}
+
+	device_init(&bdaddr);
+
+	if (!bt_io_accept(chan, connect_cb, NULL, NULL, NULL)) {
+		error("handsfree: failed to accept connection");
+		device_cleanup();
+		goto drop;
+	}
+
+	device.hsp = GPOINTER_TO_INT(data);
+	return;
+
+drop:
+	g_io_channel_shutdown(chan, TRUE, NULL);
+}
+
+static void sdp_hsp_search_cb(sdp_list_t *recs, int err, gpointer data)
+{
+	sdp_list_t *protos, *classes;
+	GError *gerr = NULL;
+	GIOChannel *io;
+	uuid_t uuid;
+	int channel;
+
+	DBG("");
+
+	if (err < 0) {
+		error("handsfree: unable to get SDP record: %s",
+								strerror(-err));
+		goto fail;
+	}
+
+	if (!recs || !recs->data) {
+		info("handsfree: no HSP SDP records found");
+		goto fail;
+	}
+
+	if (sdp_get_service_classes(recs->data, &classes) < 0) {
+		error("handsfree: unable to get service classes from record");
+		goto fail;
+	}
+
+	if (sdp_get_access_protos(recs->data, &protos) < 0) {
+		error("handsfree: unable to get access protocols from record");
+		sdp_list_free(classes, free);
+		goto fail;
+	}
+
+	/* TODO read remote version? */
+	/* TODO read volume control support */
+
+	memcpy(&uuid, classes->data, sizeof(uuid));
+	sdp_list_free(classes, free);
+
+	if (!sdp_uuid128_to_uuid(&uuid) || uuid.type != SDP_UUID16 ||
+			uuid.value.uuid16 != HEADSET_SVCLASS_ID) {
+		sdp_list_free(protos, NULL);
+		error("handsfree: invalid service record or not HSP");
+		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;
+	}
+
+	io = bt_io_connect(connect_cb, NULL, NULL, &gerr,
+				BT_IO_OPT_SOURCE_BDADDR, &adapter_addr,
+				BT_IO_OPT_DEST_BDADDR, &device.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;
+	}
+
+	device.hsp = true;
+
+	g_io_channel_unref(io);
+	return;
+
+fail:
+	device_cleanup();
+}
+
+static int sdp_search_hsp(void)
+{
+	uuid_t uuid;
+
+	sdp_uuid16_create(&uuid, HEADSET_SVCLASS_ID);
+
+	return bt_search_service(&adapter_addr, &device.bdaddr, &uuid,
+					sdp_hsp_search_cb, NULL, NULL, 0);
+}
+
+static void sdp_hfp_search_cb(sdp_list_t *recs, int err, gpointer data)
+{
+	sdp_list_t *protos, *classes;
+	GError *gerr = NULL;
+	GIOChannel *io;
+	uuid_t uuid;
+	int channel;
+
+	DBG("");
+
+	if (err < 0) {
+		error("handsfree: unable to get SDP record: %s",
+								strerror(-err));
+		goto fail;
+	}
+
+	if (!recs || !recs->data) {
+		info("handsfree: no HFP SDP records found, trying HSP");
+
+		if (sdp_search_hsp() < 0) {
+			error("handsfree: HSP SDP search failed");
+			goto fail;
+		}
+
+		return;
+	}
+
+	if (sdp_get_service_classes(recs->data, &classes) < 0) {
+		error("handsfree: unable to get service classes from record");
+		goto fail;
+	}
+
+	if (sdp_get_access_protos(recs->data, &protos) < 0) {
+		error("handsfree: unable to get access protocols from record");
+		sdp_list_free(classes, free);
+		goto fail;
+	}
+
+	/* 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_SVCLASS_ID) {
+		sdp_list_free(protos, NULL);
+		error("handsfree: invalid service record or not HFP");
+		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;
+	}
+
+	io = bt_io_connect(connect_cb, NULL, NULL, &gerr,
+				BT_IO_OPT_SOURCE_BDADDR, &adapter_addr,
+				BT_IO_OPT_DEST_BDADDR, &device.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_cleanup();
+}
+
+static int sdp_search_hfp(void)
+{
+	uuid_t uuid;
+
+	sdp_uuid16_create(&uuid, HANDSFREE_SVCLASS_ID);
+
+	return bt_search_service(&adapter_addr, &device.bdaddr, &uuid,
+					sdp_hfp_search_cb, NULL, NULL, 0);
+}
+
+static void handle_connect(const void *buf, uint16_t len)
+{
+	const struct hal_cmd_handsfree_connect *cmd = buf;
+	char addr[18];
+	uint8_t status;
+	bdaddr_t bdaddr;
+	int ret;
+
+	DBG("");
+
+	if (device.state != HAL_EV_HANDSFREE_CONN_STATE_DISCONNECTED) {
+		status = HAL_STATUS_FAILED;
+		goto failed;
+	}
+
+	android2bdaddr(&cmd->bdaddr, &bdaddr);
+
+	ba2str(&bdaddr, addr);
+	DBG("connecting to %s", addr);
+
+	device_init(&bdaddr);
+
+	/* prefer HFP over HSP */
+	ret = hfp_server ? sdp_search_hfp() : sdp_search_hsp();
+	if (ret < 0) {
+		error("handsfree: SDP search failed");
+		device_cleanup();
+		status = HAL_STATUS_FAILED;
+		goto failed;
+	}
+
+	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;
+	bdaddr_t bdaddr;
+	uint8_t status;
+
+	DBG("");
+
+	android2bdaddr(cmd->bdaddr, &bdaddr);
+
+	if (device.state == HAL_EV_HANDSFREE_CONN_STATE_DISCONNECTED ||
+			bacmp(&device.bdaddr, &bdaddr)) {
+		status = HAL_STATUS_FAILED;
+		goto failed;
+
+	}
+
+	if (device.state == HAL_EV_HANDSFREE_CONN_STATE_DISCONNECTING) {
+		status = HAL_STATUS_SUCCESS;
+		goto failed;
+	}
+
+	if (device.state == HAL_EV_HANDSFREE_CONN_STATE_CONNECTING) {
+		device_cleanup();
+	} else {
+		device_set_state(HAL_EV_HANDSFREE_CONN_STATE_DISCONNECTING);
+		hfp_gw_disconnect(device.gw);
+	}
+
+	status = HAL_STATUS_SUCCESS;
+
+failed:
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_HANDSFREE,
+					HAL_OP_HANDSFREE_DISCONNECT, status);
+}
+
+static bool disconnect_sco(void)
+{
+	if (!device.sco)
+		return false;
+
+	device_set_audio_state(HAL_EV_HANDSFREE_AUDIO_STATE_DISCONNECTING);
+
+	if (device.sco_watch) {
+		g_source_remove(device.sco_watch);
+		device.sco_watch = 0;
+	}
+
+	g_io_channel_shutdown(device.sco, TRUE, NULL);
+	g_io_channel_unref(device.sco);
+	device.sco = NULL;
+
+	device_set_audio_state(HAL_EV_HANDSFREE_AUDIO_STATE_DISCONNECTED);
+	return true;
+}
+
+static bool connect_audio(void)
+{
+	if (device.audio_state != HAL_EV_HANDSFREE_AUDIO_STATE_DISCONNECTED)
+		return false;
+
+	/* we haven't negotiated codec, start selection */
+	if ((device.features & HFP_HF_FEAT_CODEC) && !device.negotiated_codec) {
+		select_codec(0);
+		return true;
+	}
+
+	return connect_sco();
+}
+
+static void handle_connect_audio(const void *buf, uint16_t len)
+{
+	const struct hal_cmd_handsfree_connect_audio *cmd = buf;
+	bdaddr_t bdaddr;
+	uint8_t status;
+
+	DBG("");
+
+	android2bdaddr(cmd->bdaddr, &bdaddr);
+
+	if (device.audio_state != HAL_EV_HANDSFREE_AUDIO_STATE_DISCONNECTED ||
+			bacmp(&device.bdaddr, &bdaddr)) {
+		status = HAL_STATUS_FAILED;
+		goto done;
+	}
+
+	status = connect_audio() ? 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;
+	bdaddr_t bdaddr;
+	uint8_t status;
+
+	DBG("");
+
+	android2bdaddr(cmd->bdaddr, &bdaddr);
+
+	if (device.audio_state != HAL_EV_HANDSFREE_AUDIO_STATE_CONNECTED ||
+			bacmp(&device.bdaddr, &bdaddr)) {
+		status = HAL_STATUS_FAILED;
+		goto done;
+	}
+
+	status = disconnect_sco() ? 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)
+{
+	uint8_t status;
+
+	DBG("");
+
+	if (device.features & HFP_HF_FEAT_VR) {
+		hfp_gw_send_info(device.gw, "+BVRA: 1");
+		status = HAL_STATUS_SUCCESS;
+	} else {
+		status = HAL_STATUS_FAILED;
+	}
+
+	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)
+{
+	uint8_t status;
+
+	DBG("");
+
+	if (device.features & HFP_HF_FEAT_VR) {
+		hfp_gw_send_info(device.gw, "+BVRA: 0");
+		status = HAL_STATUS_SUCCESS;
+	} else {
+		status = HAL_STATUS_FAILED;
+	}
+
+	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;
+	uint8_t status, volume;
+
+	DBG("type=%u volume=%u", cmd->type, cmd->volume);
+
+	volume = cmd->volume > 15 ? 15 : cmd->volume;
+
+	switch (cmd->type) {
+	case HAL_HANDSFREE_VOLUME_TYPE_MIC:
+		hfp_gw_send_info(device.gw, "+VGM: %u", volume);
+
+		status = HAL_STATUS_SUCCESS;
+		break;
+	case HAL_HANDSFREE_VOLUME_TYPE_SPEAKER:
+		hfp_gw_send_info(device.gw, "+VGS: %u", volume);
+
+		status = HAL_STATUS_SUCCESS;
+		break;
+	default:
+		status = HAL_STATUS_FAILED;
+		break;
+	}
+
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_HANDSFREE,
+				HAL_OP_HANDSFREE_VOLUME_CONTROL, status);
+}
+
+static void update_indicator(int ind, uint8_t val)
+{
+	DBG("ind=%u new=%u old=%u", ind, val, device.inds[ind].val);
+
+	if (device.inds[ind].val == val)
+		return;
+
+	device.inds[ind].val = val;
+
+	if (!device.indicators_enabled)
+		return;
+
+	if (!device.inds[ind].active)
+		return;
+
+	/* indicator numbers in CIEV start from 1 */
+	hfp_gw_send_info(device.gw, "+CIEV: %u,%u", ind + 1, val);
+}
+
+static void handle_device_status_notif(const void *buf, uint16_t len)
+{
+	const struct hal_cmd_handsfree_device_status_notif *cmd = buf;
+
+	DBG("");
+
+	update_indicator(IND_SERVICE, cmd->state);
+	update_indicator(IND_ROAM, cmd->type);
+	update_indicator(IND_SIGNAL, cmd->signal);
+	update_indicator(IND_BATTCHG, cmd->battery);
+
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_HANDSFREE,
+					HAL_OP_HANDSFREE_DEVICE_STATUS_NOTIF,
+					HAL_STATUS_SUCCESS);
+}
+
+static void handle_cops(const void *buf, uint16_t len)
+{
+	const struct hal_cmd_handsfree_cops_response *cmd = buf;
+
+	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("");
+
+	hfp_gw_send_info(device.gw, "+COPS: 0,0,\"%.16s\"",
+					cmd->len ? (char *) cmd->buf : "");
+
+	hfp_gw_send_result(device.gw, HFP_RESULT_OK);
+
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_HANDSFREE,
+			HAL_OP_HANDSFREE_COPS_RESPONSE, HAL_STATUS_SUCCESS);
+}
+
+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;
+
+	DBG("");
+
+	/* HAL doesn't provide indicators values so need to convert here */
+	device.inds[IND_SERVICE].val = cmd->svc;
+	device.inds[IND_CALL].val = !!(cmd->num_active + cmd->num_held);
+	device.inds[IND_CALLSETUP].val = get_callsetup(cmd->state);
+	device.inds[IND_CALLHELD].val = cmd->num_held ?
+						(cmd->num_active ? 1 : 2) : 0;
+	device.inds[IND_SIGNAL].val = cmd->signal;
+	device.inds[IND_ROAM].val = cmd->roam;
+	device.inds[IND_BATTCHG].val = cmd->batt_chg;
+
+	/* Order must match indicators_defaults table */
+	hfp_gw_send_info(device.gw, "+CIND: %u,%u,%u,%u,%u,%u,%u",
+						device.inds[IND_SERVICE].val,
+						device.inds[IND_CALL].val,
+						device.inds[IND_CALLSETUP].val,
+						device.inds[IND_CALLHELD].val,
+						device.inds[IND_SIGNAL].val,
+						device.inds[IND_ROAM].val,
+						device.inds[IND_BATTCHG].val);
+
+	hfp_gw_send_result(device.gw, HFP_RESULT_OK);
+
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_HANDSFREE,
+			HAL_OP_HANDSFREE_CIND_RESPONSE, HAL_STATUS_SUCCESS);
+}
+
+static void handle_formatted_at_resp(const void *buf, uint16_t len)
+{
+	const struct hal_cmd_handsfree_formatted_at_response *cmd = buf;
+
+	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;
+	}
+
+	DBG("");
+
+	hfp_gw_send_info(device.gw, "%s", cmd->len ? (char *) cmd->buf : "");
+
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_HANDSFREE,
+					HAL_OP_HANDSFREE_FORMATTED_AT_RESPONSE,
+					HAL_STATUS_SUCCESS);
+}
+
+static void handle_at_resp(const void *buf, uint16_t len)
+{
+	const struct hal_cmd_handsfree_at_response *cmd = buf;
+
+	DBG("");
+
+	if (cmd->response == HAL_HANDSFREE_AT_RESPONSE_OK)
+		hfp_gw_send_result(device.gw, HFP_RESULT_OK);
+	else if (device.cmee_enabled)
+		hfp_gw_send_error(device.gw, cmd->error);
+	else
+		hfp_gw_send_result(device.gw, HFP_RESULT_ERROR);
+
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_HANDSFREE,
+			HAL_OP_HANDSFREE_AT_RESPONSE, HAL_STATUS_SUCCESS);
+}
+
+static void handle_clcc_resp(const void *buf, uint16_t len)
+{
+	const struct hal_cmd_handsfree_clcc_response *cmd = buf;
+	uint8_t status;
+	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("");
+
+	if (!cmd->index) {
+		hfp_gw_send_result(device.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(device.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(device.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)
+{
+	char *clip = user_data;
+
+	hfp_gw_send_info(device.gw, "RING");
+
+	if (device.clip_enabled && clip)
+		hfp_gw_send_info(device.gw, "%s", clip);
+
+	return TRUE;
+}
+
+static void phone_state_dialing(int num_active, int num_held)
+{
+	update_indicator(IND_CALLSETUP, 2);
+
+	if (num_active == 0 && num_held > 0)
+		update_indicator(IND_CALLHELD, 2);
+
+	if (device.num_active == 0 && device.num_held == 0)
+		connect_audio();
+}
+
+static void phone_state_alerting(int num_active, int num_held)
+{
+	update_indicator(IND_CALLSETUP, 3);
+}
+
+static void phone_state_waiting(int num_active, int num_held, uint8_t type,
+					const uint8_t *number, int number_len)
+{
+	char *num;
+
+	if (!device.ccwa_enabled)
+		return;
+
+	num = number_len ? (char *) number : "";
+
+	if (type == HAL_HANDSFREE_CALL_ADDRTYPE_INTERNATIONAL && num[0] != '+')
+		hfp_gw_send_info(device.gw, "+CCWA: \"+%s\",%u", num, type);
+	else
+		hfp_gw_send_info(device.gw, "+CCWA: \"%s\",%u", num, type);
+
+	update_indicator(IND_CALLSETUP, 1);
+}
+
+static void phone_state_incoming(int num_active, int num_held, uint8_t type,
+					const uint8_t *number, int number_len)
+{
+	char *clip, *num;
+
+	if (device.setup_state == HAL_HANDSFREE_CALL_STATE_INCOMING) {
+		if (device.num_active != num_active ||
+						device.num_held != num_held) {
+			/* calls changed while waiting call ie. due to
+			 * termination of active call
+			 */
+			update_indicator(IND_CALLHELD,
+					num_held ? (num_active ? 1 : 2) : 0);
+			update_indicator(IND_CALL, !!(num_active + num_held));
+		}
+
+		return;
+	}
+
+	if (device.call_hanging_up)
+		return;
+
+	if (num_active > 0 || num_held > 0) {
+		phone_state_waiting(num_active, num_held, type, number,
+								number_len);
+		return;
+	}
+
+	update_indicator(IND_CALLSETUP, 1);
+
+	num = number_len ? (char *) number : "";
+
+	if (type == HAL_HANDSFREE_CALL_ADDRTYPE_INTERNATIONAL && num[0] != '+')
+		clip = g_strdup_printf("+CLIP: \"+%s\",%u", num, type);
+	else
+		clip = g_strdup_printf("+CLIP: \"%s\",%u", num, type);
+
+	/* send first RING */
+	ring_cb(clip);
+
+	device.ring = g_timeout_add_seconds_full(G_PRIORITY_DEFAULT,
+							RING_TIMEOUT, ring_cb,
+							clip, g_free);
+
+	if (!device.ring)
+		g_free(clip);
+}
+
+static void phone_state_idle(int num_active, int num_held)
+{
+	if (device.ring) {
+		g_source_remove(device.ring);
+		device.ring = 0;
+	}
+
+	switch (device.setup_state) {
+	case HAL_HANDSFREE_CALL_STATE_INCOMING:
+		if (num_active > device.num_active) {
+			update_indicator(IND_CALL, 1);
+
+			if (device.num_active == 0 && device.num_held == 0)
+				connect_audio();
+		}
+
+		if (num_held > device.num_held)
+			update_indicator(IND_CALLHELD, 1);
+
+		update_indicator(IND_CALLSETUP, 0);
+
+		if (num_active == device.num_active &&
+						num_held == device.num_held)
+			device.call_hanging_up = true;
+
+		break;
+	case HAL_HANDSFREE_CALL_STATE_DIALING:
+	case HAL_HANDSFREE_CALL_STATE_ALERTING:
+		if (num_active > device.num_active)
+			update_indicator(IND_CALL, 1);
+
+		update_indicator(IND_CALLHELD,
+					num_held ? (num_active ? 1 : 2) : 0);
+
+		update_indicator(IND_CALLSETUP, 0);
+		break;
+	case HAL_HANDSFREE_CALL_STATE_IDLE:
+
+		if (device.call_hanging_up) {
+			device.call_hanging_up = false;
+			return;
+		}
+
+		/* check if calls swapped */
+		if (num_held != 0 && num_active != 0 &&
+				device.num_active == num_held &&
+				device.num_held == num_active) {
+			/* TODO better way for forcing indicator */
+			device.inds[IND_CALLHELD].val = 0;
+		} else if ((num_active > 0 || num_held > 0) &&
+						device.num_active == 0 &&
+						device.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();
+		} else if (num_active == 0 && num_held == 0) {
+			disconnect_sco();
+		}
+
+		update_indicator(IND_CALLHELD,
+					num_held ? (num_active ? 1 : 2) : 0);
+		update_indicator(IND_CALL, !!(num_active + num_held));
+		update_indicator(IND_CALLSETUP, 0);
+
+		/* If call was terminated due to carrier lost send NO CARRIER */
+		if (num_active == 0 && num_held == 0 &&
+				device.inds[IND_SERVICE].val == 0 &&
+				(device.num_active > 0 || device.num_held > 0))
+			hfp_gw_send_info(device.gw, "NO CARRIER");
+
+		break;
+	default:
+		DBG("unhandled state %u", device.setup_state);
+		break;
+	}
+}
+
+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);
+
+	switch (cmd->state) {
+	case HAL_HANDSFREE_CALL_STATE_DIALING:
+		phone_state_dialing(cmd->num_active, cmd->num_held);
+		break;
+	case HAL_HANDSFREE_CALL_STATE_ALERTING:
+		phone_state_alerting(cmd->num_active, cmd->num_held);
+		break;
+	case HAL_HANDSFREE_CALL_STATE_INCOMING:
+		phone_state_incoming(cmd->num_active, cmd->num_held, cmd->type,
+						cmd->number, cmd->number_len);
+		break;
+	case HAL_HANDSFREE_CALL_STATE_IDLE:
+		phone_state_idle(cmd->num_active, cmd->num_held);
+		break;
+	default:
+		DBG("unhandled new state %u (current state %u)", cmd->state,
+							device.setup_state);
+
+		status = HAL_STATUS_FAILED;
+		goto failed;
+	}
+
+	device.num_active = cmd->num_active;
+	device.num_held = cmd->num_held;
+	device.setup_state = cmd->state;
+
+	status = HAL_STATUS_SUCCESS;
+
+failed:
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_HANDSFREE,
+				HAL_OP_HANDSFREE_PHONE_STATE_CHANGE, 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, 0 },
+	/* define HAL_OP_HANDSFREE_STOP_VR */
+	{handle_stop_vr, false, 0 },
+	/* 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)},
+};
+
+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(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 = 0x0102;
+	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, &ch);
+	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);
+
+	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 void confirm_sco_cb(GIOChannel *chan, gpointer user_data)
+{
+	char address[18];
+	bdaddr_t bdaddr;
+	GError *err = NULL;
+
+	if (device.sco)
+		goto drop;
+
+	bt_io_get(chan, &err,
+			BT_IO_OPT_DEST, address,
+			BT_IO_OPT_DEST_BDADDR, &bdaddr,
+			BT_IO_OPT_INVALID);
+	if (err) {
+		error("handsfree: audio confirm failed (%s)", err->message);
+		g_error_free(err);
+		goto drop;
+	}
+
+	DBG("incoming SCO connection from %s", address);
+
+	if (device.state != HAL_EV_HANDSFREE_CONN_STATE_SLC_CONNECTED ||
+			bacmp(&device.bdaddr, &bdaddr)) {
+		error("handsfree: audio connection from %s rejected", address);
+		goto drop;
+	}
+
+	if (!bt_io_accept(chan, connect_sco_cb, NULL, NULL, NULL)) {
+		error("handsfree: failed to accept audio connection");
+		goto drop;
+	}
+
+	device_set_audio_state(HAL_EV_HANDSFREE_AUDIO_STATE_CONNECTING);
+	return;
+
+drop:
+	g_io_channel_shutdown(chan, TRUE, NULL);
+}
+
+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(0, &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 bool enable_sco_server(void)
+{
+	GError *err = NULL;
+
+	sco_server = bt_io_listen(NULL, confirm_sco_cb, NULL, NULL, &err,
+				BT_IO_OPT_SOURCE_BDADDR, &adapter_addr,
+				BT_IO_OPT_INVALID);
+	if (!sco_server) {
+		error("handsfree: Failed to listen on SCO: %s", err->message);
+		g_error_free(err);
+		cleanup_hsp_ag();
+		cleanup_hfp_ag();
+		return false;
+	}
+
+	return true;
+}
+
+static void disable_sco_server(void)
+{
+	if (sco_server) {
+		g_io_channel_shutdown(sco_server, TRUE, NULL);
+		g_io_channel_unref(sco_server);
+		sco_server = NULL;
+	}
+}
+
+bool bt_handsfree_register(struct ipc *ipc, const bdaddr_t *addr, uint8_t mode)
+{
+	DBG("mode 0x%x", mode);
+
+	bacpy(&adapter_addr, addr);
+
+	if (!enable_hsp_ag())
+		return false;
+
+	if (!enable_sco_server()) {
+		cleanup_hsp_ag();
+		return false;
+	}
+
+	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;
+
+	cleanup_hsp_ag();
+	disable_sco_server();
+	hfp_ag_features = 0;
+	return false;
+
+done:
+	hal_ipc = ipc;
+	ipc_register(hal_ipc, HAL_SERVICE_ID_HANDSFREE, cmd_handlers,
+						G_N_ELEMENTS(cmd_handlers));
+	return true;
+}
+
+void bt_handsfree_unregister(void)
+{
+	DBG("");
+
+	ipc_unregister(hal_ipc, HAL_SERVICE_ID_HANDSFREE);
+	hal_ipc = NULL;
+
+	cleanup_hfp_ag();
+	cleanup_hsp_ag();
+	disable_sco_server();
+
+	hfp_ag_features = 0;
+}
diff --git a/bluez/android/handsfree.h b/bluez/android/handsfree.h
new file mode 100644
index 0000000..e5eff47
--- /dev/null
+++ b/bluez/android/handsfree.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_handsfree_register(struct ipc *ipc, const bdaddr_t *addr, uint8_t mode);
+void bt_handsfree_unregister(void);
diff --git a/bluez/android/hardware/audio.h b/bluez/android/hardware/audio.h
new file mode 100644
index 0000000..61d92db
--- /dev/null
+++ b/bluez/android/hardware/audio.h
@@ -0,0 +1,564 @@
+/*
+ * 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_CURRENT 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"
+
+/* A2DP sink address set by framework */
+#define AUDIO_PARAMETER_A2DP_SINK_ADDRESS "a2dp_sink_address"
+
+/* Screen state */
+#define AUDIO_PARAMETER_KEY_SCREEN_STATE "screen_state"
+
+/**
+ *  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
+
+/* 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"
+
+/**
+ * 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 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;
+};
+typedef struct audio_config audio_config_t;
+
+/* 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 {
+    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 {
+    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).
+ */
+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);
+}
+
+
+/**********************************************************************/
+
+/**
+ * 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 {
+    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 */
+    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);
+
+    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);
+
+    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);
+};
+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/android/hardware/audio_effect.h b/bluez/android/hardware/audio_effect.h
new file mode 100644
index 0000000..857a300
--- /dev/null
+++ b/bluez/android/hardware/audio_effect.h
@@ -0,0 +1,1009 @@
+/*
+ * 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 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/android/hardware/bluetooth.h b/bluez/android/hardware/bluetooth.h
new file mode 100644
index 0000000..c00a8f7
--- /dev/null
+++ b/bluez/android/hardware/bluetooth.h
@@ -0,0 +1,470 @@
+/*
+ * 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 <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_ADVANCED_AUDIO_ID "a2dp"
+#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_GATT_ID "gatt"
+#define BT_PROFILE_AV_RC_ID "avrcp"
+
+/** 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_t;
+
+/** Bluetooth PinKey Code */
+typedef struct {
+    uint8_t pin[16];
+} __attribute__((packed))bt_pin_code_t;
+
+/** 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;
+
+/* 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,
+
+    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);
+/** 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;
+} bt_callbacks_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);
+
+    /** Remove Bond */
+    int (*remove_bond)(const bt_bdaddr_t *bd_addr);
+
+    /** Cancel Bond */
+    int (*cancel_bond)(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);
+} 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/android/hardware/bt_av.h b/bluez/android/hardware/bt_av.h
new file mode 100644
index 0000000..2ec00c3
--- /dev/null
+++ b/bluez/android/hardware/bt_av.h
@@ -0,0 +1,90 @@
+/*
+ * 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);
+
+/** 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_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. */
+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/android/hardware/bt_gatt.h b/bluez/android/hardware/bt_gatt.h
new file mode 100644
index 0000000..42e14c2
--- /dev/null
+++ b/bluez/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/android/hardware/bt_gatt_client.h b/bluez/android/hardware/bt_gatt_client.h
new file mode 100644
index 0000000..d6b0cb4
--- /dev/null
+++ b/bluez/android/hardware/bt_gatt_client.h
@@ -0,0 +1,289 @@
+/*
+ * 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 indicationg the status of a listen() operation
+ */
+typedef void (*listen_callback)(int status, int server_if);
+
+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;
+} 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)( int client_if, 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 );
+
+    /** 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);
+
+    /** 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 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);
+
+    /** 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/android/hardware/bt_gatt_server.h b/bluez/android/hardware/bt_gatt_server.h
new file mode 100644
index 0000000..1a5a400
--- /dev/null
+++ b/bluez/android/hardware/bt_gatt_server.h
@@ -0,0 +1,175 @@
+/*
+ * 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);
+
+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;
+} 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 );
+
+    /** 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/android/hardware/bt_gatt_types.h b/bluez/android/hardware/bt_gatt_types.h
new file mode 100644
index 0000000..0ac217e
--- /dev/null
+++ b/bluez/android/hardware/bt_gatt_types.h
@@ -0,0 +1,48 @@
+/*
+ * 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;
+
+__END_DECLS
+
+#endif /* ANDROID_INCLUDE_BT_GATT_TYPES_H */
diff --git a/bluez/android/hardware/bt_hf.h b/bluez/android/hardware/bt_hf.h
new file mode 100644
index 0000000..6135ac4
--- /dev/null
+++ b/bluez/android/hardware/bt_hf.h
@@ -0,0 +1,284 @@
+/*
+ * 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;
+
+/* 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);
+
+/** Callback for answer incoming call (ATA)
+ */
+typedef void (* bthf_answer_call_cmd_callback)();
+
+/** Callback for disconnect call (AT+CHUP)
+ */
+typedef void (* bthf_hangup_call_cmd_callback)();
+
+/** 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);
+
+/** Callback for dialing an outgoing call
+ *  If number is NULL, redial
+ */
+typedef void (* bthf_dial_call_cmd_callback)(char *number);
+
+/** Callback for sending DTMF tones
+ *  tone contains the dtmf character to be sent
+ */
+typedef void (* bthf_dtmf_cmd_callback)(char tone);
+
+/** 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);
+
+/** 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);
+
+/** Callback for CNUM (subscriber number)
+ */
+typedef void (* bthf_cnum_cmd_callback)();
+
+/** Callback for indicators (CIND)
+ */
+typedef void (* bthf_cind_cmd_callback)();
+
+/** Callback for operator selection (COPS)
+ */
+typedef void (* bthf_cops_cmd_callback)();
+
+/** Callback for call list (AT+CLCC)
+ */
+typedef void (* bthf_clcc_cmd_callback) ();
+
+/** 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);
+
+/** Callback for keypressed (HSP) event.
+ */
+typedef void (* bthf_key_pressed_cmd_callback)();
+
+/** 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_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 );
+
+    /** 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)();
+
+    /** stop voice recognition */
+    bt_status_t (*stop_voice_recognition)();
+
+    /** volume control */
+    bt_status_t (*volume_control) (bthf_volume_type_t type, int volume);
+
+    /** 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);
+
+    /** 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);
+
+    /** Pre-formatted AT response, typically in response to unknown AT cmd */
+    bt_status_t (*formatted_at_response)(const char *rsp);
+
+    /** ok/error response
+     *  ERROR (0)
+     *  OK    (1)
+     */
+    bt_status_t (*at_response) (bthf_at_response_t response_code, int error_code);
+
+    /** 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);
+
+    /** 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 );
+} bthf_interface_t;
+
+__END_DECLS
+
+#endif /* ANDROID_INCLUDE_BT_HF_H */
diff --git a/bluez/android/hardware/bt_hh.h b/bluez/android/hardware/bt_hh.h
new file mode 100644
index 0000000..09f547b
--- /dev/null
+++ b/bluez/android/hardware/bt_hh.h
@@ -0,0 +1,179 @@
+/*
+ * 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/set protocal 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);
+
+
+/** 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_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/android/hardware/bt_hl.h b/bluez/android/hardware/bt_hl.h
new file mode 100644
index 0000000..bd29e3a
--- /dev/null
+++ b/bluez/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/android/hardware/bt_pan.h b/bluez/android/hardware/bt_pan.h
new file mode 100644
index 0000000..c8b36b4
--- /dev/null
+++ b/bluez/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, bt_status_t error,
+                                            int local_role, 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/android/hardware/bt_rc.h b/bluez/android/hardware/bt_rc.h
new file mode 100644
index 0000000..d455543
--- /dev/null
+++ b/bluez/android/hardware/bt_rc.h
@@ -0,0 +1,266 @@
+/*
+ * 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 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 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;
+
+__END_DECLS
+
+#endif /* ANDROID_INCLUDE_BT_RC_H */
diff --git a/bluez/android/hardware/bt_sock.h b/bluez/android/hardware/bt_sock.h
new file mode 100644
index 0000000..a4aa046
--- /dev/null
+++ b/bluez/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/android/hardware/hardware.c b/bluez/android/hardware/hardware.c
new file mode 100644
index 0000000..4bd5eba
--- /dev/null
+++ b/bluez/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/android/hardware/hardware.h b/bluez/android/hardware/hardware.h
new file mode 100644
index 0000000..c7e8cc7
--- /dev/null
+++ b/bluez/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/android/health.c b/bluez/android/health.c
new file mode 100644
index 0000000..e4f8cbd
--- /dev/null
+++ b/bluez/android/health.c
@@ -0,0 +1,115 @@
+/*
+ *
+ *  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 <stdint.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/log.h"
+
+#include "hal-msg.h"
+#include "ipc-common.h"
+#include "ipc.h"
+#include "utils.h"
+#include "bluetooth.h"
+#include "health.h"
+
+static bdaddr_t adapter_addr;
+static struct ipc *hal_ipc = NULL;
+
+static void bt_health_register_app(const void *buf, uint16_t len)
+{
+	DBG("Not implemented");
+
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_HEALTH, HAL_OP_HEALTH_REG_APP,
+							HAL_STATUS_UNSUPPORTED);
+}
+
+static void bt_health_unregister_app(const void *buf, uint16_t len)
+{
+	DBG("Not implemented");
+
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_HEALTH, HAL_OP_HEALTH_UNREG_APP,
+							HAL_STATUS_UNSUPPORTED);
+}
+
+static void bt_health_connect_channel(const void *buf, uint16_t len)
+{
+	DBG("Not implemented");
+
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_HEALTH,
+			HAL_OP_HEALTH_CONNECT_CHANNEL, HAL_STATUS_UNSUPPORTED);
+}
+
+static void bt_health_destroy_channel(const void *buf, uint16_t len)
+{
+	DBG("Not implemented");
+
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_HEALTH,
+			HAL_OP_HEALTH_DESTROY_CHANNEL, HAL_STATUS_UNSUPPORTED);
+}
+
+static const struct ipc_handler cmd_handlers[] = {
+	/* HAL_OP_HEALTH_REG_APP */
+	{ bt_health_register_app, false,
+				sizeof(struct hal_cmd_health_reg_app) },
+	/* 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) },
+};
+
+bool bt_health_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_HEALTH, cmd_handlers,
+						G_N_ELEMENTS(cmd_handlers));
+
+	return true;
+}
+
+void bt_health_unregister(void)
+{
+	DBG("");
+
+	ipc_unregister(hal_ipc, HAL_SERVICE_ID_HEALTH);
+	hal_ipc = NULL;
+}
diff --git a/bluez/android/health.h b/bluez/android/health.h
new file mode 100644
index 0000000..0b32fd3
--- /dev/null
+++ b/bluez/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/android/hidhost.c b/bluez/android/hidhost.c
new file mode 100644
index 0000000..81e7eb8
--- /dev/null
+++ b/bluez/android/hidhost.c
@@ -0,0 +1,1398 @@
+/*
+ *
+ *  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 "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/sdp-client.h"
+#include "src/uuid-helper.h"
+#include "profiles/input/uhid_copy.h"
+#include "src/log.h"
+
+#include "hal-msg.h"
+#include "ipc-common.h"
+#include "ipc.h"
+#include "hidhost.h"
+#include "utils.h"
+
+#define L2CAP_PSM_HIDP_CTRL	0x11
+#define L2CAP_PSM_HIDP_INTR	0x13
+#define UHID_DEVICE_FILE	"/dev/uhid"
+
+/* HID message types */
+#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
+
+/* 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
+
+static bdaddr_t adapter_addr;
+
+static GIOChannel *ctrl_io = NULL;
+static GIOChannel *intr_io = NULL;
+static GSList *devices = NULL;
+
+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;
+	int		uhid_fd;
+	guint		uhid_watch_id;
+	uint8_t		last_hid_msg;
+};
+
+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 uhid_destroy(int fd)
+{
+	struct uhid_event ev;
+
+	/* destroy uHID device */
+	memset(&ev, 0, sizeof(ev));
+	ev.type = UHID_DESTROY;
+
+	if (write(fd, &ev, sizeof(ev)) < 0)
+		error("Failed to destroy uHID device: %s (%d)",
+						strerror(errno), errno);
+
+	close(fd);
+}
+
+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_watch_id) {
+		g_source_remove(dev->uhid_watch_id);
+		dev->uhid_watch_id = 0;
+	}
+
+	if (dev->uhid_fd > 0)
+		uhid_destroy(dev->uhid_fd);
+
+	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 void handle_uhid_output(struct hid_device *dev,
+						struct uhid_output_req *output)
+{
+	int fd, i;
+	uint8_t *req = NULL;
+	uint8_t req_size = 0;
+
+	if (!(dev->ctrl_io))
+		return;
+
+	req_size = 1 + (output->size / 2);
+	req = g_try_malloc0(req_size);
+	if (!req)
+		return;
+
+	req[0] = HID_MSG_SET_REPORT | output->rtype;
+	for (i = 0; i < (req_size - 1); i++)
+		sscanf((char *) &(output->data)[i * 2], "%hhx", &req[1 + i]);
+
+	fd = g_io_channel_unix_get_fd(dev->ctrl_io);
+
+	if (write(fd, req, req_size) < 0)
+		error("error writing set_report: %s (%d)",
+						strerror(errno), errno);
+
+	g_free(req);
+}
+
+static gboolean uhid_event_cb(GIOChannel *io, GIOCondition cond,
+							gpointer user_data)
+{
+	struct hid_device *dev = user_data;
+	struct uhid_event ev;
+	ssize_t bread;
+	int fd;
+
+	DBG("");
+
+	if (cond & (G_IO_ERR | G_IO_NVAL))
+		goto failed;
+
+	fd = g_io_channel_unix_get_fd(io);
+	memset(&ev, 0, sizeof(ev));
+
+	bread = read(fd, &ev, sizeof(ev));
+	if (bread < 0) {
+		DBG("read: %s (%d)", strerror(errno), errno);
+		goto failed;
+	}
+
+	DBG("uHID event type %d received", ev.type);
+
+	switch (ev.type) {
+	case UHID_START:
+	case UHID_STOP:
+		/* These are called to start and stop the underlying hardware.
+		 * We open the channels before creating the device so the
+		 * hardware is always ready. No need to handle these.
+		 * The kernel never destroys a device itself! Only an explicit
+		 * UHID_DESTROY request can remove a device. */
+
+		break;
+	case UHID_OPEN:
+	case UHID_CLOSE:
+		/* OPEN/CLOSE are sent whenever user-space opens any interface
+		 * provided by the kernel HID device. Whenever the open-count
+		 * is non-zero we must be ready for I/O. As long as it is zero,
+		 * we can decide to drop all I/O and put the device
+		 * asleep This is optional, though. */
+		break;
+	case UHID_OUTPUT:
+		handle_uhid_output(dev, &ev.u.output);
+		break;
+	case UHID_FEATURE:
+		/* TODO */
+		break;
+	case UHID_OUTPUT_EV:
+		/* This is only sent by kernels prior to linux-3.11. It
+		 * requires us to parse HID-descriptors in user-space to
+		 * properly handle it. This is redundant as the kernel
+		 * does it already. That's why newer kernels assemble
+		 * the output-reports and send it to us via UHID_OUTPUT. */
+		DBG("UHID_OUTPUT_EV unsupported");
+		break;
+	default:
+		warn("unexpected uHID event");
+	}
+
+	return TRUE;
+
+failed:
+	dev->uhid_watch_id = 0;
+	return FALSE;
+}
+
+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;
+
+	/* Wait uHID if not ready */
+	if (dev->uhid_fd < 0)
+		return TRUE;
+
+	fd = g_io_channel_unix_get_fd(chan);
+	bread = read(fd, buf, sizeof(buf));
+	if (bread < 0) {
+		error("read: %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);
+
+	if (write(dev->uhid_fd, &ev, sizeof(ev)) < 0)
+		DBG("uhid write: %s (%d)", strerror(errno), errno);
+
+	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_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("read: %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;
+	}
+
+	if (buf[0] == (HID_MSG_CONTROL | HID_VIRTUAL_CABLE_UNPLUG))
+		bt_hid_notify_virtual_unplug(dev, buf, bread);
+
+	/* 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)
+{
+	GIOCondition cond = G_IO_IN | G_IO_ERR | G_IO_NVAL;
+	struct uhid_event ev;
+	GIOChannel *io;
+	int err;
+
+	dev->uhid_fd = open(UHID_DEVICE_FILE, O_RDWR | O_CLOEXEC);
+	if (dev->uhid_fd < 0) {
+		err = -errno;
+		error("Failed to open uHID device: %s", strerror(errno));
+		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;
+
+	if (write(dev->uhid_fd, &ev, sizeof(ev)) < 0) {
+		err = -errno;
+		error("Failed to create uHID device: %s", strerror(errno));
+		close(dev->uhid_fd);
+		dev->uhid_fd = -1;
+		return err;
+	}
+
+	io = g_io_channel_unix_new(dev->uhid_fd);
+	g_io_channel_set_encoding(io, NULL, NULL);
+	dev->uhid_watch_id = g_io_add_watch(io, cond, uhid_event_cb, dev);
+	g_io_channel_unref(io);
+
+	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("%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("%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, BT_IO_SEC_LOW,
+					BT_IO_OPT_INVALID);
+	if (!dev->intr_io) {
+		error("%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("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_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;
+
+		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) {
+		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, BT_IO_SEC_LOW,
+					BT_IO_OPT_INVALID);
+	if (gerr) {
+		error("%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("Unable to get Device ID SDP record: %s", strerror(-err));
+		goto fail;
+	}
+
+	if (!recs || !recs->data) {
+		error("No 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("failed to search sdp details");
+		goto fail;
+	}
+
+	return;
+
+fail:
+	bt_hid_notify_state(dev, HAL_HIDHOST_STATE_DISCONNECTED);
+	hid_device_remove(dev);
+}
+
+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) {
+		status = HAL_STATUS_FAILED;
+		goto failed;
+	}
+
+	dev = g_new0(struct hid_device, 1);
+	bacpy(&dev->dst, &dst);
+	dev->uhid_fd = -1;
+
+	ba2str(&dev->dst, addr);
+	DBG("connecting to %s", addr);
+
+	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("Failed to search DeviceID SDP details");
+		hid_device_remove(dev);
+		status = HAL_STATUS_FAILED;
+		goto failed;
+	}
+
+	devices = g_slist_append(devices, dev);
+	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 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;
+
+	/* 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_DISCONNECT,
+									status);
+}
+
+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;
+	uint8_t hdr;
+	int fd;
+
+	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;
+	}
+
+	hdr = HID_MSG_CONTROL | HID_VIRTUAL_CABLE_UNPLUG;
+
+	fd = g_io_channel_unix_get_fd(dev->ctrl_io);
+
+	if (write(fd, &hdr, sizeof(hdr)) < 0) {
+		error("error writing virtual unplug command: %s (%d)",
+						strerror(errno), errno);
+		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("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("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("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 i, fd;
+	uint8_t *req;
+	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)) {
+		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. */
+	for (i = 0; i < (req_size - 1); i++)
+		sscanf((char *) &(cmd->data)[i * 2], "%hhx", &(req + 1)[i]);
+
+	fd = g_io_channel_unix_get_fd(dev->ctrl_io);
+
+	if (write(fd, req, req_size) < 0) {
+		error("error writing hid_set_report: %s (%d)",
+						strerror(errno), errno);
+		g_free(req);
+		status = HAL_STATUS_FAILED;
+		goto failed;
+	}
+
+	dev->last_hid_msg = HID_MSG_SET_REPORT;
+	g_free(req);
+
+	status = HAL_STATUS_SUCCESS;
+
+failed:
+	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 i, fd;
+	uint8_t *req;
+	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. */
+	for (i = 0; i < (req_size - 1); i++)
+		sscanf((char *) &(cmd->data)[i * 2], "%hhx", &(req + 1)[i]);
+
+	fd = g_io_channel_unix_get_fd(dev->intr_io);
+
+	if (write(fd, req, req_size) < 0) {
+		error("error writing data to HID device: %s (%d)",
+						strerror(errno), errno);
+		g_free(req);
+		status = HAL_STATUS_FAILED;
+		goto failed;
+	}
+
+	g_free(req);
+
+	status = HAL_STATUS_SUCCESS;
+
+failed:
+	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 src, dst;
+	char address[18];
+	uint16_t psm;
+	GError *gerr = NULL;
+	GSList *l;
+	uuid_t uuid;
+
+	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_io_channel_shutdown(chan, TRUE, NULL);
+		g_error_free(gerr);
+		return;
+	}
+
+	ba2str(&dst, address);
+	DBG("Incoming connection from %s on PSM %d", address, psm);
+
+	switch (psm) {
+	case L2CAP_PSM_HIDP_CTRL:
+		l = g_slist_find_custom(devices, &dst, device_cmp);
+		if (l)
+			return;
+
+		dev = g_new0(struct hid_device, 1);
+		bacpy(&dev->dst, &dst);
+		dev->ctrl_io = g_io_channel_ref(chan);
+		dev->uhid_fd = -1;
+
+		sdp_uuid16_create(&uuid, PNP_INFO_SVCLASS_ID);
+		if (bt_search_service(&src, &dev->dst, &uuid,
+				hid_sdp_did_search_cb, dev, NULL, 0) < 0) {
+			error("failed to search did sdp details");
+			hid_device_remove(dev);
+			return;
+		}
+
+		devices = g_slist_append(devices, dev);
+
+		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;
+	}
+}
+
+bool bt_hid_register(struct ipc *ipc, const bdaddr_t *addr, uint8_t mode)
+{
+	GError *err = NULL;
+
+	DBG("");
+
+	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("Failed to listen on ctrl 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("Failed to listen on intr 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("");
+
+	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;
+}
diff --git a/bluez/android/hidhost.h b/bluez/android/hidhost.h
new file mode 100644
index 0000000..e6b87ed
--- /dev/null
+++ b/bluez/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/android/init.bluetooth.rc b/bluez/android/init.bluetooth.rc
new file mode 100644
index 0000000..af62121
--- /dev/null
+++ b/bluez/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/logwrapper /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/logwrapper /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/android/ipc-common.h b/bluez/android/ipc-common.h
new file mode 100644
index 0000000..27736e4
--- /dev/null
+++ b/bluez/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/android/ipc-tester.c b/bluez/android/ipc-tester.c
new file mode 100644
index 0000000..63fd105
--- /dev/null
+++ b/bluez/android/ipc-tester.c
@@ -0,0 +1,1261 @@
+/*
+ *
+ *  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 "src/shared/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 + 1];
+
+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 + 1];
+	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);
+	bluetoothd_start(hci_index);
+
+failed:
+	close(pipe);
+	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,
+			},
+	};
+
+	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)
+
+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,
+		},
+};
+
+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,
+};
+
+struct ipc_hdr enable_unknown_service_hdr = {
+	.service_id = HAL_SERVICE_ID_MAX + 1,
+	.opcode = HAL_OP_REGISTER_MODULE,
+	.len = 0,
+};
+
+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,
+};
+
+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("A2DP", HAL_SERVICE_ID_A2DP, 0x03, 0,
+			HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_A2DP);
+
+	/* 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);
+
+	return tester_run();
+}
diff --git a/bluez/android/ipc.c b/bluez/android/ipc.c
new file mode 100644
index 0000000..8cd34ea
--- /dev/null
+++ b/bluez/android/ipc.c
@@ -0,0 +1,426 @@
+/*
+ *
+ *  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 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)
+{
+	if (!ipc || !ipc->notif_io)
+		return;
+
+	ipc_send(g_io_channel_unix_get_fd(ipc->notif_io), service_id, opcode,
+								len, param, -1);
+}
+
+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/android/ipc.h b/bluez/android/ipc.h
new file mode 100644
index 0000000..cc4e92d
--- /dev/null
+++ b/bluez/android/ipc.h
@@ -0,0 +1,52 @@
+/*
+ *
+ *  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 service_handler {
+	const struct ipc_handler *handler;
+	uint8_t size;
+};
+
+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_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/android/main.c b/bluez/android/main.c
new file mode 100644
index 0000000..828f81d
--- /dev/null
+++ b/bluez/android/main.c
@@ -0,0 +1,552 @@
+/*
+ *
+ *  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/capability.h>
+#include <linux/prctl.h>
+#endif
+
+#include <glib.h>
+
+#include "src/log.h"
+#include "src/sdpd.h"
+
+#include "lib/bluetooth.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"
+
+#define STARTUP_GRACE_SECONDS 5
+#define SHUTDOWN_GRACE_SECONDS 10
+
+static guint bluetooth_start_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 };
+
+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:
+		bt_bluetooth_register(hal_ipc, m->mode);
+
+		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)) {
+			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;
+	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 void service_unregister(const void *buf, uint16_t len)
+{
+	const struct hal_cmd_unregister_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:
+		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;
+	default:
+		/* This would indicate bug in HAL, as unregister should not be
+		 * called in init failed */
+		DBG("service %u not supported", m->service_id);
+		status = HAL_STATUS_FAILED;
+		goto failed;
+	}
+
+	services[m->service_id] = false;
+
+	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 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) },
+};
+
+static void bluetooth_stopped(void)
+{
+	g_main_loop_quit(event_loop);
+}
+
+static gboolean quit_eventloop(gpointer user_data)
+{
+	g_main_loop_quit(event_loop);
+	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;
+	}
+
+	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 (bluetooth_start_timeout > 0) {
+		g_source_remove(bluetooth_start_timeout);
+		bluetooth_start_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_BLUETOOTH; i < HAL_SERVICE_ID_MAX; i++) {
+		if (!services[i])
+			continue;
+
+		switch (i) {
+		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_AVRCP:
+			bt_avrcp_unregister();
+			break;
+		case HAL_SERVICE_ID_PAN:
+			bt_pan_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;
+		}
+
+		services[i] = false;
+	}
+}
+
+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;
+}
+
+int main(int argc, char *argv[])
+{
+	GOptionContext *context;
+	GError *err = NULL;
+	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, &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;
+	}
+
+	bluetooth_start_timeout = g_timeout_add_seconds(STARTUP_GRACE_SECONDS,
+							quit_eventloop, NULL);
+	if (bluetooth_start_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(bluetooth_start_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 (bluetooth_start_timeout > 0)
+		g_source_remove(bluetooth_start_timeout);
+
+	cleanup_services();
+
+	stop_sdp_server();
+	bt_bluetooth_cleanup();
+	g_main_loop_unref(event_loop);
+
+	ipc_unregister(hal_ipc, HAL_SERVICE_ID_CORE);
+	ipc_cleanup(hal_ipc);
+
+	info("Exit");
+
+	__btd_log_cleanup();
+
+	return EXIT_SUCCESS;
+}
diff --git a/bluez/android/pan.c b/bluez/android/pan.c
new file mode 100644
index 0000000..3bcab57
--- /dev/null
+++ b/bluez/android/pan.c
@@ -0,0 +1,843 @@
+/*
+ *
+ *  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 <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"
+
+static bdaddr_t adapter_addr;
+GSList *devices = NULL;
+uint8_t local_role = HAL_PAN_ROLE_NONE;
+static struct ipc *hal_ipc = NULL;
+
+struct pan_device {
+	char		iface[16];
+	bdaddr_t	dst;
+	uint8_t		conn_state;
+	uint8_t		role;
+	GIOChannel	*io;
+	struct bnep	*session;
+	guint		watch;
+};
+
+static struct {
+	uint32_t	record_id;
+	GIOChannel	*io;
+	bool		bridge;
+} nap_dev = {
+	.record_id = 0,
+	.io = NULL,
+	.bridge = false,
+};
+
+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);
+	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_dev.bridge)
+		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_dev.bridge = 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_dev.bridge)
+		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_dev.bridge = 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)
+{
+	struct hal_ev_pan_ctrl_state ev;
+
+	DBG("");
+
+	ev.state = state;
+	ev.local_role = local_role;
+	ev.status = HAL_STATUS_SUCCESS;
+
+	memset(ev.name, 0, sizeof(ev.name));
+
+	if (local_role == HAL_PAN_ROLE_NAP)
+		memcpy(ev.name, BNEP_BRIDGE, sizeof(BNEP_BRIDGE));
+	else
+		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);
+	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, dev);
+	if (perr < 0) {
+		error("bnep connect req failed: %s", strerror(-perr));
+		goto fail;
+	}
+
+	bnep_set_disconnect(dev->session, bnep_disconn_cb, dev);
+
+	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];
+	struct bnep_setup_conn_req *req = (void *) packet;
+	uint16_t src_role, dst_role, rsp = BNEP_CONN_NOT_ALLOWED;
+	int sk, n;
+
+	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 = read(sk, packet, sizeof(packet));
+	if (n  < 0) {
+		error("read(): %s(%d)", strerror(errno), errno);
+		goto failed;
+	}
+
+	/* Highest known control command id BNEP_FILTER_MULT_ADDR_RSP 0x06 */
+	if (req->type == BNEP_CONTROL &&
+			req->ctrl > BNEP_FILTER_MULT_ADDR_RSP) {
+		error("cmd not understood");
+		bnep_send_ctrl_rsp(sk, BNEP_CONTROL, BNEP_CMD_NOT_UNDERSTOOD,
+								req->ctrl);
+		goto failed;
+	}
+
+	if (req->type != BNEP_CONTROL || req->ctrl != BNEP_SETUP_CONN_REQ) {
+		error("cmd is not BNEP_SETUP_CONN_REQ %02X %02X", req->type,
+								req->ctrl);
+		goto failed;
+	}
+
+	rsp = bnep_setup_decode(req, &dst_role, &src_role);
+	if (rsp) {
+		error("bnep_setup_decode failed");
+		goto failed;
+	}
+
+	rsp = bnep_setup_chk(dst_role, src_role);
+	if (rsp) {
+		error("benp_setup_chk failed");
+		goto failed;
+	}
+
+	if (nap_create_bridge() < 0)
+		goto failed;
+
+	if (bnep_server_add(sk, dst_role, BNEP_BRIDGE, dev->iface,
+							&dev->dst) < 0) {
+		nap_remove_bridge();
+		error("server_connadd failed");
+		rsp = BNEP_CONN_NOT_ALLOWED;
+		goto failed;
+	}
+
+	rsp = BNEP_SUCCESS;
+	bnep_send_ctrl_rsp(sk, BNEP_CONTROL, BNEP_SETUP_CONN_RSP, rsp);
+
+	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);
+	bt_pan_notify_conn_state(dev, HAL_PAN_STATE_CONNECTED);
+
+	return FALSE;
+
+failed:
+	bnep_send_ctrl_rsp(sk, BNEP_CONTROL, BNEP_SETUP_CONN_RSP, rsp);
+	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_dev.io) {
+		g_io_channel_shutdown(nap_dev.io, FALSE, NULL);
+		g_io_channel_unref(nap_dev.io);
+		nap_dev.io = NULL;
+	}
+}
+
+static int register_nap_server(void)
+{
+	GError *gerr = NULL;
+
+	DBG("");
+
+	nap_dev.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_dev.io) {
+		destroy_nap_device();
+		error("%s", gerr->message);
+		g_error_free(gerr);
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static void bt_pan_enable(const void *buf, uint16_t len)
+{
+	const struct hal_cmd_pan_enable *cmd = buf;
+	uint8_t status;
+	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:
+		status = HAL_STATUS_SUCCESS;
+		goto reply;
+	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;
+
+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 *pan_record(void)
+{
+	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 = 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(&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, &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 *rec;
+	int err;
+
+	DBG("");
+
+	bacpy(&adapter_addr, addr);
+
+	rec = pan_record();
+	if (!rec) {
+		error("Failed to allocate PAN record");
+		return false;
+	}
+
+	if (bt_adapter_add_record(rec, SVC_HINT_NETWORKING) < 0) {
+		error("Failed to register PAN record");
+		sdp_record_free(rec);
+		return false;
+	}
+
+	err = bnep_init();
+	if (err < 0) {
+		error("bnep init failed");
+		bt_adapter_remove_record(rec->handle);
+		return false;
+	}
+
+	err = register_nap_server();
+	if (err < 0) {
+		error("Failed to register NAP");
+		bt_adapter_remove_record(rec->handle);
+		bnep_cleanup();
+		return false;
+	}
+
+	nap_dev.record_id = 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_dev.record_id);
+	nap_dev.record_id = 0;
+	destroy_nap_device();
+}
diff --git a/bluez/android/pan.h b/bluez/android/pan.h
new file mode 100644
index 0000000..cfbea96
--- /dev/null
+++ b/bluez/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/android/pics-a2dp.txt b/bluez/android/pics-a2dp.txt
new file mode 100644
index 0000000..1579567
--- /dev/null
+++ b/bluez/android/pics-a2dp.txt
@@ -0,0 +1,86 @@
+A2DP PICS for the PTS tool.
+
+PTS version: 5.0
+
+* - different than PTS defaults
+# - not yet implemented/supported
+
+M - mandatory if such role selected
+O - optional
+
+		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 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	False		SRC: SBC Configurations in 48 KHz sampling
+					(C.1)
+-------------------------------------------------------------------------------
+C.1: At least one of the values shall be supported.
+-------------------------------------------------------------------------------
+
+
+		Supported Codecs in SRC
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_A2DP_3_1	True		SRC: SBC encoder Codec (M)
+TSPC_A2DP_3_2	False		SRC: Additional encoder Codec (O)
+-------------------------------------------------------------------------------
+
+
+		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 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)
+-------------------------------------------------------------------------------
+
+
+		Supported codecs in SNK
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_A2DP_5_1	False		SNK: SBC decoder Codec (M)
+TSPC_A2DP_5_2	False		SNK: Additional decoder Coded (O)
+TSPC_ALL	False		Enable all test cases when set to False.
+-------------------------------------------------------------------------------
diff --git a/bluez/android/pics-avctp.txt b/bluez/android/pics-avctp.txt
new file mode 100644
index 0000000..41ad4db
--- /dev/null
+++ b/bluez/android/pics-avctp.txt
@@ -0,0 +1,75 @@
+AVCTP PICS for the PTS tool.
+
+PTS version: 5.0
+
+* - 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	False		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	False (*)	Transaction label management (M)
+TSPC_AVCTP_2_3	False (*)	Packet type field management (M)
+TSPC_AVCTP_2_4	False (*)	Message type field management (M)
+TSPC_AVCTP_2_5	False (*)	PID field management (M)
+TSPC_AVCTP_2_6	False (*)	IPID field mangement (M)
+TSPC_AVCTP_2_7	False (*)	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/android/pics-avrcp.txt b/bluez/android/pics-avrcp.txt
new file mode 100644
index 0000000..bee22c8
--- /dev/null
+++ b/bluez/android/pics-avrcp.txt
@@ -0,0 +1,626 @@
+AVRCP PICS for the PTS tool.
+
+PTS version: 5.0
+
+* - different than PTS defaults
+# - not yet implemented/supported
+
+M - mandatory if such role selected
+O - optional
+
+		Roles
+-------------------------------------------------------------------------------
+Parameter Name    Selected	Description
+-------------------------------------------------------------------------------
+SPC_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 (M)
+TSPC_AVRCP_2_3    False (*)	CT: Initiating connection release (M)
+TSPC_AVRCP_2_4    False (*)	CT: Accepting connection release (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 (O)
+TSPC_AVRCP_2_17   False		CT: Get Player Application Setting Value (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: GetCapabilities, Cover Art (C.10)
+TSPC_AVRCP_2_56   False		CT: GetImageProperties, Cover Art (C.10)
+TSPC_AVRCP_2_57   False		CT: GetImage, Cover Art (C.9)
+TSPC_AVRCP_2_58   False		CT: GetLinkedThumbnail, CoverArt (C.9)
+-------------------------------------------------------------------------------
+C.1: Mandatory to support at least one of the defined categories
+	(TSPC_AVRCP_2_7 through TSPC_AVRCP_2_10).
+C.2: Mandatory 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_7 or (TSPC_AVRCP_2_8 AND TSPC_AVRCP_2_49)
+	or TSPC_AVRCP_2_9 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.
+-------------------------------------------------------------------------------
+
+
+		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_33a  False		CT: category 1 - Operation id: F5 (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.2)
+TSPC_AVRCP_4_2    False		CT: category 2 - Operation id: 1 (C.2)
+TSPC_AVRCP_4_3    False		CT: category 2 - Operation id: 2 (C.2)
+TSPC_AVRCP_4_4    False		CT: category 2 - Operation id: 3 (C.2)
+TSPC_AVRCP_4_5    False		CT: category 2 - Operation id: 4 (C.2)
+TSPC_AVRCP_4_6    False		CT: category 2 - Operation id: 5 (C.2)
+TSPC_AVRCP_4_7    False		CT: category 2 - Operation id: 6 (C.2)
+TSPC_AVRCP_4_8    False		CT: category 2 - Operation id: 7 (C.2)
+TSPC_AVRCP_4_9    False		CT: category 2 - Operation id: 8 (C.2)
+TSPC_AVRCP_4_10   False		CT: category 2 - Operation id: 9 (C.2)
+TSPC_AVRCP_4_11   False		CT: category 2 - Operation id: dot (C.2)
+TSPC_AVRCP_4_12   False		CT: category 2 - Operation id: enter (C.2)
+TSPC_AVRCP_4_13   False		CT: category 2 - Operation id: clear (C.2)
+TSPC_AVRCP_4_14   False		CT: category 2 - Operation id: sound_select
+					(C.2)
+TSPC_AVRCP_4_15   False		CT: category 2 - Operation id: input_select
+					(C.2)
+TSPC_AVRCP_4_16   False		CT: category 2 - Operation id:
+					display_information (C.2)
+TSPC_AVRCP_4_17   False		CT: category 2 - Operation id: help (C.2)
+TSPC_AVRCP_4_18   False		CT: category 2 - Operation id: power (C.2)
+TSPC_AVRCP_4_19   False (*)	CT: category 2 - Operation id: volume_up (C.2)
+TSPC_AVRCP_4_20   False (*)	CT: category 2 - Operation id: volume_down (C.2)
+TSPC_AVRCP_4_21   False		CT: category 2 - Operation id: mute (C.2)
+TSPC_AVRCP_4_22   False		CT: category 2 - Operation id: F1 (C.2)
+TSPC_AVRCP_4_23   False		CT: category 2 - Operation id: F2 (C.2)
+TSPC_AVRCP_4_24   False		CT: category 2 - Operation id: F3 (C.2)
+TSPC_AVRCP_4_25   False		CT: category 2 - Operation id: F4 (C.2)
+TSPC_AVRCP_4_25a  False		CT: category 2 - Operation id: F5 (C.2)
+TSPC_AVRCP_4_26   False		CT: category 2 - Operation id: vendor_unique
+					(C.2)
+-------------------------------------------------------------------------------
+C.2: 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.3)
+TSPC_AVRCP_5_2    False		CT: category 3 - Operation id: 1 (C.3)
+TSPC_AVRCP_5_3    False		CT: category 3 - Operation id: 2 (C.3)
+TSPC_AVRCP_5_4    False		CT: category 3 - Operation id: 3 (C.3)
+TSPC_AVRCP_5_5    False		CT: category 3 - Operation id: 4 (C.3)
+TSPC_AVRCP_5_6    False		CT: category 3 - Operation id: 5 (C.3)
+TSPC_AVRCP_5_7    False		CT: category 3 - Operation id: 6 (C.3)
+TSPC_AVRCP_5_8    False		CT: category 3 - Operation id: 7 (C.3)
+TSPC_AVRCP_5_9    False		CT: category 3 - Operation id: 8 (C.3)
+TSPC_AVRCP_5_10   False		CT: category 3 - Operation id: 9 (C.3)
+TSPC_AVRCP_5_11   False		CT: category 3 - Operation id: dot (C.3)
+TSPC_AVRCP_5_12   False		CT: category 3 - Operation id: enter (C.3)
+TSPC_AVRCP_5_13   False		CT: category 3 - Operation id: clear (C.3)
+TSPC_AVRCP_5_14   False		CT: category 3 - Operation id: channel up (C.3)
+TSPC_AVRCP_5_15   False		CT: category 3 - Operation id: channel down
+					(C.3)
+TSPC_AVRCP_5_16   False		CT: category 3 - Operation id: previous channel
+					(C.3)
+TSPC_AVRCP_5_17   False		CT: category 3 - Operation id: sound_select
+					(C.3)
+TSPC_AVRCP_5_18   False		CT: category 3 - Operation id: input_select
+					(C.3)
+TSPC_AVRCP_5_19   False		CT: category 3 - Operation id:
+					display_information (C.3)
+TSPC_AVRCP_5_20   False		CT: category 3 - Operation id: help (C.3)
+TSPC_AVRCP_5_21   False		CT: category 3 - Operation id: power (C.3)
+TSPC_AVRCP_5_22   False		CT: category 3 - Operation id: angle (C.3)
+TSPC_AVRCP_5_23   False		CT: category 3 - Operation id: subpicture(C.3)
+TSPC_AVRCP_5_24   False		CT: category 3 - Operation id: F1 (C.3)
+TSPC_AVRCP_5_25   False		CT: category 3 - Operation id: F2 (C.3)
+TSPC_AVRCP_5_26   False		CT: category 3 - Operation id: F3 (C.3)
+TSPC_AVRCP_5_27   False		CT: category 3 - Operation id: F4 (C.3)
+TSPC_AVRCP_5_27a  False		CT: category 3 - Operation id: F5 (C.3)
+TSPC_AVRCP_5_28   False		CT: category 3 - Operation id: vendor_unique
+					(C.3)
+-------------------------------------------------------------------------------
+C.3: 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.4)
+TSPC_AVRCP_6_2    False		CT: category 4 - Operation id: up (C.4)
+TSPC_AVRCP_6_3    False		CT: category 4 - Operation id: down (C.4)
+TSPC_AVRCP_6_4    False		CT: category 4 - Operation id: left (C.4)
+TSPC_AVRCP_6_5    False		CT: category 4 - Operation id: right (C.4)
+TSPC_AVRCP_6_6    False		CT: category 4 - Operation id: right up (C.4)
+TSPC_AVRCP_6_7    False		CT: category 4 - Operation id: right down (C.4)
+TSPC_AVRCP_6_8    False		CT: category 4 - Operation id: left up (C.4)
+TSPC_AVRCP_6_9    False		CT: category 4 - Operation id: left down (C.4)
+TSPC_AVRCP_6_10   False		CT: category 4 - Operation id: root menu (C.4)
+TSPC_AVRCP_6_11   False		CT: category 4 - Operation id: setup menu (C.4)
+TSPC_AVRCP_6_12   False		CT: category 4 - Operation id: contents menu
+					(C.4)
+TSPC_AVRCP_6_13   False		CT: category 4 - Operation id: favorite menu
+					(C.4)
+TSPC_AVRCP_6_14   False		CT: category 4 - Operation id: exit (C.4)
+TSPC_AVRCP_6_15   False		CT: category 4 - Operation id: 0 (C.4)
+TSPC_AVRCP_6_16   False		CT: category 4 - Operation id: 1 (C.4)
+TSPC_AVRCP_6_17   False		CT: category 4 - Operation id: 2 (C.4)
+TSPC_AVRCP_6_18   False		CT: category 4 - Operation id: 3 (C.4)
+TSPC_AVRCP_6_19   False		CT: category 4 - Operation id: 4 (C.4)
+TSPC_AVRCP_6_20   False		CT: category 4 - Operation id: 5 (C.4)
+TSPC_AVRCP_6_21   False		CT: category 4 - Operation id: 6 (C.4)
+TSPC_AVRCP_6_22   False		CT: category 4 - Operation id: 7 (C.4)
+TSPC_AVRCP_6_23   False		CT: category 4 - Operation id: 8 (C.4)
+TSPC_AVRCP_6_24   False		CT: category 4 - Operation id: 9 (C.4)
+TSPC_AVRCP_6_25   False		CT: category 4 - Operation id: dot (C.4)
+TSPC_AVRCP_6_26   False		CT: category 4 - Operation id: enter (C.4)
+TSPC_AVRCP_6_27   False		CT: category 4 - Operation id: clear (C.4)
+TSPC_AVRCP_6_28   False		CT: category 4 - Operation id:
+					display_information (C.4)
+TSPC_AVRCP_6_29   False		CT: category 4 - Operation id: help (C.4)
+TSPC_AVRCP_6_30   False		CT: category 4 - Operation id: page up (C.4)
+TSPC_AVRCP_6_31   False		CT: category 4 - Operation id: page down (C.4)
+TSPC_AVRCP_6_32   False		CT: category 4 - Operation id: power (C.4)
+TSPC_AVRCP_6_33   False		CT: category 4 - Operation id: F1 (C.4)
+TSPC_AVRCP_6_34   False		CT: category 4 - Operation id: F2 (C.4)
+TSPC_AVRCP_6_35   False		CT: category 4 - Operation id: F3 (C.4)
+TSPC_AVRCP_6_36   False		CT: category 4 - Operation id: F4 (C.4)
+TSPC_AVRCP_6_36a  False		CT: category 4 - Operation id: F5 (C.4)
+TSPC_AVRCP_6_37   False		CT: category 4 - Operation id: vendor_unique
+					(C.4)
+-------------------------------------------------------------------------------
+C.4: Mandatory to support at least one of these operation_ids if the device
+	supports category 4 (TSPC_AVRCP_2_9).
+-------------------------------------------------------------------------------
+
+
+		Target Features
+-------------------------------------------------------------------------------
+Parameter Name    Selected	Description
+-------------------------------------------------------------------------------
+TSPC_AVRCP_7_1    True (*)	TG: Initiating connection establishment (O)
+TSPC_AVRCP_7_2    True		TG: Accept connection establishment (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 (C.14)
+TSPC_AVRCP_7_13   False		TG: List Player Application Setting Values
+					(C.14)
+TSPC_AVRCP_7_14   False		TG: Get Current Player Application Settings
+					(C.14)
+TSPC_AVRCP_7_15   False		TG: Set Player Application Setting Value (C.14)
+TSPC_AVRCP_7_16   False		TG: Get Player Application Setting Attribute
+					(O)
+TSPC_AVRCP_7_17   False		TG: Get Player Application Setting Value (O)
+TSPC_AVRCP_7_18   False		TG: Inform Displayable Character Set (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 ContinuingResponse (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: Supports initiation of browsing channel
+					establishment (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: GetCapabilities, Cover Art (C.12)
+TSPC_AVRCP_7_69   False		TG: GetImageProperties, Cover Art (C.12)
+TSPC_AVRCP_7_70   False		TG: GetImage, Cover Art (C.12)
+TSPC_AVRCP_7_71   False		TG: GetLinkedThumbnail, Cover Art (C.12)
+-------------------------------------------------------------------------------
+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.
+-------------------------------------------------------------------------------
+
+		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_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/android/pics-did.txt b/bluez/android/pics-did.txt
new file mode 100644
index 0000000..58cee41
--- /dev/null
+++ b/bluez/android/pics-did.txt
@@ -0,0 +1,23 @@
+DID PICS for the PTS tool.
+
+PTS version: 5.0
+
+* - 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/android/pics-gap.txt b/bluez/android/pics-gap.txt
new file mode 100644
index 0000000..56a33d5
--- /dev/null
+++ b/bluez/android/pics-gap.txt
@@ -0,0 +1,739 @@
+GAP PICS for the PTS tool.
+
+PTS version: 5.0
+
+* - different than PTS defaults
+# - not yet implemented/supported
+
+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	False		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)
+-------------------------------------------------------------------------------
+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	False		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	False		Non-bondable mode (O)
+TSPC_GAP_1_7	True (*)	Bondable mode (C.2)
+-------------------------------------------------------------------------------
+C.1: Mandatory if TSPC_GAP_0_2 is supported, otherwise Optional.
+C.2: Mandatory if TSPC_GAP_3_5 is supported, otherwise Optional.
+-------------------------------------------------------------------------------
+
+
+		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	False		No security (C.6)
+-------------------------------------------------------------------------------
+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	False		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)
+-------------------------------------------------------------------------------
+
+
+		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	False (*)	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 (M)
+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)
+-------------------------------------------------------------------------------
+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
+TSPC_GAP_11_2	False		Broadcaster: Resolvable Private Address
+					Generation Procedure
+-------------------------------------------------------------------------------
+
+
+		Observer Physical Layer
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_GAP_12_1	True (#)	Observer: Receiver
+TSPC_GAP_12_2	False		Observer: Transmitter
+-------------------------------------------------------------------------------
+
+
+		Observer Link Layer States
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_GAP_13_1	True (#)	Observer: Standby
+TSPC_GAP_13_2	True (#)	Observer: Scanning
+-------------------------------------------------------------------------------
+
+
+		Observer Link Layer Scanning Types
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_GAP_14_1	True (#)	Observer: Passive Scanning
+TSPC_GAP_14_2	False		Observer: Active Scanning
+-------------------------------------------------------------------------------
+
+
+		Observer Connection Modes and Procedures
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_GAP_15_1	True (#)	Observer: Non-Connectable Mode
+-------------------------------------------------------------------------------
+
+
+		Observer Broadcasting and Observing Features
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_GAP_16_1	True (#)	Observer: Observation Procedure
+-------------------------------------------------------------------------------
+
+
+		Observer Privacy Feature
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_GAP_17_1	False		Observer: Privacy Feature (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	False		Peripheral: Transmitter
+TSPC_GAP_18_2	False		Peripheral: Receiver
+-------------------------------------------------------------------------------
+
+
+		Peripheral Link Layer States
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_GAP_19_1	False		Peripheral: Standby
+TSPC_GAP_19_2	False		Peripheral: Advertising
+TSPC_GAP_19_3	False		Peripheral: Connection, Slave Role
+-------------------------------------------------------------------------------
+
+
+		Peripheral Link Layer Advertising Event Types
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_GAP_20_1	False		Peripheral: Connectable Undirected Event
+TSPC_GAP_20_2	False		Peripheral: Connectable Directed Event
+TSPC_GAP_20_3	False		Peripheral: Non-Connectable Undirected Event
+TSPC_GAP_20_4	False		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	False		AD Type-Local Name (C.1)
+TSPC_GAP_20A_3	False		AD Type-Flags (C.2)
+TSPC_GAP_20A_4	False		AD Type-Manufacturer Specific Data (C.1)
+TSPC_GAP_20A_5	False		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)
+-------------------------------------------------------------------------------
+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	False (*)	Peripheral: Connection Update Procedure (M)
+TSPC_GAP_21_2	False (*)	Peripheral: Channel Map Update Procedure (M)
+TSPC_GAP_21_3	False		Peripheral: Encryption Procedure (O)
+TSPC_GAP_21_4	False (*)	Peripheral: Feature Exchange Procedure (M)
+TSPC_GAP_21_5	False (*)	Peripheral: Version Exchange Procedure (M)
+TSPC_GAP_21_6	False (*)	Peripheral: Termination Procedure (M)
+-------------------------------------------------------------------------------
+
+
+		Peripheral Discovery Modes and Procedures
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_GAP_22_1	False		Peripheral: Non-Discoverable Mode (C.2)
+TSPC_GAP_22_2	False		Peripheral: Limited Discoverable Mode (C.1)
+TSPC_GAP_22_3	False		Peripheral: General Discoverable Mode (C.1)
+TSPC_GAP_22_4	False		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	False		Peripheral: Non-Connectable Mode (C.1)
+TSPC_GAP_23_2	False		Peripheral: Directed Connectable Mode (O)
+TSPC_GAP_23_3	False		Peripheral: Undirected Connectable Mode (M)
+TSPC_GAP_23_4	False		Peripheral: Connection Parameter Update
+					Procedure (O)
+TSPC_GAP_23_5	False		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	False		Peripheral: Non-Bondable Mode (M)
+TSPC_GAP_24_2	False		Peripheral: Bondable Mode (C.1)
+TSPC_GAP_24_3	False		Peripheral: Bonding Procedure  (C.2)
+TSPC_GAP_24_4	False		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	False		Peripheral: Security Mode (O)
+TSPC_GAP_25_2	False		Peripheral: Security Mode 2 (O)
+TSPC_GAP_25_3	False		Peripheral: Authentication Procedure (C.2)
+TSPC_GAP_25_4	False		Peripheral: Authorization Procedure (O)
+TSPC_GAP_25_5	False		Peripheral: Connection Data Signing Procedure
+					(O)
+TSPC_GAP_25_6	False		Peripheral: Authenticate Signed Data Procedure
+					(O)
+TSPC_GAP_25_7	False		Peripheral: Authenticated Pairing
+					(LE security mode 1 level 3) (C.1)
+TSPC_GAP_25_8	False		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 (O)
+TSPC_GAP_26_2	False		Peripheral: Non-Resolvable Private Address
+					Generation Procedure (C.1)
+TSPC_GAP_26_3	False		Peripheral: Resolvable Private Address
+					Generation Procedure (C.2)
+-------------------------------------------------------------------------------
+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	False (*)	Peripheral: Device Name (M)
+TSPC_GAP_27_2	False (*)	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)
+-------------------------------------------------------------------------------
+
+
+		Central Discovery Modes and Procedures
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_GAP_32_1	False		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 Connectin 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	False		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	False		Central: Authenticated Pairing
+					(LE security mode 1 level 3) (C.1)
+TSPC_GAP_35_8	False		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 (C.3)
+TSPC_GAP_36_2	False		Central: Non-Resolvable Private Address
+					Generation Procedure (C.1)
+TSPC_GAP_36_3	False		Central: Resolvable Private Address Resolution
+					Procedure (C.2)
+TSPC_GAP_36_4	False		Central: Write to Privacy Characteristic
+					(Enable/Disable Privacy) (O)
+-------------------------------------------------------------------------------
+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	True (*#)	BR/EDR/LE: Observer (C.1)
+TSPC_GAP_38_3	False		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	False		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	False		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)
+-------------------------------------------------------------------------------
+
+
+		Central Simultaneous BR/EDR and LE Transports
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_GAP_42_1	False		Simultaneous BR/EDR and LE Transports –	BR/EDR
+					Slave to the same device (C.1)
+TSPC_GAP_42_2	False		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.
+-------------------------------------------------------------------------------
+
+
+		Peripheral BR/EDR/LE Modes
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_GAP_43_1	False		Peripheral BR/EDR/LE: Non-Discoverable Mode
+				(C.1)
+TSPC_GAP_43_2	False		Peripheral BR/EDR/LE: Discoverable Mode
+				(C.2)
+TSPC_GAP_43_3	False		Peripheral BR/EDR/LE: Non-Connectable Mode
+				(C.3)
+TSPC_GAP_43_4	False (*)	Peripheral BR/EDR/LE: Connectable Mode  (M)
+TSPC_GAP_43_5	False		Peripheral BR/EDR/LE: Non-Bondable Mode (C.4)
+TSPC_GAP_43_6	False		Peripheral 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.
+-------------------------------------------------------------------------------
+
+
+		Peripheral BR/EDR/LE Security Aspects
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_GAP_44_1	False (*)	Peripheral BR/EDR/LE: Security Aspects (M)
+-------------------------------------------------------------------------------
+
+
+		Peripheral Simultaneous BR/EDR and LE Transports
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_GAP_45_1	False		Simultaneous BR/EDR and LE Transports – BR/EDR
+					Slave to the same device (C.1)
+TSPC_GAP_45_2	False		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_SM_1_1	False		Master Role (Initiator)
+TSPC_SM_1_2	False		Slave Role (Responder)
+TSPC_SM_2_4	False		OOB supported (O)
+TSPC_ALL	False		Turns on all
+-------------------------------------------------------------------------------
diff --git a/bluez/android/pics-gatt.txt b/bluez/android/pics-gatt.txt
new file mode 100644
index 0000000..8abf8c2
--- /dev/null
+++ b/bluez/android/pics-gatt.txt
@@ -0,0 +1,322 @@
+GATT PICS for the PTS tool.
+
+PTS version: 5.0
+
+* - 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	False (*)	Complete GATT client (C.3)
+TSPC_GATT_1A_2	False (*)	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.1)
+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	True		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_1A_1 is supported, otherwise Optional
+-------------------------------------------------------------------------------
+
+
+
+		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	True		Server: Read Multiple Characteristic
+					Values (C.4)
+TSPC_GATT_4_12	True		Server: Write without Response (C.2)
+TSPC_GATT_4_13	False (*)	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_1A_2 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	False (*)	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	False (*)	Exchange MTU Request (O)
+TSPC_ATT_3_4	False (*)	Find Information Request (O)
+TSPC_ATT_3_6	False (*)	Find by Type Value Request (O)
+TSPC_ATT_3_8	False (*)	Read by Type Request (O)
+TSPC_ATT_3_10	False (*)	Read Request (O)
+TSPC_ATT_3_12	False (*)	Read Blob Request (O)
+TSPC_ATT_3_14	False (*)	Read Multiple Request (O)
+TSPC_ATT_3_16	False (*)	Read by Group Type Request (O)
+TSPC_ATT_3_17	False (*)	Read by Group Type Response (C.6)
+TSPC_ATT_3_18	False (*)	Write Request (O)
+TSPC_ATT_3_20	False (*)	Write Command (O)
+TSPC_ATT_3_21	False (*)	Signed Write Command (O)
+TSPC_ATT_3_22	False (*)	Prepare Write Request (O)
+TSPC_ATT_3_24	False (*)	Execute Write Request (C.8)
+TSPC_ATT_3_26	True		Handle Value Notification (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	False (*)	Write Response (C.3)
+TSPC_ATT_4_20	False (*)	Write Command (O)
+TSPC_ATT_4_21	False (*)	Signed Write Command (O)
+TSPC_ATT_4_23	False (*)	Prepare Write Response (C.4)
+TSPC_ATT_4_25	False (*)	Execute Write Response (C.5)
+TSPC_ATT_4_26	False (*)	Handle Value Notification (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	False (*)	LE Security Mode 1 (C.2)
+TSPC_ATT_5_4	False (*)	LE Authentication Procedure (C.2)
+TSPC_ATT_5_7	False (*)	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	False (*)	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/android/pics-hfp.txt b/bluez/android/pics-hfp.txt
new file mode 100644
index 0000000..6c9808f
--- /dev/null
+++ b/bluez/android/pics-hfp.txt
@@ -0,0 +1,217 @@
+HFP PICS for the PTS tool.
+
+PTS version: 5.0
+
+* - 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)
+-------------------------------------------------------------------------------
+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
+					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 (*)s	Wide Band Speech service (C.8)
+TSPC_HFP_2_25	False		Support roaming function (O)
+-------------------------------------------------------------------------------
+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_12is 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_24 otherwise optional
+-------------------------------------------------------------------------------
+
+
+		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)
+-------------------------------------------------------------------------------
+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_18a, 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_24 otherwise optional
+-------------------------------------------------------------------------------
+
+
+		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)
+-------------------------------------------------------------------------------
+C.1: Mandatory if Wide band speech service is supported TSPC_HFP_2_24 or
+	TSPC_HFP_3_24, 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/android/pics-hid.txt b/bluez/android/pics-hid.txt
new file mode 100644
index 0000000..ac37e45
--- /dev/null
+++ b/bluez/android/pics-hid.txt
@@ -0,0 +1,287 @@
+HID PICS for the PTS tool.
+
+PTS version: 5.0
+
+* - 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 (M.1)
+TSPC_HID_2_2	True		Host: Accept HID connection (M.1)
+TSPC_HID_2_3	True		Host: Terminate HID connection (M.1)
+TSPC_HID_2_4	True		Host: Accept termination of HID connection (M.1)
+TSPC_HID_2_5	True		Host: Support for virtual cables (M.1)
+TSPC_HID_2_6	True		Host: HID initiated connection (M.1)
+TSPC_HID_2_7	True		Host: Host initiated connection (M.1)
+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.2)
+-------------------------------------------------------------------------------
+M.1: Mandatory to support IF (TSPC_HID_1_1) supported.
+C.1: Optional for Boot Mode Only Hosts (TSPC_HID_1_3); otherwise Mandatory
+	for Host Role (TSPC_HID_1_1).
+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.
+-------------------------------------------------------------------------------
+
+
+		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); otherwise Mandatory if
+	(TSPC_HID_2_12), otherwise Optional.
+C.2: Mandatory for Boot Mode Only Hosts (TSPC_HID_1_3); 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)
+TSPC_HID_5_2	False		Host : Get_Protocol command (C.1)
+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 (M.1)
+TSPC_HID_5_6	False (*)	Host : Get_Report command (M.2)
+-------------------------------------------------------------------------------
+M.1: Mandatory IF (TSPC_HID_1_1) supported AND (TSPC_HID_2_13) supported.
+C.1: Mandatory for Boot Mode Only Hosts (TSPC_HID_1_3); otherwise Optional.
+	If either Set_Protocol or Get_Protocol supported, both are Mandatory.
+M.2: Mandatory IF (TSPC_HID_1_1) Supported AND (TSPC_HID_2_12) Supported
+-------------------------------------------------------------------------------
+
+
+		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) (M.1)
+TSPC_HID_6_9	True		Host : Request Master Slave switch (M.1)
+TSPC_HID_6_10	True		Host : Accept Master Slave switch requests (M.1)
+TSPC_HID_6_11	False		Host : Hold mode (O)
+TSPC_HID_6_12	True		Host : Sniff mode (M.1)
+TSPC_HID_6_13	False		Host : Park mode (O)
+-------------------------------------------------------------------------------
+C.1: If Host Authentication supported, both (TSPC_HID_6_1) AND (TSPC_HID_6_2)
+	must be supported.
+C.2: If Pairing supported both (TSPC_HID_6_3) AND (TSPC_HID_6_4) must
+	be supported.
+M.1: Mandatory IF (TSPC_HID_1_1) supported.
+C.3: Mandatory IF (TSPC_HID_6_5) encryption supported.
+-------------------------------------------------------------------------------
+
+
+		Host Link Control Requirements
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_HID_7_1	True		Host : Supports inquiry, 79 channel (M.1)
+TSPC_HID_7_2	False (*)	Host : Supports inquiry scan, 79 channel (X)
+-------------------------------------------------------------------------------
+M.1: Mandatory to support IF (TSPC_HID_1_1) supported.
+X: 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.1)
+TSPC_HID_9_3	False		Hid : Terminate HID connection (O)
+TSPC_HID_9_4	False (*)	Hid : Accept Termination of HID connection (M.1)
+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)
+-------------------------------------------------------------------------------
+M.1: Mandatory IF (TSPC_HID_1_2) supported.
+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.
+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_2) 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)
+TSPC_HID_12_2	False		Hid : Get_Protocol command (C.1)
+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
+-------------------------------------------------------------------------------
+
+
+		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 : Initiate pairing before connection
+					completed (X)
+TSPC_HID_13_4	False		Hid : Initiate pairing after connection
+					completed (X)
+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.
+X: Feature should not be used by a HID device, but can be supported in LM.
+-------------------------------------------------------------------------------
+
+
+		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/android/pics-hsp.txt b/bluez/android/pics-hsp.txt
new file mode 100644
index 0000000..34ad44a
--- /dev/null
+++ b/bluez/android/pics-hsp.txt
@@ -0,0 +1,103 @@
+HSP PICS for the PTS tool.
+
+PTS version: 5.0
+
+* - 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/android/pics-l2cap.txt b/bluez/android/pics-l2cap.txt
new file mode 100644
index 0000000..7b49627
--- /dev/null
+++ b/bluez/android/pics-l2cap.txt
@@ -0,0 +1,159 @@
+L2CAP PICS for the PTS tool.
+
+PTS version: 5.0
+
+* - 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.1)
+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)
+-------------------------------------------------------------------------------
+C.1: Mandatory IF BR/EDR or BR/EDR/LE is claimed, ELSE 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 claimed, ELSE 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_4	True		Support of command echo request (C.21)
+TSPC_L2CAP_2_3  True            Support of connection oriented data
+                                        channel (C.20)
+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	True (*)	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	False		Unicast Connectionless Data, Reception (C.1, C.16)
+TSPC_L2CAP_2_36	False		Ability to transmit an unencrypted packet over
+					a Unicast connectionless L2CAP
+					channel (C.16)
+TSPC_L2CAP_2_37	False		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	False		Send disconnect request command (O)
+-------------------------------------------------------------------------------
+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 BR/EDR/LE AND SUM_ICS 31/7 (CSA1) OR
+	SUM_ICS 31/8 (3.0) OR SUM_ICS 31/9 (3.0+HS) OR SUM_ICS 31/10 (4.0))
+	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 are 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 SUM_ICS 31/9 (3.0 + HS) is claimed, ELSE Optional.
+C.13: Mandatory IF SUM_ICS 31/9 (3.0 + HS) is claimed, ELSE Optional.
+C.14: Optional IF SUM_ICS 31/8 OR 31/9 OR 31/10 OR 31/11 is claimed, ELSE Excluded.
+C.15: Optional IF TSPC_L2CAP_2_29 is claimed, ELSE Excluded.
+C.16: Optional IF (SUM_ICS 31/8 OR SUM_ICS 31/9 OR 31/10 OR 31/11) is claimed,
+       ELSE Excluded.
+C.17: Mandatory IF LE OR BR/EDR/LE is claimed, ELSE Excluded.
+C.18: Optional IF (SUM_ICS 31/10 AND 1/4) is claimed, ELSE Excluded.
+C.19: Mandatory IF (SUM_ICS 31/10 AND 1/3) is claimed, ELSE Excluded.
+C.20: Mandatory IF LE OR BR/EDR/LE, is claimed, ELSE Excluded
+C.21: Optional IF LE OR BR/EDR/LE, is claimed, ELSE Excluded
+C.22: Mandatory IF TSPC_L2CAP_2_29 is claimed, ELSE 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)
+-------------------------------------------------------------------------------
+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.
+-------------------------------------------------------------------------------
diff --git a/bluez/android/pics-map.txt b/bluez/android/pics-map.txt
new file mode 100644
index 0000000..2875885
--- /dev/null
+++ b/bluez/android/pics-map.txt
@@ -0,0 +1,175 @@
+MAP PICS for the PTS tool.
+
+PTS version: 5.0
+
+* - 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	False		MSE: SMS_CDMA (C1)
+TSPC_MAP_3_6d	False (*)	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/android/pics-opp.txt b/bluez/android/pics-opp.txt
new file mode 100644
index 0000000..cd4acce
--- /dev/null
+++ b/bluez/android/pics-opp.txt
@@ -0,0 +1,186 @@
+OPP PICS for the PTS tool.
+
+PTS version: 5.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.
+TSPC_OPP_1_2	True (*)	Role: Object Push Server.
+-------------------------------------------------------------------------------
+C.1: It is 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.1)
+TSPC_OPP_2_2	True		Client: Authentication/PIN exchange supported.
+					(M.1)
+TSPC_OPP_2_2a	True (*)	Client: Require Authentication/PIN by default.
+					(O)
+TSPC_OPP_2_3	True		Client: Object Push is supported. (M.1)
+TSPC_OPP_2_4	True (*)	Client: vCard 2.1 format is supported for
+					Object Push. (C.3)
+TSPC_OPP_2_5	False		Client: vCalender 1.0 format is supported for
+					Object Push. (O)
+TSPC_OPP_2_6	False		Client: vMsg as defined in IrMC 1.1 is supported
+					for Object Push. (O)
+TSPC_OPP_2_7	False		Client: vNote as defined in IrMC 1.1 is
+					supported for Object Push. (O)
+TSPC_OPP_2_8	True (*)	Client: Support content formats other than those
+					declared in TSPC_OPP_2_44 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 vMsg objects using
+					different PUT operations. (C.6)
+TSPC_OPP_2_10b	False		Client: Push multiple vMsg 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 is supported. (O)
+TSPC_OPP_2_14	False		Client: vCard 2.1 format is supported for
+					Business Card Pull. (C.1)
+TSPC_OPP_2_15	False		Client: Exchange business card is supported. (O)
+TSPC_OPP_2_16	False		Client: vCard 2.1 format is supported for
+					Business Card Exchange (C.2)
+-------------------------------------------------------------------------------
+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.
+M.1: Mandatory to Support IF (TSPC_OPP_1_1) 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.
+-------------------------------------------------------------------------------
+
+
+		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 is supported. (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 format is supported for Object
+					Push. (C.3)
+TSPC_OPP_3_5	False		Server: vCalender 1.0 format is supported for
+					Object Push. (O)
+TSPC_OPP_3_6	False		Server: vMsg as defined in IrMC 1.1 is supported
+					for Object Push. (O)
+TSPC_OPP_3_7	False		Server: vNote as defined in IrMC 1.1 is
+					supported for Object Push. (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 rejectt. (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 is supported. (O.1)
+TSPC_OPP_3_14	False		Server: vCard 2.1 format is supported for
+					Business Card Pull. (C.1)
+TSPC_OPP_3_15	False		Server: Business card pull reject. (O)
+TSPC_OPP_3_16	False		Server: Business card exchange is supported.
+					(O.2)
+TSPC_OPP_3_17	False		Server: vCard 2.1 format is supported for
+					Business Card Exchange (C.2)
+TSPC_OPP_3_18	False		Server: Business card exchange reject. (O)
+-------------------------------------------------------------------------------
+M.1: Mandatory to Support IF (TSPC_OPP_1_2) supported.
+C.2: Mandatory to Support IF (TSPC_OPP_3_16) Business Card Exchange is
+	supported.
+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.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.
+-------------------------------------------------------------------------------
+
+
+		Additional OPP Capabilities
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_OPP_4_1	False		Abort-Push Operation is supported. (O)
+TSPC_OPP_4_2	False		Disconnect of OBEX session should be tested.
+TSPC_OPP_4_3	False		Multiple vCards transferred as a single vObject
+					is supported. (C.1)
+TSPC_OPP_4_4	False		Multiple vCards transfer is supported. (C.1)
+TSPC_OPP_4_5	False		vCards with multiple Phone Number Fields is
+					supported. (C.1)
+TSPC_OPP_4_6	False		Server supports Push vCal to Different Time
+					Zone. (C.1)
+TSPC_ALL	False		Turn on all test cases.
+-------------------------------------------------------------------------------
+C.1: Optional if TSPC_OPP_1_2 is supported, otherwise excluded.
+-------------------------------------------------------------------------------
diff --git a/bluez/android/pics-pan.txt b/bluez/android/pics-pan.txt
new file mode 100644
index 0000000..0caa629
--- /dev/null
+++ b/bluez/android/pics-pan.txt
@@ -0,0 +1,148 @@
+PAN PICS for the PTS tool.
+
+PTS version: 5.0
+
+* - 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_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	True		PANU: Support LLMNR Sender for IPv4 (C.2)
+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/android/pics-pbap.txt b/bluez/android/pics-pbap.txt
new file mode 100644
index 0000000..e3c6091
--- /dev/null
+++ b/bluez/android/pics-pbap.txt
@@ -0,0 +1,442 @@
+PBAP PICS for the PTS tool.
+
+PTS version: 5.0
+
+* - different than PTS defaults
+# - not yet implemented/supported
+
+M - mandatory
+O - optional
+
+		Major Profile Version (X.Y)
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_PBAP_0_1	False		Role: PBAP 1.0 (C.1)
+TSPC_PBAP_0_2	True (*)	Role: PBAP 1.1 (C.1)
+TSPC_PBAP_0_3	False (*)	Role: PBAP 1.2 (C.1)
+-------------------------------------------------------------------------------
+C.1: Mandatory to support one and only one major profile version.
+-------------------------------------------------------------------------------
+
+
+		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.
+-------------------------------------------------------------------------------
+
+
+		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 (M)
+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.1)
+-------------------------------------------------------------------------------
+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.
+-------------------------------------------------------------------------------
+
+
+		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)
+-------------------------------------------------------------------------------
+
+
+		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	False (*)	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_0_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	False		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/android/pixit-a2dp.txt b/bluez/android/pixit-a2dp.txt
new file mode 100644
index 0000000..d060045
--- /dev/null
+++ b/bluez/android/pixit-a2dp.txt
@@ -0,0 +1,30 @@
+A2DP PIXIT for the PTS tool.
+
+PTS version: 5.0
+
+* - 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_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			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		3EEE
+-------------------------------------------------------------------------------
diff --git a/bluez/android/pixit-avctp.txt b/bluez/android/pixit-avctp.txt
new file mode 100644
index 0000000..c5782fd
--- /dev/null
+++ b/bluez/android/pixit-avctp.txt
@@ -0,0 +1,39 @@
+AVCTP PIXIT for the PTS tool.
+
+PTS version: 5.0
+
+* - 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		08606E414394 (&)
+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
+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/android/pixit-avrcp.txt b/bluez/android/pixit-avrcp.txt
new file mode 100644
index 0000000..9b278ad
--- /dev/null
+++ b/bluez/android/pixit-avrcp.txt
@@ -0,0 +1,36 @@
+AVRCP PIXIT for the PTS tool.
+
+PTS version: 5.0
+
+* - 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		tomorrow
+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/android/pixit-did.txt b/bluez/android/pixit-did.txt
new file mode 100644
index 0000000..8c64d52
--- /dev/null
+++ b/bluez/android/pixit-did.txt
@@ -0,0 +1,24 @@
+DID PIXIT for the PTS tool.
+
+PTS version: 5.0
+
+* - 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/android/pixit-gap.txt b/bluez/android/pixit-gap.txt
new file mode 100644
index 0000000..f3c7726
--- /dev/null
+++ b/bluez/android/pixit-gap.txt
@@ -0,0 +1,57 @@
+GAP PIXIT for the PTS tool.
+
+PTS version: 5.0
+
+* - different than PTS defaults
+& - should be set to IUT Bluetooth address
+
+		Required PIXIT settings
+-------------------------------------------------------------------------------
+Parameter Name						Value
+-------------------------------------------------------------------------------
+TSPX_bd_addr_iut					112233445566 (*&)
+TSPX_bd_addr_PTS					000000000000
+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			10000
+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			0007
+TSPX_iut_valid_timeout_multiplier			0960
+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
+-------------------------------------------------------------------------------
diff --git a/bluez/android/pixit-gatt.txt b/bluez/android/pixit-gatt.txt
new file mode 100644
index 0000000..7bb8a28
--- /dev/null
+++ b/bluez/android/pixit-gatt.txt
@@ -0,0 +1,31 @@
+GATT PIXIT for the PTS tool.
+
+PTS version: 5.0
+
+* - 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
+TSPX_tester_appearance					0000
+-------------------------------------------------------------------------------
diff --git a/bluez/android/pixit-hfp.txt b/bluez/android/pixit-hfp.txt
new file mode 100644
index 0000000..f539e07
--- /dev/null
+++ b/bluez/android/pixit-hfp.txt
@@ -0,0 +1,38 @@
+HFP PIXIT for the PTS tool.
+
+PTS version: 5.0
+
+* - 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
+-------------------------------------------------------------------------------
diff --git a/bluez/android/pixit-hid.txt b/bluez/android/pixit-hid.txt
new file mode 100644
index 0000000..1d36c92
--- /dev/null
+++ b/bluez/android/pixit-hid.txt
@@ -0,0 +1,30 @@
+HID PIXIT for the PTS tool.
+
+PTS version: 5.0
+
+* - 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
+-------------------------------------------------------------------------------
diff --git a/bluez/android/pixit-hsp.txt b/bluez/android/pixit-hsp.txt
new file mode 100644
index 0000000..ee106a7
--- /dev/null
+++ b/bluez/android/pixit-hsp.txt
@@ -0,0 +1,30 @@
+HSP PIXIT for the PTS tool.
+
+PTS version: 5.0
+
+* - 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/android/pixit-l2cap.txt b/bluez/android/pixit-l2cap.txt
new file mode 100644
index 0000000..a307625
--- /dev/null
+++ b/bluez/android/pixit-l2cap.txt
@@ -0,0 +1,41 @@
+L2CAP PIXIT for the PTS tool.
+
+PTS version: 5.0
+
+* - 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                                  FALSE
+TSPX_delete_link_key                                   FALSE
+TSPX_pin_code                                          0000
+TSPX_flushto                                           FFFF
+TSPX_inmtu                                             02A0
+TSPX_no_fail_verditcs                                  FALSE
+TSPX_oumtu                                             02A0
+TSPX_iut_role_initiator                                FALSE
+TSPX_psm                                               1011 (*)
+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
+-------------------------------------------------------------------------------
diff --git a/bluez/android/pixit-map.txt b/bluez/android/pixit-map.txt
new file mode 100644
index 0000000..48419f0
--- /dev/null
+++ b/bluez/android/pixit-map.txt
@@ -0,0 +1,46 @@
+MAP PIXIT for the PTS tool.
+
+PTS version: 5.0
+
+* - different than PTS defaults
+& - should be set to IUT Bluetooth address
+# - should be set to tester's phone number
+
+		Required PIXIT settings
+-------------------------------------------------------------------------------
+Parameter Name						Value
+-------------------------------------------------------------------------------
+TSPX_auth_password					0000
+TSPX_auth_user_id					PTS
+TSPX_bd_addr_iut					08606E414394 (*&)
+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_use_fixed_MASInstanceID				FALSE
+TSPX_MASInstanceID_SMS					0
+TSPX_MASInstanceID_MMS					0
+TSPX_MASInstanceID_Email				0
+TSPX_message_notification_l2cap_psm			1003
+TSPX_message_notification_rfcomm_channel		9
+TSPX_upload_msg_phonenumber				123456789 (#)
+TSPX_upload_msg_emailaddress				PTS_Test@bluetooth.com
+-------------------------------------------------------------------------------
diff --git a/bluez/android/pixit-opp.txt b/bluez/android/pixit-opp.txt
new file mode 100644
index 0000000..93f6a29
--- /dev/null
+++ b/bluez/android/pixit-opp.txt
@@ -0,0 +1,27 @@
+OPP PIXIT for the PTS tool.
+
+PTS version: 5.0
+
+* - different than PTS defaults
+& - should be set to IUT Bluetooth address
+
+		Required PIXIT settings
+-------------------------------------------------------------------------------
+Parameter Name						Value
+-------------------------------------------------------------------------------
+TSPX_unsupported_extension				dat
+TSPX_supported_extension				jpg
+TSPX_security_enabled					False
+TSPX_bd_addr_iut					112233445566 (*&)
+TSPX_client_class_of_device				100104
+TSPX_server_class_of_device				100104
+TSPX_rfcomm_channel					8
+TSPX_pin_code						0000
+TSPX_reply_reject_push					True
+TSPX_delete_link_key					False
+TSPX_time_guard						180000
+TSPX_use_implicit_send					True
+TSPX_secure_simple_pairing_pass_key_confirmation	False
+TSPX_PTS_sends_real_jpg_data				True
+TSPX_confirm_received_objects				True
+-------------------------------------------------------------------------------
diff --git a/bluez/android/pixit-pan.txt b/bluez/android/pixit-pan.txt
new file mode 100644
index 0000000..21434fb
--- /dev/null
+++ b/bluez/android/pixit-pan.txt
@@ -0,0 +1,30 @@
+PAN PIXIT for the PTS tool.
+
+PTS version: 5.0
+
+* - different than PTS defaults
+& - should be set to IUT or PTS Bluetooth 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.167.152
+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
+-------------------------------------------------------------------------------
diff --git a/bluez/android/pixit-pbap.txt b/bluez/android/pixit-pbap.txt
new file mode 100644
index 0000000..470f4c5
--- /dev/null
+++ b/bluez/android/pixit-pbap.txt
@@ -0,0 +1,35 @@
+PBAP PIXIT for the PTS tool.
+
+PTS version: 5.0
+
+* - 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
+-------------------------------------------------------------------------------
diff --git a/bluez/android/pts-a2dp.txt b/bluez/android/pts-a2dp.txt
new file mode 100644
index 0000000..4e4b2d3
--- /dev/null
+++ b/bluez/android/pts-a2dp.txt
@@ -0,0 +1,60 @@
+PTS test results for A2DP
+
+PTS version: 5.0
+Tested: 21-Feb-2014
+Android version: 4.4.2
+
+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	N/A
+TC_SRC_REL_BV_01_I	PASS	Connect to PTS from IUT. When requested
+				disconnect from IUT
+TC_SRC_REL_BV_02_I	PASS
+TC_SRC_SET_BV_01_I	PASS	Connect to PTS (open a2dp)
+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	IUT must be moved out of range
+TC_SRC_SET_BV_06_I	PASS	IUT must be moved out of range
+TC_SRC_SUS_BV_01_I	PASS	Stop streaming
+TC_SRC_SUS_BV_02_I	PASS
+TC_SRC_SDP_BV_01_I	PASS
+TC_SRC_AS_BV_01_I	PASS	Requires checking if the output on the IUT is
+				correct
+-------------------------------------------------------------------------------
+
+
+		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
+-------------------------------------------------------------------------------
diff --git a/bluez/android/pts-avctp.txt b/bluez/android/pts-avctp.txt
new file mode 100644
index 0000000..64fd5fc
--- /dev/null
+++ b/bluez/android/pts-avctp.txt
@@ -0,0 +1,42 @@
+PTS test results for AVCTP
+
+PTS version: 5.0
+Tested: 18-Mar-2014
+Android version: 4.4.2
+
+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	N/A
+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/android/pts-avrcp.txt b/bluez/android/pts-avrcp.txt
new file mode 100644
index 0000000..b6f591b
--- /dev/null
+++ b/bluez/android/pts-avrcp.txt
@@ -0,0 +1,199 @@
+PTS test results for AVRCP
+
+PTS version: 5.0
+Tested: 07-Mar-2014
+Android version: 4.4.2
+
+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_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_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_C	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_CB_BV_07_C	N/A
+TC_CT_MCN_CB_BV_07_I	N/A
+TC_CT_MCN_NP_BV_01_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_C	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_C	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_NP_BV_08_C	N/A
+TC_CT_MCN_SRC_BV_01_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_C	N/A
+TC_CT_MCN_SRC_BV_03_I	N/A
+TC_CT_MCN_SRC_BV_04_I	N/A
+TC_CT_MCN_SRC_BV_05_C	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_01_I	N/A
+TC_CT_MPS_BV_02_I	N/A
+TC_CT_MPS_BV_03_C	N/A
+TC_CT_MPS_BV_03_I	N/A
+TC_CT_MPS_BV_08_C	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_01_I	PASS	adb logcat: check VOLUME_CHANGED value
+TC_CT_VLH_BV_02_I	PASS	Send SetAbsolute Volume command by pressing
+				volume up or down buttons
+TC_CT_VLH_BV_03_C	PASS
+-------------------------------------------------------------------------------
+
+
+		Target (TG)
+-------------------------------------------------------------------------------
+Test Name		Result	Notes
+-------------------------------------------------------------------------------
+TC_TG_BGN_BV_01_I	N/A
+TC_TG_BGN_BV_02_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_01_I	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_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_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_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_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
+TC_TG_RCR_BV_04_C	PASS
+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/android/pts-did.txt b/bluez/android/pts-did.txt
new file mode 100644
index 0000000..b9ad0c7
--- /dev/null
+++ b/bluez/android/pts-did.txt
@@ -0,0 +1,20 @@
+PTS test results for DID
+
+PTS version: 5.0
+Tested: 28-Jan-2014
+Android version: 4.4.2
+
+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/android/pts-gap.txt b/bluez/android/pts-gap.txt
new file mode 100644
index 0000000..9526038
--- /dev/null
+++ b/bluez/android/pts-gap.txt
@@ -0,0 +1,156 @@
+PTS test results for GAP
+
+PTS version: 5.0
+Tested: 28-Jan-2014
+Android version: 4.4.2
+
+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 in non-discoverable mode
+TC_MOD_LDIS_BV_01_C	N/A
+TC_MOD_LDIS_BV_02_C	N/A
+TC_MOD_LDIS_BV_03_C	N/A
+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	N/A
+TC_DISC_NONM_BV_02_C	N/A
+TC_DISC_LIMM_BV_01_C	N/A
+TC_DISC_LIMM_BV_02_C	N/A
+TC_DISC_LIMM_BV_03_C	N/A
+TC_DISC_LIMM_BV_04_C	N/A
+TC_DISC_GENM_BV_01_C	N/A
+TC_DISC_GENM_BV_02_C	N/A
+TC_DISC_GENM_BV_03_C	N/A
+TC_DISC_GENM_BV_04_C	N/A
+TC_DISC_LIMP_BV_01_C	N/A
+TC_DISC_LIMP_BV_02_C	N/A
+TC_DISC_LIMP_BV_03_C	N/A
+TC_DISC_LIMP_BV_04_C	N/A
+TC_DISC_LIMP_BV_05_C	N/A
+TC_DISC_GENP_BV_01_C	INC	LE not supported yet
+TC_DISC_GENP_BV_02_C	INC	LE not supported yet
+TC_DISC_GENP_BV_03_C	INC	LE not supported yet
+TC_DISC_GENP_BV_04_C	INC	LE not supported yet
+TC_DISC_GENP_BV_05_C	INC	LE not supported yet
+TC_IDLE_GIN_BV_01_C	PASS	Start inquiry from IUT
+TC_IDLE_LIN_BV_01_C	N/A
+TC_IDLE_NAMP_BV_01_C	INC	LE not supported yet
+TC_IDLE_NAMP_BV_02_C	INC	LE not supported yet
+TC_CONN_NCON_BV_01_C	N/A
+TC_CONN_NCON_BV_02_C	N/A
+TC_CONN_NCON_BV_03_C	N/A
+TC_CONN_DCON_BV_01_C	N/A
+TC_CONN_DCON_BV_02_C	N/A
+TC_CONN_DCON_BV_03_C	N/A
+TC_CONN_UCON_BV_01_C	N/A
+TC_CONN_UCON_BV_02_C	N/A
+TC_CONN_UCON_BV_03_C	N/A
+TC_CONN_UCON_BV_04_C	N/A
+TC_CONN_UCON_BV_05_C	N/A
+TC_CONN_ACEP_BV_01_C	INC	LE not supported yet
+TC_CONN_ACEP_BV_02_C	N/A
+TC_CONN_GCEP_BV_01_C	INC	LE not supported yet
+TC_CONN_GCEP_BV_02_C	INC	LE not supported yet
+TC_CONN_GCEP_BV_03_C	N/A
+TC_CONN_GCEP_BV_04_C	N/A
+TC_CONN_SCEP_BV_01_C	INC	LE not supported yet
+TC_CONN_SCEP_BV_02_C	N/A
+TC_CONN_DCEP_BV_01_C	INC	LE not supported yet
+TC_CONN_DCEP_BV_02_C	N/A
+TC_CONN_DCEP_BV_03_C	INC	LE not supported yet
+TC_CONN_DCEP_BV_04_C	N/A
+TC_CONN_CPUP_BV_01_C	N/A
+TC_CONN_CPUP_BV_02_C	N/A
+TC_CONN_CPUP_BV_03_C	N/A
+TC_CONN_CPUP_BV_04_C	INC	LE not supported yet
+TC_CONN_CPUP_BV_05_C	INC	LE not supported yet
+TC_CONN_CPUP_BV_06_C	INC	LE not supported yet
+TC_CONN_TERM_BV_01_C	INC	LE not supported yet
+TC_CONN_PRDA_BV_01_C	N/A
+TC_CONN_PRDA_BV_02_C	INC	LE not supported yet
+TC_BOND_NBON_BV_01_C	N/A
+TC_BOND_NBON_BV_02_C	N/A
+TC_BOND_NBON_BV_03_C	N/A
+TC_BOND_BON_BV_01_C	N/A
+TC_BOND_BON_BV_02_C	INC	LE not supported yet
+TC_BOND_BON_BV_03_C	N/A
+TC_BOND_BON_BV_04_C	INC	LE not supported yet
+TC_SEC_AUT_BV_11_C	N/A
+TC_SEC_AUT_BV_12_C	INC	LE not supported yet
+TC_SEC_AUT_BV_13_C	N/A
+TC_SEC_AUT_BV_14_C	N/A
+TC_SEC_AUT_BV_15_C	N/A
+TC_SEC_AUT_BV_16_C	INC	LE not supported yet
+TC_SEC_AUT_BV_17_C	N/A
+TC_SEC_AUT_BV_18_C	N/A
+TC_SEC_AUT_BV_19_C	N/A
+TC_SEC_AUT_BV_20_C	N/A
+TC_SEC_AUT_BV_21_C	N/A
+TC_SEC_AUT_BV_22_C	N/A
+TC_SEC_AUT_BV_23_C	N/A
+TC_SEC_AUT_BV_24_C	N/A
+TC_SEC_CSIGN_BV_01_C	INC	LE not supported yet
+TC_SEC_CSIGN_BV_02_C	INC	LE not supported yet
+TC_SEC_CSIGN_BI_01_C	INC	LE not supported yet
+TC_SEC_CSIGN_BI_02_C	INC	LE not supported yet
+TC_SEC_CSIGN_BI_03_C	INC	LE not supported yet
+TC_SEC_CSIGN_BI_04_C	INC	LE not supported yet
+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_ADV_BV_01_C		N/A
+TC_ADV_BV_02_C		N/A
+TC_ADV_BV_03_C		N/A
+TC_ADV_BV_04_C		N/A
+TC_ADV_BV_05_C		N/A
+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_GAT_BV_01_C		INC	LE not supported yet
+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	N/A
+TC_DM_CON_BV_01_C	N/A
+TC_DM_NBON_BV_01_C	N/A
+TC_DM_BON_BV_01_C	INC	LE not supported yet
+TC_DM_GIN_BV_01_C	INC	LE not supported yet
+TC_DM_LIN_BV_01_C	N/A
+TC_DM_NAD_BV_01_C	PASS	Start inquiry from IUT
+TC_DM_NAD_BV_02_C	INC	LE not supported yet
+TC_DM_LEP_BV_01_C	N/A
+TC_DM_LEP_BV_02_C	N/A
+TC_DM_LEP_BV_03_C	N/A
+TC_DM_LEP_BV_04_C	N/A
+TC_DM_LEP_BV_05_C	N/A
+TC_DM_LEP_BV_06_C	INC	LE not supported yet
+-------------------------------------------------------------------------------
diff --git a/bluez/android/pts-hfp.txt b/bluez/android/pts-hfp.txt
new file mode 100644
index 0000000..196fa14
--- /dev/null
+++ b/bluez/android/pts-hfp.txt
@@ -0,0 +1,248 @@
+PTS test results for HFP
+
+PTS version: 5.1
+Tested: 25-Mar-2014
+Android version: 4.4.2
+
+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_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	PASS
+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_ACC_BV_08_I	INC	Possible PTS issue #12039
+TC_AG_ACC_BV_09_I	PASS
+TC_AG_ACC_BV_10_I	INC	Possible PTS issue #12039
+TC_AG_ACC_BV_11_I	INC	Possible PTS issue #12039
+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_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_03_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/android/pts-hid.txt b/bluez/android/pts-hid.txt
new file mode 100644
index 0000000..22ef800
--- /dev/null
+++ b/bluez/android/pts-hid.txt
@@ -0,0 +1,76 @@
+PTS test results for HID
+
+PTS version: 4.9
+Tested: 14-Nov-2013
+
+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_02_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	from shell execute:
+				haltest
+				bluetooth enable
+				hidhost connect <PTS bdaddr>
+				hidhost send_data <PTS bdaddr> ff00
+TC_HOS_HDT_BV_02_I	PASS
+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	from shell execute:
+				haltest
+				bluetooth enable
+				hidhost connect <PTS bdaddr>
+				hidhost send_data <PTS bdaddr> ff00
+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	PASS
+TC_DEV_HCE_BV_04_I	PASS
+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/android/pts-hsp.txt b/bluez/android/pts-hsp.txt
new file mode 100644
index 0000000..e7fef68
--- /dev/null
+++ b/bluez/android/pts-hsp.txt
@@ -0,0 +1,41 @@
+PTS test results for HSP
+
+PTS version: 5.0
+Tested: 24-Mar-2014
+Android version: 4.4.2
+
+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
+HSP_TC_AG_ACR_BV_01_I	PASS
+HSP_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/android/pts-l2cap.txt b/bluez/android/pts-l2cap.txt
new file mode 100644
index 0000000..905c688
--- /dev/null
+++ b/bluez/android/pts-l2cap.txt
@@ -0,0 +1,151 @@
+PTS test results for L2CAP
+
+PTS version: 5.0
+Tested: 29-Jan-2014
+Android version: 4.4.2
+
+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_COS_CED_BV_01_C     PASS    l2test -n -P 33 <bdaddr>
+TC_COS_CED_BV_03_C     PASS
+TC_COS_CED_BV_04_C     N/A
+TC_COS_CED_BV_05_C     PASS
+TC_COS_CED_BV_07_C     PASS
+TC_COS_CED_BV_08_C     PASS
+TC_COS_CED_BV_09_C     PASS
+TC_COS_CED_BV_10_C     N/A
+TC_COS_CED_BV_11_C     PASS
+TC_COS_CED_BI_01_C     PASS
+TC_COS_CFD_BV_01_C     PASS
+TC_COS_CFD_BV_02_C     PASS
+TC_COS_CFD_BV_03_C     PASS
+TC_COS_CFD_BV_08_C     PASS
+TC_COS_CFD_BV_09_C     PASS
+TC_COS_CFD_BV_10_C     N/A
+TC_COS_CFD_BI_11_C     PASS
+TC_COS_CFD_BV_12_C     PASS
+TC_COS_CFD_BV_13_C     N/A
+TC_COS_IEX_BV_01_C     PASS
+TC_COS_IEX_BV_02_C     PASS
+TC_COS_ECH_BV_01_C     PASS
+TC_COS_ECH_BV_02_C     PASS
+TC_CLS_CLR_BV_01_C     N/A
+TC_CLS_UCD_BV_01_C     N/A
+TC_CLS_UCD_BV_02_C     N/A
+TC_CLS_UCD_BV_03_C     N/A
+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 -X ertm -P 33 <bdaddr>
+TC_CMC_BV_02_C         PASS    l2test -X ertm -P 33 <bdaddr>
+TC_CMC_BV_03_C         PASS    l2test -X ertm -P 33 <bdaddr>
+TC_CMC_BV_04_C         PASS    l2test -X streaming -P 33 <bdaddr>
+TC_CMC_BV_05_C         PASS    l2test -X streaming -P 33 <bdaddr>
+TC_CMC_BV_06_C         PASS    l2test -X streaming -P 33 <bdaddr>
+TC_CMC_BV_07_C         PASS    l2test -X basic -P 33 <bdaddr>
+TC_CMC_BV_08_C         PASS    l2test -X basic -P 33 <bdaddr>
+TC_CMC_BV_09_C         PASS    l2test -X streaming -P 33 <bdaddr>
+TC_CMC_BV_10_C         PASS    l2test -n <bdaddr>
+TC_CMC_BV_11_C         PASS    l2test -n <bdaddr>
+TC_CMC_BV_12_C         PASS    l2test -n -X ertm -P 33 <bdaddr>
+TC_CMC_BV_13_C         PASS    l2test -s -X streaming -P 33 <bdaddr>
+TC_CMC_BV_14_C         PASS    l2test -X streaming -P 33 <bdaddr>
+TC_CMC_BV_15_C         PASS    l2test -X streaming -P 33 <bdaddr>
+TC_CMC_BI_01_C         PASS
+TC_CMC_BI_02_C         PASS
+TC_CMC_BI_03_C         PASS
+TC_CMC_BI_04_C         PASS
+TC_CMC_BI_05_C         PASS
+TC_CMC_BI_06_C         PASS
+TC_FOC_BV_01_C         PASS    l2test -X ertm -P 33 -F 0 &
+TC_FOC_BV_02_C         PASS    l2test -X ertm -P 33 -F 0 &
+TC_FOC_BV_03_C         PASS    l2test -X ertm -P 33 -F 0 &
+TC_FOC_BV_04_C         PASS    l2test -n -X ertm -P <bdaddr> &
+TC_OFS_BV_01_C         PASS
+TC_OFS_BV_02_C         PASS    l2test -X ertm -P 33 -F 0 &
+TC_OFS_BV_03_C         PASS
+TC_OFS_BV_04_C         PASS
+TC_OFS_BV_05_C         PASS    l2test -X ertm -P 33 -F 0 &
+TC_OFS_BV_06_C         PASS    l2test -X ertm -P 33 -F 0 &
+TC_OFS_BV_07_C         PASS
+TC_OFS_BV_08_C         PASS
+TC_ERM_BV_01_C         PASS    l2test -X ertm -P 33 -B -Y 3 &
+TC_ERM_BV_02_C         PASS    l2test -X ertm -P 33 &
+TC_ERM_BV_03_C         PASS    l2test -X ertm -P 33 &
+TC_ERM_BV_04_C         PASS    l2test -X ertm -P 33 -B &
+TC_ERM_BV_05_C         PASS
+TC_ERM_BV_06_C         PASS
+TC_ERM_BV_07_C         PASS    l2test -X ertm -P 33 &
+TC_ERM_BV_08_C         PASS
+TC_ERM_BV_09_C         PASS    l2test -X ertm -P 33 &
+TC_ERM_BV_10_C         PASS    l2test -x -X ertm -P 33 -D 35000 -Q 1 -R -N 1 &
+				btmgmt disconnect
+TC_ERM_BV_11_C         PASS    l2test -x -X ertm -P 33 -D 35000 -Q 1 -R -N 1 &
+				btmgmt disconnect
+TC_ERM_BV_12_C         PASS    l2test -x -X ertm -P 33 -R -N 1
+TC_ERM_BV_13_C         PASS
+TC_ERM_BV_14_C         PASS
+TC_ERM_BV_15_C         PASS
+TC_ERM_BV_16_C         PASS
+TC_ERM_BV_17_C         PASS
+TC_ERM_BV_18_C         PASS    l2test -x -X ertm -P 33 -R -N 1
+TC_ERM_BV_19_C         PASS    l2test -x -X ertm -P 33 -R -N 1
+TC_ERM_BV_20_C         PASS
+TC_ERM_BV_21_C         PASS
+TC_ERM_BV_22_C         PASS
+TC_ERM_BV_23_C         PASS
+TC_ERM_BI_01_C         PASS
+TC_ERM_BI_02_C         PASS
+TC_ERM_BI_03_C         PASS
+TC_ERM_BI_04_C         PASS
+TC_ERM_BI_05_C         PASS
+TC_STM_BV_01_C         PASS    l2test -x -X ertm -P 33 -R -N 1 -Y 3 &
+TC_STM_BV_02_C         PASS    l2test -x -X ertm -P 33 -R -N 1 -Y 3 &
+TC_STM_BV_03_C         PASS    l2test -x -X ertm -P 33 -R -N 1 -Y 3 &
+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 -n -P 33 <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      N/A
+TC_LE_CPU_BV_02_C      N/A
+TC_LE_CPU_BI_01_C      N/A
+TC_LE_CPU_BI_02_C      N/A
+TC_LE_REJ_BV_01_C      N/A
diff --git a/bluez/android/pts-map.txt b/bluez/android/pts-map.txt
new file mode 100644
index 0000000..856077d
--- /dev/null
+++ b/bluez/android/pts-map.txt
@@ -0,0 +1,98 @@
+PTS test results for MAP
+
+PTS version: 5.0
+Tested: 19-Feb-2014
+
+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	If prompted tester must accept obex request
+TC_MSE_MSM_BV_06_I	PASS	If prompted tester must accept obex request
+TC_MSE_MSM_BV_07_I	PASS	If prompted tester must accept obex request
+TC_MSE_MSM_BV_08_I	PASS	If prompted tester must accept obex request
+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	If prompted tester must accept obex request
+TC_MSE_MNR_BV_04_I	PASS	If prompted tester must accept obex request
+TC_MSE_MMB_BV_09_I	PASS	If prompted tester must accept obex request
+TC_MSE_MMB_BV_10_I	PASS	If prompted tester must accept obex request
+TC_MSE_MMB_BV_11_I	PASS	If prompted tester must accept obex request
+TC_MSE_MMB_BV_20_I	PASS	If prompted tester must accept obex request
+TC_MSE_MMB_BV_12_I	N/A
+TC_MSE_MMB_BV_18_I	N/A
+TC_MSE_MMB_BV_13_I	PASS	If prompted tester must accept obex request
+TC_MSE_MMB_BV_14_I	PASS	If prompted tester must accept obex request
+TC_MSE_MMB_BV_15_I	PASS	If prompted tester must accept obex request
+TC_MSE_MMB_BV_16_I	PASS	Android MAP server bug, fix provided at
+				android-review.googlesource.com/#/c/82757
+TC_MSE_MMD_BV_02_I	PASS	If prompted tester must accept obex request
+TC_MSE_MMU_BV_02_I	PASS	If prompted tester must accept obex request,
+				at least one SMS must be present in inbox
+TC_MSE_MMU_BV_03_I	PASS	If prompted tester must accept obex request,
+				when asked by PTS tester must verify that SMS
+				was successfully delivered onto network
+TC_MSE_MMN_BV_02_I	PASS	If prompted tester must accept obex request,
+				when asked by PTS tester must send SMS to IUT
+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_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/android/pts-opp.txt b/bluez/android/pts-opp.txt
new file mode 100644
index 0000000..fe4106f
--- /dev/null
+++ b/bluez/android/pts-opp.txt
@@ -0,0 +1,98 @@
+PTS test results for OPP
+
+PTS version: 5.0
+Tested: 28-Jan-2014
+Android version: 4.4.2
+
+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_CLIENT_OPH_BV_01_I	PASS
+TC_CLIENT_OPH_BV_02_I	N/A
+TC_CLIENT_OPH_BV_03_I	PASS	Tester must send a contact to PTS
+TC_CLIENT_OPH_BV_04_I	N/A
+TC_CLIENT_OPH_BV_05_I	PASS	Tester must send a contact to PTS
+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
+TC_CLIENT_OPH_BV_20_I	PASS
+TC_CLIENT_OPH_BV_22_I	PASS
+TC_CLIENT_OPH_BV_23_I	PASS
+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_OPH_BV_34_I	PASS
+TC_CLIENT_OPH_BI_01_C	PASS
+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_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_SERVER_OPH_BV_01_I	PASS	IUT must be in the connectable mode
+TC_SERVER_OPH_BV_02_I	PASS	IUT must be in the connectable mode
+TC_SERVER_OPH_BV_03_I	PASS	IUT must be in the connectable mode. Tester must
+					accept incoming file
+TC_SERVER_OPH_BV_04_I	PASS	IUT must be in the connectable mode. Tester must
+					accept incoming file
+TC_SERVER_OPH_BV_05_I	PASS	IUT must be in the connectable mode. Tester must
+					reject incoming file
+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	IUT must be in the connectable mode
+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	IUT must be in the connectable mode
+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	IUT must be in the connectable mode
+TC_SERVER_OPH_BV_19_I	PASS	IUT must be in the connectable mode
+TC_SERVER_OPH_BV_21_I	N/A
+TC_SERVER_OPH_BV_22_I	PASS	IUT must be in the connectable mode
+TC_SERVER_OPH_BV_23_I	PASS	IUT must be in the connectable mode
+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_OPH_BV_34_I	PASS	IUT must be in the connectable mode
+TC_SERVER_BCP_BV_01_I	N/A
+TC_SERVER_BCP_BV_02_I	PASS	IUT must be in the connectable mode
+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_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_CLIENT_OPH_BV_27_I	N/A
+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
+-------------------------------------------------------------------------------
diff --git a/bluez/android/pts-pan.txt b/bluez/android/pts-pan.txt
new file mode 100644
index 0000000..94dceec
--- /dev/null
+++ b/bluez/android/pts-pan.txt
@@ -0,0 +1,82 @@
+PTS test results for PAN
+
+PTS version: 5.0
+Tested: 19-Feb-2014
+Android version: 4.4.2
+
+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_GN_BROADCAST_0_BV_03_C		N/A
+TC_GN_Ipv4_Autonet_BV_01_I		N/A
+TC_GN_Ipv6_Autonet_BV_02_I		N/A
+TC_GN_IP_DHCP_BV_03_I			N/A
+TC_GN_IP_LLMNR_BV_01_I			N/A
+TC_GN_IP_LLMNR_BV_02_I			N/A
+TC_GN_IP_DNS_BV_01_I			N/A
+TC_GN_IP_APP_BV_01_I			N/A
+TC_GN_IP_APP_BV_02_I			N/A
+TC_GN_IP_APP_BV_03_I			N/A
+TC_GN_IP_APP_BV_04_I			N/A
+TC_GN_IP_APP_BV_05_I			N/A
+TC_SDP_GN_BV_02_C			N/A
+TC_MISC_GN_UUID_BV_01_C			N/A
+TC_MISC_GN_UUID_BV_02_C			N/A
+TC_BNEP_NAP_BROADCAST_0_BV_01_C		N/A
+TC_BNEP_NAP_BROADCAST_0_BV_02_C		N/A
+TC_BNEP_NAP_FORWARD_UNICAST_BV_05_C	N/A
+TC_BNEP_NAP_FORWARD_UNICAST_BV_06_C	N/A
+TC_BNEP_NAP_MULTICAST_0_BV_03_C		N/A
+TC_BNEP_NAP_MULTICAST_0_BV_04_C		N/A
+TC_BNEP_BRIDGE_RX_BV_02_I		PASS	Tester needs to send one
+						forward-unicast	BNEP packet
+						with given data (5 times)
+						0x00, 0x01, 0xFF followed by
+						0x00, 0x01, 0x9E, 0x9F
+						sendip -d <hexdata>
+TC_BNEP_BRIDGE_TX_BV_01_I		PASS
+TC_NAP_Ipv4_Autonet_BV_01_I		N/A
+TC_NAP_Ipv6_Autonet_BV_02_I		N/A
+TC_NAP_IP_DHCP_BV_03_I			N/A
+TC_NAP_IP_LLMNR_BV_01_I			N/A
+TC_NAP_IP_LLMNR_BV_02_I			N/A
+TC_NAP_IP_DNS_BV_01_I			N/A
+TC_NAP_IP_APP_BV_01_I			N/A
+TC_NAP_IP_APP_BV_02_I			N/A
+TC_NAP_IP_APP_BV_03_I			N/A
+TC_NAP_IP_APP_BV_04_I			N/A
+TC_NAP_IP_APP_BV_05_I			N/A
+TC_SDP_NAP_BV_01_C			PASS
+TC_MISC_NAP_UUID_BV_01_C		PASS
+TC_MISC_NAP_UUID_BV_02_C		PASS
+TC_BNEP_PANU_BROADCAST_0_BV_04_C	N/A
+TC_PANU_Ipv4_Autonet_BV_01_I		PASS	When prompted connect to PTS.
+						From IUT: issue ARP request
+						iperf -c <addr> -p <port#>
+TC_PANU_Ipv6_Autonet_BV_02_I		N/A
+TC_PANU_IP_LLMNR_BV_01_I		PASS	When prompted connect to PTS.
+						From IUT: send LLMNR request
+						command
+						iperf -c <addr> -p <port#>
+TC_PANU_IP_LLMNR_BV_02_I		N/A
+TC_PANU_IP_DHCP_BV_03_I			N/A
+TC_PANU_IP_DNS_BV_01_I			N/A
+TC_PANU_IP_APP_BV_01_I			N/A
+TC_PANU_IP_APP_BV_02_I			N/A
+TC_PANU_IP_APP_BV_03_I			N/A
+TC_PANU_IP_APP_BV_04_I			N/A
+TC_PANU_IP_APP_BV_05_I			PASS	When prompted, connect to PTS
+						and then when prompted terminate
+						connection (IUT side)
+TC_SDP_PANU_BV_01_C			N/A
+TC_MISC_PANU_UUID_BV_01_C		N/A
+TC_MISC_PANU_UUID_BV_02_C		N/A
+TC_MISC_ROLE_BV_01_C			N/A
+TC_MISC_ROLE_BV_BV_02_C			N/A
+--------------------------------------------------------------------------------
diff --git a/bluez/android/pts-pbap.txt b/bluez/android/pts-pbap.txt
new file mode 100644
index 0000000..ecaaefa
--- /dev/null
+++ b/bluez/android/pts-pbap.txt
@@ -0,0 +1,135 @@
+PTS test results for PBAP
+
+PTS version: 5.0
+Tested: 19-Feb-2014
+Android version: 4.4.2
+
+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	FAIL	Android PBAP server bug
+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	PTS 5.0 issue #11816 (updated ETS available)
+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
+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_PDF_BV_01_I	PASS	Tester must compare phone book size with the
+				value given by PTS
+-------------------------------------------------------------------------------
diff --git a/bluez/android/socket.c b/bluez/android/socket.c
new file mode 100644
index 0000000..8374489
--- /dev/null
+++ b/bluez/android/socket.c
@@ -0,0 +1,1178 @@
+/*
+ *
+ *  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 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 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 = DEFAULT_MAS_INSTANCE;
+	uint8_t mtype = DEFAULT_MAS_MSG_TYPE;
+	sdp_record_t *record;
+	uuid_t uuid;
+
+	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);
+
+	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 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_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_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("");
+
+	/* 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("");
+
+	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/android/socket.h b/bluez/android/socket.h
new file mode 100644
index 0000000..b0e78c6
--- /dev/null
+++ b/bluez/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/android/system-emulator.c b/bluez/android/system-emulator.c
new file mode 100644
index 0000000..dc46814
--- /dev/null
+++ b/bluez/android/system-emulator.c
@@ -0,0 +1,230 @@
+/*
+ *
+ *  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 <sys/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>
+
+#include "monitor/mainloop.h"
+
+static char exec_dir[PATH_MAX + 1];
+
+static pid_t daemon_pid = -1;
+static pid_t snoop_pid = -1;
+
+static void ctl_start(void)
+{
+	char prg_name[PATH_MAX + 1];
+	char *prg_argv[5];
+	char *prg_envp[3];
+	pid_t pid;
+
+	snprintf(prg_name, sizeof(prg_name), "%s/%s", exec_dir, "bluetoothd");
+
+	prg_argv[0] = "/usr/bin/valgrind";
+	prg_argv[1] = "--leak-check=full";
+	prg_argv[2] = prg_name;
+	prg_argv[3] = "-d";
+	prg_argv[4] = NULL;
+
+	prg_envp[0] = "G_SLICE=always-malloc";
+	prg_envp[1] = "G_DEBUG=gc-friendly";
+	prg_envp[2] = 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);
+
+	daemon_pid = pid;
+}
+
+static void snoop_start(void)
+{
+	char prg_name[PATH_MAX + 1];
+	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/android/system/audio.h b/bluez/android/system/audio.h
new file mode 100644
index 0000000..26a0a3b
--- /dev/null
+++ b/bluez/android/system/audio.h
@@ -0,0 +1,609 @@
+/*
+ * 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 <sys/cdefs.h>
+#include <sys/types.h>
+
+#define popcount __builtin_popcount
+
+__BEGIN_DECLS
+
+/* 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"
+
+typedef int audio_io_handle_t;
+
+/* Audio stream types */
+typedef enum {
+    AUDIO_STREAM_DEFAULT          = -1,
+    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 media/java/android/media/MediaRecorder.java!
+ */
+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;
+
+/* 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,
+} audio_session_t;
+
+/* Audio sub formats (see enum audio_format). */
+
+/* PCM sub formats */
+typedef enum {
+    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_fmt_t;
+
+/* 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_NONE            = 0x0,
+} 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 in 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,
+    AUDIO_FORMAT_HE_AAC_V2           = 0x06000000UL,
+    AUDIO_FORMAT_VORBIS              = 0x07000000UL,
+    AUDIO_FORMAT_MAIN_MASK           = 0xFF000000UL,
+    AUDIO_FORMAT_SUB_MASK            = 0x00FFFFFFUL,
+
+    /* Aliases */
+    AUDIO_FORMAT_PCM_16_BIT          = (AUDIO_FORMAT_PCM |
+                                        AUDIO_FORMAT_PCM_SUB_16_BIT),
+    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_t;
+
+enum {
+    /* 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,
+
+    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_SURROUND = (AUDIO_CHANNEL_OUT_FRONT_LEFT |
+                                  AUDIO_CHANNEL_OUT_FRONT_RIGHT |
+                                  AUDIO_CHANNEL_OUT_FRONT_CENTER |
+                                  AUDIO_CHANNEL_OUT_BACK_CENTER),
+    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),
+    // 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),
+
+    /* 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,
+
+    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),
+};
+
+typedef uint32_t audio_channel_mask_t;
+
+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;
+
+typedef enum {
+    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_ANLG_DOCK_HEADSET         = 0x800,
+    AUDIO_DEVICE_OUT_DGTL_DOCK_HEADSET         = 0x1000,
+    AUDIO_DEVICE_OUT_USB_ACCESSORY             = 0x2000,
+    AUDIO_DEVICE_OUT_USB_DEVICE                = 0x4000,
+    AUDIO_DEVICE_OUT_REMOTE_SUBMIX             = 0x8000,
+    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_AUX_DIGITAL |
+                                 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_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_VOICE_CALL            = AUDIO_DEVICE_BIT_IN | 0x40,
+    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,
+    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_AUX_DIGITAL |
+                               AUDIO_DEVICE_IN_VOICE_CALL |
+                               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_DEFAULT),
+    AUDIO_DEVICE_IN_ALL_SCO = AUDIO_DEVICE_IN_BLUETOOTH_SCO_HEADSET,
+};
+
+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_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_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),
+};
+
+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_device(audio_devices_t device)
+{
+    if ((popcount(device) == 1) && (device & AUDIO_DEVICE_OUT_ALL_A2DP))
+        return true;
+    else
+        return false;
+}
+
+static inline bool audio_is_bluetooth_sco_device(audio_devices_t device)
+{
+    device &= ~AUDIO_DEVICE_BIT_IN;
+    if ((popcount(device) == 1) && (device & (AUDIO_DEVICE_OUT_ALL_SCO |
+                   AUDIO_DEVICE_IN_BLUETOOTH_SCO_HEADSET)))
+        return true;
+    else
+        return false;
+}
+
+static inline bool audio_is_usb_device(audio_devices_t device)
+{
+    if ((popcount(device) == 1) && (device & AUDIO_DEVICE_OUT_ALL_USB))
+        return true;
+    else
+        return false;
+}
+
+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;
+}
+
+static inline bool audio_is_input_channel(audio_channel_mask_t channel)
+{
+    if ((channel & ~AUDIO_CHANNEL_IN_ALL) == 0)
+        return channel != 0;
+    else
+        return false;
+}
+
+static inline bool audio_is_output_channel(audio_channel_mask_t channel)
+{
+    if ((channel & ~AUDIO_CHANNEL_OUT_ALL) == 0)
+        return channel != 0;
+    else
+        return false;
+}
+
+/* Derive an output channel mask 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 0 if the number of channels exceeds that of the
+ * configurations for which a default channel mask is defined.
+ */
+static inline audio_channel_mask_t audio_channel_out_mask_from_count(uint32_t channel_count)
+{
+    switch(channel_count) {
+    case 1:
+        return AUDIO_CHANNEL_OUT_MONO;
+    case 2:
+        return AUDIO_CHANNEL_OUT_STEREO;
+    case 3:
+        return (AUDIO_CHANNEL_OUT_STEREO | AUDIO_CHANNEL_OUT_FRONT_CENTER);
+    case 4: // 4.0
+        return AUDIO_CHANNEL_OUT_QUAD;
+    case 5: // 5.0
+        return (AUDIO_CHANNEL_OUT_QUAD | AUDIO_CHANNEL_OUT_FRONT_CENTER);
+    case 6: // 5.1
+        return AUDIO_CHANNEL_OUT_5POINT1;
+    case 7: // 6.1
+        return (AUDIO_CHANNEL_OUT_5POINT1 | AUDIO_CHANNEL_OUT_BACK_CENTER);
+    case 8:
+        return AUDIO_CHANNEL_OUT_7POINT1;
+    default:
+        return 0;
+    }
+}
+
+/* Similar to above, but for input.  Currently handles only mono and stereo. */
+static inline audio_channel_mask_t audio_channel_in_mask_from_count(uint32_t channel_count)
+{
+    switch (channel_count) {
+    case 1:
+        return AUDIO_CHANNEL_IN_MONO;
+    case 2:
+        return AUDIO_CHANNEL_IN_STEREO;
+    default:
+        return 0;
+    }
+}
+
+static inline bool audio_is_valid_format(audio_format_t format)
+{
+    switch (format & AUDIO_FORMAT_MAIN_MASK) {
+    case AUDIO_FORMAT_PCM:
+        if (format != AUDIO_FORMAT_PCM_16_BIT &&
+                format != AUDIO_FORMAT_PCM_8_BIT) {
+            return false;
+        }
+    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:
+        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_16_BIT:
+        size = sizeof(int16_t);
+        break;
+    case AUDIO_FORMAT_PCM_8_BIT:
+        size = sizeof(uint8_t);
+        break;
+    default:
+        break;
+    }
+    return size;
+}
+
+__END_DECLS
+
+#endif  // ANDROID_AUDIO_CORE_H
diff --git a/bluez/android/test-ipc.c b/bluez/android/test-ipc.c
new file mode 100644
index 0000000..bb7d15f
--- /dev/null
+++ b/bluez/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/android/utils.h b/bluez/android/utils.h
new file mode 100644
index 0000000..560e991
--- /dev/null
+++ b/bluez/android/utils.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
+ *
+ */
+
+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);
+}
diff --git a/bluez/attrib/att-database.h b/bluez/attrib/att-database.h
new file mode 100644
index 0000000..48c50e3
--- /dev/null
+++ b/bluez/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/attrib/att.c b/bluez/attrib/att.c
new file mode 100644
index 0000000..8e9c06d
--- /dev/null
+++ b/bluez/attrib/att.c
@@ -0,0 +1,1193 @@
+/*
+ *
+ *  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 <bluetooth/bluetooth.h>
+
+#include <glib.h>
+
+#include "src/shared/util.h"
+#include "lib/uuid.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_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/attrib/att.h b/bluez/attrib/att.h
new file mode 100644
index 0000000..c612d80
--- /dev/null
+++ b/bluez/attrib/att.h
@@ -0,0 +1,187 @@
+/*
+ *
+ *  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
+ *
+ */
+
+/* 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);
+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/attrib/gatt-service.c b/bluez/attrib/gatt-service.c
new file mode 100644
index 0000000..874552b
--- /dev/null
+++ b/bluez/attrib/gatt-service.c
@@ -0,0 +1,362 @@
+/*
+ *
+ *  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 "src/adapter.h"
+#include "src/shared/util.h"
+#include "lib/uuid.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;
+		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 (handle = start_handle; 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/attrib/gatt-service.h b/bluez/attrib/gatt-service.h
new file mode 100644
index 0000000..728d3a8
--- /dev/null
+++ b/bluez/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/attrib/gatt.c b/bluez/attrib/gatt.c
new file mode 100644
index 0000000..7363b4b
--- /dev/null
+++ b/bluez/attrib/gatt.c
@@ -0,0 +1,992 @@
+/*
+ *
+ *  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 <bluetooth/sdp.h>
+#include <bluetooth/sdp_lib.h>
+
+#include "src/shared/util.h"
+#include "lib/uuid.h"
+#include "att.h"
+#include "gattrib.h"
+#include "gatt.h"
+
+struct discover_primary {
+	int ref;
+	GAttrib *attrib;
+	bt_uuid_t uuid;
+	GSList *primaries;
+	gatt_cb_t cb;
+	void *user_data;
+};
+
+/* Used for the Included Services Discovery (ISD) procedure */
+struct included_discovery {
+	GAttrib		*attrib;
+	int		refs;
+	int		err;
+	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;
+	bt_uuid_t *uuid;
+	uint16_t end;
+	GSList *characteristics;
+	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 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;
+
+	buf = g_attrib_get_buffer(dp->attrib, &buflen);
+	oplen = encode_discover_primary(range->end + 1, 0xffff, &dp->uuid,
+								buf, buflen);
+
+	if (oplen == 0)
+		goto done;
+
+	g_attrib_send(dp->attrib, 0, 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;
+	}
+
+	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]);
+
+		/*
+		 * FIXME: Check before "for". Elements in the Attribute
+		 * Data List have the same length (list->len).
+		 */
+		if (list->len == 6)
+			type = BT_UUID16;
+		else if (list->len == 20)
+			type = BT_UUID128;
+		else {
+			/* Skipping invalid data */
+			continue;
+		}
+
+		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 (end != 0xffff) {
+		size_t buflen;
+		uint8_t *buf = g_attrib_get_buffer(dp->attrib, &buflen);
+		guint16 oplen = encode_discover_primary(end + 1, 0xffff, NULL,
+								buf, buflen);
+
+		g_attrib_send(dp->attrib, 0, 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;
+
+	if (uuid) {
+		dp->uuid = *uuid;
+		cb = primary_by_uuid_cb;
+	} else
+		cb = primary_all_cb;
+
+	return g_attrib_send(attrib, 0, buf, plen, cb,
+					discover_primary_ref(dp),
+					discover_primary_unref);
+}
+
+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, 0, 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);
+
+	return g_attrib_send(isd->attrib, 0, buf, oplen, find_included_cb,
+				isd_ref(isd), (GDestroyNotify) isd_unref);
+}
+
+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 < isd->end_handle)
+		find_included(isd, last_handle + 1);
+
+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->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 = ATT_ECODE_ATTR_NOT_FOUND;
+	uint16_t last = 0;
+
+	if (status) {
+		err = status;
+		goto done;
+	}
+
+	list = dec_read_by_type_resp(ipdu, iplen);
+	if (list == NULL) {
+		err = ATT_ECODE_IO;
+		goto done;
+	}
+
+	for (i = 0; i < list->num; i++) {
+		uint8_t *value = list->data[i];
+		struct gatt_char *chars;
+		bt_uuid_t uuid128;
+		uint8_t type;
+
+		last = get_le16(value);
+
+		/*
+		 * FIXME: Check before "for". Elements in the Attribute
+		 * Data List have the same length (list->len).
+		 */
+		if (list->len == 7)
+			type = BT_UUID16;
+		else
+			type = BT_UUID128;
+
+		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) {
+			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 != 0 && (last + 1 < 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(last + 1, dc->end, &uuid, buf,
+									buflen);
+
+		if (oplen == 0)
+			return;
+
+		g_attrib_send(dc->attrib, 0, buf, oplen, char_discovered_cb,
+				discover_char_ref(dc), discover_char_unref);
+
+		return;
+	}
+
+done:
+	err = (dc->characteristics ? 0 : err);
+	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->uuid = g_memdup(uuid, sizeof(bt_uuid_t));
+
+	return g_attrib_send(attrib, 0, buf, plen, char_discovered_cb,
+				discover_char_ref(dc), discover_char_unref);
+}
+
+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;
+
+	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 = 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_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, 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_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);
+}
+
+guint gatt_discover_char_desc(GAttrib *attrib, uint16_t start, uint16_t end,
+				GAttribResultFunc func, gpointer user_data)
+{
+	uint8_t *buf;
+	size_t buflen;
+	guint16 plen;
+
+	buf = g_attrib_get_buffer(attrib, &buflen);
+	plen = enc_find_info_req(start, end, buf, buflen);
+	if (plen == 0)
+		return 0;
+
+	return g_attrib_send(attrib, 0, buf, plen, func, user_data, NULL);
+}
+
+guint gatt_write_cmd(GAttrib *attrib, uint16_t handle, 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);
+}
+
+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/attrib/gatt.h b/bluez/attrib/gatt.h
new file mode 100644
index 0000000..a11e473
--- /dev/null
+++ b/bluez/attrib/gatt.h
@@ -0,0 +1,102 @@
+/*
+ *
+ *  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 <bluetooth/sdp.h>
+
+/*
+ * 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;
+};
+
+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, uint8_t *value,
+					size_t vlen, GAttribResultFunc func,
+					gpointer user_data);
+
+guint gatt_discover_char_desc(GAttrib *attrib, uint16_t start, uint16_t end,
+				GAttribResultFunc func, gpointer user_data);
+
+guint gatt_write_cmd(GAttrib *attrib, uint16_t handle, uint8_t *value, int vlen,
+				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/attrib/gattrib.c b/bluez/attrib/gattrib.c
new file mode 100644
index 0000000..912dffb
--- /dev/null
+++ b/bluez/attrib/gattrib.c
@@ -0,0 +1,772 @@
+/*
+ *
+ *  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 <stdbool.h>
+#include <string.h>
+#include <glib.h>
+
+#include <stdio.h>
+
+#include <bluetooth/bluetooth.h>
+
+#include "btio/btio.h"
+#include "lib/uuid.h"
+#include "src/shared/util.h"
+#include "src/log.h"
+#include "attrib/att.h"
+#include "attrib/gattrib.h"
+
+#define GATT_TIMEOUT 30
+
+struct _GAttrib {
+	GIOChannel *io;
+	int refs;
+	uint8_t *buf;
+	size_t buflen;
+	guint read_watch;
+	guint write_watch;
+	guint timeout_watch;
+	GQueue *requests;
+	GQueue *responses;
+	GSList *events;
+	guint next_cmd_id;
+	GDestroyNotify destroy;
+	gpointer destroy_user_data;
+	bool stale;
+};
+
+struct command {
+	guint id;
+	guint8 opcode;
+	guint8 *pdu;
+	guint16 len;
+	guint8 expected;
+	bool sent;
+	GAttribResultFunc func;
+	gpointer user_data;
+	GDestroyNotify notify;
+};
+
+struct event {
+	guint id;
+	guint8 expected;
+	guint16 handle;
+	GAttribNotifyFunc func;
+	gpointer user_data;
+	GDestroyNotify notify;
+};
+
+static guint8 opcode2expected(guint8 opcode)
+{
+	switch (opcode) {
+	case ATT_OP_MTU_REQ:
+		return ATT_OP_MTU_RESP;
+
+	case ATT_OP_FIND_INFO_REQ:
+		return ATT_OP_FIND_INFO_RESP;
+
+	case ATT_OP_FIND_BY_TYPE_REQ:
+		return ATT_OP_FIND_BY_TYPE_RESP;
+
+	case ATT_OP_READ_BY_TYPE_REQ:
+		return ATT_OP_READ_BY_TYPE_RESP;
+
+	case ATT_OP_READ_REQ:
+		return ATT_OP_READ_RESP;
+
+	case ATT_OP_READ_BLOB_REQ:
+		return ATT_OP_READ_BLOB_RESP;
+
+	case ATT_OP_READ_MULTI_REQ:
+		return ATT_OP_READ_MULTI_RESP;
+
+	case ATT_OP_READ_BY_GROUP_REQ:
+		return ATT_OP_READ_BY_GROUP_RESP;
+
+	case ATT_OP_WRITE_REQ:
+		return ATT_OP_WRITE_RESP;
+
+	case ATT_OP_PREP_WRITE_REQ:
+		return ATT_OP_PREP_WRITE_RESP;
+
+	case ATT_OP_EXEC_WRITE_REQ:
+		return ATT_OP_EXEC_WRITE_RESP;
+
+	case ATT_OP_HANDLE_IND:
+		return ATT_OP_HANDLE_CNF;
+	}
+
+	return 0;
+}
+
+static bool is_response(guint8 opcode)
+{
+	switch (opcode) {
+	case ATT_OP_ERROR:
+	case ATT_OP_MTU_RESP:
+	case ATT_OP_FIND_INFO_RESP:
+	case ATT_OP_FIND_BY_TYPE_RESP:
+	case ATT_OP_READ_BY_TYPE_RESP:
+	case ATT_OP_READ_RESP:
+	case ATT_OP_READ_BLOB_RESP:
+	case ATT_OP_READ_MULTI_RESP:
+	case ATT_OP_READ_BY_GROUP_RESP:
+	case ATT_OP_WRITE_RESP:
+	case ATT_OP_PREP_WRITE_RESP:
+	case ATT_OP_EXEC_WRITE_RESP:
+	case ATT_OP_HANDLE_CNF:
+		return true;
+	}
+
+	return false;
+}
+
+GAttrib *g_attrib_ref(GAttrib *attrib)
+{
+	int refs;
+
+	if (!attrib)
+		return NULL;
+
+	refs = __sync_add_and_fetch(&attrib->refs, 1);
+
+	DBG("%p: ref=%d", attrib, refs);
+
+	return attrib;
+}
+
+static void command_destroy(struct command *cmd)
+{
+	if (cmd->notify)
+		cmd->notify(cmd->user_data);
+
+	g_free(cmd->pdu);
+	g_free(cmd);
+}
+
+static void event_destroy(struct event *evt)
+{
+	if (evt->notify)
+		evt->notify(evt->user_data);
+
+	g_free(evt);
+}
+
+static void attrib_destroy(GAttrib *attrib)
+{
+	GSList *l;
+	struct command *c;
+
+	while ((c = g_queue_pop_head(attrib->requests)))
+		command_destroy(c);
+
+	while ((c = g_queue_pop_head(attrib->responses)))
+		command_destroy(c);
+
+	g_queue_free(attrib->requests);
+	attrib->requests = NULL;
+
+	g_queue_free(attrib->responses);
+	attrib->responses = NULL;
+
+	for (l = attrib->events; l; l = l->next)
+		event_destroy(l->data);
+
+	g_slist_free(attrib->events);
+	attrib->events = NULL;
+
+	if (attrib->timeout_watch > 0)
+		g_source_remove(attrib->timeout_watch);
+
+	if (attrib->write_watch > 0)
+		g_source_remove(attrib->write_watch);
+
+	if (attrib->read_watch > 0)
+		g_source_remove(attrib->read_watch);
+
+	if (attrib->io)
+		g_io_channel_unref(attrib->io);
+
+	g_free(attrib->buf);
+
+	if (attrib->destroy)
+		attrib->destroy(attrib->destroy_user_data);
+
+	g_free(attrib);
+}
+
+void g_attrib_unref(GAttrib *attrib)
+{
+	int refs;
+
+	if (!attrib)
+		return;
+
+	refs = __sync_sub_and_fetch(&attrib->refs, 1);
+
+	DBG("%p: ref=%d", attrib, refs);
+
+	if (refs > 0)
+		return;
+
+	attrib_destroy(attrib);
+}
+
+GIOChannel *g_attrib_get_channel(GAttrib *attrib)
+{
+	if (!attrib)
+		return NULL;
+
+	return attrib->io;
+}
+
+gboolean g_attrib_set_destroy_function(GAttrib *attrib,
+		GDestroyNotify destroy, gpointer user_data)
+{
+	if (attrib == NULL)
+		return FALSE;
+
+	attrib->destroy = destroy;
+	attrib->destroy_user_data = user_data;
+
+	return TRUE;
+}
+
+static gboolean disconnect_timeout(gpointer data)
+{
+	struct _GAttrib *attrib = data;
+	struct command *c;
+
+	g_attrib_ref(attrib);
+
+	c = g_queue_pop_head(attrib->requests);
+	if (c == NULL)
+		goto done;
+
+	if (c->func)
+		c->func(ATT_ECODE_TIMEOUT, NULL, 0, c->user_data);
+
+	command_destroy(c);
+
+	while ((c = g_queue_pop_head(attrib->requests))) {
+		if (c->func)
+			c->func(ATT_ECODE_ABORTED, NULL, 0, c->user_data);
+		command_destroy(c);
+	}
+
+done:
+	attrib->stale = true;
+
+	g_attrib_unref(attrib);
+
+	return FALSE;
+}
+
+static gboolean can_write_data(GIOChannel *io, GIOCondition cond,
+								gpointer data)
+{
+	struct _GAttrib *attrib = data;
+	struct command *cmd;
+	GError *gerr = NULL;
+	gsize len;
+	GIOStatus iostat;
+	GQueue *queue;
+
+	if (attrib->stale)
+		return FALSE;
+
+	if (cond & (G_IO_HUP | G_IO_ERR | G_IO_NVAL))
+		return FALSE;
+
+	queue = attrib->responses;
+	cmd = g_queue_peek_head(queue);
+	if (cmd == NULL) {
+		queue = attrib->requests;
+		cmd = g_queue_peek_head(queue);
+	}
+	if (cmd == NULL)
+		return FALSE;
+
+	/*
+	 * Verify that we didn't already send this command. This can only
+	 * happen with elementes from attrib->requests.
+	 */
+	if (cmd->sent)
+		return FALSE;
+
+	iostat = g_io_channel_write_chars(io, (char *) cmd->pdu, cmd->len,
+								&len, &gerr);
+	if (iostat != G_IO_STATUS_NORMAL) {
+		if (gerr) {
+			error("%s", gerr->message);
+			g_error_free(gerr);
+		}
+
+		return FALSE;
+	}
+
+	if (cmd->expected == 0) {
+		g_queue_pop_head(queue);
+		command_destroy(cmd);
+
+		return TRUE;
+	}
+
+	cmd->sent = true;
+
+	if (attrib->timeout_watch == 0)
+		attrib->timeout_watch = g_timeout_add_seconds(GATT_TIMEOUT,
+						disconnect_timeout, attrib);
+
+	return FALSE;
+}
+
+static void destroy_sender(gpointer data)
+{
+	struct _GAttrib *attrib = data;
+
+	attrib->write_watch = 0;
+	g_attrib_unref(attrib);
+}
+
+static void wake_up_sender(struct _GAttrib *attrib)
+{
+	if (attrib->write_watch > 0)
+		return;
+
+	attrib = g_attrib_ref(attrib);
+	attrib->write_watch = g_io_add_watch_full(attrib->io,
+				G_PRIORITY_DEFAULT, G_IO_OUT,
+				can_write_data, attrib, destroy_sender);
+}
+
+static bool match_event(struct event *evt, const uint8_t *pdu, gsize len)
+{
+	guint16 handle;
+
+	if (evt->expected == GATTRIB_ALL_EVENTS)
+		return true;
+
+	if (!is_response(pdu[0]) && evt->expected == GATTRIB_ALL_REQS)
+		return true;
+
+	if (evt->expected == pdu[0] && evt->handle == GATTRIB_ALL_HANDLES)
+		return true;
+
+	if (len < 3)
+		return false;
+
+	handle = get_le16(&pdu[1]);
+
+	if (evt->expected == pdu[0] && evt->handle == handle)
+		return true;
+
+	return false;
+}
+
+static gboolean received_data(GIOChannel *io, GIOCondition cond, gpointer data)
+{
+	struct _GAttrib *attrib = data;
+	struct command *cmd = NULL;
+	GSList *l;
+	uint8_t buf[512], status;
+	gsize len;
+	GIOStatus iostat;
+
+	if (attrib->stale)
+		return FALSE;
+
+	if (cond & (G_IO_HUP | G_IO_ERR | G_IO_NVAL)) {
+		struct command *c;
+
+		while ((c = g_queue_pop_head(attrib->requests))) {
+			if (c->func)
+				c->func(ATT_ECODE_IO, NULL, 0, c->user_data);
+			command_destroy(c);
+		}
+
+		attrib->read_watch = 0;
+
+		return FALSE;
+	}
+
+	memset(buf, 0, sizeof(buf));
+
+	iostat = g_io_channel_read_chars(io, (char *) buf, sizeof(buf),
+								&len, NULL);
+	if (iostat != G_IO_STATUS_NORMAL) {
+		status = ATT_ECODE_IO;
+		goto done;
+	}
+
+	for (l = attrib->events; l; l = l->next) {
+		struct event *evt = l->data;
+
+		if (match_event(evt, buf, len))
+			evt->func(buf, len, evt->user_data);
+	}
+
+	if (!is_response(buf[0]))
+		return TRUE;
+
+	if (attrib->timeout_watch > 0) {
+		g_source_remove(attrib->timeout_watch);
+		attrib->timeout_watch = 0;
+	}
+
+	cmd = g_queue_pop_head(attrib->requests);
+	if (cmd == NULL) {
+		/* Keep the watch if we have events to report */
+		return attrib->events != NULL;
+	}
+
+	if (buf[0] == ATT_OP_ERROR) {
+		status = buf[4];
+		goto done;
+	}
+
+	if (cmd->expected != buf[0]) {
+		status = ATT_ECODE_IO;
+		goto done;
+	}
+
+	status = 0;
+
+done:
+	if (!g_queue_is_empty(attrib->requests) ||
+					!g_queue_is_empty(attrib->responses))
+		wake_up_sender(attrib);
+
+	if (cmd) {
+		if (cmd->func)
+			cmd->func(status, buf, len, cmd->user_data);
+
+		command_destroy(cmd);
+	}
+
+	return TRUE;
+}
+
+GAttrib *g_attrib_new(GIOChannel *io)
+{
+	struct _GAttrib *attrib;
+	uint16_t imtu;
+	uint16_t att_mtu;
+	uint16_t cid;
+	GError *gerr = NULL;
+
+	g_io_channel_set_encoding(io, NULL, NULL);
+	g_io_channel_set_buffered(io, FALSE);
+
+	bt_io_get(io, &gerr, BT_IO_OPT_IMTU, &imtu,
+				BT_IO_OPT_CID, &cid, BT_IO_OPT_INVALID);
+	if (gerr) {
+		error("%s", gerr->message);
+		g_error_free(gerr);
+		return NULL;
+	}
+
+	attrib = g_try_new0(struct _GAttrib, 1);
+	if (attrib == NULL)
+		return NULL;
+
+	att_mtu = (cid == ATT_CID) ? ATT_DEFAULT_LE_MTU : imtu;
+
+	attrib->buf = g_malloc0(att_mtu);
+	attrib->buflen = att_mtu;
+
+	attrib->io = g_io_channel_ref(io);
+	attrib->requests = g_queue_new();
+	attrib->responses = g_queue_new();
+
+	attrib->read_watch = g_io_add_watch(attrib->io,
+			G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL,
+			received_data, attrib);
+
+	return g_attrib_ref(attrib);
+}
+
+guint g_attrib_send(GAttrib *attrib, guint id, const guint8 *pdu, guint16 len,
+			GAttribResultFunc func, gpointer user_data,
+			GDestroyNotify notify)
+{
+	struct command *c;
+	GQueue *queue;
+	uint8_t opcode;
+
+	if (attrib->stale)
+		return 0;
+
+	c = g_try_new0(struct command, 1);
+	if (c == NULL)
+		return 0;
+
+	opcode = pdu[0];
+
+	c->opcode = opcode;
+	c->expected = opcode2expected(opcode);
+	c->pdu = g_malloc(len);
+	memcpy(c->pdu, pdu, len);
+	c->len = len;
+	c->func = func;
+	c->user_data = user_data;
+	c->notify = notify;
+
+	if (is_response(opcode))
+		queue = attrib->responses;
+	else
+		queue = attrib->requests;
+
+	if (id) {
+		c->id = id;
+		if (!is_response(opcode))
+			g_queue_push_head(queue, c);
+		else
+			/* Don't re-order responses even if an ID is given */
+			g_queue_push_tail(queue, c);
+	} else {
+		c->id = ++attrib->next_cmd_id;
+		g_queue_push_tail(queue, c);
+	}
+
+	/*
+	 * If a command was added to the queue and it was empty before, wake up
+	 * the sender. If the sender was already woken up by the second queue,
+	 * wake_up_sender will just return.
+	 */
+	if (g_queue_get_length(queue) == 1)
+		wake_up_sender(attrib);
+
+	return c->id;
+}
+
+static int command_cmp_by_id(gconstpointer a, gconstpointer b)
+{
+	const struct command *cmd = a;
+	guint id = GPOINTER_TO_UINT(b);
+
+	return cmd->id - id;
+}
+
+gboolean g_attrib_cancel(GAttrib *attrib, guint id)
+{
+	GList *l = NULL;
+	struct command *cmd;
+	GQueue *queue;
+
+	if (attrib == NULL)
+		return FALSE;
+
+	queue = attrib->requests;
+	if (queue)
+		l = g_queue_find_custom(queue, GUINT_TO_POINTER(id),
+					command_cmp_by_id);
+	if (l == NULL) {
+		queue = attrib->responses;
+		if (!queue)
+			return FALSE;
+		l = g_queue_find_custom(queue, GUINT_TO_POINTER(id),
+					command_cmp_by_id);
+	}
+
+	if (l == NULL)
+		return FALSE;
+
+	cmd = l->data;
+
+	if (cmd == g_queue_peek_head(queue) && cmd->sent)
+		cmd->func = NULL;
+	else {
+		g_queue_remove(queue, cmd);
+		command_destroy(cmd);
+	}
+
+	return TRUE;
+}
+
+static gboolean cancel_all_per_queue(GQueue *queue)
+{
+	struct command *c, *head = NULL;
+	gboolean first = TRUE;
+
+	if (queue == NULL)
+		return FALSE;
+
+	while ((c = g_queue_pop_head(queue))) {
+		if (first && c->sent) {
+			/* If the command was sent ignore its callback ... */
+			c->func = NULL;
+			head = c;
+			continue;
+		}
+
+		first = FALSE;
+		command_destroy(c);
+	}
+
+	if (head) {
+		/* ... and put it back in the queue */
+		g_queue_push_head(queue, head);
+	}
+
+	return TRUE;
+}
+
+gboolean g_attrib_cancel_all(GAttrib *attrib)
+{
+	gboolean ret;
+
+	if (attrib == NULL)
+		return FALSE;
+
+	ret = cancel_all_per_queue(attrib->requests);
+	ret = cancel_all_per_queue(attrib->responses) && ret;
+
+	return ret;
+}
+
+gboolean g_attrib_set_debug(GAttrib *attrib,
+		GAttribDebugFunc func, gpointer user_data)
+{
+	return TRUE;
+}
+
+uint8_t *g_attrib_get_buffer(GAttrib *attrib, size_t *len)
+{
+	if (len == NULL)
+		return NULL;
+
+	*len = attrib->buflen;
+
+	return attrib->buf;
+}
+
+gboolean g_attrib_set_mtu(GAttrib *attrib, int mtu)
+{
+	if (mtu < ATT_DEFAULT_LE_MTU)
+		return FALSE;
+
+	attrib->buf = g_realloc(attrib->buf, mtu);
+
+	attrib->buflen = mtu;
+
+	return TRUE;
+}
+
+guint g_attrib_register(GAttrib *attrib, guint8 opcode, guint16 handle,
+				GAttribNotifyFunc func, gpointer user_data,
+				GDestroyNotify notify)
+{
+	static guint next_evt_id = 0;
+	struct event *event;
+
+	event = g_try_new0(struct event, 1);
+	if (event == NULL)
+		return 0;
+
+	event->expected = opcode;
+	event->handle = handle;
+	event->func = func;
+	event->user_data = user_data;
+	event->notify = notify;
+	event->id = ++next_evt_id;
+
+	attrib->events = g_slist_append(attrib->events, event);
+
+	return event->id;
+}
+
+static int event_cmp_by_id(gconstpointer a, gconstpointer b)
+{
+	const struct event *evt = a;
+	guint id = GPOINTER_TO_UINT(b);
+
+	return evt->id - id;
+}
+
+gboolean g_attrib_is_encrypted(GAttrib *attrib)
+{
+	BtIOSecLevel sec_level;
+
+	if (!bt_io_get(attrib->io, NULL,
+			BT_IO_OPT_SEC_LEVEL, &sec_level,
+			BT_IO_OPT_INVALID))
+		return FALSE;
+
+	return sec_level > BT_IO_SEC_LOW;
+}
+
+gboolean g_attrib_unregister(GAttrib *attrib, guint id)
+{
+	struct event *evt;
+	GSList *l;
+
+	if (id == 0) {
+		warn("%s: invalid id", __func__);
+		return FALSE;
+	}
+
+	l = g_slist_find_custom(attrib->events, GUINT_TO_POINTER(id),
+							event_cmp_by_id);
+	if (l == NULL)
+		return FALSE;
+
+	evt = l->data;
+
+	attrib->events = g_slist_remove(attrib->events, evt);
+
+	if (evt->notify)
+		evt->notify(evt->user_data);
+
+	g_free(evt);
+
+	return TRUE;
+}
+
+gboolean g_attrib_unregister_all(GAttrib *attrib)
+{
+	GSList *l;
+
+	if (attrib->events == NULL)
+		return FALSE;
+
+	for (l = attrib->events; l; l = l->next) {
+		struct event *evt = l->data;
+
+		if (evt->notify)
+			evt->notify(evt->user_data);
+
+		g_free(evt);
+	}
+
+	g_slist_free(attrib->events);
+	attrib->events = NULL;
+
+	return TRUE;
+}
diff --git a/bluez/attrib/gattrib.h b/bluez/attrib/gattrib.h
new file mode 100644
index 0000000..3fe92c7
--- /dev/null
+++ b/bluez/attrib/gattrib.h
@@ -0,0 +1,79 @@
+/*
+ *
+ *  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_EVENTS 0xFF
+#define GATTRIB_ALL_REQS 0xFE
+#define GATTRIB_ALL_HANDLES 0x0000
+
+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);
+GAttrib *g_attrib_ref(GAttrib *attrib);
+void g_attrib_unref(GAttrib *attrib);
+
+GIOChannel *g_attrib_get_channel(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);
+
+gboolean g_attrib_set_debug(GAttrib *attrib,
+		GAttribDebugFunc func, gpointer user_data);
+
+guint g_attrib_register(GAttrib *attrib, guint8 opcode, guint16 handle,
+				GAttribNotifyFunc func, gpointer user_data,
+				GDestroyNotify notify);
+
+gboolean g_attrib_is_encrypted(GAttrib *attrib);
+
+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/attrib/gatttool.c b/bluez/attrib/gatttool.c
new file mode 100644
index 0000000..ca178ce
--- /dev/null
+++ b/bluez/attrib/gatttool.c
@@ -0,0 +1,629 @@
+/*
+ *
+ *  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 <glib.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/hci.h>
+#include <bluetooth/hci_lib.h>
+
+#include "src/shared/util.h"
+#include "lib/uuid.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;
+
+	if (err) {
+		g_printerr("%s\n", err->message);
+		got_error = TRUE;
+		g_main_loop_quit(event_loop);
+	}
+
+	attrib = g_attrib_new(io);
+
+	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);
+
+	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);
+
+	return FALSE;
+
+error:
+	g_main_loop_quit(event_loop);
+	return FALSE;
+}
+
+static void char_desc_cb(guint8 status, const guint8 *pdu, guint16 plen,
+							gpointer user_data)
+{
+	struct att_data_list *list;
+	guint8 format;
+	int i;
+
+	if (status != 0) {
+		g_printerr("Discover all characteristic descriptors failed: "
+						"%s\n", att_ecode2str(status));
+		goto done;
+	}
+
+	list = dec_find_info_resp(pdu, plen, &format);
+	if (list == NULL)
+		goto done;
+
+	for (i = 0; i < list->num; i++) {
+		char uuidstr[MAX_LEN_UUID_STR];
+		uint16_t handle;
+		uint8_t *value;
+		bt_uuid_t uuid;
+
+		value = list->data[i];
+		handle = get_le16(value);
+
+		if (format == 0x01)
+			bt_uuid16_create(&uuid, get_le16(&value[2]));
+		else {
+			uint128_t u128;
+
+			/* Converts from LE to BE byte order */
+			bswap_128(&value[2], &u128);
+			bt_uuid128_create(&uuid, u128);
+		}
+
+		bt_uuid_to_string(&uuid, uuidstr, MAX_LEN_UUID_STR);
+		g_print("handle = 0x%04x, uuid = %s\n", handle, uuidstr);
+	}
+
+	att_data_list_free(list);
+
+done:
+	if (!opt_listen)
+		g_main_loop_quit(event_loop);
+}
+
+static gboolean characteristics_desc(gpointer user_data)
+{
+	GAttrib *attrib = user_data;
+
+	gatt_discover_char_desc(attrib, opt_start, opt_end, 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/attrib/gatttool.h b/bluez/attrib/gatttool.h
new file mode 100644
index 0000000..8f0913c
--- /dev/null
+++ b/bluez/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/attrib/interactive.c b/bluez/attrib/interactive.c
new file mode 100644
index 0000000..4865944
--- /dev/null
+++ b/bluez/attrib/interactive.c
@@ -0,0 +1,1042 @@
+/*
+ *
+ *  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 "src/shared/util.h"
+#include "lib/uuid.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)
+{
+	if (err) {
+		set_state(STATE_DISCONNECTED);
+		error("%s\n", err->message);
+		return;
+	}
+
+	attrib = g_attrib_new(iochannel);
+	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(guint8 status, const guint8 *pdu, guint16 plen,
+							gpointer user_data)
+{
+	struct att_data_list *list;
+	guint8 format;
+	uint16_t handle = 0xffff;
+	int i;
+
+	if (status != 0) {
+		rl_printf("Discover descriptors finished: %s\n",
+						att_ecode2str(status));
+		return;
+	}
+
+	list = dec_find_info_resp(pdu, plen, &format);
+	if (list == NULL)
+		return;
+
+	for (i = 0; i < list->num; i++) {
+		char uuidstr[MAX_LEN_UUID_STR];
+		uint8_t *value;
+		bt_uuid_t uuid;
+
+		value = list->data[i];
+		handle = get_le16(value);
+
+		if (format == 0x01)
+			bt_uuid16_create(&uuid, get_le16(&value[2]));
+		else {
+			uint128_t u128;
+
+			/* Converts from LE to BE byte order */
+			bswap_128(&value[2], &u128);
+			bt_uuid128_create(&uuid, u128);
+		}
+
+		bt_uuid_to_string(&uuid, uuidstr, MAX_LEN_UUID_STR);
+		rl_printf("handle: 0x%04x, uuid: %s\n", handle, uuidstr);
+	}
+
+	att_data_list_free(list);
+
+	if (handle != 0xffff && handle < end)
+		gatt_discover_char_desc(attrib, handle + 1, end, char_desc_cb,
+									NULL);
+}
+
+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_char_desc(attrib, start, end, 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/attrib/utils.c b/bluez/attrib/utils.c
new file mode 100644
index 0000000..8f9a4c0
--- /dev/null
+++ b/bluez/attrib/utils.c
@@ -0,0 +1,120 @@
+/*
+ *
+ *  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 <bluetooth/bluetooth.h>
+#include <bluetooth/hci.h>
+#include <bluetooth/hci_lib.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/btio/btio.c b/bluez/btio/btio.c
new file mode 100644
index 0000000..d58e986
--- /dev/null
+++ b/bluez/btio/btio.c
@@ -0,0 +1,1566 @@
+/*
+ *
+ *  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 <bluetooth/bluetooth.h>
+#include <bluetooth/l2cap.h>
+#include <bluetooth/rfcomm.h>
+#include <bluetooth/sco.h>
+
+#include <glib.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;
+
+	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;
+		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_peers(int sock, struct sockaddr *src, struct sockaddr *dst,
+				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;
+	}
+
+	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;
+	socklen_t len;
+	gboolean flushable = FALSE;
+	uint32_t priority;
+
+	if (!get_peers(sock, (struct sockaddr *) &src,
+				(struct sockaddr *) &dst, 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:
+			ba2str(&dst.l2_bdaddr, va_arg(args, char *));
+			break;
+		case BT_IO_OPT_DEST_BDADDR:
+			bacpy(va_arg(args, bdaddr_t *), &dst.l2_bdaddr);
+			break;
+		case BT_IO_OPT_DEST_TYPE:
+			*(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:
+			*(va_arg(args, uint16_t *)) = src.l2_psm ?
+					btohs(src.l2_psm) : btohs(dst.l2_psm);
+			break;
+		case BT_IO_OPT_CID:
+			*(va_arg(args, uint16_t *)) = src.l2_cid ?
+					btohs(src.l2_cid) : 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;
+			}
+
+			if (getsockopt(sock, SOL_BLUETOOTH, BT_SNDMTU,
+							&l2o.omtu, &len) < 0) {
+				ERROR_FAILED(err, "getsockopt(BT_RCVMTU)",
+									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;
+		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;
+	int flags;
+	socklen_t len;
+	uint8_t dev_class[3];
+	uint16_t handle;
+
+	if (!get_peers(sock, (struct sockaddr *) &src,
+				(struct sockaddr *) &dst, 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:
+			ba2str(&dst.rc_bdaddr, va_arg(args, char *));
+			break;
+		case BT_IO_OPT_DEST_BDADDR:
+			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:
+			*(va_arg(args, uint8_t *)) = src.rc_channel ?
+					src.rc_channel : 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:
+			*(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;
+		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;
+
+	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_peers(sock, (struct sockaddr *) &src,
+				(struct sockaddr *) &dst, sizeof(src), 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;
+		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);
+	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);
+	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;
+	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;
+	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/btio/btio.h b/bluez/btio/btio.h
new file mode 100644
index 0000000..2dce9f0
--- /dev/null
+++ b/bluez/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/client/agent.c b/bluez/client/agent.c
new file mode 100644
index 0000000..2d9dffd
--- /dev/null
+++ b/bluez/client/agent.c
@@ -0,0 +1,486 @@
+/*
+ *
+ *  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.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 DBusMessage *release_agent(DBusConnection *conn,
+					DBusMessage *msg, void *user_data)
+{
+	agent_registered = FALSE;
+	agent_capability = NULL;
+
+	rl_printf("Agent released\n");
+
+	if (pending_message) {
+		dbus_message_unref(pending_message);
+		pending_message = NULL;
+	}
+
+	agent_release_prompt();
+
+	g_dbus_unregister_interface(conn, AGENT_PATH, AGENT_INTERFACE);
+
+	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) {
+		agent_registered = FALSE;
+		agent_capability = NULL;
+		rl_printf("Agent unregistered\n");
+
+		if (g_dbus_unregister_interface(conn, AGENT_PATH,
+						AGENT_INTERFACE) == FALSE)
+			rl_printf("Failed to unregister agent object\n");
+	} 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 (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/client/agent.h b/bluez/client/agent.h
new file mode 100644
index 0000000..0fbe8e5
--- /dev/null
+++ b/bluez/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/client/display.c b/bluez/client/display.c
new file mode 100644
index 0000000..bc891af
--- /dev/null
+++ b/bluez/client/display.c
@@ -0,0 +1,64 @@
+/*
+ *
+ *  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 <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_redisplay();
+		free(saved_line);
+	}
+}
diff --git a/bluez/client/display.h b/bluez/client/display.h
new file mode 100644
index 0000000..91a0be9
--- /dev/null
+++ b/bluez/client/display.h
@@ -0,0 +1,32 @@
+/*
+ *
+ *  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)));
diff --git a/bluez/client/main.c b/bluez/client/main.c
new file mode 100644
index 0000000..5791e95
--- /dev/null
+++ b/bluez/client/main.c
@@ -0,0 +1,1509 @@
+/*
+ *
+ *  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 <signal.h>
+#include <sys/signalfd.h>
+
+#include <readline/readline.h>
+#include <readline/history.h>
+#include <glib.h>
+#include <gdbus.h>
+
+#include "monitor/uuid.h"
+#include "agent.h"
+#include "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]# "
+
+static GMainLoop *main_loop;
+static DBusConnection *dbus_conn;
+
+static GDBusProxy *agent_manager;
+static char *auto_register_agent = NULL;
+
+static GDBusProxy *default_ctrl;
+static GList *ctrl_list;
+static GList *dev_list;
+
+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;
+	const char *valstr;
+
+	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 inavlid\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;
+	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 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);
+		}
+	}
+}
+
+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);
+		}
+	} 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;
+
+			g_list_free(dev_list);
+			dev_list = NULL;
+		}
+	} else if (!strcmp(interface, "org.bluez.AgentManager1")) {
+		if (agent_manager == proxy)
+			agent_manager = NULL;
+	}
+}
+
+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("");
+
+			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);
+	}
+}
+
+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 cmd_info(const char *arg)
+{
+	GDBusProxy *proxy;
+	DBusMessageIter iter;
+	const char *address;
+
+	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_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");
+}
+
+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;
+
+	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, "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;
+
+	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;
+	}
+
+	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;
+
+	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;
+	}
+
+	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;
+
+	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;
+	}
+
+	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;
+
+	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;
+	}
+
+	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)
+{
+	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 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,
+							NULL, 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)
+{
+	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");
+}
+
+static void cmd_disconn(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, "Disconnect", NULL, disconn_reply,
+							NULL, NULL) == FALSE) {
+		rl_printf("Failed to disconnect\n");
+		return;
+	}
+
+	rl_printf("Attempting to disconnect from %s\n", arg);
+}
+
+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 *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" },
+	{ "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 },
+	{ "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 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 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 },
+};
+
+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_signal_watch(client, message_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);
+
+	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/compile b/bluez/compile
new file mode 100755
index 0000000..862a14e
--- /dev/null
+++ b/bluez/compile
@@ -0,0 +1,343 @@
+#! /bin/sh
+# Wrapper for compilers which do not understand '-c -o'.
+
+scriptversion=2012-03-05.13; # UTC
+
+# Copyright (C) 1999, 2000, 2003, 2004, 2005, 2009, 2010, 2012 Free
+# Software Foundation, Inc.
+# Written by Tom Tromey <tromey@cygnus.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, or (at your option)
+# any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+# As a special exception to the GNU General Public License, if you
+# distribute this file as part of a program that contains a
+# configuration script generated by Autoconf, you may include it under
+# the same distribution terms that you use for the rest of that program.
+
+# This file is maintained in Automake, please report
+# bugs to <bug-automake@gnu.org> or send patches to
+# <automake-patches@gnu.org>.
+
+nl='
+'
+
+# We need space, tab and new line, in precisely that order.  Quoting is
+# there to prevent tools from complaining about whitespace usage.
+IFS=" ""	$nl"
+
+file_conv=
+
+# func_file_conv build_file lazy
+# Convert a $build file to $host form and store it in $file
+# Currently only supports Windows hosts. If the determined conversion
+# type is listed in (the comma separated) LAZY, no conversion will
+# take place.
+func_file_conv ()
+{
+  file=$1
+  case $file in
+    / | /[!/]*) # absolute file, and not a UNC file
+      if test -z "$file_conv"; then
+	# lazily determine how to convert abs files
+	case `uname -s` in
+	  MINGW*)
+	    file_conv=mingw
+	    ;;
+	  CYGWIN*)
+	    file_conv=cygwin
+	    ;;
+	  *)
+	    file_conv=wine
+	    ;;
+	esac
+      fi
+      case $file_conv/,$2, in
+	*,$file_conv,*)
+	  ;;
+	mingw/*)
+	  file=`cmd //C echo "$file " | sed -e 's/"\(.*\) " *$/\1/'`
+	  ;;
+	cygwin/*)
+	  file=`cygpath -m "$file" || echo "$file"`
+	  ;;
+	wine/*)
+	  file=`winepath -w "$file" || echo "$file"`
+	  ;;
+      esac
+      ;;
+  esac
+}
+
+# func_cl_dashL linkdir
+# Make cl look for libraries in LINKDIR
+func_cl_dashL ()
+{
+  func_file_conv "$1"
+  if test -z "$lib_path"; then
+    lib_path=$file
+  else
+    lib_path="$lib_path;$file"
+  fi
+  linker_opts="$linker_opts -LIBPATH:$file"
+}
+
+# func_cl_dashl library
+# Do a library search-path lookup for cl
+func_cl_dashl ()
+{
+  lib=$1
+  found=no
+  save_IFS=$IFS
+  IFS=';'
+  for dir in $lib_path $LIB
+  do
+    IFS=$save_IFS
+    if $shared && test -f "$dir/$lib.dll.lib"; then
+      found=yes
+      lib=$dir/$lib.dll.lib
+      break
+    fi
+    if test -f "$dir/$lib.lib"; then
+      found=yes
+      lib=$dir/$lib.lib
+      break
+    fi
+  done
+  IFS=$save_IFS
+
+  if test "$found" != yes; then
+    lib=$lib.lib
+  fi
+}
+
+# func_cl_wrapper cl arg...
+# Adjust compile command to suit cl
+func_cl_wrapper ()
+{
+  # Assume a capable shell
+  lib_path=
+  shared=:
+  linker_opts=
+  for arg
+  do
+    if test -n "$eat"; then
+      eat=
+    else
+      case $1 in
+	-o)
+	  # configure might choose to run compile as 'compile cc -o foo foo.c'.
+	  eat=1
+	  case $2 in
+	    *.o | *.[oO][bB][jJ])
+	      func_file_conv "$2"
+	      set x "$@" -Fo"$file"
+	      shift
+	      ;;
+	    *)
+	      func_file_conv "$2"
+	      set x "$@" -Fe"$file"
+	      shift
+	      ;;
+	  esac
+	  ;;
+	-I)
+	  eat=1
+	  func_file_conv "$2" mingw
+	  set x "$@" -I"$file"
+	  shift
+	  ;;
+	-I*)
+	  func_file_conv "${1#-I}" mingw
+	  set x "$@" -I"$file"
+	  shift
+	  ;;
+	-l)
+	  eat=1
+	  func_cl_dashl "$2"
+	  set x "$@" "$lib"
+	  shift
+	  ;;
+	-l*)
+	  func_cl_dashl "${1#-l}"
+	  set x "$@" "$lib"
+	  shift
+	  ;;
+	-L)
+	  eat=1
+	  func_cl_dashL "$2"
+	  ;;
+	-L*)
+	  func_cl_dashL "${1#-L}"
+	  ;;
+	-static)
+	  shared=false
+	  ;;
+	-Wl,*)
+	  arg=${1#-Wl,}
+	  save_ifs="$IFS"; IFS=','
+	  for flag in $arg; do
+	    IFS="$save_ifs"
+	    linker_opts="$linker_opts $flag"
+	  done
+	  IFS="$save_ifs"
+	  ;;
+	-Xlinker)
+	  eat=1
+	  linker_opts="$linker_opts $2"
+	  ;;
+	-*)
+	  set x "$@" "$1"
+	  shift
+	  ;;
+	*.cc | *.CC | *.cxx | *.CXX | *.[cC]++)
+	  func_file_conv "$1"
+	  set x "$@" -Tp"$file"
+	  shift
+	  ;;
+	*.c | *.cpp | *.CPP | *.lib | *.LIB | *.Lib | *.OBJ | *.obj | *.[oO])
+	  func_file_conv "$1" mingw
+	  set x "$@" "$file"
+	  shift
+	  ;;
+	*)
+	  set x "$@" "$1"
+	  shift
+	  ;;
+      esac
+    fi
+    shift
+  done
+  if test -n "$linker_opts"; then
+    linker_opts="-link$linker_opts"
+  fi
+  exec "$@" $linker_opts
+  exit 1
+}
+
+eat=
+
+case $1 in
+  '')
+     echo "$0: No command.  Try '$0 --help' for more information." 1>&2
+     exit 1;
+     ;;
+  -h | --h*)
+    cat <<\EOF
+Usage: compile [--help] [--version] PROGRAM [ARGS]
+
+Wrapper for compilers which do not understand '-c -o'.
+Remove '-o dest.o' from ARGS, run PROGRAM with the remaining
+arguments, and rename the output as expected.
+
+If you are trying to build a whole package this is not the
+right script to run: please start by reading the file 'INSTALL'.
+
+Report bugs to <bug-automake@gnu.org>.
+EOF
+    exit $?
+    ;;
+  -v | --v*)
+    echo "compile $scriptversion"
+    exit $?
+    ;;
+  cl | *[/\\]cl | cl.exe | *[/\\]cl.exe )
+    func_cl_wrapper "$@"      # Doesn't return...
+    ;;
+esac
+
+ofile=
+cfile=
+
+for arg
+do
+  if test -n "$eat"; then
+    eat=
+  else
+    case $1 in
+      -o)
+	# configure might choose to run compile as 'compile cc -o foo foo.c'.
+	# So we strip '-o arg' only if arg is an object.
+	eat=1
+	case $2 in
+	  *.o | *.obj)
+	    ofile=$2
+	    ;;
+	  *)
+	    set x "$@" -o "$2"
+	    shift
+	    ;;
+	esac
+	;;
+      *.c)
+	cfile=$1
+	set x "$@" "$1"
+	shift
+	;;
+      *)
+	set x "$@" "$1"
+	shift
+	;;
+    esac
+  fi
+  shift
+done
+
+if test -z "$ofile" || test -z "$cfile"; then
+  # If no '-o' option was seen then we might have been invoked from a
+  # pattern rule where we don't need one.  That is ok -- this is a
+  # normal compilation that the losing compiler can handle.  If no
+  # '.c' file was seen then we are probably linking.  That is also
+  # ok.
+  exec "$@"
+fi
+
+# Name of file we expect compiler to create.
+cofile=`echo "$cfile" | sed 's|^.*[\\/]||; s|^[a-zA-Z]:||; s/\.c$/.o/'`
+
+# Create the lock directory.
+# Note: use '[/\\:.-]' here to ensure that we don't use the same name
+# that we are using for the .o file.  Also, base the name on the expected
+# object file name, since that is what matters with a parallel build.
+lockdir=`echo "$cofile" | sed -e 's|[/\\:.-]|_|g'`.d
+while true; do
+  if mkdir "$lockdir" >/dev/null 2>&1; then
+    break
+  fi
+  sleep 1
+done
+# FIXME: race condition here if user kills between mkdir and trap.
+trap "rmdir '$lockdir'; exit 1" 1 2 15
+
+# Run the compile.
+"$@"
+ret=$?
+
+if test -f "$cofile"; then
+  test "$cofile" = "$ofile" || mv "$cofile" "$ofile"
+elif test -f "${cofile}bj"; then
+  test "${cofile}bj" = "$ofile" || mv "${cofile}bj" "$ofile"
+fi
+
+rmdir "$lockdir"
+exit $ret
+
+# Local Variables:
+# mode: shell-script
+# sh-indentation: 2
+# eval: (add-hook 'write-file-hooks 'time-stamp)
+# time-stamp-start: "scriptversion="
+# time-stamp-format: "%:y-%02m-%02d.%02H"
+# time-stamp-time-zone: "UTC"
+# time-stamp-end: "; # UTC"
+# End:
diff --git a/bluez/config.guess b/bluez/config.guess
new file mode 100755
index 0000000..d622a44
--- /dev/null
+++ b/bluez/config.guess
@@ -0,0 +1,1530 @@
+#! /bin/sh
+# Attempt to guess a canonical system name.
+#   Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999,
+#   2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010,
+#   2011, 2012 Free Software Foundation, Inc.
+
+timestamp='2012-02-10'
+
+# This file is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, see <http://www.gnu.org/licenses/>.
+#
+# As a special exception to the GNU General Public License, if you
+# distribute this file as part of a program that contains a
+# configuration script generated by Autoconf, you may include it under
+# the same distribution terms that you use for the rest of that program.
+
+
+# Originally written by Per Bothner.  Please send patches (context
+# diff format) to <config-patches@gnu.org> and include a ChangeLog
+# entry.
+#
+# This script attempts to guess a canonical system name similar to
+# config.sub.  If it succeeds, it prints the system name on stdout, and
+# exits with 0.  Otherwise, it exits with 1.
+#
+# You can get the latest version of this script from:
+# http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.guess;hb=HEAD
+
+me=`echo "$0" | sed -e 's,.*/,,'`
+
+usage="\
+Usage: $0 [OPTION]
+
+Output the configuration name of the system \`$me' is run on.
+
+Operation modes:
+  -h, --help         print this help, then exit
+  -t, --time-stamp   print date of last modification, then exit
+  -v, --version      print version number, then exit
+
+Report bugs and patches to <config-patches@gnu.org>."
+
+version="\
+GNU config.guess ($timestamp)
+
+Originally written by Per Bothner.
+Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000,
+2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012
+Free Software Foundation, Inc.
+
+This is free software; see the source for copying conditions.  There is NO
+warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE."
+
+help="
+Try \`$me --help' for more information."
+
+# Parse command line
+while test $# -gt 0 ; do
+  case $1 in
+    --time-stamp | --time* | -t )
+       echo "$timestamp" ; exit ;;
+    --version | -v )
+       echo "$version" ; exit ;;
+    --help | --h* | -h )
+       echo "$usage"; exit ;;
+    -- )     # Stop option processing
+       shift; break ;;
+    - )	# Use stdin as input.
+       break ;;
+    -* )
+       echo "$me: invalid option $1$help" >&2
+       exit 1 ;;
+    * )
+       break ;;
+  esac
+done
+
+if test $# != 0; then
+  echo "$me: too many arguments$help" >&2
+  exit 1
+fi
+
+trap 'exit 1' 1 2 15
+
+# CC_FOR_BUILD -- compiler used by this script. Note that the use of a
+# compiler to aid in system detection is discouraged as it requires
+# temporary files to be created and, as you can see below, it is a
+# headache to deal with in a portable fashion.
+
+# Historically, `CC_FOR_BUILD' used to be named `HOST_CC'. We still
+# use `HOST_CC' if defined, but it is deprecated.
+
+# Portable tmp directory creation inspired by the Autoconf team.
+
+set_cc_for_build='
+trap "exitcode=\$?; (rm -f \$tmpfiles 2>/dev/null; rmdir \$tmp 2>/dev/null) && exit \$exitcode" 0 ;
+trap "rm -f \$tmpfiles 2>/dev/null; rmdir \$tmp 2>/dev/null; exit 1" 1 2 13 15 ;
+: ${TMPDIR=/tmp} ;
+ { tmp=`(umask 077 && mktemp -d "$TMPDIR/cgXXXXXX") 2>/dev/null` && test -n "$tmp" && test -d "$tmp" ; } ||
+ { test -n "$RANDOM" && tmp=$TMPDIR/cg$$-$RANDOM && (umask 077 && mkdir $tmp) ; } ||
+ { tmp=$TMPDIR/cg-$$ && (umask 077 && mkdir $tmp) && echo "Warning: creating insecure temp directory" >&2 ; } ||
+ { echo "$me: cannot create a temporary directory in $TMPDIR" >&2 ; exit 1 ; } ;
+dummy=$tmp/dummy ;
+tmpfiles="$dummy.c $dummy.o $dummy.rel $dummy" ;
+case $CC_FOR_BUILD,$HOST_CC,$CC in
+ ,,)    echo "int x;" > $dummy.c ;
+	for c in cc gcc c89 c99 ; do
+	  if ($c -c -o $dummy.o $dummy.c) >/dev/null 2>&1 ; then
+	     CC_FOR_BUILD="$c"; break ;
+	  fi ;
+	done ;
+	if test x"$CC_FOR_BUILD" = x ; then
+	  CC_FOR_BUILD=no_compiler_found ;
+	fi
+	;;
+ ,,*)   CC_FOR_BUILD=$CC ;;
+ ,*,*)  CC_FOR_BUILD=$HOST_CC ;;
+esac ; set_cc_for_build= ;'
+
+# This is needed to find uname on a Pyramid OSx when run in the BSD universe.
+# (ghazi@noc.rutgers.edu 1994-08-24)
+if (test -f /.attbin/uname) >/dev/null 2>&1 ; then
+	PATH=$PATH:/.attbin ; export PATH
+fi
+
+UNAME_MACHINE=`(uname -m) 2>/dev/null` || UNAME_MACHINE=unknown
+UNAME_RELEASE=`(uname -r) 2>/dev/null` || UNAME_RELEASE=unknown
+UNAME_SYSTEM=`(uname -s) 2>/dev/null`  || UNAME_SYSTEM=unknown
+UNAME_VERSION=`(uname -v) 2>/dev/null` || UNAME_VERSION=unknown
+
+# Note: order is significant - the case branches are not exclusive.
+
+case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in
+    *:NetBSD:*:*)
+	# NetBSD (nbsd) targets should (where applicable) match one or
+	# more of the tuples: *-*-netbsdelf*, *-*-netbsdaout*,
+	# *-*-netbsdecoff* and *-*-netbsd*.  For targets that recently
+	# switched to ELF, *-*-netbsd* would select the old
+	# object file format.  This provides both forward
+	# compatibility and a consistent mechanism for selecting the
+	# object file format.
+	#
+	# Note: NetBSD doesn't particularly care about the vendor
+	# portion of the name.  We always set it to "unknown".
+	sysctl="sysctl -n hw.machine_arch"
+	UNAME_MACHINE_ARCH=`(/sbin/$sysctl 2>/dev/null || \
+	    /usr/sbin/$sysctl 2>/dev/null || echo unknown)`
+	case "${UNAME_MACHINE_ARCH}" in
+	    armeb) machine=armeb-unknown ;;
+	    arm*) machine=arm-unknown ;;
+	    sh3el) machine=shl-unknown ;;
+	    sh3eb) machine=sh-unknown ;;
+	    sh5el) machine=sh5le-unknown ;;
+	    *) machine=${UNAME_MACHINE_ARCH}-unknown ;;
+	esac
+	# The Operating System including object format, if it has switched
+	# to ELF recently, or will in the future.
+	case "${UNAME_MACHINE_ARCH}" in
+	    arm*|i386|m68k|ns32k|sh3*|sparc|vax)
+		eval $set_cc_for_build
+		if echo __ELF__ | $CC_FOR_BUILD -E - 2>/dev/null \
+			| grep -q __ELF__
+		then
+		    # Once all utilities can be ECOFF (netbsdecoff) or a.out (netbsdaout).
+		    # Return netbsd for either.  FIX?
+		    os=netbsd
+		else
+		    os=netbsdelf
+		fi
+		;;
+	    *)
+		os=netbsd
+		;;
+	esac
+	# The OS release
+	# Debian GNU/NetBSD machines have a different userland, and
+	# thus, need a distinct triplet. However, they do not need
+	# kernel version information, so it can be replaced with a
+	# suitable tag, in the style of linux-gnu.
+	case "${UNAME_VERSION}" in
+	    Debian*)
+		release='-gnu'
+		;;
+	    *)
+		release=`echo ${UNAME_RELEASE}|sed -e 's/[-_].*/\./'`
+		;;
+	esac
+	# Since CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM:
+	# contains redundant information, the shorter form:
+	# CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM is used.
+	echo "${machine}-${os}${release}"
+	exit ;;
+    *:OpenBSD:*:*)
+	UNAME_MACHINE_ARCH=`arch | sed 's/OpenBSD.//'`
+	echo ${UNAME_MACHINE_ARCH}-unknown-openbsd${UNAME_RELEASE}
+	exit ;;
+    *:ekkoBSD:*:*)
+	echo ${UNAME_MACHINE}-unknown-ekkobsd${UNAME_RELEASE}
+	exit ;;
+    *:SolidBSD:*:*)
+	echo ${UNAME_MACHINE}-unknown-solidbsd${UNAME_RELEASE}
+	exit ;;
+    macppc:MirBSD:*:*)
+	echo powerpc-unknown-mirbsd${UNAME_RELEASE}
+	exit ;;
+    *:MirBSD:*:*)
+	echo ${UNAME_MACHINE}-unknown-mirbsd${UNAME_RELEASE}
+	exit ;;
+    alpha:OSF1:*:*)
+	case $UNAME_RELEASE in
+	*4.0)
+		UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $3}'`
+		;;
+	*5.*)
+		UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $4}'`
+		;;
+	esac
+	# According to Compaq, /usr/sbin/psrinfo has been available on
+	# OSF/1 and Tru64 systems produced since 1995.  I hope that
+	# covers most systems running today.  This code pipes the CPU
+	# types through head -n 1, so we only detect the type of CPU 0.
+	ALPHA_CPU_TYPE=`/usr/sbin/psrinfo -v | sed -n -e 's/^  The alpha \(.*\) processor.*$/\1/p' | head -n 1`
+	case "$ALPHA_CPU_TYPE" in
+	    "EV4 (21064)")
+		UNAME_MACHINE="alpha" ;;
+	    "EV4.5 (21064)")
+		UNAME_MACHINE="alpha" ;;
+	    "LCA4 (21066/21068)")
+		UNAME_MACHINE="alpha" ;;
+	    "EV5 (21164)")
+		UNAME_MACHINE="alphaev5" ;;
+	    "EV5.6 (21164A)")
+		UNAME_MACHINE="alphaev56" ;;
+	    "EV5.6 (21164PC)")
+		UNAME_MACHINE="alphapca56" ;;
+	    "EV5.7 (21164PC)")
+		UNAME_MACHINE="alphapca57" ;;
+	    "EV6 (21264)")
+		UNAME_MACHINE="alphaev6" ;;
+	    "EV6.7 (21264A)")
+		UNAME_MACHINE="alphaev67" ;;
+	    "EV6.8CB (21264C)")
+		UNAME_MACHINE="alphaev68" ;;
+	    "EV6.8AL (21264B)")
+		UNAME_MACHINE="alphaev68" ;;
+	    "EV6.8CX (21264D)")
+		UNAME_MACHINE="alphaev68" ;;
+	    "EV6.9A (21264/EV69A)")
+		UNAME_MACHINE="alphaev69" ;;
+	    "EV7 (21364)")
+		UNAME_MACHINE="alphaev7" ;;
+	    "EV7.9 (21364A)")
+		UNAME_MACHINE="alphaev79" ;;
+	esac
+	# A Pn.n version is a patched version.
+	# A Vn.n version is a released version.
+	# A Tn.n version is a released field test version.
+	# A Xn.n version is an unreleased experimental baselevel.
+	# 1.2 uses "1.2" for uname -r.
+	echo ${UNAME_MACHINE}-dec-osf`echo ${UNAME_RELEASE} | sed -e 's/^[PVTX]//' | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz'`
+	# Reset EXIT trap before exiting to avoid spurious non-zero exit code.
+	exitcode=$?
+	trap '' 0
+	exit $exitcode ;;
+    Alpha\ *:Windows_NT*:*)
+	# How do we know it's Interix rather than the generic POSIX subsystem?
+	# Should we change UNAME_MACHINE based on the output of uname instead
+	# of the specific Alpha model?
+	echo alpha-pc-interix
+	exit ;;
+    21064:Windows_NT:50:3)
+	echo alpha-dec-winnt3.5
+	exit ;;
+    Amiga*:UNIX_System_V:4.0:*)
+	echo m68k-unknown-sysv4
+	exit ;;
+    *:[Aa]miga[Oo][Ss]:*:*)
+	echo ${UNAME_MACHINE}-unknown-amigaos
+	exit ;;
+    *:[Mm]orph[Oo][Ss]:*:*)
+	echo ${UNAME_MACHINE}-unknown-morphos
+	exit ;;
+    *:OS/390:*:*)
+	echo i370-ibm-openedition
+	exit ;;
+    *:z/VM:*:*)
+	echo s390-ibm-zvmoe
+	exit ;;
+    *:OS400:*:*)
+	echo powerpc-ibm-os400
+	exit ;;
+    arm:RISC*:1.[012]*:*|arm:riscix:1.[012]*:*)
+	echo arm-acorn-riscix${UNAME_RELEASE}
+	exit ;;
+    arm:riscos:*:*|arm:RISCOS:*:*)
+	echo arm-unknown-riscos
+	exit ;;
+    SR2?01:HI-UX/MPP:*:* | SR8000:HI-UX/MPP:*:*)
+	echo hppa1.1-hitachi-hiuxmpp
+	exit ;;
+    Pyramid*:OSx*:*:* | MIS*:OSx*:*:* | MIS*:SMP_DC-OSx*:*:*)
+	# akee@wpdis03.wpafb.af.mil (Earle F. Ake) contributed MIS and NILE.
+	if test "`(/bin/universe) 2>/dev/null`" = att ; then
+		echo pyramid-pyramid-sysv3
+	else
+		echo pyramid-pyramid-bsd
+	fi
+	exit ;;
+    NILE*:*:*:dcosx)
+	echo pyramid-pyramid-svr4
+	exit ;;
+    DRS?6000:unix:4.0:6*)
+	echo sparc-icl-nx6
+	exit ;;
+    DRS?6000:UNIX_SV:4.2*:7* | DRS?6000:isis:4.2*:7*)
+	case `/usr/bin/uname -p` in
+	    sparc) echo sparc-icl-nx7; exit ;;
+	esac ;;
+    s390x:SunOS:*:*)
+	echo ${UNAME_MACHINE}-ibm-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
+	exit ;;
+    sun4H:SunOS:5.*:*)
+	echo sparc-hal-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
+	exit ;;
+    sun4*:SunOS:5.*:* | tadpole*:SunOS:5.*:*)
+	echo sparc-sun-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
+	exit ;;
+    i86pc:AuroraUX:5.*:* | i86xen:AuroraUX:5.*:*)
+	echo i386-pc-auroraux${UNAME_RELEASE}
+	exit ;;
+    i86pc:SunOS:5.*:* | i86xen:SunOS:5.*:*)
+	eval $set_cc_for_build
+	SUN_ARCH="i386"
+	# If there is a compiler, see if it is configured for 64-bit objects.
+	# Note that the Sun cc does not turn __LP64__ into 1 like gcc does.
+	# This test works for both compilers.
+	if [ "$CC_FOR_BUILD" != 'no_compiler_found' ]; then
+	    if (echo '#ifdef __amd64'; echo IS_64BIT_ARCH; echo '#endif') | \
+		(CCOPTS= $CC_FOR_BUILD -E - 2>/dev/null) | \
+		grep IS_64BIT_ARCH >/dev/null
+	    then
+		SUN_ARCH="x86_64"
+	    fi
+	fi
+	echo ${SUN_ARCH}-pc-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
+	exit ;;
+    sun4*:SunOS:6*:*)
+	# According to config.sub, this is the proper way to canonicalize
+	# SunOS6.  Hard to guess exactly what SunOS6 will be like, but
+	# it's likely to be more like Solaris than SunOS4.
+	echo sparc-sun-solaris3`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
+	exit ;;
+    sun4*:SunOS:*:*)
+	case "`/usr/bin/arch -k`" in
+	    Series*|S4*)
+		UNAME_RELEASE=`uname -v`
+		;;
+	esac
+	# Japanese Language versions have a version number like `4.1.3-JL'.
+	echo sparc-sun-sunos`echo ${UNAME_RELEASE}|sed -e 's/-/_/'`
+	exit ;;
+    sun3*:SunOS:*:*)
+	echo m68k-sun-sunos${UNAME_RELEASE}
+	exit ;;
+    sun*:*:4.2BSD:*)
+	UNAME_RELEASE=`(sed 1q /etc/motd | awk '{print substr($5,1,3)}') 2>/dev/null`
+	test "x${UNAME_RELEASE}" = "x" && UNAME_RELEASE=3
+	case "`/bin/arch`" in
+	    sun3)
+		echo m68k-sun-sunos${UNAME_RELEASE}
+		;;
+	    sun4)
+		echo sparc-sun-sunos${UNAME_RELEASE}
+		;;
+	esac
+	exit ;;
+    aushp:SunOS:*:*)
+	echo sparc-auspex-sunos${UNAME_RELEASE}
+	exit ;;
+    # The situation for MiNT is a little confusing.  The machine name
+    # can be virtually everything (everything which is not
+    # "atarist" or "atariste" at least should have a processor
+    # > m68000).  The system name ranges from "MiNT" over "FreeMiNT"
+    # to the lowercase version "mint" (or "freemint").  Finally
+    # the system name "TOS" denotes a system which is actually not
+    # MiNT.  But MiNT is downward compatible to TOS, so this should
+    # be no problem.
+    atarist[e]:*MiNT:*:* | atarist[e]:*mint:*:* | atarist[e]:*TOS:*:*)
+	echo m68k-atari-mint${UNAME_RELEASE}
+	exit ;;
+    atari*:*MiNT:*:* | atari*:*mint:*:* | atarist[e]:*TOS:*:*)
+	echo m68k-atari-mint${UNAME_RELEASE}
+	exit ;;
+    *falcon*:*MiNT:*:* | *falcon*:*mint:*:* | *falcon*:*TOS:*:*)
+	echo m68k-atari-mint${UNAME_RELEASE}
+	exit ;;
+    milan*:*MiNT:*:* | milan*:*mint:*:* | *milan*:*TOS:*:*)
+	echo m68k-milan-mint${UNAME_RELEASE}
+	exit ;;
+    hades*:*MiNT:*:* | hades*:*mint:*:* | *hades*:*TOS:*:*)
+	echo m68k-hades-mint${UNAME_RELEASE}
+	exit ;;
+    *:*MiNT:*:* | *:*mint:*:* | *:*TOS:*:*)
+	echo m68k-unknown-mint${UNAME_RELEASE}
+	exit ;;
+    m68k:machten:*:*)
+	echo m68k-apple-machten${UNAME_RELEASE}
+	exit ;;
+    powerpc:machten:*:*)
+	echo powerpc-apple-machten${UNAME_RELEASE}
+	exit ;;
+    RISC*:Mach:*:*)
+	echo mips-dec-mach_bsd4.3
+	exit ;;
+    RISC*:ULTRIX:*:*)
+	echo mips-dec-ultrix${UNAME_RELEASE}
+	exit ;;
+    VAX*:ULTRIX*:*:*)
+	echo vax-dec-ultrix${UNAME_RELEASE}
+	exit ;;
+    2020:CLIX:*:* | 2430:CLIX:*:*)
+	echo clipper-intergraph-clix${UNAME_RELEASE}
+	exit ;;
+    mips:*:*:UMIPS | mips:*:*:RISCos)
+	eval $set_cc_for_build
+	sed 's/^	//' << EOF >$dummy.c
+#ifdef __cplusplus
+#include <stdio.h>  /* for printf() prototype */
+	int main (int argc, char *argv[]) {
+#else
+	int main (argc, argv) int argc; char *argv[]; {
+#endif
+	#if defined (host_mips) && defined (MIPSEB)
+	#if defined (SYSTYPE_SYSV)
+	  printf ("mips-mips-riscos%ssysv\n", argv[1]); exit (0);
+	#endif
+	#if defined (SYSTYPE_SVR4)
+	  printf ("mips-mips-riscos%ssvr4\n", argv[1]); exit (0);
+	#endif
+	#if defined (SYSTYPE_BSD43) || defined(SYSTYPE_BSD)
+	  printf ("mips-mips-riscos%sbsd\n", argv[1]); exit (0);
+	#endif
+	#endif
+	  exit (-1);
+	}
+EOF
+	$CC_FOR_BUILD -o $dummy $dummy.c &&
+	  dummyarg=`echo "${UNAME_RELEASE}" | sed -n 's/\([0-9]*\).*/\1/p'` &&
+	  SYSTEM_NAME=`$dummy $dummyarg` &&
+	    { echo "$SYSTEM_NAME"; exit; }
+	echo mips-mips-riscos${UNAME_RELEASE}
+	exit ;;
+    Motorola:PowerMAX_OS:*:*)
+	echo powerpc-motorola-powermax
+	exit ;;
+    Motorola:*:4.3:PL8-*)
+	echo powerpc-harris-powermax
+	exit ;;
+    Night_Hawk:*:*:PowerMAX_OS | Synergy:PowerMAX_OS:*:*)
+	echo powerpc-harris-powermax
+	exit ;;
+    Night_Hawk:Power_UNIX:*:*)
+	echo powerpc-harris-powerunix
+	exit ;;
+    m88k:CX/UX:7*:*)
+	echo m88k-harris-cxux7
+	exit ;;
+    m88k:*:4*:R4*)
+	echo m88k-motorola-sysv4
+	exit ;;
+    m88k:*:3*:R3*)
+	echo m88k-motorola-sysv3
+	exit ;;
+    AViiON:dgux:*:*)
+	# DG/UX returns AViiON for all architectures
+	UNAME_PROCESSOR=`/usr/bin/uname -p`
+	if [ $UNAME_PROCESSOR = mc88100 ] || [ $UNAME_PROCESSOR = mc88110 ]
+	then
+	    if [ ${TARGET_BINARY_INTERFACE}x = m88kdguxelfx ] || \
+	       [ ${TARGET_BINARY_INTERFACE}x = x ]
+	    then
+		echo m88k-dg-dgux${UNAME_RELEASE}
+	    else
+		echo m88k-dg-dguxbcs${UNAME_RELEASE}
+	    fi
+	else
+	    echo i586-dg-dgux${UNAME_RELEASE}
+	fi
+	exit ;;
+    M88*:DolphinOS:*:*)	# DolphinOS (SVR3)
+	echo m88k-dolphin-sysv3
+	exit ;;
+    M88*:*:R3*:*)
+	# Delta 88k system running SVR3
+	echo m88k-motorola-sysv3
+	exit ;;
+    XD88*:*:*:*) # Tektronix XD88 system running UTekV (SVR3)
+	echo m88k-tektronix-sysv3
+	exit ;;
+    Tek43[0-9][0-9]:UTek:*:*) # Tektronix 4300 system running UTek (BSD)
+	echo m68k-tektronix-bsd
+	exit ;;
+    *:IRIX*:*:*)
+	echo mips-sgi-irix`echo ${UNAME_RELEASE}|sed -e 's/-/_/g'`
+	exit ;;
+    ????????:AIX?:[12].1:2)   # AIX 2.2.1 or AIX 2.1.1 is RT/PC AIX.
+	echo romp-ibm-aix     # uname -m gives an 8 hex-code CPU id
+	exit ;;               # Note that: echo "'`uname -s`'" gives 'AIX '
+    i*86:AIX:*:*)
+	echo i386-ibm-aix
+	exit ;;
+    ia64:AIX:*:*)
+	if [ -x /usr/bin/oslevel ] ; then
+		IBM_REV=`/usr/bin/oslevel`
+	else
+		IBM_REV=${UNAME_VERSION}.${UNAME_RELEASE}
+	fi
+	echo ${UNAME_MACHINE}-ibm-aix${IBM_REV}
+	exit ;;
+    *:AIX:2:3)
+	if grep bos325 /usr/include/stdio.h >/dev/null 2>&1; then
+		eval $set_cc_for_build
+		sed 's/^		//' << EOF >$dummy.c
+		#include <sys/systemcfg.h>
+
+		main()
+			{
+			if (!__power_pc())
+				exit(1);
+			puts("powerpc-ibm-aix3.2.5");
+			exit(0);
+			}
+EOF
+		if $CC_FOR_BUILD -o $dummy $dummy.c && SYSTEM_NAME=`$dummy`
+		then
+			echo "$SYSTEM_NAME"
+		else
+			echo rs6000-ibm-aix3.2.5
+		fi
+	elif grep bos324 /usr/include/stdio.h >/dev/null 2>&1; then
+		echo rs6000-ibm-aix3.2.4
+	else
+		echo rs6000-ibm-aix3.2
+	fi
+	exit ;;
+    *:AIX:*:[4567])
+	IBM_CPU_ID=`/usr/sbin/lsdev -C -c processor -S available | sed 1q | awk '{ print $1 }'`
+	if /usr/sbin/lsattr -El ${IBM_CPU_ID} | grep ' POWER' >/dev/null 2>&1; then
+		IBM_ARCH=rs6000
+	else
+		IBM_ARCH=powerpc
+	fi
+	if [ -x /usr/bin/oslevel ] ; then
+		IBM_REV=`/usr/bin/oslevel`
+	else
+		IBM_REV=${UNAME_VERSION}.${UNAME_RELEASE}
+	fi
+	echo ${IBM_ARCH}-ibm-aix${IBM_REV}
+	exit ;;
+    *:AIX:*:*)
+	echo rs6000-ibm-aix
+	exit ;;
+    ibmrt:4.4BSD:*|romp-ibm:BSD:*)
+	echo romp-ibm-bsd4.4
+	exit ;;
+    ibmrt:*BSD:*|romp-ibm:BSD:*)            # covers RT/PC BSD and
+	echo romp-ibm-bsd${UNAME_RELEASE}   # 4.3 with uname added to
+	exit ;;                             # report: romp-ibm BSD 4.3
+    *:BOSX:*:*)
+	echo rs6000-bull-bosx
+	exit ;;
+    DPX/2?00:B.O.S.:*:*)
+	echo m68k-bull-sysv3
+	exit ;;
+    9000/[34]??:4.3bsd:1.*:*)
+	echo m68k-hp-bsd
+	exit ;;
+    hp300:4.4BSD:*:* | 9000/[34]??:4.3bsd:2.*:*)
+	echo m68k-hp-bsd4.4
+	exit ;;
+    9000/[34678]??:HP-UX:*:*)
+	HPUX_REV=`echo ${UNAME_RELEASE}|sed -e 's/[^.]*.[0B]*//'`
+	case "${UNAME_MACHINE}" in
+	    9000/31? )            HP_ARCH=m68000 ;;
+	    9000/[34]?? )         HP_ARCH=m68k ;;
+	    9000/[678][0-9][0-9])
+		if [ -x /usr/bin/getconf ]; then
+		    sc_cpu_version=`/usr/bin/getconf SC_CPU_VERSION 2>/dev/null`
+		    sc_kernel_bits=`/usr/bin/getconf SC_KERNEL_BITS 2>/dev/null`
+		    case "${sc_cpu_version}" in
+		      523) HP_ARCH="hppa1.0" ;; # CPU_PA_RISC1_0
+		      528) HP_ARCH="hppa1.1" ;; # CPU_PA_RISC1_1
+		      532)                      # CPU_PA_RISC2_0
+			case "${sc_kernel_bits}" in
+			  32) HP_ARCH="hppa2.0n" ;;
+			  64) HP_ARCH="hppa2.0w" ;;
+			  '') HP_ARCH="hppa2.0" ;;   # HP-UX 10.20
+			esac ;;
+		    esac
+		fi
+		if [ "${HP_ARCH}" = "" ]; then
+		    eval $set_cc_for_build
+		    sed 's/^		//' << EOF >$dummy.c
+
+		#define _HPUX_SOURCE
+		#include <stdlib.h>
+		#include <unistd.h>
+
+		int main ()
+		{
+		#if defined(_SC_KERNEL_BITS)
+		    long bits = sysconf(_SC_KERNEL_BITS);
+		#endif
+		    long cpu  = sysconf (_SC_CPU_VERSION);
+
+		    switch (cpu)
+			{
+			case CPU_PA_RISC1_0: puts ("hppa1.0"); break;
+			case CPU_PA_RISC1_1: puts ("hppa1.1"); break;
+			case CPU_PA_RISC2_0:
+		#if defined(_SC_KERNEL_BITS)
+			    switch (bits)
+				{
+				case 64: puts ("hppa2.0w"); break;
+				case 32: puts ("hppa2.0n"); break;
+				default: puts ("hppa2.0"); break;
+				} break;
+		#else  /* !defined(_SC_KERNEL_BITS) */
+			    puts ("hppa2.0"); break;
+		#endif
+			default: puts ("hppa1.0"); break;
+			}
+		    exit (0);
+		}
+EOF
+		    (CCOPTS= $CC_FOR_BUILD -o $dummy $dummy.c 2>/dev/null) && HP_ARCH=`$dummy`
+		    test -z "$HP_ARCH" && HP_ARCH=hppa
+		fi ;;
+	esac
+	if [ ${HP_ARCH} = "hppa2.0w" ]
+	then
+	    eval $set_cc_for_build
+
+	    # hppa2.0w-hp-hpux* has a 64-bit kernel and a compiler generating
+	    # 32-bit code.  hppa64-hp-hpux* has the same kernel and a compiler
+	    # generating 64-bit code.  GNU and HP use different nomenclature:
+	    #
+	    # $ CC_FOR_BUILD=cc ./config.guess
+	    # => hppa2.0w-hp-hpux11.23
+	    # $ CC_FOR_BUILD="cc +DA2.0w" ./config.guess
+	    # => hppa64-hp-hpux11.23
+
+	    if echo __LP64__ | (CCOPTS= $CC_FOR_BUILD -E - 2>/dev/null) |
+		grep -q __LP64__
+	    then
+		HP_ARCH="hppa2.0w"
+	    else
+		HP_ARCH="hppa64"
+	    fi
+	fi
+	echo ${HP_ARCH}-hp-hpux${HPUX_REV}
+	exit ;;
+    ia64:HP-UX:*:*)
+	HPUX_REV=`echo ${UNAME_RELEASE}|sed -e 's/[^.]*.[0B]*//'`
+	echo ia64-hp-hpux${HPUX_REV}
+	exit ;;
+    3050*:HI-UX:*:*)
+	eval $set_cc_for_build
+	sed 's/^	//' << EOF >$dummy.c
+	#include <unistd.h>
+	int
+	main ()
+	{
+	  long cpu = sysconf (_SC_CPU_VERSION);
+	  /* The order matters, because CPU_IS_HP_MC68K erroneously returns
+	     true for CPU_PA_RISC1_0.  CPU_IS_PA_RISC returns correct
+	     results, however.  */
+	  if (CPU_IS_PA_RISC (cpu))
+	    {
+	      switch (cpu)
+		{
+		  case CPU_PA_RISC1_0: puts ("hppa1.0-hitachi-hiuxwe2"); break;
+		  case CPU_PA_RISC1_1: puts ("hppa1.1-hitachi-hiuxwe2"); break;
+		  case CPU_PA_RISC2_0: puts ("hppa2.0-hitachi-hiuxwe2"); break;
+		  default: puts ("hppa-hitachi-hiuxwe2"); break;
+		}
+	    }
+	  else if (CPU_IS_HP_MC68K (cpu))
+	    puts ("m68k-hitachi-hiuxwe2");
+	  else puts ("unknown-hitachi-hiuxwe2");
+	  exit (0);
+	}
+EOF
+	$CC_FOR_BUILD -o $dummy $dummy.c && SYSTEM_NAME=`$dummy` &&
+		{ echo "$SYSTEM_NAME"; exit; }
+	echo unknown-hitachi-hiuxwe2
+	exit ;;
+    9000/7??:4.3bsd:*:* | 9000/8?[79]:4.3bsd:*:* )
+	echo hppa1.1-hp-bsd
+	exit ;;
+    9000/8??:4.3bsd:*:*)
+	echo hppa1.0-hp-bsd
+	exit ;;
+    *9??*:MPE/iX:*:* | *3000*:MPE/iX:*:*)
+	echo hppa1.0-hp-mpeix
+	exit ;;
+    hp7??:OSF1:*:* | hp8?[79]:OSF1:*:* )
+	echo hppa1.1-hp-osf
+	exit ;;
+    hp8??:OSF1:*:*)
+	echo hppa1.0-hp-osf
+	exit ;;
+    i*86:OSF1:*:*)
+	if [ -x /usr/sbin/sysversion ] ; then
+	    echo ${UNAME_MACHINE}-unknown-osf1mk
+	else
+	    echo ${UNAME_MACHINE}-unknown-osf1
+	fi
+	exit ;;
+    parisc*:Lites*:*:*)
+	echo hppa1.1-hp-lites
+	exit ;;
+    C1*:ConvexOS:*:* | convex:ConvexOS:C1*:*)
+	echo c1-convex-bsd
+	exit ;;
+    C2*:ConvexOS:*:* | convex:ConvexOS:C2*:*)
+	if getsysinfo -f scalar_acc
+	then echo c32-convex-bsd
+	else echo c2-convex-bsd
+	fi
+	exit ;;
+    C34*:ConvexOS:*:* | convex:ConvexOS:C34*:*)
+	echo c34-convex-bsd
+	exit ;;
+    C38*:ConvexOS:*:* | convex:ConvexOS:C38*:*)
+	echo c38-convex-bsd
+	exit ;;
+    C4*:ConvexOS:*:* | convex:ConvexOS:C4*:*)
+	echo c4-convex-bsd
+	exit ;;
+    CRAY*Y-MP:*:*:*)
+	echo ymp-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/'
+	exit ;;
+    CRAY*[A-Z]90:*:*:*)
+	echo ${UNAME_MACHINE}-cray-unicos${UNAME_RELEASE} \
+	| sed -e 's/CRAY.*\([A-Z]90\)/\1/' \
+	      -e y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/ \
+	      -e 's/\.[^.]*$/.X/'
+	exit ;;
+    CRAY*TS:*:*:*)
+	echo t90-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/'
+	exit ;;
+    CRAY*T3E:*:*:*)
+	echo alphaev5-cray-unicosmk${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/'
+	exit ;;
+    CRAY*SV1:*:*:*)
+	echo sv1-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/'
+	exit ;;
+    *:UNICOS/mp:*:*)
+	echo craynv-cray-unicosmp${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/'
+	exit ;;
+    F30[01]:UNIX_System_V:*:* | F700:UNIX_System_V:*:*)
+	FUJITSU_PROC=`uname -m | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz'`
+	FUJITSU_SYS=`uname -p | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/\///'`
+	FUJITSU_REL=`echo ${UNAME_RELEASE} | sed -e 's/ /_/'`
+	echo "${FUJITSU_PROC}-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}"
+	exit ;;
+    5000:UNIX_System_V:4.*:*)
+	FUJITSU_SYS=`uname -p | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/\///'`
+	FUJITSU_REL=`echo ${UNAME_RELEASE} | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/ /_/'`
+	echo "sparc-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}"
+	exit ;;
+    i*86:BSD/386:*:* | i*86:BSD/OS:*:* | *:Ascend\ Embedded/OS:*:*)
+	echo ${UNAME_MACHINE}-pc-bsdi${UNAME_RELEASE}
+	exit ;;
+    sparc*:BSD/OS:*:*)
+	echo sparc-unknown-bsdi${UNAME_RELEASE}
+	exit ;;
+    *:BSD/OS:*:*)
+	echo ${UNAME_MACHINE}-unknown-bsdi${UNAME_RELEASE}
+	exit ;;
+    *:FreeBSD:*:*)
+	UNAME_PROCESSOR=`/usr/bin/uname -p`
+	case ${UNAME_PROCESSOR} in
+	    amd64)
+		echo x86_64-unknown-freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` ;;
+	    *)
+		echo ${UNAME_PROCESSOR}-unknown-freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` ;;
+	esac
+	exit ;;
+    i*:CYGWIN*:*)
+	echo ${UNAME_MACHINE}-pc-cygwin
+	exit ;;
+    *:MINGW*:*)
+	echo ${UNAME_MACHINE}-pc-mingw32
+	exit ;;
+    i*:MSYS*:*)
+	echo ${UNAME_MACHINE}-pc-msys
+	exit ;;
+    i*:windows32*:*)
+	# uname -m includes "-pc" on this system.
+	echo ${UNAME_MACHINE}-mingw32
+	exit ;;
+    i*:PW*:*)
+	echo ${UNAME_MACHINE}-pc-pw32
+	exit ;;
+    *:Interix*:*)
+	case ${UNAME_MACHINE} in
+	    x86)
+		echo i586-pc-interix${UNAME_RELEASE}
+		exit ;;
+	    authenticamd | genuineintel | EM64T)
+		echo x86_64-unknown-interix${UNAME_RELEASE}
+		exit ;;
+	    IA64)
+		echo ia64-unknown-interix${UNAME_RELEASE}
+		exit ;;
+	esac ;;
+    [345]86:Windows_95:* | [345]86:Windows_98:* | [345]86:Windows_NT:*)
+	echo i${UNAME_MACHINE}-pc-mks
+	exit ;;
+    8664:Windows_NT:*)
+	echo x86_64-pc-mks
+	exit ;;
+    i*:Windows_NT*:* | Pentium*:Windows_NT*:*)
+	# How do we know it's Interix rather than the generic POSIX subsystem?
+	# It also conflicts with pre-2.0 versions of AT&T UWIN. Should we
+	# UNAME_MACHINE based on the output of uname instead of i386?
+	echo i586-pc-interix
+	exit ;;
+    i*:UWIN*:*)
+	echo ${UNAME_MACHINE}-pc-uwin
+	exit ;;
+    amd64:CYGWIN*:*:* | x86_64:CYGWIN*:*:*)
+	echo x86_64-unknown-cygwin
+	exit ;;
+    p*:CYGWIN*:*)
+	echo powerpcle-unknown-cygwin
+	exit ;;
+    prep*:SunOS:5.*:*)
+	echo powerpcle-unknown-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
+	exit ;;
+    *:GNU:*:*)
+	# the GNU system
+	echo `echo ${UNAME_MACHINE}|sed -e 's,[-/].*$,,'`-unknown-gnu`echo ${UNAME_RELEASE}|sed -e 's,/.*$,,'`
+	exit ;;
+    *:GNU/*:*:*)
+	# other systems with GNU libc and userland
+	echo ${UNAME_MACHINE}-unknown-`echo ${UNAME_SYSTEM} | sed 's,^[^/]*/,,' | tr '[A-Z]' '[a-z]'``echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'`-gnu
+	exit ;;
+    i*86:Minix:*:*)
+	echo ${UNAME_MACHINE}-pc-minix
+	exit ;;
+    aarch64:Linux:*:*)
+	echo ${UNAME_MACHINE}-unknown-linux-gnu
+	exit ;;
+    aarch64_be:Linux:*:*)
+	UNAME_MACHINE=aarch64_be
+	echo ${UNAME_MACHINE}-unknown-linux-gnu
+	exit ;;
+    alpha:Linux:*:*)
+	case `sed -n '/^cpu model/s/^.*: \(.*\)/\1/p' < /proc/cpuinfo` in
+	  EV5)   UNAME_MACHINE=alphaev5 ;;
+	  EV56)  UNAME_MACHINE=alphaev56 ;;
+	  PCA56) UNAME_MACHINE=alphapca56 ;;
+	  PCA57) UNAME_MACHINE=alphapca56 ;;
+	  EV6)   UNAME_MACHINE=alphaev6 ;;
+	  EV67)  UNAME_MACHINE=alphaev67 ;;
+	  EV68*) UNAME_MACHINE=alphaev68 ;;
+	esac
+	objdump --private-headers /bin/sh | grep -q ld.so.1
+	if test "$?" = 0 ; then LIBC="libc1" ; else LIBC="" ; fi
+	echo ${UNAME_MACHINE}-unknown-linux-gnu${LIBC}
+	exit ;;
+    arm*:Linux:*:*)
+	eval $set_cc_for_build
+	if echo __ARM_EABI__ | $CC_FOR_BUILD -E - 2>/dev/null \
+	    | grep -q __ARM_EABI__
+	then
+	    echo ${UNAME_MACHINE}-unknown-linux-gnu
+	else
+	    if echo __ARM_PCS_VFP | $CC_FOR_BUILD -E - 2>/dev/null \
+		| grep -q __ARM_PCS_VFP
+	    then
+		echo ${UNAME_MACHINE}-unknown-linux-gnueabi
+	    else
+		echo ${UNAME_MACHINE}-unknown-linux-gnueabihf
+	    fi
+	fi
+	exit ;;
+    avr32*:Linux:*:*)
+	echo ${UNAME_MACHINE}-unknown-linux-gnu
+	exit ;;
+    cris:Linux:*:*)
+	echo ${UNAME_MACHINE}-axis-linux-gnu
+	exit ;;
+    crisv32:Linux:*:*)
+	echo ${UNAME_MACHINE}-axis-linux-gnu
+	exit ;;
+    frv:Linux:*:*)
+	echo ${UNAME_MACHINE}-unknown-linux-gnu
+	exit ;;
+    hexagon:Linux:*:*)
+	echo ${UNAME_MACHINE}-unknown-linux-gnu
+	exit ;;
+    i*86:Linux:*:*)
+	LIBC=gnu
+	eval $set_cc_for_build
+	sed 's/^	//' << EOF >$dummy.c
+	#ifdef __dietlibc__
+	LIBC=dietlibc
+	#endif
+EOF
+	eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep '^LIBC'`
+	echo "${UNAME_MACHINE}-pc-linux-${LIBC}"
+	exit ;;
+    ia64:Linux:*:*)
+	echo ${UNAME_MACHINE}-unknown-linux-gnu
+	exit ;;
+    m32r*:Linux:*:*)
+	echo ${UNAME_MACHINE}-unknown-linux-gnu
+	exit ;;
+    m68*:Linux:*:*)
+	echo ${UNAME_MACHINE}-unknown-linux-gnu
+	exit ;;
+    mips:Linux:*:* | mips64:Linux:*:*)
+	eval $set_cc_for_build
+	sed 's/^	//' << EOF >$dummy.c
+	#undef CPU
+	#undef ${UNAME_MACHINE}
+	#undef ${UNAME_MACHINE}el
+	#if defined(__MIPSEL__) || defined(__MIPSEL) || defined(_MIPSEL) || defined(MIPSEL)
+	CPU=${UNAME_MACHINE}el
+	#else
+	#if defined(__MIPSEB__) || defined(__MIPSEB) || defined(_MIPSEB) || defined(MIPSEB)
+	CPU=${UNAME_MACHINE}
+	#else
+	CPU=
+	#endif
+	#endif
+EOF
+	eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep '^CPU'`
+	test x"${CPU}" != x && { echo "${CPU}-unknown-linux-gnu"; exit; }
+	;;
+    or32:Linux:*:*)
+	echo ${UNAME_MACHINE}-unknown-linux-gnu
+	exit ;;
+    padre:Linux:*:*)
+	echo sparc-unknown-linux-gnu
+	exit ;;
+    parisc64:Linux:*:* | hppa64:Linux:*:*)
+	echo hppa64-unknown-linux-gnu
+	exit ;;
+    parisc:Linux:*:* | hppa:Linux:*:*)
+	# Look for CPU level
+	case `grep '^cpu[^a-z]*:' /proc/cpuinfo 2>/dev/null | cut -d' ' -f2` in
+	  PA7*) echo hppa1.1-unknown-linux-gnu ;;
+	  PA8*) echo hppa2.0-unknown-linux-gnu ;;
+	  *)    echo hppa-unknown-linux-gnu ;;
+	esac
+	exit ;;
+    ppc64:Linux:*:*)
+	echo powerpc64-unknown-linux-gnu
+	exit ;;
+    ppc:Linux:*:*)
+	echo powerpc-unknown-linux-gnu
+	exit ;;
+    s390:Linux:*:* | s390x:Linux:*:*)
+	echo ${UNAME_MACHINE}-ibm-linux
+	exit ;;
+    sh64*:Linux:*:*)
+	echo ${UNAME_MACHINE}-unknown-linux-gnu
+	exit ;;
+    sh*:Linux:*:*)
+	echo ${UNAME_MACHINE}-unknown-linux-gnu
+	exit ;;
+    sparc:Linux:*:* | sparc64:Linux:*:*)
+	echo ${UNAME_MACHINE}-unknown-linux-gnu
+	exit ;;
+    tile*:Linux:*:*)
+	echo ${UNAME_MACHINE}-unknown-linux-gnu
+	exit ;;
+    vax:Linux:*:*)
+	echo ${UNAME_MACHINE}-dec-linux-gnu
+	exit ;;
+    x86_64:Linux:*:*)
+	echo ${UNAME_MACHINE}-unknown-linux-gnu
+	exit ;;
+    xtensa*:Linux:*:*)
+	echo ${UNAME_MACHINE}-unknown-linux-gnu
+	exit ;;
+    i*86:DYNIX/ptx:4*:*)
+	# ptx 4.0 does uname -s correctly, with DYNIX/ptx in there.
+	# earlier versions are messed up and put the nodename in both
+	# sysname and nodename.
+	echo i386-sequent-sysv4
+	exit ;;
+    i*86:UNIX_SV:4.2MP:2.*)
+	# Unixware is an offshoot of SVR4, but it has its own version
+	# number series starting with 2...
+	# I am not positive that other SVR4 systems won't match this,
+	# I just have to hope.  -- rms.
+	# Use sysv4.2uw... so that sysv4* matches it.
+	echo ${UNAME_MACHINE}-pc-sysv4.2uw${UNAME_VERSION}
+	exit ;;
+    i*86:OS/2:*:*)
+	# If we were able to find `uname', then EMX Unix compatibility
+	# is probably installed.
+	echo ${UNAME_MACHINE}-pc-os2-emx
+	exit ;;
+    i*86:XTS-300:*:STOP)
+	echo ${UNAME_MACHINE}-unknown-stop
+	exit ;;
+    i*86:atheos:*:*)
+	echo ${UNAME_MACHINE}-unknown-atheos
+	exit ;;
+    i*86:syllable:*:*)
+	echo ${UNAME_MACHINE}-pc-syllable
+	exit ;;
+    i*86:LynxOS:2.*:* | i*86:LynxOS:3.[01]*:* | i*86:LynxOS:4.[02]*:*)
+	echo i386-unknown-lynxos${UNAME_RELEASE}
+	exit ;;
+    i*86:*DOS:*:*)
+	echo ${UNAME_MACHINE}-pc-msdosdjgpp
+	exit ;;
+    i*86:*:4.*:* | i*86:SYSTEM_V:4.*:*)
+	UNAME_REL=`echo ${UNAME_RELEASE} | sed 's/\/MP$//'`
+	if grep Novell /usr/include/link.h >/dev/null 2>/dev/null; then
+		echo ${UNAME_MACHINE}-univel-sysv${UNAME_REL}
+	else
+		echo ${UNAME_MACHINE}-pc-sysv${UNAME_REL}
+	fi
+	exit ;;
+    i*86:*:5:[678]*)
+	# UnixWare 7.x, OpenUNIX and OpenServer 6.
+	case `/bin/uname -X | grep "^Machine"` in
+	    *486*)	     UNAME_MACHINE=i486 ;;
+	    *Pentium)	     UNAME_MACHINE=i586 ;;
+	    *Pent*|*Celeron) UNAME_MACHINE=i686 ;;
+	esac
+	echo ${UNAME_MACHINE}-unknown-sysv${UNAME_RELEASE}${UNAME_SYSTEM}${UNAME_VERSION}
+	exit ;;
+    i*86:*:3.2:*)
+	if test -f /usr/options/cb.name; then
+		UNAME_REL=`sed -n 's/.*Version //p' </usr/options/cb.name`
+		echo ${UNAME_MACHINE}-pc-isc$UNAME_REL
+	elif /bin/uname -X 2>/dev/null >/dev/null ; then
+		UNAME_REL=`(/bin/uname -X|grep Release|sed -e 's/.*= //')`
+		(/bin/uname -X|grep i80486 >/dev/null) && UNAME_MACHINE=i486
+		(/bin/uname -X|grep '^Machine.*Pentium' >/dev/null) \
+			&& UNAME_MACHINE=i586
+		(/bin/uname -X|grep '^Machine.*Pent *II' >/dev/null) \
+			&& UNAME_MACHINE=i686
+		(/bin/uname -X|grep '^Machine.*Pentium Pro' >/dev/null) \
+			&& UNAME_MACHINE=i686
+		echo ${UNAME_MACHINE}-pc-sco$UNAME_REL
+	else
+		echo ${UNAME_MACHINE}-pc-sysv32
+	fi
+	exit ;;
+    pc:*:*:*)
+	# Left here for compatibility:
+	# uname -m prints for DJGPP always 'pc', but it prints nothing about
+	# the processor, so we play safe by assuming i586.
+	# Note: whatever this is, it MUST be the same as what config.sub
+	# prints for the "djgpp" host, or else GDB configury will decide that
+	# this is a cross-build.
+	echo i586-pc-msdosdjgpp
+	exit ;;
+    Intel:Mach:3*:*)
+	echo i386-pc-mach3
+	exit ;;
+    paragon:*:*:*)
+	echo i860-intel-osf1
+	exit ;;
+    i860:*:4.*:*) # i860-SVR4
+	if grep Stardent /usr/include/sys/uadmin.h >/dev/null 2>&1 ; then
+	  echo i860-stardent-sysv${UNAME_RELEASE} # Stardent Vistra i860-SVR4
+	else # Add other i860-SVR4 vendors below as they are discovered.
+	  echo i860-unknown-sysv${UNAME_RELEASE}  # Unknown i860-SVR4
+	fi
+	exit ;;
+    mini*:CTIX:SYS*5:*)
+	# "miniframe"
+	echo m68010-convergent-sysv
+	exit ;;
+    mc68k:UNIX:SYSTEM5:3.51m)
+	echo m68k-convergent-sysv
+	exit ;;
+    M680?0:D-NIX:5.3:*)
+	echo m68k-diab-dnix
+	exit ;;
+    M68*:*:R3V[5678]*:*)
+	test -r /sysV68 && { echo 'm68k-motorola-sysv'; exit; } ;;
+    3[345]??:*:4.0:3.0 | 3[34]??A:*:4.0:3.0 | 3[34]??,*:*:4.0:3.0 | 3[34]??/*:*:4.0:3.0 | 4400:*:4.0:3.0 | 4850:*:4.0:3.0 | SKA40:*:4.0:3.0 | SDS2:*:4.0:3.0 | SHG2:*:4.0:3.0 | S7501*:*:4.0:3.0)
+	OS_REL=''
+	test -r /etc/.relid \
+	&& OS_REL=.`sed -n 's/[^ ]* [^ ]* \([0-9][0-9]\).*/\1/p' < /etc/.relid`
+	/bin/uname -p 2>/dev/null | grep 86 >/dev/null \
+	  && { echo i486-ncr-sysv4.3${OS_REL}; exit; }
+	/bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \
+	  && { echo i586-ncr-sysv4.3${OS_REL}; exit; } ;;
+    3[34]??:*:4.0:* | 3[34]??,*:*:4.0:*)
+	/bin/uname -p 2>/dev/null | grep 86 >/dev/null \
+	  && { echo i486-ncr-sysv4; exit; } ;;
+    NCR*:*:4.2:* | MPRAS*:*:4.2:*)
+	OS_REL='.3'
+	test -r /etc/.relid \
+	    && OS_REL=.`sed -n 's/[^ ]* [^ ]* \([0-9][0-9]\).*/\1/p' < /etc/.relid`
+	/bin/uname -p 2>/dev/null | grep 86 >/dev/null \
+	    && { echo i486-ncr-sysv4.3${OS_REL}; exit; }
+	/bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \
+	    && { echo i586-ncr-sysv4.3${OS_REL}; exit; }
+	/bin/uname -p 2>/dev/null | /bin/grep pteron >/dev/null \
+	    && { echo i586-ncr-sysv4.3${OS_REL}; exit; } ;;
+    m68*:LynxOS:2.*:* | m68*:LynxOS:3.0*:*)
+	echo m68k-unknown-lynxos${UNAME_RELEASE}
+	exit ;;
+    mc68030:UNIX_System_V:4.*:*)
+	echo m68k-atari-sysv4
+	exit ;;
+    TSUNAMI:LynxOS:2.*:*)
+	echo sparc-unknown-lynxos${UNAME_RELEASE}
+	exit ;;
+    rs6000:LynxOS:2.*:*)
+	echo rs6000-unknown-lynxos${UNAME_RELEASE}
+	exit ;;
+    PowerPC:LynxOS:2.*:* | PowerPC:LynxOS:3.[01]*:* | PowerPC:LynxOS:4.[02]*:*)
+	echo powerpc-unknown-lynxos${UNAME_RELEASE}
+	exit ;;
+    SM[BE]S:UNIX_SV:*:*)
+	echo mips-dde-sysv${UNAME_RELEASE}
+	exit ;;
+    RM*:ReliantUNIX-*:*:*)
+	echo mips-sni-sysv4
+	exit ;;
+    RM*:SINIX-*:*:*)
+	echo mips-sni-sysv4
+	exit ;;
+    *:SINIX-*:*:*)
+	if uname -p 2>/dev/null >/dev/null ; then
+		UNAME_MACHINE=`(uname -p) 2>/dev/null`
+		echo ${UNAME_MACHINE}-sni-sysv4
+	else
+		echo ns32k-sni-sysv
+	fi
+	exit ;;
+    PENTIUM:*:4.0*:*)	# Unisys `ClearPath HMP IX 4000' SVR4/MP effort
+			# says <Richard.M.Bartel@ccMail.Census.GOV>
+	echo i586-unisys-sysv4
+	exit ;;
+    *:UNIX_System_V:4*:FTX*)
+	# From Gerald Hewes <hewes@openmarket.com>.
+	# How about differentiating between stratus architectures? -djm
+	echo hppa1.1-stratus-sysv4
+	exit ;;
+    *:*:*:FTX*)
+	# From seanf@swdc.stratus.com.
+	echo i860-stratus-sysv4
+	exit ;;
+    i*86:VOS:*:*)
+	# From Paul.Green@stratus.com.
+	echo ${UNAME_MACHINE}-stratus-vos
+	exit ;;
+    *:VOS:*:*)
+	# From Paul.Green@stratus.com.
+	echo hppa1.1-stratus-vos
+	exit ;;
+    mc68*:A/UX:*:*)
+	echo m68k-apple-aux${UNAME_RELEASE}
+	exit ;;
+    news*:NEWS-OS:6*:*)
+	echo mips-sony-newsos6
+	exit ;;
+    R[34]000:*System_V*:*:* | R4000:UNIX_SYSV:*:* | R*000:UNIX_SV:*:*)
+	if [ -d /usr/nec ]; then
+		echo mips-nec-sysv${UNAME_RELEASE}
+	else
+		echo mips-unknown-sysv${UNAME_RELEASE}
+	fi
+	exit ;;
+    BeBox:BeOS:*:*)	# BeOS running on hardware made by Be, PPC only.
+	echo powerpc-be-beos
+	exit ;;
+    BeMac:BeOS:*:*)	# BeOS running on Mac or Mac clone, PPC only.
+	echo powerpc-apple-beos
+	exit ;;
+    BePC:BeOS:*:*)	# BeOS running on Intel PC compatible.
+	echo i586-pc-beos
+	exit ;;
+    BePC:Haiku:*:*)	# Haiku running on Intel PC compatible.
+	echo i586-pc-haiku
+	exit ;;
+    SX-4:SUPER-UX:*:*)
+	echo sx4-nec-superux${UNAME_RELEASE}
+	exit ;;
+    SX-5:SUPER-UX:*:*)
+	echo sx5-nec-superux${UNAME_RELEASE}
+	exit ;;
+    SX-6:SUPER-UX:*:*)
+	echo sx6-nec-superux${UNAME_RELEASE}
+	exit ;;
+    SX-7:SUPER-UX:*:*)
+	echo sx7-nec-superux${UNAME_RELEASE}
+	exit ;;
+    SX-8:SUPER-UX:*:*)
+	echo sx8-nec-superux${UNAME_RELEASE}
+	exit ;;
+    SX-8R:SUPER-UX:*:*)
+	echo sx8r-nec-superux${UNAME_RELEASE}
+	exit ;;
+    Power*:Rhapsody:*:*)
+	echo powerpc-apple-rhapsody${UNAME_RELEASE}
+	exit ;;
+    *:Rhapsody:*:*)
+	echo ${UNAME_MACHINE}-apple-rhapsody${UNAME_RELEASE}
+	exit ;;
+    *:Darwin:*:*)
+	UNAME_PROCESSOR=`uname -p` || UNAME_PROCESSOR=unknown
+	case $UNAME_PROCESSOR in
+	    i386)
+		eval $set_cc_for_build
+		if [ "$CC_FOR_BUILD" != 'no_compiler_found' ]; then
+		  if (echo '#ifdef __LP64__'; echo IS_64BIT_ARCH; echo '#endif') | \
+		      (CCOPTS= $CC_FOR_BUILD -E - 2>/dev/null) | \
+		      grep IS_64BIT_ARCH >/dev/null
+		  then
+		      UNAME_PROCESSOR="x86_64"
+		  fi
+		fi ;;
+	    unknown) UNAME_PROCESSOR=powerpc ;;
+	esac
+	echo ${UNAME_PROCESSOR}-apple-darwin${UNAME_RELEASE}
+	exit ;;
+    *:procnto*:*:* | *:QNX:[0123456789]*:*)
+	UNAME_PROCESSOR=`uname -p`
+	if test "$UNAME_PROCESSOR" = "x86"; then
+		UNAME_PROCESSOR=i386
+		UNAME_MACHINE=pc
+	fi
+	echo ${UNAME_PROCESSOR}-${UNAME_MACHINE}-nto-qnx${UNAME_RELEASE}
+	exit ;;
+    *:QNX:*:4*)
+	echo i386-pc-qnx
+	exit ;;
+    NEO-?:NONSTOP_KERNEL:*:*)
+	echo neo-tandem-nsk${UNAME_RELEASE}
+	exit ;;
+    NSE-?:NONSTOP_KERNEL:*:*)
+	echo nse-tandem-nsk${UNAME_RELEASE}
+	exit ;;
+    NSR-?:NONSTOP_KERNEL:*:*)
+	echo nsr-tandem-nsk${UNAME_RELEASE}
+	exit ;;
+    *:NonStop-UX:*:*)
+	echo mips-compaq-nonstopux
+	exit ;;
+    BS2000:POSIX*:*:*)
+	echo bs2000-siemens-sysv
+	exit ;;
+    DS/*:UNIX_System_V:*:*)
+	echo ${UNAME_MACHINE}-${UNAME_SYSTEM}-${UNAME_RELEASE}
+	exit ;;
+    *:Plan9:*:*)
+	# "uname -m" is not consistent, so use $cputype instead. 386
+	# is converted to i386 for consistency with other x86
+	# operating systems.
+	if test "$cputype" = "386"; then
+	    UNAME_MACHINE=i386
+	else
+	    UNAME_MACHINE="$cputype"
+	fi
+	echo ${UNAME_MACHINE}-unknown-plan9
+	exit ;;
+    *:TOPS-10:*:*)
+	echo pdp10-unknown-tops10
+	exit ;;
+    *:TENEX:*:*)
+	echo pdp10-unknown-tenex
+	exit ;;
+    KS10:TOPS-20:*:* | KL10:TOPS-20:*:* | TYPE4:TOPS-20:*:*)
+	echo pdp10-dec-tops20
+	exit ;;
+    XKL-1:TOPS-20:*:* | TYPE5:TOPS-20:*:*)
+	echo pdp10-xkl-tops20
+	exit ;;
+    *:TOPS-20:*:*)
+	echo pdp10-unknown-tops20
+	exit ;;
+    *:ITS:*:*)
+	echo pdp10-unknown-its
+	exit ;;
+    SEI:*:*:SEIUX)
+	echo mips-sei-seiux${UNAME_RELEASE}
+	exit ;;
+    *:DragonFly:*:*)
+	echo ${UNAME_MACHINE}-unknown-dragonfly`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'`
+	exit ;;
+    *:*VMS:*:*)
+	UNAME_MACHINE=`(uname -p) 2>/dev/null`
+	case "${UNAME_MACHINE}" in
+	    A*) echo alpha-dec-vms ; exit ;;
+	    I*) echo ia64-dec-vms ; exit ;;
+	    V*) echo vax-dec-vms ; exit ;;
+	esac ;;
+    *:XENIX:*:SysV)
+	echo i386-pc-xenix
+	exit ;;
+    i*86:skyos:*:*)
+	echo ${UNAME_MACHINE}-pc-skyos`echo ${UNAME_RELEASE}` | sed -e 's/ .*$//'
+	exit ;;
+    i*86:rdos:*:*)
+	echo ${UNAME_MACHINE}-pc-rdos
+	exit ;;
+    i*86:AROS:*:*)
+	echo ${UNAME_MACHINE}-pc-aros
+	exit ;;
+    x86_64:VMkernel:*:*)
+	echo ${UNAME_MACHINE}-unknown-esx
+	exit ;;
+esac
+
+#echo '(No uname command or uname output not recognized.)' 1>&2
+#echo "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" 1>&2
+
+eval $set_cc_for_build
+cat >$dummy.c <<EOF
+#ifdef _SEQUENT_
+# include <sys/types.h>
+# include <sys/utsname.h>
+#endif
+main ()
+{
+#if defined (sony)
+#if defined (MIPSEB)
+  /* BFD wants "bsd" instead of "newsos".  Perhaps BFD should be changed,
+     I don't know....  */
+  printf ("mips-sony-bsd\n"); exit (0);
+#else
+#include <sys/param.h>
+  printf ("m68k-sony-newsos%s\n",
+#ifdef NEWSOS4
+	"4"
+#else
+	""
+#endif
+	); exit (0);
+#endif
+#endif
+
+#if defined (__arm) && defined (__acorn) && defined (__unix)
+  printf ("arm-acorn-riscix\n"); exit (0);
+#endif
+
+#if defined (hp300) && !defined (hpux)
+  printf ("m68k-hp-bsd\n"); exit (0);
+#endif
+
+#if defined (NeXT)
+#if !defined (__ARCHITECTURE__)
+#define __ARCHITECTURE__ "m68k"
+#endif
+  int version;
+  version=`(hostinfo | sed -n 's/.*NeXT Mach \([0-9]*\).*/\1/p') 2>/dev/null`;
+  if (version < 4)
+    printf ("%s-next-nextstep%d\n", __ARCHITECTURE__, version);
+  else
+    printf ("%s-next-openstep%d\n", __ARCHITECTURE__, version);
+  exit (0);
+#endif
+
+#if defined (MULTIMAX) || defined (n16)
+#if defined (UMAXV)
+  printf ("ns32k-encore-sysv\n"); exit (0);
+#else
+#if defined (CMU)
+  printf ("ns32k-encore-mach\n"); exit (0);
+#else
+  printf ("ns32k-encore-bsd\n"); exit (0);
+#endif
+#endif
+#endif
+
+#if defined (__386BSD__)
+  printf ("i386-pc-bsd\n"); exit (0);
+#endif
+
+#if defined (sequent)
+#if defined (i386)
+  printf ("i386-sequent-dynix\n"); exit (0);
+#endif
+#if defined (ns32000)
+  printf ("ns32k-sequent-dynix\n"); exit (0);
+#endif
+#endif
+
+#if defined (_SEQUENT_)
+    struct utsname un;
+
+    uname(&un);
+
+    if (strncmp(un.version, "V2", 2) == 0) {
+	printf ("i386-sequent-ptx2\n"); exit (0);
+    }
+    if (strncmp(un.version, "V1", 2) == 0) { /* XXX is V1 correct? */
+	printf ("i386-sequent-ptx1\n"); exit (0);
+    }
+    printf ("i386-sequent-ptx\n"); exit (0);
+
+#endif
+
+#if defined (vax)
+# if !defined (ultrix)
+#  include <sys/param.h>
+#  if defined (BSD)
+#   if BSD == 43
+      printf ("vax-dec-bsd4.3\n"); exit (0);
+#   else
+#    if BSD == 199006
+      printf ("vax-dec-bsd4.3reno\n"); exit (0);
+#    else
+      printf ("vax-dec-bsd\n"); exit (0);
+#    endif
+#   endif
+#  else
+    printf ("vax-dec-bsd\n"); exit (0);
+#  endif
+# else
+    printf ("vax-dec-ultrix\n"); exit (0);
+# endif
+#endif
+
+#if defined (alliant) && defined (i860)
+  printf ("i860-alliant-bsd\n"); exit (0);
+#endif
+
+  exit (1);
+}
+EOF
+
+$CC_FOR_BUILD -o $dummy $dummy.c 2>/dev/null && SYSTEM_NAME=`$dummy` &&
+	{ echo "$SYSTEM_NAME"; exit; }
+
+# Apollos put the system type in the environment.
+
+test -d /usr/apollo && { echo ${ISP}-apollo-${SYSTYPE}; exit; }
+
+# Convex versions that predate uname can use getsysinfo(1)
+
+if [ -x /usr/convex/getsysinfo ]
+then
+    case `getsysinfo -f cpu_type` in
+    c1*)
+	echo c1-convex-bsd
+	exit ;;
+    c2*)
+	if getsysinfo -f scalar_acc
+	then echo c32-convex-bsd
+	else echo c2-convex-bsd
+	fi
+	exit ;;
+    c34*)
+	echo c34-convex-bsd
+	exit ;;
+    c38*)
+	echo c38-convex-bsd
+	exit ;;
+    c4*)
+	echo c4-convex-bsd
+	exit ;;
+    esac
+fi
+
+cat >&2 <<EOF
+$0: unable to guess system type
+
+This script, last modified $timestamp, has failed to recognize
+the operating system you are using. It is advised that you
+download the most up to date version of the config scripts from
+
+  http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.guess;hb=HEAD
+and
+  http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.sub;hb=HEAD
+
+If the version you run ($0) is already up to date, please
+send the following data and any information you think might be
+pertinent to <config-patches@gnu.org> in order to provide the needed
+information to handle your system.
+
+config.guess timestamp = $timestamp
+
+uname -m = `(uname -m) 2>/dev/null || echo unknown`
+uname -r = `(uname -r) 2>/dev/null || echo unknown`
+uname -s = `(uname -s) 2>/dev/null || echo unknown`
+uname -v = `(uname -v) 2>/dev/null || echo unknown`
+
+/usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null`
+/bin/uname -X     = `(/bin/uname -X) 2>/dev/null`
+
+hostinfo               = `(hostinfo) 2>/dev/null`
+/bin/universe          = `(/bin/universe) 2>/dev/null`
+/usr/bin/arch -k       = `(/usr/bin/arch -k) 2>/dev/null`
+/bin/arch              = `(/bin/arch) 2>/dev/null`
+/usr/bin/oslevel       = `(/usr/bin/oslevel) 2>/dev/null`
+/usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null`
+
+UNAME_MACHINE = ${UNAME_MACHINE}
+UNAME_RELEASE = ${UNAME_RELEASE}
+UNAME_SYSTEM  = ${UNAME_SYSTEM}
+UNAME_VERSION = ${UNAME_VERSION}
+EOF
+
+exit 1
+
+# Local variables:
+# eval: (add-hook 'write-file-hooks 'time-stamp)
+# time-stamp-start: "timestamp='"
+# time-stamp-format: "%:y-%02m-%02d"
+# time-stamp-end: "'"
+# End:
diff --git a/bluez/config.h.in b/bluez/config.h.in
new file mode 100644
index 0000000..3a68c09
--- /dev/null
+++ b/bluez/config.h.in
@@ -0,0 +1,129 @@
+/* config.h.in.  Generated from configure.ac by autoheader.  */
+
+/* Directory for the Android daemon storage files */
+#undef ANDROID_STORAGEDIR
+
+/* Directory for the configuration files */
+#undef CONFIGDIR
+
+/* Define to 1 if you have the <dlfcn.h> header file. */
+#undef HAVE_DLFCN_H
+
+/* Define to 1 if you have the <inttypes.h> header file. */
+#undef HAVE_INTTYPES_H
+
+/* Define to 1 if you have the <memory.h> header file. */
+#undef HAVE_MEMORY_H
+
+/* Define to 1 if you have the <readline/readline.h> header file. */
+#undef HAVE_READLINE_READLINE_H
+
+/* Define to 1 if you have the <stdint.h> header file. */
+#undef HAVE_STDINT_H
+
+/* Define to 1 if you have the <stdlib.h> header file. */
+#undef HAVE_STDLIB_H
+
+/* Define to 1 if you have the <strings.h> header file. */
+#undef HAVE_STRINGS_H
+
+/* Define to 1 if you have the <string.h> header file. */
+#undef HAVE_STRING_H
+
+/* Define to 1 if you have the <sys/stat.h> header file. */
+#undef HAVE_SYS_STAT_H
+
+/* Define to 1 if you have the <sys/types.h> header file. */
+#undef HAVE_SYS_TYPES_H
+
+/* Define to 1 if you have the udev_hwdb_new() function. */
+#undef HAVE_UDEV_HWDB_NEW
+
+/* Define to 1 if you have the <unistd.h> header file. */
+#undef HAVE_UNISTD_H
+
+/* Define to the sub-directory in which libtool stores uninstalled libraries.
+   */
+#undef LT_OBJDIR
+
+/* Define if threading support is required */
+#undef NEED_THREADS
+
+/* Define to 1 if your C compiler doesn't accept -c and -o together. */
+#undef NO_MINUS_C_MINUS_O
+
+/* Name of package */
+#undef PACKAGE
+
+/* Define to the address where bug reports for this package should be sent. */
+#undef PACKAGE_BUGREPORT
+
+/* Define to the full name of this package. */
+#undef PACKAGE_NAME
+
+/* Define to the full name and version of this package. */
+#undef PACKAGE_STRING
+
+/* Define to the one symbol short name of this package. */
+#undef PACKAGE_TARNAME
+
+/* Define to the home page for this package. */
+#undef PACKAGE_URL
+
+/* Define to the version of this package. */
+#undef PACKAGE_VERSION
+
+/* Define to 1 if you have the ANSI C header files. */
+#undef STDC_HEADERS
+
+/* Directory for the storage files */
+#undef STORAGEDIR
+
+/* Enable extensions on AIX 3, Interix.  */
+#ifndef _ALL_SOURCE
+# undef _ALL_SOURCE
+#endif
+/* Enable GNU extensions on systems that have them.  */
+#ifndef _GNU_SOURCE
+# undef _GNU_SOURCE
+#endif
+/* Enable threading extensions on Solaris.  */
+#ifndef _POSIX_PTHREAD_SEMANTICS
+# undef _POSIX_PTHREAD_SEMANTICS
+#endif
+/* Enable extensions on HP NonStop.  */
+#ifndef _TANDEM_SOURCE
+# undef _TANDEM_SOURCE
+#endif
+/* Enable general extensions on Solaris.  */
+#ifndef __EXTENSIONS__
+# undef __EXTENSIONS__
+#endif
+
+
+/* Version number of package */
+#undef VERSION
+
+/* Define to 1 if on MINIX. */
+#undef _MINIX
+
+/* Define to 2 if the system does not provide POSIX.1 features except with
+   this defined. */
+#undef _POSIX_1_SOURCE
+
+/* Define to 1 if you need to in order for `stat' and other things to work. */
+#undef _POSIX_SOURCE
+
+/* Define to the equivalent of the C99 'restrict' keyword, or to
+   nothing if this is not supported.  Do not define if restrict is
+   supported directly.  */
+#undef restrict
+/* Work around a bug in Sun C++: it does not support _Restrict or
+   __restrict__, even though the corresponding Sun C compiler ends up with
+   "#define restrict _Restrict" or "#define restrict __restrict__" in the
+   previous line.  Perhaps some future version of Sun C++ will work with
+   restrict; if so, hopefully it defines __RESTRICT like Sun C does.  */
+#if defined __SUNPRO_CC && !defined __RESTRICT
+# define _Restrict
+# define __restrict__
+#endif
diff --git a/bluez/config.sub b/bluez/config.sub
new file mode 100755
index 0000000..6205f84
--- /dev/null
+++ b/bluez/config.sub
@@ -0,0 +1,1782 @@
+#! /bin/sh
+# Configuration validation subroutine script.
+#   Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999,
+#   2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010,
+#   2011, 2012 Free Software Foundation, Inc.
+
+timestamp='2012-04-18'
+
+# This file is (in principle) common to ALL GNU software.
+# The presence of a machine in this file suggests that SOME GNU software
+# can handle that machine.  It does not imply ALL GNU software can.
+#
+# This file is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, see <http://www.gnu.org/licenses/>.
+#
+# As a special exception to the GNU General Public License, if you
+# distribute this file as part of a program that contains a
+# configuration script generated by Autoconf, you may include it under
+# the same distribution terms that you use for the rest of that program.
+
+
+# Please send patches to <config-patches@gnu.org>.  Submit a context
+# diff and a properly formatted GNU ChangeLog entry.
+#
+# Configuration subroutine to validate and canonicalize a configuration type.
+# Supply the specified configuration type as an argument.
+# If it is invalid, we print an error message on stderr and exit with code 1.
+# Otherwise, we print the canonical config type on stdout and succeed.
+
+# You can get the latest version of this script from:
+# http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.sub;hb=HEAD
+
+# This file is supposed to be the same for all GNU packages
+# and recognize all the CPU types, system types and aliases
+# that are meaningful with *any* GNU software.
+# Each package is responsible for reporting which valid configurations
+# it does not support.  The user should be able to distinguish
+# a failure to support a valid configuration from a meaningless
+# configuration.
+
+# The goal of this file is to map all the various variations of a given
+# machine specification into a single specification in the form:
+#	CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM
+# or in some cases, the newer four-part form:
+#	CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM
+# It is wrong to echo any other type of specification.
+
+me=`echo "$0" | sed -e 's,.*/,,'`
+
+usage="\
+Usage: $0 [OPTION] CPU-MFR-OPSYS
+       $0 [OPTION] ALIAS
+
+Canonicalize a configuration name.
+
+Operation modes:
+  -h, --help         print this help, then exit
+  -t, --time-stamp   print date of last modification, then exit
+  -v, --version      print version number, then exit
+
+Report bugs and patches to <config-patches@gnu.org>."
+
+version="\
+GNU config.sub ($timestamp)
+
+Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000,
+2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012
+Free Software Foundation, Inc.
+
+This is free software; see the source for copying conditions.  There is NO
+warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE."
+
+help="
+Try \`$me --help' for more information."
+
+# Parse command line
+while test $# -gt 0 ; do
+  case $1 in
+    --time-stamp | --time* | -t )
+       echo "$timestamp" ; exit ;;
+    --version | -v )
+       echo "$version" ; exit ;;
+    --help | --h* | -h )
+       echo "$usage"; exit ;;
+    -- )     # Stop option processing
+       shift; break ;;
+    - )	# Use stdin as input.
+       break ;;
+    -* )
+       echo "$me: invalid option $1$help"
+       exit 1 ;;
+
+    *local*)
+       # First pass through any local machine types.
+       echo $1
+       exit ;;
+
+    * )
+       break ;;
+  esac
+done
+
+case $# in
+ 0) echo "$me: missing argument$help" >&2
+    exit 1;;
+ 1) ;;
+ *) echo "$me: too many arguments$help" >&2
+    exit 1;;
+esac
+
+# Separate what the user gave into CPU-COMPANY and OS or KERNEL-OS (if any).
+# Here we must recognize all the valid KERNEL-OS combinations.
+maybe_os=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\2/'`
+case $maybe_os in
+  nto-qnx* | linux-gnu* | linux-android* | linux-dietlibc | linux-newlib* | \
+  linux-uclibc* | uclinux-uclibc* | uclinux-gnu* | kfreebsd*-gnu* | \
+  knetbsd*-gnu* | netbsd*-gnu* | \
+  kopensolaris*-gnu* | \
+  storm-chaos* | os2-emx* | rtmk-nova*)
+    os=-$maybe_os
+    basic_machine=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\1/'`
+    ;;
+  android-linux)
+    os=-linux-android
+    basic_machine=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\1/'`-unknown
+    ;;
+  *)
+    basic_machine=`echo $1 | sed 's/-[^-]*$//'`
+    if [ $basic_machine != $1 ]
+    then os=`echo $1 | sed 's/.*-/-/'`
+    else os=; fi
+    ;;
+esac
+
+### Let's recognize common machines as not being operating systems so
+### that things like config.sub decstation-3100 work.  We also
+### recognize some manufacturers as not being operating systems, so we
+### can provide default operating systems below.
+case $os in
+	-sun*os*)
+		# Prevent following clause from handling this invalid input.
+		;;
+	-dec* | -mips* | -sequent* | -encore* | -pc532* | -sgi* | -sony* | \
+	-att* | -7300* | -3300* | -delta* | -motorola* | -sun[234]* | \
+	-unicom* | -ibm* | -next | -hp | -isi* | -apollo | -altos* | \
+	-convergent* | -ncr* | -news | -32* | -3600* | -3100* | -hitachi* |\
+	-c[123]* | -convex* | -sun | -crds | -omron* | -dg | -ultra | -tti* | \
+	-harris | -dolphin | -highlevel | -gould | -cbm | -ns | -masscomp | \
+	-apple | -axis | -knuth | -cray | -microblaze)
+		os=
+		basic_machine=$1
+		;;
+	-bluegene*)
+		os=-cnk
+		;;
+	-sim | -cisco | -oki | -wec | -winbond)
+		os=
+		basic_machine=$1
+		;;
+	-scout)
+		;;
+	-wrs)
+		os=-vxworks
+		basic_machine=$1
+		;;
+	-chorusos*)
+		os=-chorusos
+		basic_machine=$1
+		;;
+	-chorusrdb)
+		os=-chorusrdb
+		basic_machine=$1
+		;;
+	-hiux*)
+		os=-hiuxwe2
+		;;
+	-sco6)
+		os=-sco5v6
+		basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+		;;
+	-sco5)
+		os=-sco3.2v5
+		basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+		;;
+	-sco4)
+		os=-sco3.2v4
+		basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+		;;
+	-sco3.2.[4-9]*)
+		os=`echo $os | sed -e 's/sco3.2./sco3.2v/'`
+		basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+		;;
+	-sco3.2v[4-9]*)
+		# Don't forget version if it is 3.2v4 or newer.
+		basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+		;;
+	-sco5v6*)
+		# Don't forget version if it is 3.2v4 or newer.
+		basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+		;;
+	-sco*)
+		os=-sco3.2v2
+		basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+		;;
+	-udk*)
+		basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+		;;
+	-isc)
+		os=-isc2.2
+		basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+		;;
+	-clix*)
+		basic_machine=clipper-intergraph
+		;;
+	-isc*)
+		basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+		;;
+	-lynx*178)
+		os=-lynxos178
+		;;
+	-lynx*5)
+		os=-lynxos5
+		;;
+	-lynx*)
+		os=-lynxos
+		;;
+	-ptx*)
+		basic_machine=`echo $1 | sed -e 's/86-.*/86-sequent/'`
+		;;
+	-windowsnt*)
+		os=`echo $os | sed -e 's/windowsnt/winnt/'`
+		;;
+	-psos*)
+		os=-psos
+		;;
+	-mint | -mint[0-9]*)
+		basic_machine=m68k-atari
+		os=-mint
+		;;
+esac
+
+# Decode aliases for certain CPU-COMPANY combinations.
+case $basic_machine in
+	# Recognize the basic CPU types without company name.
+	# Some are omitted here because they have special meanings below.
+	1750a | 580 \
+	| a29k \
+	| aarch64 | aarch64_be \
+	| alpha | alphaev[4-8] | alphaev56 | alphaev6[78] | alphapca5[67] \
+	| alpha64 | alpha64ev[4-8] | alpha64ev56 | alpha64ev6[78] | alpha64pca5[67] \
+	| am33_2.0 \
+	| arc | arm | arm[bl]e | arme[lb] | armv[2345] | armv[345][lb] | avr | avr32 \
+        | be32 | be64 \
+	| bfin \
+	| c4x | clipper \
+	| d10v | d30v | dlx | dsp16xx \
+	| epiphany \
+	| fido | fr30 | frv \
+	| h8300 | h8500 | hppa | hppa1.[01] | hppa2.0 | hppa2.0[nw] | hppa64 \
+	| hexagon \
+	| i370 | i860 | i960 | ia64 \
+	| ip2k | iq2000 \
+	| le32 | le64 \
+	| lm32 \
+	| m32c | m32r | m32rle | m68000 | m68k | m88k \
+	| maxq | mb | microblaze | mcore | mep | metag \
+	| mips | mipsbe | mipseb | mipsel | mipsle \
+	| mips16 \
+	| mips64 | mips64el \
+	| mips64octeon | mips64octeonel \
+	| mips64orion | mips64orionel \
+	| mips64r5900 | mips64r5900el \
+	| mips64vr | mips64vrel \
+	| mips64vr4100 | mips64vr4100el \
+	| mips64vr4300 | mips64vr4300el \
+	| mips64vr5000 | mips64vr5000el \
+	| mips64vr5900 | mips64vr5900el \
+	| mipsisa32 | mipsisa32el \
+	| mipsisa32r2 | mipsisa32r2el \
+	| mipsisa64 | mipsisa64el \
+	| mipsisa64r2 | mipsisa64r2el \
+	| mipsisa64sb1 | mipsisa64sb1el \
+	| mipsisa64sr71k | mipsisa64sr71kel \
+	| mipstx39 | mipstx39el \
+	| mn10200 | mn10300 \
+	| moxie \
+	| mt \
+	| msp430 \
+	| nds32 | nds32le | nds32be \
+	| nios | nios2 \
+	| ns16k | ns32k \
+	| open8 \
+	| or32 \
+	| pdp10 | pdp11 | pj | pjl \
+	| powerpc | powerpc64 | powerpc64le | powerpcle \
+	| pyramid \
+	| rl78 | rx \
+	| score \
+	| sh | sh[1234] | sh[24]a | sh[24]aeb | sh[23]e | sh[34]eb | sheb | shbe | shle | sh[1234]le | sh3ele \
+	| sh64 | sh64le \
+	| sparc | sparc64 | sparc64b | sparc64v | sparc86x | sparclet | sparclite \
+	| sparcv8 | sparcv9 | sparcv9b | sparcv9v \
+	| spu \
+	| tahoe | tic4x | tic54x | tic55x | tic6x | tic80 | tron \
+	| ubicom32 \
+	| v850 | v850e | v850e1 | v850e2 | v850es | v850e2v3 \
+	| we32k \
+	| x86 | xc16x | xstormy16 | xtensa \
+	| z8k | z80)
+		basic_machine=$basic_machine-unknown
+		;;
+	c54x)
+		basic_machine=tic54x-unknown
+		;;
+	c55x)
+		basic_machine=tic55x-unknown
+		;;
+	c6x)
+		basic_machine=tic6x-unknown
+		;;
+	m6811 | m68hc11 | m6812 | m68hc12 | m68hcs12x | picochip)
+		basic_machine=$basic_machine-unknown
+		os=-none
+		;;
+	m88110 | m680[12346]0 | m683?2 | m68360 | m5200 | v70 | w65 | z8k)
+		;;
+	ms1)
+		basic_machine=mt-unknown
+		;;
+
+	strongarm | thumb | xscale)
+		basic_machine=arm-unknown
+		;;
+	xgate)
+		basic_machine=$basic_machine-unknown
+		os=-none
+		;;
+	xscaleeb)
+		basic_machine=armeb-unknown
+		;;
+
+	xscaleel)
+		basic_machine=armel-unknown
+		;;
+
+	# We use `pc' rather than `unknown'
+	# because (1) that's what they normally are, and
+	# (2) the word "unknown" tends to confuse beginning users.
+	i*86 | x86_64)
+	  basic_machine=$basic_machine-pc
+	  ;;
+	# Object if more than one company name word.
+	*-*-*)
+		echo Invalid configuration \`$1\': machine \`$basic_machine\' not recognized 1>&2
+		exit 1
+		;;
+	# Recognize the basic CPU types with company name.
+	580-* \
+	| a29k-* \
+	| aarch64-* | aarch64_be-* \
+	| alpha-* | alphaev[4-8]-* | alphaev56-* | alphaev6[78]-* \
+	| alpha64-* | alpha64ev[4-8]-* | alpha64ev56-* | alpha64ev6[78]-* \
+	| alphapca5[67]-* | alpha64pca5[67]-* | arc-* \
+	| arm-*  | armbe-* | armle-* | armeb-* | armv*-* \
+	| avr-* | avr32-* \
+	| be32-* | be64-* \
+	| bfin-* | bs2000-* \
+	| c[123]* | c30-* | [cjt]90-* | c4x-* \
+	| clipper-* | craynv-* | cydra-* \
+	| d10v-* | d30v-* | dlx-* \
+	| elxsi-* \
+	| f30[01]-* | f700-* | fido-* | fr30-* | frv-* | fx80-* \
+	| h8300-* | h8500-* \
+	| hppa-* | hppa1.[01]-* | hppa2.0-* | hppa2.0[nw]-* | hppa64-* \
+	| hexagon-* \
+	| i*86-* | i860-* | i960-* | ia64-* \
+	| ip2k-* | iq2000-* \
+	| le32-* | le64-* \
+	| lm32-* \
+	| m32c-* | m32r-* | m32rle-* \
+	| m68000-* | m680[012346]0-* | m68360-* | m683?2-* | m68k-* \
+	| m88110-* | m88k-* | maxq-* | mcore-* | metag-* | microblaze-* \
+	| mips-* | mipsbe-* | mipseb-* | mipsel-* | mipsle-* \
+	| mips16-* \
+	| mips64-* | mips64el-* \
+	| mips64octeon-* | mips64octeonel-* \
+	| mips64orion-* | mips64orionel-* \
+	| mips64r5900-* | mips64r5900el-* \
+	| mips64vr-* | mips64vrel-* \
+	| mips64vr4100-* | mips64vr4100el-* \
+	| mips64vr4300-* | mips64vr4300el-* \
+	| mips64vr5000-* | mips64vr5000el-* \
+	| mips64vr5900-* | mips64vr5900el-* \
+	| mipsisa32-* | mipsisa32el-* \
+	| mipsisa32r2-* | mipsisa32r2el-* \
+	| mipsisa64-* | mipsisa64el-* \
+	| mipsisa64r2-* | mipsisa64r2el-* \
+	| mipsisa64sb1-* | mipsisa64sb1el-* \
+	| mipsisa64sr71k-* | mipsisa64sr71kel-* \
+	| mipstx39-* | mipstx39el-* \
+	| mmix-* \
+	| mt-* \
+	| msp430-* \
+	| nds32-* | nds32le-* | nds32be-* \
+	| nios-* | nios2-* \
+	| none-* | np1-* | ns16k-* | ns32k-* \
+	| open8-* \
+	| orion-* \
+	| pdp10-* | pdp11-* | pj-* | pjl-* | pn-* | power-* \
+	| powerpc-* | powerpc64-* | powerpc64le-* | powerpcle-* \
+	| pyramid-* \
+	| rl78-* | romp-* | rs6000-* | rx-* \
+	| sh-* | sh[1234]-* | sh[24]a-* | sh[24]aeb-* | sh[23]e-* | sh[34]eb-* | sheb-* | shbe-* \
+	| shle-* | sh[1234]le-* | sh3ele-* | sh64-* | sh64le-* \
+	| sparc-* | sparc64-* | sparc64b-* | sparc64v-* | sparc86x-* | sparclet-* \
+	| sparclite-* \
+	| sparcv8-* | sparcv9-* | sparcv9b-* | sparcv9v-* | sv1-* | sx?-* \
+	| tahoe-* \
+	| tic30-* | tic4x-* | tic54x-* | tic55x-* | tic6x-* | tic80-* \
+	| tile*-* \
+	| tron-* \
+	| ubicom32-* \
+	| v850-* | v850e-* | v850e1-* | v850es-* | v850e2-* | v850e2v3-* \
+	| vax-* \
+	| we32k-* \
+	| x86-* | x86_64-* | xc16x-* | xps100-* \
+	| xstormy16-* | xtensa*-* \
+	| ymp-* \
+	| z8k-* | z80-*)
+		;;
+	# Recognize the basic CPU types without company name, with glob match.
+	xtensa*)
+		basic_machine=$basic_machine-unknown
+		;;
+	# Recognize the various machine names and aliases which stand
+	# for a CPU type and a company and sometimes even an OS.
+	386bsd)
+		basic_machine=i386-unknown
+		os=-bsd
+		;;
+	3b1 | 7300 | 7300-att | att-7300 | pc7300 | safari | unixpc)
+		basic_machine=m68000-att
+		;;
+	3b*)
+		basic_machine=we32k-att
+		;;
+	a29khif)
+		basic_machine=a29k-amd
+		os=-udi
+		;;
+	abacus)
+		basic_machine=abacus-unknown
+		;;
+	adobe68k)
+		basic_machine=m68010-adobe
+		os=-scout
+		;;
+	alliant | fx80)
+		basic_machine=fx80-alliant
+		;;
+	altos | altos3068)
+		basic_machine=m68k-altos
+		;;
+	am29k)
+		basic_machine=a29k-none
+		os=-bsd
+		;;
+	amd64)
+		basic_machine=x86_64-pc
+		;;
+	amd64-*)
+		basic_machine=x86_64-`echo $basic_machine | sed 's/^[^-]*-//'`
+		;;
+	amdahl)
+		basic_machine=580-amdahl
+		os=-sysv
+		;;
+	amiga | amiga-*)
+		basic_machine=m68k-unknown
+		;;
+	amigaos | amigados)
+		basic_machine=m68k-unknown
+		os=-amigaos
+		;;
+	amigaunix | amix)
+		basic_machine=m68k-unknown
+		os=-sysv4
+		;;
+	apollo68)
+		basic_machine=m68k-apollo
+		os=-sysv
+		;;
+	apollo68bsd)
+		basic_machine=m68k-apollo
+		os=-bsd
+		;;
+	aros)
+		basic_machine=i386-pc
+		os=-aros
+		;;
+	aux)
+		basic_machine=m68k-apple
+		os=-aux
+		;;
+	balance)
+		basic_machine=ns32k-sequent
+		os=-dynix
+		;;
+	blackfin)
+		basic_machine=bfin-unknown
+		os=-linux
+		;;
+	blackfin-*)
+		basic_machine=bfin-`echo $basic_machine | sed 's/^[^-]*-//'`
+		os=-linux
+		;;
+	bluegene*)
+		basic_machine=powerpc-ibm
+		os=-cnk
+		;;
+	c54x-*)
+		basic_machine=tic54x-`echo $basic_machine | sed 's/^[^-]*-//'`
+		;;
+	c55x-*)
+		basic_machine=tic55x-`echo $basic_machine | sed 's/^[^-]*-//'`
+		;;
+	c6x-*)
+		basic_machine=tic6x-`echo $basic_machine | sed 's/^[^-]*-//'`
+		;;
+	c90)
+		basic_machine=c90-cray
+		os=-unicos
+		;;
+	cegcc)
+		basic_machine=arm-unknown
+		os=-cegcc
+		;;
+	convex-c1)
+		basic_machine=c1-convex
+		os=-bsd
+		;;
+	convex-c2)
+		basic_machine=c2-convex
+		os=-bsd
+		;;
+	convex-c32)
+		basic_machine=c32-convex
+		os=-bsd
+		;;
+	convex-c34)
+		basic_machine=c34-convex
+		os=-bsd
+		;;
+	convex-c38)
+		basic_machine=c38-convex
+		os=-bsd
+		;;
+	cray | j90)
+		basic_machine=j90-cray
+		os=-unicos
+		;;
+	craynv)
+		basic_machine=craynv-cray
+		os=-unicosmp
+		;;
+	cr16 | cr16-*)
+		basic_machine=cr16-unknown
+		os=-elf
+		;;
+	crds | unos)
+		basic_machine=m68k-crds
+		;;
+	crisv32 | crisv32-* | etraxfs*)
+		basic_machine=crisv32-axis
+		;;
+	cris | cris-* | etrax*)
+		basic_machine=cris-axis
+		;;
+	crx)
+		basic_machine=crx-unknown
+		os=-elf
+		;;
+	da30 | da30-*)
+		basic_machine=m68k-da30
+		;;
+	decstation | decstation-3100 | pmax | pmax-* | pmin | dec3100 | decstatn)
+		basic_machine=mips-dec
+		;;
+	decsystem10* | dec10*)
+		basic_machine=pdp10-dec
+		os=-tops10
+		;;
+	decsystem20* | dec20*)
+		basic_machine=pdp10-dec
+		os=-tops20
+		;;
+	delta | 3300 | motorola-3300 | motorola-delta \
+	      | 3300-motorola | delta-motorola)
+		basic_machine=m68k-motorola
+		;;
+	delta88)
+		basic_machine=m88k-motorola
+		os=-sysv3
+		;;
+	dicos)
+		basic_machine=i686-pc
+		os=-dicos
+		;;
+	djgpp)
+		basic_machine=i586-pc
+		os=-msdosdjgpp
+		;;
+	dpx20 | dpx20-*)
+		basic_machine=rs6000-bull
+		os=-bosx
+		;;
+	dpx2* | dpx2*-bull)
+		basic_machine=m68k-bull
+		os=-sysv3
+		;;
+	ebmon29k)
+		basic_machine=a29k-amd
+		os=-ebmon
+		;;
+	elxsi)
+		basic_machine=elxsi-elxsi
+		os=-bsd
+		;;
+	encore | umax | mmax)
+		basic_machine=ns32k-encore
+		;;
+	es1800 | OSE68k | ose68k | ose | OSE)
+		basic_machine=m68k-ericsson
+		os=-ose
+		;;
+	fx2800)
+		basic_machine=i860-alliant
+		;;
+	genix)
+		basic_machine=ns32k-ns
+		;;
+	gmicro)
+		basic_machine=tron-gmicro
+		os=-sysv
+		;;
+	go32)
+		basic_machine=i386-pc
+		os=-go32
+		;;
+	h3050r* | hiux*)
+		basic_machine=hppa1.1-hitachi
+		os=-hiuxwe2
+		;;
+	h8300hms)
+		basic_machine=h8300-hitachi
+		os=-hms
+		;;
+	h8300xray)
+		basic_machine=h8300-hitachi
+		os=-xray
+		;;
+	h8500hms)
+		basic_machine=h8500-hitachi
+		os=-hms
+		;;
+	harris)
+		basic_machine=m88k-harris
+		os=-sysv3
+		;;
+	hp300-*)
+		basic_machine=m68k-hp
+		;;
+	hp300bsd)
+		basic_machine=m68k-hp
+		os=-bsd
+		;;
+	hp300hpux)
+		basic_machine=m68k-hp
+		os=-hpux
+		;;
+	hp3k9[0-9][0-9] | hp9[0-9][0-9])
+		basic_machine=hppa1.0-hp
+		;;
+	hp9k2[0-9][0-9] | hp9k31[0-9])
+		basic_machine=m68000-hp
+		;;
+	hp9k3[2-9][0-9])
+		basic_machine=m68k-hp
+		;;
+	hp9k6[0-9][0-9] | hp6[0-9][0-9])
+		basic_machine=hppa1.0-hp
+		;;
+	hp9k7[0-79][0-9] | hp7[0-79][0-9])
+		basic_machine=hppa1.1-hp
+		;;
+	hp9k78[0-9] | hp78[0-9])
+		# FIXME: really hppa2.0-hp
+		basic_machine=hppa1.1-hp
+		;;
+	hp9k8[67]1 | hp8[67]1 | hp9k80[24] | hp80[24] | hp9k8[78]9 | hp8[78]9 | hp9k893 | hp893)
+		# FIXME: really hppa2.0-hp
+		basic_machine=hppa1.1-hp
+		;;
+	hp9k8[0-9][13679] | hp8[0-9][13679])
+		basic_machine=hppa1.1-hp
+		;;
+	hp9k8[0-9][0-9] | hp8[0-9][0-9])
+		basic_machine=hppa1.0-hp
+		;;
+	hppa-next)
+		os=-nextstep3
+		;;
+	hppaosf)
+		basic_machine=hppa1.1-hp
+		os=-osf
+		;;
+	hppro)
+		basic_machine=hppa1.1-hp
+		os=-proelf
+		;;
+	i370-ibm* | ibm*)
+		basic_machine=i370-ibm
+		;;
+	i*86v32)
+		basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'`
+		os=-sysv32
+		;;
+	i*86v4*)
+		basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'`
+		os=-sysv4
+		;;
+	i*86v)
+		basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'`
+		os=-sysv
+		;;
+	i*86sol2)
+		basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'`
+		os=-solaris2
+		;;
+	i386mach)
+		basic_machine=i386-mach
+		os=-mach
+		;;
+	i386-vsta | vsta)
+		basic_machine=i386-unknown
+		os=-vsta
+		;;
+	iris | iris4d)
+		basic_machine=mips-sgi
+		case $os in
+		    -irix*)
+			;;
+		    *)
+			os=-irix4
+			;;
+		esac
+		;;
+	isi68 | isi)
+		basic_machine=m68k-isi
+		os=-sysv
+		;;
+	m68knommu)
+		basic_machine=m68k-unknown
+		os=-linux
+		;;
+	m68knommu-*)
+		basic_machine=m68k-`echo $basic_machine | sed 's/^[^-]*-//'`
+		os=-linux
+		;;
+	m88k-omron*)
+		basic_machine=m88k-omron
+		;;
+	magnum | m3230)
+		basic_machine=mips-mips
+		os=-sysv
+		;;
+	merlin)
+		basic_machine=ns32k-utek
+		os=-sysv
+		;;
+	microblaze)
+		basic_machine=microblaze-xilinx
+		;;
+	mingw32)
+		basic_machine=i386-pc
+		os=-mingw32
+		;;
+	mingw32ce)
+		basic_machine=arm-unknown
+		os=-mingw32ce
+		;;
+	miniframe)
+		basic_machine=m68000-convergent
+		;;
+	*mint | -mint[0-9]* | *MiNT | *MiNT[0-9]*)
+		basic_machine=m68k-atari
+		os=-mint
+		;;
+	mips3*-*)
+		basic_machine=`echo $basic_machine | sed -e 's/mips3/mips64/'`
+		;;
+	mips3*)
+		basic_machine=`echo $basic_machine | sed -e 's/mips3/mips64/'`-unknown
+		;;
+	monitor)
+		basic_machine=m68k-rom68k
+		os=-coff
+		;;
+	morphos)
+		basic_machine=powerpc-unknown
+		os=-morphos
+		;;
+	msdos)
+		basic_machine=i386-pc
+		os=-msdos
+		;;
+	ms1-*)
+		basic_machine=`echo $basic_machine | sed -e 's/ms1-/mt-/'`
+		;;
+	msys)
+		basic_machine=i386-pc
+		os=-msys
+		;;
+	mvs)
+		basic_machine=i370-ibm
+		os=-mvs
+		;;
+	nacl)
+		basic_machine=le32-unknown
+		os=-nacl
+		;;
+	ncr3000)
+		basic_machine=i486-ncr
+		os=-sysv4
+		;;
+	netbsd386)
+		basic_machine=i386-unknown
+		os=-netbsd
+		;;
+	netwinder)
+		basic_machine=armv4l-rebel
+		os=-linux
+		;;
+	news | news700 | news800 | news900)
+		basic_machine=m68k-sony
+		os=-newsos
+		;;
+	news1000)
+		basic_machine=m68030-sony
+		os=-newsos
+		;;
+	news-3600 | risc-news)
+		basic_machine=mips-sony
+		os=-newsos
+		;;
+	necv70)
+		basic_machine=v70-nec
+		os=-sysv
+		;;
+	next | m*-next )
+		basic_machine=m68k-next
+		case $os in
+		    -nextstep* )
+			;;
+		    -ns2*)
+		      os=-nextstep2
+			;;
+		    *)
+		      os=-nextstep3
+			;;
+		esac
+		;;
+	nh3000)
+		basic_machine=m68k-harris
+		os=-cxux
+		;;
+	nh[45]000)
+		basic_machine=m88k-harris
+		os=-cxux
+		;;
+	nindy960)
+		basic_machine=i960-intel
+		os=-nindy
+		;;
+	mon960)
+		basic_machine=i960-intel
+		os=-mon960
+		;;
+	nonstopux)
+		basic_machine=mips-compaq
+		os=-nonstopux
+		;;
+	np1)
+		basic_machine=np1-gould
+		;;
+	neo-tandem)
+		basic_machine=neo-tandem
+		;;
+	nse-tandem)
+		basic_machine=nse-tandem
+		;;
+	nsr-tandem)
+		basic_machine=nsr-tandem
+		;;
+	op50n-* | op60c-*)
+		basic_machine=hppa1.1-oki
+		os=-proelf
+		;;
+	openrisc | openrisc-*)
+		basic_machine=or32-unknown
+		;;
+	os400)
+		basic_machine=powerpc-ibm
+		os=-os400
+		;;
+	OSE68000 | ose68000)
+		basic_machine=m68000-ericsson
+		os=-ose
+		;;
+	os68k)
+		basic_machine=m68k-none
+		os=-os68k
+		;;
+	pa-hitachi)
+		basic_machine=hppa1.1-hitachi
+		os=-hiuxwe2
+		;;
+	paragon)
+		basic_machine=i860-intel
+		os=-osf
+		;;
+	parisc)
+		basic_machine=hppa-unknown
+		os=-linux
+		;;
+	parisc-*)
+		basic_machine=hppa-`echo $basic_machine | sed 's/^[^-]*-//'`
+		os=-linux
+		;;
+	pbd)
+		basic_machine=sparc-tti
+		;;
+	pbb)
+		basic_machine=m68k-tti
+		;;
+	pc532 | pc532-*)
+		basic_machine=ns32k-pc532
+		;;
+	pc98)
+		basic_machine=i386-pc
+		;;
+	pc98-*)
+		basic_machine=i386-`echo $basic_machine | sed 's/^[^-]*-//'`
+		;;
+	pentium | p5 | k5 | k6 | nexgen | viac3)
+		basic_machine=i586-pc
+		;;
+	pentiumpro | p6 | 6x86 | athlon | athlon_*)
+		basic_machine=i686-pc
+		;;
+	pentiumii | pentium2 | pentiumiii | pentium3)
+		basic_machine=i686-pc
+		;;
+	pentium4)
+		basic_machine=i786-pc
+		;;
+	pentium-* | p5-* | k5-* | k6-* | nexgen-* | viac3-*)
+		basic_machine=i586-`echo $basic_machine | sed 's/^[^-]*-//'`
+		;;
+	pentiumpro-* | p6-* | 6x86-* | athlon-*)
+		basic_machine=i686-`echo $basic_machine | sed 's/^[^-]*-//'`
+		;;
+	pentiumii-* | pentium2-* | pentiumiii-* | pentium3-*)
+		basic_machine=i686-`echo $basic_machine | sed 's/^[^-]*-//'`
+		;;
+	pentium4-*)
+		basic_machine=i786-`echo $basic_machine | sed 's/^[^-]*-//'`
+		;;
+	pn)
+		basic_machine=pn-gould
+		;;
+	power)	basic_machine=power-ibm
+		;;
+	ppc | ppcbe)	basic_machine=powerpc-unknown
+		;;
+	ppc-* | ppcbe-*)
+		basic_machine=powerpc-`echo $basic_machine | sed 's/^[^-]*-//'`
+		;;
+	ppcle | powerpclittle | ppc-le | powerpc-little)
+		basic_machine=powerpcle-unknown
+		;;
+	ppcle-* | powerpclittle-*)
+		basic_machine=powerpcle-`echo $basic_machine | sed 's/^[^-]*-//'`
+		;;
+	ppc64)	basic_machine=powerpc64-unknown
+		;;
+	ppc64-*) basic_machine=powerpc64-`echo $basic_machine | sed 's/^[^-]*-//'`
+		;;
+	ppc64le | powerpc64little | ppc64-le | powerpc64-little)
+		basic_machine=powerpc64le-unknown
+		;;
+	ppc64le-* | powerpc64little-*)
+		basic_machine=powerpc64le-`echo $basic_machine | sed 's/^[^-]*-//'`
+		;;
+	ps2)
+		basic_machine=i386-ibm
+		;;
+	pw32)
+		basic_machine=i586-unknown
+		os=-pw32
+		;;
+	rdos)
+		basic_machine=i386-pc
+		os=-rdos
+		;;
+	rom68k)
+		basic_machine=m68k-rom68k
+		os=-coff
+		;;
+	rm[46]00)
+		basic_machine=mips-siemens
+		;;
+	rtpc | rtpc-*)
+		basic_machine=romp-ibm
+		;;
+	s390 | s390-*)
+		basic_machine=s390-ibm
+		;;
+	s390x | s390x-*)
+		basic_machine=s390x-ibm
+		;;
+	sa29200)
+		basic_machine=a29k-amd
+		os=-udi
+		;;
+	sb1)
+		basic_machine=mipsisa64sb1-unknown
+		;;
+	sb1el)
+		basic_machine=mipsisa64sb1el-unknown
+		;;
+	sde)
+		basic_machine=mipsisa32-sde
+		os=-elf
+		;;
+	sei)
+		basic_machine=mips-sei
+		os=-seiux
+		;;
+	sequent)
+		basic_machine=i386-sequent
+		;;
+	sh)
+		basic_machine=sh-hitachi
+		os=-hms
+		;;
+	sh5el)
+		basic_machine=sh5le-unknown
+		;;
+	sh64)
+		basic_machine=sh64-unknown
+		;;
+	sparclite-wrs | simso-wrs)
+		basic_machine=sparclite-wrs
+		os=-vxworks
+		;;
+	sps7)
+		basic_machine=m68k-bull
+		os=-sysv2
+		;;
+	spur)
+		basic_machine=spur-unknown
+		;;
+	st2000)
+		basic_machine=m68k-tandem
+		;;
+	stratus)
+		basic_machine=i860-stratus
+		os=-sysv4
+		;;
+	strongarm-* | thumb-*)
+		basic_machine=arm-`echo $basic_machine | sed 's/^[^-]*-//'`
+		;;
+	sun2)
+		basic_machine=m68000-sun
+		;;
+	sun2os3)
+		basic_machine=m68000-sun
+		os=-sunos3
+		;;
+	sun2os4)
+		basic_machine=m68000-sun
+		os=-sunos4
+		;;
+	sun3os3)
+		basic_machine=m68k-sun
+		os=-sunos3
+		;;
+	sun3os4)
+		basic_machine=m68k-sun
+		os=-sunos4
+		;;
+	sun4os3)
+		basic_machine=sparc-sun
+		os=-sunos3
+		;;
+	sun4os4)
+		basic_machine=sparc-sun
+		os=-sunos4
+		;;
+	sun4sol2)
+		basic_machine=sparc-sun
+		os=-solaris2
+		;;
+	sun3 | sun3-*)
+		basic_machine=m68k-sun
+		;;
+	sun4)
+		basic_machine=sparc-sun
+		;;
+	sun386 | sun386i | roadrunner)
+		basic_machine=i386-sun
+		;;
+	sv1)
+		basic_machine=sv1-cray
+		os=-unicos
+		;;
+	symmetry)
+		basic_machine=i386-sequent
+		os=-dynix
+		;;
+	t3e)
+		basic_machine=alphaev5-cray
+		os=-unicos
+		;;
+	t90)
+		basic_machine=t90-cray
+		os=-unicos
+		;;
+	tile*)
+		basic_machine=$basic_machine-unknown
+		os=-linux-gnu
+		;;
+	tx39)
+		basic_machine=mipstx39-unknown
+		;;
+	tx39el)
+		basic_machine=mipstx39el-unknown
+		;;
+	toad1)
+		basic_machine=pdp10-xkl
+		os=-tops20
+		;;
+	tower | tower-32)
+		basic_machine=m68k-ncr
+		;;
+	tpf)
+		basic_machine=s390x-ibm
+		os=-tpf
+		;;
+	udi29k)
+		basic_machine=a29k-amd
+		os=-udi
+		;;
+	ultra3)
+		basic_machine=a29k-nyu
+		os=-sym1
+		;;
+	v810 | necv810)
+		basic_machine=v810-nec
+		os=-none
+		;;
+	vaxv)
+		basic_machine=vax-dec
+		os=-sysv
+		;;
+	vms)
+		basic_machine=vax-dec
+		os=-vms
+		;;
+	vpp*|vx|vx-*)
+		basic_machine=f301-fujitsu
+		;;
+	vxworks960)
+		basic_machine=i960-wrs
+		os=-vxworks
+		;;
+	vxworks68)
+		basic_machine=m68k-wrs
+		os=-vxworks
+		;;
+	vxworks29k)
+		basic_machine=a29k-wrs
+		os=-vxworks
+		;;
+	w65*)
+		basic_machine=w65-wdc
+		os=-none
+		;;
+	w89k-*)
+		basic_machine=hppa1.1-winbond
+		os=-proelf
+		;;
+	xbox)
+		basic_machine=i686-pc
+		os=-mingw32
+		;;
+	xps | xps100)
+		basic_machine=xps100-honeywell
+		;;
+	xscale-* | xscalee[bl]-*)
+		basic_machine=`echo $basic_machine | sed 's/^xscale/arm/'`
+		;;
+	ymp)
+		basic_machine=ymp-cray
+		os=-unicos
+		;;
+	z8k-*-coff)
+		basic_machine=z8k-unknown
+		os=-sim
+		;;
+	z80-*-coff)
+		basic_machine=z80-unknown
+		os=-sim
+		;;
+	none)
+		basic_machine=none-none
+		os=-none
+		;;
+
+# Here we handle the default manufacturer of certain CPU types.  It is in
+# some cases the only manufacturer, in others, it is the most popular.
+	w89k)
+		basic_machine=hppa1.1-winbond
+		;;
+	op50n)
+		basic_machine=hppa1.1-oki
+		;;
+	op60c)
+		basic_machine=hppa1.1-oki
+		;;
+	romp)
+		basic_machine=romp-ibm
+		;;
+	mmix)
+		basic_machine=mmix-knuth
+		;;
+	rs6000)
+		basic_machine=rs6000-ibm
+		;;
+	vax)
+		basic_machine=vax-dec
+		;;
+	pdp10)
+		# there are many clones, so DEC is not a safe bet
+		basic_machine=pdp10-unknown
+		;;
+	pdp11)
+		basic_machine=pdp11-dec
+		;;
+	we32k)
+		basic_machine=we32k-att
+		;;
+	sh[1234] | sh[24]a | sh[24]aeb | sh[34]eb | sh[1234]le | sh[23]ele)
+		basic_machine=sh-unknown
+		;;
+	sparc | sparcv8 | sparcv9 | sparcv9b | sparcv9v)
+		basic_machine=sparc-sun
+		;;
+	cydra)
+		basic_machine=cydra-cydrome
+		;;
+	orion)
+		basic_machine=orion-highlevel
+		;;
+	orion105)
+		basic_machine=clipper-highlevel
+		;;
+	mac | mpw | mac-mpw)
+		basic_machine=m68k-apple
+		;;
+	pmac | pmac-mpw)
+		basic_machine=powerpc-apple
+		;;
+	*-unknown)
+		# Make sure to match an already-canonicalized machine name.
+		;;
+	*)
+		echo Invalid configuration \`$1\': machine \`$basic_machine\' not recognized 1>&2
+		exit 1
+		;;
+esac
+
+# Here we canonicalize certain aliases for manufacturers.
+case $basic_machine in
+	*-digital*)
+		basic_machine=`echo $basic_machine | sed 's/digital.*/dec/'`
+		;;
+	*-commodore*)
+		basic_machine=`echo $basic_machine | sed 's/commodore.*/cbm/'`
+		;;
+	*)
+		;;
+esac
+
+# Decode manufacturer-specific aliases for certain operating systems.
+
+if [ x"$os" != x"" ]
+then
+case $os in
+	# First match some system type aliases
+	# that might get confused with valid system types.
+	# -solaris* is a basic system type, with this one exception.
+	-auroraux)
+		os=-auroraux
+		;;
+	-solaris1 | -solaris1.*)
+		os=`echo $os | sed -e 's|solaris1|sunos4|'`
+		;;
+	-solaris)
+		os=-solaris2
+		;;
+	-svr4*)
+		os=-sysv4
+		;;
+	-unixware*)
+		os=-sysv4.2uw
+		;;
+	-gnu/linux*)
+		os=`echo $os | sed -e 's|gnu/linux|linux-gnu|'`
+		;;
+	# First accept the basic system types.
+	# The portable systems comes first.
+	# Each alternative MUST END IN A *, to match a version number.
+	# -sysv* is not here because it comes later, after sysvr4.
+	-gnu* | -bsd* | -mach* | -minix* | -genix* | -ultrix* | -irix* \
+	      | -*vms* | -sco* | -esix* | -isc* | -aix* | -cnk* | -sunos | -sunos[34]*\
+	      | -hpux* | -unos* | -osf* | -luna* | -dgux* | -auroraux* | -solaris* \
+	      | -sym* | -kopensolaris* \
+	      | -amigaos* | -amigados* | -msdos* | -newsos* | -unicos* | -aof* \
+	      | -aos* | -aros* \
+	      | -nindy* | -vxsim* | -vxworks* | -ebmon* | -hms* | -mvs* \
+	      | -clix* | -riscos* | -uniplus* | -iris* | -rtu* | -xenix* \
+	      | -hiux* | -386bsd* | -knetbsd* | -mirbsd* | -netbsd* \
+	      | -openbsd* | -solidbsd* \
+	      | -ekkobsd* | -kfreebsd* | -freebsd* | -riscix* | -lynxos* \
+	      | -bosx* | -nextstep* | -cxux* | -aout* | -elf* | -oabi* \
+	      | -ptx* | -coff* | -ecoff* | -winnt* | -domain* | -vsta* \
+	      | -udi* | -eabi* | -lites* | -ieee* | -go32* | -aux* \
+	      | -chorusos* | -chorusrdb* | -cegcc* \
+	      | -cygwin* | -msys* | -pe* | -psos* | -moss* | -proelf* | -rtems* \
+	      | -mingw32* | -linux-gnu* | -linux-android* \
+	      | -linux-newlib* | -linux-uclibc* \
+	      | -uxpv* | -beos* | -mpeix* | -udk* \
+	      | -interix* | -uwin* | -mks* | -rhapsody* | -darwin* | -opened* \
+	      | -openstep* | -oskit* | -conix* | -pw32* | -nonstopux* \
+	      | -storm-chaos* | -tops10* | -tenex* | -tops20* | -its* \
+	      | -os2* | -vos* | -palmos* | -uclinux* | -nucleus* \
+	      | -morphos* | -superux* | -rtmk* | -rtmk-nova* | -windiss* \
+	      | -powermax* | -dnix* | -nx6 | -nx7 | -sei* | -dragonfly* \
+	      | -skyos* | -haiku* | -rdos* | -toppers* | -drops* | -es*)
+	# Remember, each alternative MUST END IN *, to match a version number.
+		;;
+	-qnx*)
+		case $basic_machine in
+		    x86-* | i*86-*)
+			;;
+		    *)
+			os=-nto$os
+			;;
+		esac
+		;;
+	-nto-qnx*)
+		;;
+	-nto*)
+		os=`echo $os | sed -e 's|nto|nto-qnx|'`
+		;;
+	-sim | -es1800* | -hms* | -xray | -os68k* | -none* | -v88r* \
+	      | -windows* | -osx | -abug | -netware* | -os9* | -beos* | -haiku* \
+	      | -macos* | -mpw* | -magic* | -mmixware* | -mon960* | -lnews*)
+		;;
+	-mac*)
+		os=`echo $os | sed -e 's|mac|macos|'`
+		;;
+	-linux-dietlibc)
+		os=-linux-dietlibc
+		;;
+	-linux*)
+		os=`echo $os | sed -e 's|linux|linux-gnu|'`
+		;;
+	-sunos5*)
+		os=`echo $os | sed -e 's|sunos5|solaris2|'`
+		;;
+	-sunos6*)
+		os=`echo $os | sed -e 's|sunos6|solaris3|'`
+		;;
+	-opened*)
+		os=-openedition
+		;;
+	-os400*)
+		os=-os400
+		;;
+	-wince*)
+		os=-wince
+		;;
+	-osfrose*)
+		os=-osfrose
+		;;
+	-osf*)
+		os=-osf
+		;;
+	-utek*)
+		os=-bsd
+		;;
+	-dynix*)
+		os=-bsd
+		;;
+	-acis*)
+		os=-aos
+		;;
+	-atheos*)
+		os=-atheos
+		;;
+	-syllable*)
+		os=-syllable
+		;;
+	-386bsd)
+		os=-bsd
+		;;
+	-ctix* | -uts*)
+		os=-sysv
+		;;
+	-nova*)
+		os=-rtmk-nova
+		;;
+	-ns2 )
+		os=-nextstep2
+		;;
+	-nsk*)
+		os=-nsk
+		;;
+	# Preserve the version number of sinix5.
+	-sinix5.*)
+		os=`echo $os | sed -e 's|sinix|sysv|'`
+		;;
+	-sinix*)
+		os=-sysv4
+		;;
+	-tpf*)
+		os=-tpf
+		;;
+	-triton*)
+		os=-sysv3
+		;;
+	-oss*)
+		os=-sysv3
+		;;
+	-svr4)
+		os=-sysv4
+		;;
+	-svr3)
+		os=-sysv3
+		;;
+	-sysvr4)
+		os=-sysv4
+		;;
+	# This must come after -sysvr4.
+	-sysv*)
+		;;
+	-ose*)
+		os=-ose
+		;;
+	-es1800*)
+		os=-ose
+		;;
+	-xenix)
+		os=-xenix
+		;;
+	-*mint | -mint[0-9]* | -*MiNT | -MiNT[0-9]*)
+		os=-mint
+		;;
+	-aros*)
+		os=-aros
+		;;
+	-kaos*)
+		os=-kaos
+		;;
+	-zvmoe)
+		os=-zvmoe
+		;;
+	-dicos*)
+		os=-dicos
+		;;
+	-nacl*)
+		;;
+	-none)
+		;;
+	*)
+		# Get rid of the `-' at the beginning of $os.
+		os=`echo $os | sed 's/[^-]*-//'`
+		echo Invalid configuration \`$1\': system \`$os\' not recognized 1>&2
+		exit 1
+		;;
+esac
+else
+
+# Here we handle the default operating systems that come with various machines.
+# The value should be what the vendor currently ships out the door with their
+# machine or put another way, the most popular os provided with the machine.
+
+# Note that if you're going to try to match "-MANUFACTURER" here (say,
+# "-sun"), then you have to tell the case statement up towards the top
+# that MANUFACTURER isn't an operating system.  Otherwise, code above
+# will signal an error saying that MANUFACTURER isn't an operating
+# system, and we'll never get to this point.
+
+case $basic_machine in
+	score-*)
+		os=-elf
+		;;
+	spu-*)
+		os=-elf
+		;;
+	*-acorn)
+		os=-riscix1.2
+		;;
+	arm*-rebel)
+		os=-linux
+		;;
+	arm*-semi)
+		os=-aout
+		;;
+	c4x-* | tic4x-*)
+		os=-coff
+		;;
+	hexagon-*)
+		os=-elf
+		;;
+	tic54x-*)
+		os=-coff
+		;;
+	tic55x-*)
+		os=-coff
+		;;
+	tic6x-*)
+		os=-coff
+		;;
+	# This must come before the *-dec entry.
+	pdp10-*)
+		os=-tops20
+		;;
+	pdp11-*)
+		os=-none
+		;;
+	*-dec | vax-*)
+		os=-ultrix4.2
+		;;
+	m68*-apollo)
+		os=-domain
+		;;
+	i386-sun)
+		os=-sunos4.0.2
+		;;
+	m68000-sun)
+		os=-sunos3
+		;;
+	m68*-cisco)
+		os=-aout
+		;;
+	mep-*)
+		os=-elf
+		;;
+	mips*-cisco)
+		os=-elf
+		;;
+	mips*-*)
+		os=-elf
+		;;
+	or32-*)
+		os=-coff
+		;;
+	*-tti)	# must be before sparc entry or we get the wrong os.
+		os=-sysv3
+		;;
+	sparc-* | *-sun)
+		os=-sunos4.1.1
+		;;
+	*-be)
+		os=-beos
+		;;
+	*-haiku)
+		os=-haiku
+		;;
+	*-ibm)
+		os=-aix
+		;;
+	*-knuth)
+		os=-mmixware
+		;;
+	*-wec)
+		os=-proelf
+		;;
+	*-winbond)
+		os=-proelf
+		;;
+	*-oki)
+		os=-proelf
+		;;
+	*-hp)
+		os=-hpux
+		;;
+	*-hitachi)
+		os=-hiux
+		;;
+	i860-* | *-att | *-ncr | *-altos | *-motorola | *-convergent)
+		os=-sysv
+		;;
+	*-cbm)
+		os=-amigaos
+		;;
+	*-dg)
+		os=-dgux
+		;;
+	*-dolphin)
+		os=-sysv3
+		;;
+	m68k-ccur)
+		os=-rtu
+		;;
+	m88k-omron*)
+		os=-luna
+		;;
+	*-next )
+		os=-nextstep
+		;;
+	*-sequent)
+		os=-ptx
+		;;
+	*-crds)
+		os=-unos
+		;;
+	*-ns)
+		os=-genix
+		;;
+	i370-*)
+		os=-mvs
+		;;
+	*-next)
+		os=-nextstep3
+		;;
+	*-gould)
+		os=-sysv
+		;;
+	*-highlevel)
+		os=-bsd
+		;;
+	*-encore)
+		os=-bsd
+		;;
+	*-sgi)
+		os=-irix
+		;;
+	*-siemens)
+		os=-sysv4
+		;;
+	*-masscomp)
+		os=-rtu
+		;;
+	f30[01]-fujitsu | f700-fujitsu)
+		os=-uxpv
+		;;
+	*-rom68k)
+		os=-coff
+		;;
+	*-*bug)
+		os=-coff
+		;;
+	*-apple)
+		os=-macos
+		;;
+	*-atari*)
+		os=-mint
+		;;
+	*)
+		os=-none
+		;;
+esac
+fi
+
+# Here we handle the case where we know the os, and the CPU type, but not the
+# manufacturer.  We pick the logical manufacturer.
+vendor=unknown
+case $basic_machine in
+	*-unknown)
+		case $os in
+			-riscix*)
+				vendor=acorn
+				;;
+			-sunos*)
+				vendor=sun
+				;;
+			-cnk*|-aix*)
+				vendor=ibm
+				;;
+			-beos*)
+				vendor=be
+				;;
+			-hpux*)
+				vendor=hp
+				;;
+			-mpeix*)
+				vendor=hp
+				;;
+			-hiux*)
+				vendor=hitachi
+				;;
+			-unos*)
+				vendor=crds
+				;;
+			-dgux*)
+				vendor=dg
+				;;
+			-luna*)
+				vendor=omron
+				;;
+			-genix*)
+				vendor=ns
+				;;
+			-mvs* | -opened*)
+				vendor=ibm
+				;;
+			-os400*)
+				vendor=ibm
+				;;
+			-ptx*)
+				vendor=sequent
+				;;
+			-tpf*)
+				vendor=ibm
+				;;
+			-vxsim* | -vxworks* | -windiss*)
+				vendor=wrs
+				;;
+			-aux*)
+				vendor=apple
+				;;
+			-hms*)
+				vendor=hitachi
+				;;
+			-mpw* | -macos*)
+				vendor=apple
+				;;
+			-*mint | -mint[0-9]* | -*MiNT | -MiNT[0-9]*)
+				vendor=atari
+				;;
+			-vos*)
+				vendor=stratus
+				;;
+		esac
+		basic_machine=`echo $basic_machine | sed "s/unknown/$vendor/"`
+		;;
+esac
+
+echo $basic_machine$os
+exit
+
+# Local variables:
+# eval: (add-hook 'write-file-hooks 'time-stamp)
+# time-stamp-start: "timestamp='"
+# time-stamp-format: "%:y-%02m-%02d"
+# time-stamp-end: "'"
+# End:
diff --git a/bluez/configure b/bluez/configure
new file mode 100755
index 0000000..f70bbed
--- /dev/null
+++ b/bluez/configure
@@ -0,0 +1,16187 @@
+#! /bin/sh
+# Guess values for system-dependent variables and create Makefiles.
+# Generated by GNU Autoconf 2.69 for bluez 5.17.
+#
+#
+# Copyright (C) 1992-1996, 1998-2012 Free Software Foundation, Inc.
+#
+#
+# This configure script is free software; the Free Software Foundation
+# gives unlimited permission to copy, distribute and modify it.
+## -------------------- ##
+## M4sh Initialization. ##
+## -------------------- ##
+
+# Be more Bourne compatible
+DUALCASE=1; export DUALCASE # for MKS sh
+if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then :
+  emulate sh
+  NULLCMD=:
+  # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which
+  # is contrary to our usage.  Disable this feature.
+  alias -g '${1+"$@"}'='"$@"'
+  setopt NO_GLOB_SUBST
+else
+  case `(set -o) 2>/dev/null` in #(
+  *posix*) :
+    set -o posix ;; #(
+  *) :
+     ;;
+esac
+fi
+
+
+as_nl='
+'
+export as_nl
+# Printing a long string crashes Solaris 7 /usr/bin/printf.
+as_echo='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\'
+as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo
+as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo$as_echo
+# Prefer a ksh shell builtin over an external printf program on Solaris,
+# but without wasting forks for bash or zsh.
+if test -z "$BASH_VERSION$ZSH_VERSION" \
+    && (test "X`print -r -- $as_echo`" = "X$as_echo") 2>/dev/null; then
+  as_echo='print -r --'
+  as_echo_n='print -rn --'
+elif (test "X`printf %s $as_echo`" = "X$as_echo") 2>/dev/null; then
+  as_echo='printf %s\n'
+  as_echo_n='printf %s'
+else
+  if test "X`(/usr/ucb/echo -n -n $as_echo) 2>/dev/null`" = "X-n $as_echo"; then
+    as_echo_body='eval /usr/ucb/echo -n "$1$as_nl"'
+    as_echo_n='/usr/ucb/echo -n'
+  else
+    as_echo_body='eval expr "X$1" : "X\\(.*\\)"'
+    as_echo_n_body='eval
+      arg=$1;
+      case $arg in #(
+      *"$as_nl"*)
+	expr "X$arg" : "X\\(.*\\)$as_nl";
+	arg=`expr "X$arg" : ".*$as_nl\\(.*\\)"`;;
+      esac;
+      expr "X$arg" : "X\\(.*\\)" | tr -d "$as_nl"
+    '
+    export as_echo_n_body
+    as_echo_n='sh -c $as_echo_n_body as_echo'
+  fi
+  export as_echo_body
+  as_echo='sh -c $as_echo_body as_echo'
+fi
+
+# The user is always right.
+if test "${PATH_SEPARATOR+set}" != set; then
+  PATH_SEPARATOR=:
+  (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && {
+    (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 ||
+      PATH_SEPARATOR=';'
+  }
+fi
+
+
+# IFS
+# We need space, tab and new line, in precisely that order.  Quoting is
+# there to prevent editors from complaining about space-tab.
+# (If _AS_PATH_WALK were called with IFS unset, it would disable word
+# splitting by setting IFS to empty value.)
+IFS=" ""	$as_nl"
+
+# Find who we are.  Look in the path if we contain no directory separator.
+as_myself=
+case $0 in #((
+  *[\\/]* ) as_myself=$0 ;;
+  *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break
+  done
+IFS=$as_save_IFS
+
+     ;;
+esac
+# We did not find ourselves, most probably we were run as `sh COMMAND'
+# in which case we are not to be found in the path.
+if test "x$as_myself" = x; then
+  as_myself=$0
+fi
+if test ! -f "$as_myself"; then
+  $as_echo "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2
+  exit 1
+fi
+
+# Unset variables that we do not need and which cause bugs (e.g. in
+# pre-3.0 UWIN ksh).  But do not cause bugs in bash 2.01; the "|| exit 1"
+# suppresses any "Segmentation fault" message there.  '((' could
+# trigger a bug in pdksh 5.2.14.
+for as_var in BASH_ENV ENV MAIL MAILPATH
+do eval test x\${$as_var+set} = xset \
+  && ( (unset $as_var) || exit 1) >/dev/null 2>&1 && unset $as_var || :
+done
+PS1='$ '
+PS2='> '
+PS4='+ '
+
+# NLS nuisances.
+LC_ALL=C
+export LC_ALL
+LANGUAGE=C
+export LANGUAGE
+
+# CDPATH.
+(unset CDPATH) >/dev/null 2>&1 && unset CDPATH
+
+# Use a proper internal environment variable to ensure we don't fall
+  # into an infinite loop, continuously re-executing ourselves.
+  if test x"${_as_can_reexec}" != xno && test "x$CONFIG_SHELL" != x; then
+    _as_can_reexec=no; export _as_can_reexec;
+    # We cannot yet assume a decent shell, so we have to provide a
+# neutralization value for shells without unset; and this also
+# works around shells that cannot unset nonexistent variables.
+# Preserve -v and -x to the replacement shell.
+BASH_ENV=/dev/null
+ENV=/dev/null
+(unset BASH_ENV) >/dev/null 2>&1 && unset BASH_ENV ENV
+case $- in # ((((
+  *v*x* | *x*v* ) as_opts=-vx ;;
+  *v* ) as_opts=-v ;;
+  *x* ) as_opts=-x ;;
+  * ) as_opts= ;;
+esac
+exec $CONFIG_SHELL $as_opts "$as_myself" ${1+"$@"}
+# Admittedly, this is quite paranoid, since all the known shells bail
+# out after a failed `exec'.
+$as_echo "$0: could not re-execute with $CONFIG_SHELL" >&2
+as_fn_exit 255
+  fi
+  # We don't want this to propagate to other subprocesses.
+          { _as_can_reexec=; unset _as_can_reexec;}
+if test "x$CONFIG_SHELL" = x; then
+  as_bourne_compatible="if test -n \"\${ZSH_VERSION+set}\" && (emulate sh) >/dev/null 2>&1; then :
+  emulate sh
+  NULLCMD=:
+  # Pre-4.2 versions of Zsh do word splitting on \${1+\"\$@\"}, which
+  # is contrary to our usage.  Disable this feature.
+  alias -g '\${1+\"\$@\"}'='\"\$@\"'
+  setopt NO_GLOB_SUBST
+else
+  case \`(set -o) 2>/dev/null\` in #(
+  *posix*) :
+    set -o posix ;; #(
+  *) :
+     ;;
+esac
+fi
+"
+  as_required="as_fn_return () { (exit \$1); }
+as_fn_success () { as_fn_return 0; }
+as_fn_failure () { as_fn_return 1; }
+as_fn_ret_success () { return 0; }
+as_fn_ret_failure () { return 1; }
+
+exitcode=0
+as_fn_success || { exitcode=1; echo as_fn_success failed.; }
+as_fn_failure && { exitcode=1; echo as_fn_failure succeeded.; }
+as_fn_ret_success || { exitcode=1; echo as_fn_ret_success failed.; }
+as_fn_ret_failure && { exitcode=1; echo as_fn_ret_failure succeeded.; }
+if ( set x; as_fn_ret_success y && test x = \"\$1\" ); then :
+
+else
+  exitcode=1; echo positional parameters were not saved.
+fi
+test x\$exitcode = x0 || exit 1
+test -x / || exit 1"
+  as_suggested="  as_lineno_1=";as_suggested=$as_suggested$LINENO;as_suggested=$as_suggested" as_lineno_1a=\$LINENO
+  as_lineno_2=";as_suggested=$as_suggested$LINENO;as_suggested=$as_suggested" as_lineno_2a=\$LINENO
+  eval 'test \"x\$as_lineno_1'\$as_run'\" != \"x\$as_lineno_2'\$as_run'\" &&
+  test \"x\`expr \$as_lineno_1'\$as_run' + 1\`\" = \"x\$as_lineno_2'\$as_run'\"' || exit 1
+test \$(( 1 + 1 )) = 2 || exit 1
+
+  test -n \"\${ZSH_VERSION+set}\${BASH_VERSION+set}\" || (
+    ECHO='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\'
+    ECHO=\$ECHO\$ECHO\$ECHO\$ECHO\$ECHO
+    ECHO=\$ECHO\$ECHO\$ECHO\$ECHO\$ECHO\$ECHO
+    PATH=/empty FPATH=/empty; export PATH FPATH
+    test \"X\`printf %s \$ECHO\`\" = \"X\$ECHO\" \\
+      || test \"X\`print -r -- \$ECHO\`\" = \"X\$ECHO\" ) || exit 1"
+  if (eval "$as_required") 2>/dev/null; then :
+  as_have_required=yes
+else
+  as_have_required=no
+fi
+  if test x$as_have_required = xyes && (eval "$as_suggested") 2>/dev/null; then :
+
+else
+  as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+as_found=false
+for as_dir in /bin$PATH_SEPARATOR/usr/bin$PATH_SEPARATOR$PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  as_found=:
+  case $as_dir in #(
+	 /*)
+	   for as_base in sh bash ksh sh5; do
+	     # Try only shells that exist, to save several forks.
+	     as_shell=$as_dir/$as_base
+	     if { test -f "$as_shell" || test -f "$as_shell.exe"; } &&
+		    { $as_echo "$as_bourne_compatible""$as_required" | as_run=a "$as_shell"; } 2>/dev/null; then :
+  CONFIG_SHELL=$as_shell as_have_required=yes
+		   if { $as_echo "$as_bourne_compatible""$as_suggested" | as_run=a "$as_shell"; } 2>/dev/null; then :
+  break 2
+fi
+fi
+	   done;;
+       esac
+  as_found=false
+done
+$as_found || { if { test -f "$SHELL" || test -f "$SHELL.exe"; } &&
+	      { $as_echo "$as_bourne_compatible""$as_required" | as_run=a "$SHELL"; } 2>/dev/null; then :
+  CONFIG_SHELL=$SHELL as_have_required=yes
+fi; }
+IFS=$as_save_IFS
+
+
+      if test "x$CONFIG_SHELL" != x; then :
+  export CONFIG_SHELL
+             # We cannot yet assume a decent shell, so we have to provide a
+# neutralization value for shells without unset; and this also
+# works around shells that cannot unset nonexistent variables.
+# Preserve -v and -x to the replacement shell.
+BASH_ENV=/dev/null
+ENV=/dev/null
+(unset BASH_ENV) >/dev/null 2>&1 && unset BASH_ENV ENV
+case $- in # ((((
+  *v*x* | *x*v* ) as_opts=-vx ;;
+  *v* ) as_opts=-v ;;
+  *x* ) as_opts=-x ;;
+  * ) as_opts= ;;
+esac
+exec $CONFIG_SHELL $as_opts "$as_myself" ${1+"$@"}
+# Admittedly, this is quite paranoid, since all the known shells bail
+# out after a failed `exec'.
+$as_echo "$0: could not re-execute with $CONFIG_SHELL" >&2
+exit 255
+fi
+
+    if test x$as_have_required = xno; then :
+  $as_echo "$0: This script requires a shell more modern than all"
+  $as_echo "$0: the shells that I found on your system."
+  if test x${ZSH_VERSION+set} = xset ; then
+    $as_echo "$0: In particular, zsh $ZSH_VERSION has bugs and should"
+    $as_echo "$0: be upgraded to zsh 4.3.4 or later."
+  else
+    $as_echo "$0: Please tell bug-autoconf@gnu.org about your system,
+$0: including any error possibly output before this
+$0: message. Then install a modern shell, or manually run
+$0: the script under such a shell if you do have one."
+  fi
+  exit 1
+fi
+fi
+fi
+SHELL=${CONFIG_SHELL-/bin/sh}
+export SHELL
+# Unset more variables known to interfere with behavior of common tools.
+CLICOLOR_FORCE= GREP_OPTIONS=
+unset CLICOLOR_FORCE GREP_OPTIONS
+
+## --------------------- ##
+## M4sh Shell Functions. ##
+## --------------------- ##
+# as_fn_unset VAR
+# ---------------
+# Portably unset VAR.
+as_fn_unset ()
+{
+  { eval $1=; unset $1;}
+}
+as_unset=as_fn_unset
+
+# as_fn_set_status STATUS
+# -----------------------
+# Set $? to STATUS, without forking.
+as_fn_set_status ()
+{
+  return $1
+} # as_fn_set_status
+
+# as_fn_exit STATUS
+# -----------------
+# Exit the shell with STATUS, even in a "trap 0" or "set -e" context.
+as_fn_exit ()
+{
+  set +e
+  as_fn_set_status $1
+  exit $1
+} # as_fn_exit
+
+# as_fn_mkdir_p
+# -------------
+# Create "$as_dir" as a directory, including parents if necessary.
+as_fn_mkdir_p ()
+{
+
+  case $as_dir in #(
+  -*) as_dir=./$as_dir;;
+  esac
+  test -d "$as_dir" || eval $as_mkdir_p || {
+    as_dirs=
+    while :; do
+      case $as_dir in #(
+      *\'*) as_qdir=`$as_echo "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #'(
+      *) as_qdir=$as_dir;;
+      esac
+      as_dirs="'$as_qdir' $as_dirs"
+      as_dir=`$as_dirname -- "$as_dir" ||
+$as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+	 X"$as_dir" : 'X\(//\)[^/]' \| \
+	 X"$as_dir" : 'X\(//\)$' \| \
+	 X"$as_dir" : 'X\(/\)' \| . 2>/dev/null ||
+$as_echo X"$as_dir" |
+    sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{
+	    s//\1/
+	    q
+	  }
+	  /^X\(\/\/\)[^/].*/{
+	    s//\1/
+	    q
+	  }
+	  /^X\(\/\/\)$/{
+	    s//\1/
+	    q
+	  }
+	  /^X\(\/\).*/{
+	    s//\1/
+	    q
+	  }
+	  s/.*/./; q'`
+      test -d "$as_dir" && break
+    done
+    test -z "$as_dirs" || eval "mkdir $as_dirs"
+  } || test -d "$as_dir" || as_fn_error $? "cannot create directory $as_dir"
+
+
+} # as_fn_mkdir_p
+
+# as_fn_executable_p FILE
+# -----------------------
+# Test if FILE is an executable regular file.
+as_fn_executable_p ()
+{
+  test -f "$1" && test -x "$1"
+} # as_fn_executable_p
+# as_fn_append VAR VALUE
+# ----------------------
+# Append the text in VALUE to the end of the definition contained in VAR. Take
+# advantage of any shell optimizations that allow amortized linear growth over
+# repeated appends, instead of the typical quadratic growth present in naive
+# implementations.
+if (eval "as_var=1; as_var+=2; test x\$as_var = x12") 2>/dev/null; then :
+  eval 'as_fn_append ()
+  {
+    eval $1+=\$2
+  }'
+else
+  as_fn_append ()
+  {
+    eval $1=\$$1\$2
+  }
+fi # as_fn_append
+
+# as_fn_arith ARG...
+# ------------------
+# Perform arithmetic evaluation on the ARGs, and store the result in the
+# global $as_val. Take advantage of shells that can avoid forks. The arguments
+# must be portable across $(()) and expr.
+if (eval "test \$(( 1 + 1 )) = 2") 2>/dev/null; then :
+  eval 'as_fn_arith ()
+  {
+    as_val=$(( $* ))
+  }'
+else
+  as_fn_arith ()
+  {
+    as_val=`expr "$@" || test $? -eq 1`
+  }
+fi # as_fn_arith
+
+
+# as_fn_error STATUS ERROR [LINENO LOG_FD]
+# ----------------------------------------
+# Output "`basename $0`: error: ERROR" to stderr. If LINENO and LOG_FD are
+# provided, also output the error to LOG_FD, referencing LINENO. Then exit the
+# script with STATUS, using 1 if that was 0.
+as_fn_error ()
+{
+  as_status=$1; test $as_status -eq 0 && as_status=1
+  if test "$4"; then
+    as_lineno=${as_lineno-"$3"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+    $as_echo "$as_me:${as_lineno-$LINENO}: error: $2" >&$4
+  fi
+  $as_echo "$as_me: error: $2" >&2
+  as_fn_exit $as_status
+} # as_fn_error
+
+if expr a : '\(a\)' >/dev/null 2>&1 &&
+   test "X`expr 00001 : '.*\(...\)'`" = X001; then
+  as_expr=expr
+else
+  as_expr=false
+fi
+
+if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then
+  as_basename=basename
+else
+  as_basename=false
+fi
+
+if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then
+  as_dirname=dirname
+else
+  as_dirname=false
+fi
+
+as_me=`$as_basename -- "$0" ||
+$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \
+	 X"$0" : 'X\(//\)$' \| \
+	 X"$0" : 'X\(/\)' \| . 2>/dev/null ||
+$as_echo X/"$0" |
+    sed '/^.*\/\([^/][^/]*\)\/*$/{
+	    s//\1/
+	    q
+	  }
+	  /^X\/\(\/\/\)$/{
+	    s//\1/
+	    q
+	  }
+	  /^X\/\(\/\).*/{
+	    s//\1/
+	    q
+	  }
+	  s/.*/./; q'`
+
+# Avoid depending upon Character Ranges.
+as_cr_letters='abcdefghijklmnopqrstuvwxyz'
+as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ'
+as_cr_Letters=$as_cr_letters$as_cr_LETTERS
+as_cr_digits='0123456789'
+as_cr_alnum=$as_cr_Letters$as_cr_digits
+
+
+  as_lineno_1=$LINENO as_lineno_1a=$LINENO
+  as_lineno_2=$LINENO as_lineno_2a=$LINENO
+  eval 'test "x$as_lineno_1'$as_run'" != "x$as_lineno_2'$as_run'" &&
+  test "x`expr $as_lineno_1'$as_run' + 1`" = "x$as_lineno_2'$as_run'"' || {
+  # Blame Lee E. McMahon (1931-1989) for sed's syntax.  :-)
+  sed -n '
+    p
+    /[$]LINENO/=
+  ' <$as_myself |
+    sed '
+      s/[$]LINENO.*/&-/
+      t lineno
+      b
+      :lineno
+      N
+      :loop
+      s/[$]LINENO\([^'$as_cr_alnum'_].*\n\)\(.*\)/\2\1\2/
+      t loop
+      s/-\n.*//
+    ' >$as_me.lineno &&
+  chmod +x "$as_me.lineno" ||
+    { $as_echo "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2; as_fn_exit 1; }
+
+  # If we had to re-execute with $CONFIG_SHELL, we're ensured to have
+  # already done that, so ensure we don't try to do so again and fall
+  # in an infinite loop.  This has already happened in practice.
+  _as_can_reexec=no; export _as_can_reexec
+  # Don't try to exec as it changes $[0], causing all sort of problems
+  # (the dirname of $[0] is not the place where we might find the
+  # original and so on.  Autoconf is especially sensitive to this).
+  . "./$as_me.lineno"
+  # Exit status is that of the last command.
+  exit
+}
+
+ECHO_C= ECHO_N= ECHO_T=
+case `echo -n x` in #(((((
+-n*)
+  case `echo 'xy\c'` in
+  *c*) ECHO_T='	';;	# ECHO_T is single tab character.
+  xy)  ECHO_C='\c';;
+  *)   echo `echo ksh88 bug on AIX 6.1` > /dev/null
+       ECHO_T='	';;
+  esac;;
+*)
+  ECHO_N='-n';;
+esac
+
+rm -f conf$$ conf$$.exe conf$$.file
+if test -d conf$$.dir; then
+  rm -f conf$$.dir/conf$$.file
+else
+  rm -f conf$$.dir
+  mkdir conf$$.dir 2>/dev/null
+fi
+if (echo >conf$$.file) 2>/dev/null; then
+  if ln -s conf$$.file conf$$ 2>/dev/null; then
+    as_ln_s='ln -s'
+    # ... but there are two gotchas:
+    # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail.
+    # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable.
+    # In both cases, we have to default to `cp -pR'.
+    ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe ||
+      as_ln_s='cp -pR'
+  elif ln conf$$.file conf$$ 2>/dev/null; then
+    as_ln_s=ln
+  else
+    as_ln_s='cp -pR'
+  fi
+else
+  as_ln_s='cp -pR'
+fi
+rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file
+rmdir conf$$.dir 2>/dev/null
+
+if mkdir -p . 2>/dev/null; then
+  as_mkdir_p='mkdir -p "$as_dir"'
+else
+  test -d ./-p && rmdir ./-p
+  as_mkdir_p=false
+fi
+
+as_test_x='test -x'
+as_executable_p=as_fn_executable_p
+
+# Sed expression to map a string onto a valid CPP name.
+as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'"
+
+# Sed expression to map a string onto a valid variable name.
+as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'"
+
+SHELL=${CONFIG_SHELL-/bin/sh}
+
+
+test -n "$DJDIR" || exec 7<&0 </dev/null
+exec 6>&1
+
+# Name of the host.
+# hostname on some systems (SVR3.2, old GNU/Linux) returns a bogus exit status,
+# so uname gets run too.
+ac_hostname=`(hostname || uname -n) 2>/dev/null | sed 1q`
+
+#
+# Initializations.
+#
+ac_default_prefix=/usr/local
+ac_clean_files=
+ac_config_libobj_dir=.
+LIBOBJS=
+cross_compiling=no
+subdirs=
+MFLAGS=
+MAKEFLAGS=
+
+# Identity of this package.
+PACKAGE_NAME='bluez'
+PACKAGE_TARNAME='bluez'
+PACKAGE_VERSION='5.17'
+PACKAGE_STRING='bluez 5.17'
+PACKAGE_BUGREPORT=''
+PACKAGE_URL=''
+
+# Factoring default headers for most tests.
+ac_includes_default="\
+#include <stdio.h>
+#ifdef HAVE_SYS_TYPES_H
+# include <sys/types.h>
+#endif
+#ifdef HAVE_SYS_STAT_H
+# include <sys/stat.h>
+#endif
+#ifdef STDC_HEADERS
+# include <stdlib.h>
+# include <stddef.h>
+#else
+# ifdef HAVE_STDLIB_H
+#  include <stdlib.h>
+# endif
+#endif
+#ifdef HAVE_STRING_H
+# if !defined STDC_HEADERS && defined HAVE_MEMORY_H
+#  include <memory.h>
+# endif
+# include <string.h>
+#endif
+#ifdef HAVE_STRINGS_H
+# include <strings.h>
+#endif
+#ifdef HAVE_INTTYPES_H
+# include <inttypes.h>
+#endif
+#ifdef HAVE_STDINT_H
+# include <stdint.h>
+#endif
+#ifdef HAVE_UNISTD_H
+# include <unistd.h>
+#endif"
+
+ac_default_prefix=/usr/local
+ac_subst_vars='am__EXEEXT_FALSE
+am__EXEEXT_TRUE
+LTLIBOBJS
+LIBOBJS
+SBC_LIBS
+SBC_CFLAGS
+ANDROID_FALSE
+ANDROID_TRUE
+CONFIGDIR
+SIXAXIS_FALSE
+SIXAXIS_TRUE
+EXPERIMENTAL_FALSE
+EXPERIMENTAL_TRUE
+DATAFILES_FALSE
+DATAFILES_TRUE
+SYSTEMD_USERUNITDIR
+SYSTEMD_SYSTEMUNITDIR
+SYSTEMD_FALSE
+SYSTEMD_TRUE
+READLINE_FALSE
+READLINE_TRUE
+CLIENT_FALSE
+CLIENT_TRUE
+OBEX_FALSE
+OBEX_TRUE
+ICAL_LIBS
+ICAL_CFLAGS
+CUPS_FALSE
+CUPS_TRUE
+HID2HCI_FALSE
+HID2HCI_TRUE
+UDEV_DIR
+UDEV_FALSE
+UDEV_TRUE
+UDEV_LIBS
+UDEV_CFLAGS
+MONITOR_FALSE
+MONITOR_TRUE
+TOOLS_FALSE
+TOOLS_TRUE
+TEST_FALSE
+TEST_TRUE
+LIBRARY_FALSE
+LIBRARY_TRUE
+DBUS_SESSIONBUSDIR
+DBUS_SYSTEMBUSDIR
+DBUS_CONFDIR
+DBUS_LIBS
+DBUS_CFLAGS
+GTHREAD_LIBS
+GTHREAD_CFLAGS
+GLIB_LIBS
+GLIB_CFLAGS
+MISC_LDFLAGS
+MISC_CFLAGS
+OTOOL64
+OTOOL
+LIPO
+NMEDIT
+DSYMUTIL
+MANIFEST_TOOL
+RANLIB
+ac_ct_AR
+AR
+DLLTOOL
+OBJDUMP
+LN_S
+NM
+ac_ct_DUMPBIN
+DUMPBIN
+LD
+FGREP
+SED
+host_os
+host_vendor
+host_cpu
+host
+build_os
+build_vendor
+build_cpu
+build
+LIBTOOL
+WARNING_CFLAGS
+PKG_CONFIG_LIBDIR
+PKG_CONFIG_PATH
+PKG_CONFIG
+MAINT
+MAINTAINER_MODE_FALSE
+MAINTAINER_MODE_TRUE
+EGREP
+GREP
+CPP
+am__fastdepCC_FALSE
+am__fastdepCC_TRUE
+CCDEPMODE
+am__nodep
+AMDEPBACKSLASH
+AMDEP_FALSE
+AMDEP_TRUE
+am__quote
+am__include
+DEPDIR
+OBJEXT
+EXEEXT
+ac_ct_CC
+CPPFLAGS
+LDFLAGS
+CFLAGS
+CC
+AM_BACKSLASH
+AM_DEFAULT_VERBOSITY
+AM_DEFAULT_V
+AM_V
+am__untar
+am__tar
+AMTAR
+am__leading_dot
+SET_MAKE
+AWK
+mkdir_p
+MKDIR_P
+INSTALL_STRIP_PROGRAM
+STRIP
+install_sh
+MAKEINFO
+AUTOHEADER
+AUTOMAKE
+AUTOCONF
+ACLOCAL
+VERSION
+PACKAGE
+CYGPATH_W
+am__isrc
+INSTALL_DATA
+INSTALL_SCRIPT
+INSTALL_PROGRAM
+target_alias
+host_alias
+build_alias
+LIBS
+ECHO_T
+ECHO_N
+ECHO_C
+DEFS
+mandir
+localedir
+libdir
+psdir
+pdfdir
+dvidir
+htmldir
+infodir
+docdir
+oldincludedir
+includedir
+localstatedir
+sharedstatedir
+sysconfdir
+datadir
+datarootdir
+libexecdir
+sbindir
+bindir
+program_transform_name
+prefix
+exec_prefix
+PACKAGE_URL
+PACKAGE_BUGREPORT
+PACKAGE_STRING
+PACKAGE_VERSION
+PACKAGE_TARNAME
+PACKAGE_NAME
+PATH_SEPARATOR
+SHELL'
+ac_subst_files=''
+ac_user_opts='
+enable_option_checking
+enable_silent_rules
+enable_dependency_tracking
+enable_maintainer_mode
+enable_static
+enable_shared
+with_pic
+enable_fast_install
+with_gnu_ld
+with_sysroot
+enable_libtool_lock
+enable_optimization
+enable_debug
+enable_pie
+enable_threads
+with_dbusconfdir
+with_dbussystembusdir
+with_dbussessionbusdir
+enable_library
+enable_test
+enable_tools
+enable_monitor
+enable_udev
+with_udevdir
+enable_cups
+enable_obex
+enable_client
+enable_systemd
+with_systemdsystemunitdir
+with_systemduserunitdir
+enable_datafiles
+enable_experimental
+enable_sixaxis
+enable_android
+'
+      ac_precious_vars='build_alias
+host_alias
+target_alias
+CC
+CFLAGS
+LDFLAGS
+LIBS
+CPPFLAGS
+CPP
+PKG_CONFIG
+PKG_CONFIG_PATH
+PKG_CONFIG_LIBDIR
+GLIB_CFLAGS
+GLIB_LIBS
+GTHREAD_CFLAGS
+GTHREAD_LIBS
+DBUS_CFLAGS
+DBUS_LIBS
+UDEV_CFLAGS
+UDEV_LIBS
+ICAL_CFLAGS
+ICAL_LIBS
+SBC_CFLAGS
+SBC_LIBS'
+
+
+# Initialize some variables set by options.
+ac_init_help=
+ac_init_version=false
+ac_unrecognized_opts=
+ac_unrecognized_sep=
+# The variables have the same names as the options, with
+# dashes changed to underlines.
+cache_file=/dev/null
+exec_prefix=NONE
+no_create=
+no_recursion=
+prefix=NONE
+program_prefix=NONE
+program_suffix=NONE
+program_transform_name=s,x,x,
+silent=
+site=
+srcdir=
+verbose=
+x_includes=NONE
+x_libraries=NONE
+
+# Installation directory options.
+# These are left unexpanded so users can "make install exec_prefix=/foo"
+# and all the variables that are supposed to be based on exec_prefix
+# by default will actually change.
+# Use braces instead of parens because sh, perl, etc. also accept them.
+# (The list follows the same order as the GNU Coding Standards.)
+bindir='${exec_prefix}/bin'
+sbindir='${exec_prefix}/sbin'
+libexecdir='${exec_prefix}/libexec'
+datarootdir='${prefix}/share'
+datadir='${datarootdir}'
+sysconfdir='${prefix}/etc'
+sharedstatedir='${prefix}/com'
+localstatedir='${prefix}/var'
+includedir='${prefix}/include'
+oldincludedir='/usr/include'
+docdir='${datarootdir}/doc/${PACKAGE_TARNAME}'
+infodir='${datarootdir}/info'
+htmldir='${docdir}'
+dvidir='${docdir}'
+pdfdir='${docdir}'
+psdir='${docdir}'
+libdir='${exec_prefix}/lib'
+localedir='${datarootdir}/locale'
+mandir='${datarootdir}/man'
+
+ac_prev=
+ac_dashdash=
+for ac_option
+do
+  # If the previous option needs an argument, assign it.
+  if test -n "$ac_prev"; then
+    eval $ac_prev=\$ac_option
+    ac_prev=
+    continue
+  fi
+
+  case $ac_option in
+  *=?*) ac_optarg=`expr "X$ac_option" : '[^=]*=\(.*\)'` ;;
+  *=)   ac_optarg= ;;
+  *)    ac_optarg=yes ;;
+  esac
+
+  # Accept the important Cygnus configure options, so we can diagnose typos.
+
+  case $ac_dashdash$ac_option in
+  --)
+    ac_dashdash=yes ;;
+
+  -bindir | --bindir | --bindi | --bind | --bin | --bi)
+    ac_prev=bindir ;;
+  -bindir=* | --bindir=* | --bindi=* | --bind=* | --bin=* | --bi=*)
+    bindir=$ac_optarg ;;
+
+  -build | --build | --buil | --bui | --bu)
+    ac_prev=build_alias ;;
+  -build=* | --build=* | --buil=* | --bui=* | --bu=*)
+    build_alias=$ac_optarg ;;
+
+  -cache-file | --cache-file | --cache-fil | --cache-fi \
+  | --cache-f | --cache- | --cache | --cach | --cac | --ca | --c)
+    ac_prev=cache_file ;;
+  -cache-file=* | --cache-file=* | --cache-fil=* | --cache-fi=* \
+  | --cache-f=* | --cache-=* | --cache=* | --cach=* | --cac=* | --ca=* | --c=*)
+    cache_file=$ac_optarg ;;
+
+  --config-cache | -C)
+    cache_file=config.cache ;;
+
+  -datadir | --datadir | --datadi | --datad)
+    ac_prev=datadir ;;
+  -datadir=* | --datadir=* | --datadi=* | --datad=*)
+    datadir=$ac_optarg ;;
+
+  -datarootdir | --datarootdir | --datarootdi | --datarootd | --dataroot \
+  | --dataroo | --dataro | --datar)
+    ac_prev=datarootdir ;;
+  -datarootdir=* | --datarootdir=* | --datarootdi=* | --datarootd=* \
+  | --dataroot=* | --dataroo=* | --dataro=* | --datar=*)
+    datarootdir=$ac_optarg ;;
+
+  -disable-* | --disable-*)
+    ac_useropt=`expr "x$ac_option" : 'x-*disable-\(.*\)'`
+    # Reject names that are not valid shell variable names.
+    expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null &&
+      as_fn_error $? "invalid feature name: $ac_useropt"
+    ac_useropt_orig=$ac_useropt
+    ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'`
+    case $ac_user_opts in
+      *"
+"enable_$ac_useropt"
+"*) ;;
+      *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--disable-$ac_useropt_orig"
+	 ac_unrecognized_sep=', ';;
+    esac
+    eval enable_$ac_useropt=no ;;
+
+  -docdir | --docdir | --docdi | --doc | --do)
+    ac_prev=docdir ;;
+  -docdir=* | --docdir=* | --docdi=* | --doc=* | --do=*)
+    docdir=$ac_optarg ;;
+
+  -dvidir | --dvidir | --dvidi | --dvid | --dvi | --dv)
+    ac_prev=dvidir ;;
+  -dvidir=* | --dvidir=* | --dvidi=* | --dvid=* | --dvi=* | --dv=*)
+    dvidir=$ac_optarg ;;
+
+  -enable-* | --enable-*)
+    ac_useropt=`expr "x$ac_option" : 'x-*enable-\([^=]*\)'`
+    # Reject names that are not valid shell variable names.
+    expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null &&
+      as_fn_error $? "invalid feature name: $ac_useropt"
+    ac_useropt_orig=$ac_useropt
+    ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'`
+    case $ac_user_opts in
+      *"
+"enable_$ac_useropt"
+"*) ;;
+      *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--enable-$ac_useropt_orig"
+	 ac_unrecognized_sep=', ';;
+    esac
+    eval enable_$ac_useropt=\$ac_optarg ;;
+
+  -exec-prefix | --exec_prefix | --exec-prefix | --exec-prefi \
+  | --exec-pref | --exec-pre | --exec-pr | --exec-p | --exec- \
+  | --exec | --exe | --ex)
+    ac_prev=exec_prefix ;;
+  -exec-prefix=* | --exec_prefix=* | --exec-prefix=* | --exec-prefi=* \
+  | --exec-pref=* | --exec-pre=* | --exec-pr=* | --exec-p=* | --exec-=* \
+  | --exec=* | --exe=* | --ex=*)
+    exec_prefix=$ac_optarg ;;
+
+  -gas | --gas | --ga | --g)
+    # Obsolete; use --with-gas.
+    with_gas=yes ;;
+
+  -help | --help | --hel | --he | -h)
+    ac_init_help=long ;;
+  -help=r* | --help=r* | --hel=r* | --he=r* | -hr*)
+    ac_init_help=recursive ;;
+  -help=s* | --help=s* | --hel=s* | --he=s* | -hs*)
+    ac_init_help=short ;;
+
+  -host | --host | --hos | --ho)
+    ac_prev=host_alias ;;
+  -host=* | --host=* | --hos=* | --ho=*)
+    host_alias=$ac_optarg ;;
+
+  -htmldir | --htmldir | --htmldi | --htmld | --html | --htm | --ht)
+    ac_prev=htmldir ;;
+  -htmldir=* | --htmldir=* | --htmldi=* | --htmld=* | --html=* | --htm=* \
+  | --ht=*)
+    htmldir=$ac_optarg ;;
+
+  -includedir | --includedir | --includedi | --included | --include \
+  | --includ | --inclu | --incl | --inc)
+    ac_prev=includedir ;;
+  -includedir=* | --includedir=* | --includedi=* | --included=* | --include=* \
+  | --includ=* | --inclu=* | --incl=* | --inc=*)
+    includedir=$ac_optarg ;;
+
+  -infodir | --infodir | --infodi | --infod | --info | --inf)
+    ac_prev=infodir ;;
+  -infodir=* | --infodir=* | --infodi=* | --infod=* | --info=* | --inf=*)
+    infodir=$ac_optarg ;;
+
+  -libdir | --libdir | --libdi | --libd)
+    ac_prev=libdir ;;
+  -libdir=* | --libdir=* | --libdi=* | --libd=*)
+    libdir=$ac_optarg ;;
+
+  -libexecdir | --libexecdir | --libexecdi | --libexecd | --libexec \
+  | --libexe | --libex | --libe)
+    ac_prev=libexecdir ;;
+  -libexecdir=* | --libexecdir=* | --libexecdi=* | --libexecd=* | --libexec=* \
+  | --libexe=* | --libex=* | --libe=*)
+    libexecdir=$ac_optarg ;;
+
+  -localedir | --localedir | --localedi | --localed | --locale)
+    ac_prev=localedir ;;
+  -localedir=* | --localedir=* | --localedi=* | --localed=* | --locale=*)
+    localedir=$ac_optarg ;;
+
+  -localstatedir | --localstatedir | --localstatedi | --localstated \
+  | --localstate | --localstat | --localsta | --localst | --locals)
+    ac_prev=localstatedir ;;
+  -localstatedir=* | --localstatedir=* | --localstatedi=* | --localstated=* \
+  | --localstate=* | --localstat=* | --localsta=* | --localst=* | --locals=*)
+    localstatedir=$ac_optarg ;;
+
+  -mandir | --mandir | --mandi | --mand | --man | --ma | --m)
+    ac_prev=mandir ;;
+  -mandir=* | --mandir=* | --mandi=* | --mand=* | --man=* | --ma=* | --m=*)
+    mandir=$ac_optarg ;;
+
+  -nfp | --nfp | --nf)
+    # Obsolete; use --without-fp.
+    with_fp=no ;;
+
+  -no-create | --no-create | --no-creat | --no-crea | --no-cre \
+  | --no-cr | --no-c | -n)
+    no_create=yes ;;
+
+  -no-recursion | --no-recursion | --no-recursio | --no-recursi \
+  | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r)
+    no_recursion=yes ;;
+
+  -oldincludedir | --oldincludedir | --oldincludedi | --oldincluded \
+  | --oldinclude | --oldinclud | --oldinclu | --oldincl | --oldinc \
+  | --oldin | --oldi | --old | --ol | --o)
+    ac_prev=oldincludedir ;;
+  -oldincludedir=* | --oldincludedir=* | --oldincludedi=* | --oldincluded=* \
+  | --oldinclude=* | --oldinclud=* | --oldinclu=* | --oldincl=* | --oldinc=* \
+  | --oldin=* | --oldi=* | --old=* | --ol=* | --o=*)
+    oldincludedir=$ac_optarg ;;
+
+  -prefix | --prefix | --prefi | --pref | --pre | --pr | --p)
+    ac_prev=prefix ;;
+  -prefix=* | --prefix=* | --prefi=* | --pref=* | --pre=* | --pr=* | --p=*)
+    prefix=$ac_optarg ;;
+
+  -program-prefix | --program-prefix | --program-prefi | --program-pref \
+  | --program-pre | --program-pr | --program-p)
+    ac_prev=program_prefix ;;
+  -program-prefix=* | --program-prefix=* | --program-prefi=* \
+  | --program-pref=* | --program-pre=* | --program-pr=* | --program-p=*)
+    program_prefix=$ac_optarg ;;
+
+  -program-suffix | --program-suffix | --program-suffi | --program-suff \
+  | --program-suf | --program-su | --program-s)
+    ac_prev=program_suffix ;;
+  -program-suffix=* | --program-suffix=* | --program-suffi=* \
+  | --program-suff=* | --program-suf=* | --program-su=* | --program-s=*)
+    program_suffix=$ac_optarg ;;
+
+  -program-transform-name | --program-transform-name \
+  | --program-transform-nam | --program-transform-na \
+  | --program-transform-n | --program-transform- \
+  | --program-transform | --program-transfor \
+  | --program-transfo | --program-transf \
+  | --program-trans | --program-tran \
+  | --progr-tra | --program-tr | --program-t)
+    ac_prev=program_transform_name ;;
+  -program-transform-name=* | --program-transform-name=* \
+  | --program-transform-nam=* | --program-transform-na=* \
+  | --program-transform-n=* | --program-transform-=* \
+  | --program-transform=* | --program-transfor=* \
+  | --program-transfo=* | --program-transf=* \
+  | --program-trans=* | --program-tran=* \
+  | --progr-tra=* | --program-tr=* | --program-t=*)
+    program_transform_name=$ac_optarg ;;
+
+  -pdfdir | --pdfdir | --pdfdi | --pdfd | --pdf | --pd)
+    ac_prev=pdfdir ;;
+  -pdfdir=* | --pdfdir=* | --pdfdi=* | --pdfd=* | --pdf=* | --pd=*)
+    pdfdir=$ac_optarg ;;
+
+  -psdir | --psdir | --psdi | --psd | --ps)
+    ac_prev=psdir ;;
+  -psdir=* | --psdir=* | --psdi=* | --psd=* | --ps=*)
+    psdir=$ac_optarg ;;
+
+  -q | -quiet | --quiet | --quie | --qui | --qu | --q \
+  | -silent | --silent | --silen | --sile | --sil)
+    silent=yes ;;
+
+  -sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb)
+    ac_prev=sbindir ;;
+  -sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \
+  | --sbi=* | --sb=*)
+    sbindir=$ac_optarg ;;
+
+  -sharedstatedir | --sharedstatedir | --sharedstatedi \
+  | --sharedstated | --sharedstate | --sharedstat | --sharedsta \
+  | --sharedst | --shareds | --shared | --share | --shar \
+  | --sha | --sh)
+    ac_prev=sharedstatedir ;;
+  -sharedstatedir=* | --sharedstatedir=* | --sharedstatedi=* \
+  | --sharedstated=* | --sharedstate=* | --sharedstat=* | --sharedsta=* \
+  | --sharedst=* | --shareds=* | --shared=* | --share=* | --shar=* \
+  | --sha=* | --sh=*)
+    sharedstatedir=$ac_optarg ;;
+
+  -site | --site | --sit)
+    ac_prev=site ;;
+  -site=* | --site=* | --sit=*)
+    site=$ac_optarg ;;
+
+  -srcdir | --srcdir | --srcdi | --srcd | --src | --sr)
+    ac_prev=srcdir ;;
+  -srcdir=* | --srcdir=* | --srcdi=* | --srcd=* | --src=* | --sr=*)
+    srcdir=$ac_optarg ;;
+
+  -sysconfdir | --sysconfdir | --sysconfdi | --sysconfd | --sysconf \
+  | --syscon | --sysco | --sysc | --sys | --sy)
+    ac_prev=sysconfdir ;;
+  -sysconfdir=* | --sysconfdir=* | --sysconfdi=* | --sysconfd=* | --sysconf=* \
+  | --syscon=* | --sysco=* | --sysc=* | --sys=* | --sy=*)
+    sysconfdir=$ac_optarg ;;
+
+  -target | --target | --targe | --targ | --tar | --ta | --t)
+    ac_prev=target_alias ;;
+  -target=* | --target=* | --targe=* | --targ=* | --tar=* | --ta=* | --t=*)
+    target_alias=$ac_optarg ;;
+
+  -v | -verbose | --verbose | --verbos | --verbo | --verb)
+    verbose=yes ;;
+
+  -version | --version | --versio | --versi | --vers | -V)
+    ac_init_version=: ;;
+
+  -with-* | --with-*)
+    ac_useropt=`expr "x$ac_option" : 'x-*with-\([^=]*\)'`
+    # Reject names that are not valid shell variable names.
+    expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null &&
+      as_fn_error $? "invalid package name: $ac_useropt"
+    ac_useropt_orig=$ac_useropt
+    ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'`
+    case $ac_user_opts in
+      *"
+"with_$ac_useropt"
+"*) ;;
+      *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--with-$ac_useropt_orig"
+	 ac_unrecognized_sep=', ';;
+    esac
+    eval with_$ac_useropt=\$ac_optarg ;;
+
+  -without-* | --without-*)
+    ac_useropt=`expr "x$ac_option" : 'x-*without-\(.*\)'`
+    # Reject names that are not valid shell variable names.
+    expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null &&
+      as_fn_error $? "invalid package name: $ac_useropt"
+    ac_useropt_orig=$ac_useropt
+    ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'`
+    case $ac_user_opts in
+      *"
+"with_$ac_useropt"
+"*) ;;
+      *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--without-$ac_useropt_orig"
+	 ac_unrecognized_sep=', ';;
+    esac
+    eval with_$ac_useropt=no ;;
+
+  --x)
+    # Obsolete; use --with-x.
+    with_x=yes ;;
+
+  -x-includes | --x-includes | --x-include | --x-includ | --x-inclu \
+  | --x-incl | --x-inc | --x-in | --x-i)
+    ac_prev=x_includes ;;
+  -x-includes=* | --x-includes=* | --x-include=* | --x-includ=* | --x-inclu=* \
+  | --x-incl=* | --x-inc=* | --x-in=* | --x-i=*)
+    x_includes=$ac_optarg ;;
+
+  -x-libraries | --x-libraries | --x-librarie | --x-librari \
+  | --x-librar | --x-libra | --x-libr | --x-lib | --x-li | --x-l)
+    ac_prev=x_libraries ;;
+  -x-libraries=* | --x-libraries=* | --x-librarie=* | --x-librari=* \
+  | --x-librar=* | --x-libra=* | --x-libr=* | --x-lib=* | --x-li=* | --x-l=*)
+    x_libraries=$ac_optarg ;;
+
+  -*) as_fn_error $? "unrecognized option: \`$ac_option'
+Try \`$0 --help' for more information"
+    ;;
+
+  *=*)
+    ac_envvar=`expr "x$ac_option" : 'x\([^=]*\)='`
+    # Reject names that are not valid shell variable names.
+    case $ac_envvar in #(
+      '' | [0-9]* | *[!_$as_cr_alnum]* )
+      as_fn_error $? "invalid variable name: \`$ac_envvar'" ;;
+    esac
+    eval $ac_envvar=\$ac_optarg
+    export $ac_envvar ;;
+
+  *)
+    # FIXME: should be removed in autoconf 3.0.
+    $as_echo "$as_me: WARNING: you should use --build, --host, --target" >&2
+    expr "x$ac_option" : ".*[^-._$as_cr_alnum]" >/dev/null &&
+      $as_echo "$as_me: WARNING: invalid host type: $ac_option" >&2
+    : "${build_alias=$ac_option} ${host_alias=$ac_option} ${target_alias=$ac_option}"
+    ;;
+
+  esac
+done
+
+if test -n "$ac_prev"; then
+  ac_option=--`echo $ac_prev | sed 's/_/-/g'`
+  as_fn_error $? "missing argument to $ac_option"
+fi
+
+if test -n "$ac_unrecognized_opts"; then
+  case $enable_option_checking in
+    no) ;;
+    fatal) as_fn_error $? "unrecognized options: $ac_unrecognized_opts" ;;
+    *)     $as_echo "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2 ;;
+  esac
+fi
+
+# Check all directory arguments for consistency.
+for ac_var in	exec_prefix prefix bindir sbindir libexecdir datarootdir \
+		datadir sysconfdir sharedstatedir localstatedir includedir \
+		oldincludedir docdir infodir htmldir dvidir pdfdir psdir \
+		libdir localedir mandir
+do
+  eval ac_val=\$$ac_var
+  # Remove trailing slashes.
+  case $ac_val in
+    */ )
+      ac_val=`expr "X$ac_val" : 'X\(.*[^/]\)' \| "X$ac_val" : 'X\(.*\)'`
+      eval $ac_var=\$ac_val;;
+  esac
+  # Be sure to have absolute directory names.
+  case $ac_val in
+    [\\/$]* | ?:[\\/]* )  continue;;
+    NONE | '' ) case $ac_var in *prefix ) continue;; esac;;
+  esac
+  as_fn_error $? "expected an absolute directory name for --$ac_var: $ac_val"
+done
+
+# There might be people who depend on the old broken behavior: `$host'
+# used to hold the argument of --host etc.
+# FIXME: To remove some day.
+build=$build_alias
+host=$host_alias
+target=$target_alias
+
+# FIXME: To remove some day.
+if test "x$host_alias" != x; then
+  if test "x$build_alias" = x; then
+    cross_compiling=maybe
+  elif test "x$build_alias" != "x$host_alias"; then
+    cross_compiling=yes
+  fi
+fi
+
+ac_tool_prefix=
+test -n "$host_alias" && ac_tool_prefix=$host_alias-
+
+test "$silent" = yes && exec 6>/dev/null
+
+
+ac_pwd=`pwd` && test -n "$ac_pwd" &&
+ac_ls_di=`ls -di .` &&
+ac_pwd_ls_di=`cd "$ac_pwd" && ls -di .` ||
+  as_fn_error $? "working directory cannot be determined"
+test "X$ac_ls_di" = "X$ac_pwd_ls_di" ||
+  as_fn_error $? "pwd does not report name of working directory"
+
+
+# Find the source files, if location was not specified.
+if test -z "$srcdir"; then
+  ac_srcdir_defaulted=yes
+  # Try the directory containing this script, then the parent directory.
+  ac_confdir=`$as_dirname -- "$as_myself" ||
+$as_expr X"$as_myself" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+	 X"$as_myself" : 'X\(//\)[^/]' \| \
+	 X"$as_myself" : 'X\(//\)$' \| \
+	 X"$as_myself" : 'X\(/\)' \| . 2>/dev/null ||
+$as_echo X"$as_myself" |
+    sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{
+	    s//\1/
+	    q
+	  }
+	  /^X\(\/\/\)[^/].*/{
+	    s//\1/
+	    q
+	  }
+	  /^X\(\/\/\)$/{
+	    s//\1/
+	    q
+	  }
+	  /^X\(\/\).*/{
+	    s//\1/
+	    q
+	  }
+	  s/.*/./; q'`
+  srcdir=$ac_confdir
+  if test ! -r "$srcdir/$ac_unique_file"; then
+    srcdir=..
+  fi
+else
+  ac_srcdir_defaulted=no
+fi
+if test ! -r "$srcdir/$ac_unique_file"; then
+  test "$ac_srcdir_defaulted" = yes && srcdir="$ac_confdir or .."
+  as_fn_error $? "cannot find sources ($ac_unique_file) in $srcdir"
+fi
+ac_msg="sources are in $srcdir, but \`cd $srcdir' does not work"
+ac_abs_confdir=`(
+	cd "$srcdir" && test -r "./$ac_unique_file" || as_fn_error $? "$ac_msg"
+	pwd)`
+# When building in place, set srcdir=.
+if test "$ac_abs_confdir" = "$ac_pwd"; then
+  srcdir=.
+fi
+# Remove unnecessary trailing slashes from srcdir.
+# Double slashes in file names in object file debugging info
+# mess up M-x gdb in Emacs.
+case $srcdir in
+*/) srcdir=`expr "X$srcdir" : 'X\(.*[^/]\)' \| "X$srcdir" : 'X\(.*\)'`;;
+esac
+for ac_var in $ac_precious_vars; do
+  eval ac_env_${ac_var}_set=\${${ac_var}+set}
+  eval ac_env_${ac_var}_value=\$${ac_var}
+  eval ac_cv_env_${ac_var}_set=\${${ac_var}+set}
+  eval ac_cv_env_${ac_var}_value=\$${ac_var}
+done
+
+#
+# Report the --help message.
+#
+if test "$ac_init_help" = "long"; then
+  # Omit some internal or obsolete options to make the list less imposing.
+  # This message is too long to be a string in the A/UX 3.1 sh.
+  cat <<_ACEOF
+\`configure' configures bluez 5.17 to adapt to many kinds of systems.
+
+Usage: $0 [OPTION]... [VAR=VALUE]...
+
+To assign environment variables (e.g., CC, CFLAGS...), specify them as
+VAR=VALUE.  See below for descriptions of some of the useful variables.
+
+Defaults for the options are specified in brackets.
+
+Configuration:
+  -h, --help              display this help and exit
+      --help=short        display options specific to this package
+      --help=recursive    display the short help of all the included packages
+  -V, --version           display version information and exit
+  -q, --quiet, --silent   do not print \`checking ...' messages
+      --cache-file=FILE   cache test results in FILE [disabled]
+  -C, --config-cache      alias for \`--cache-file=config.cache'
+  -n, --no-create         do not create output files
+      --srcdir=DIR        find the sources in DIR [configure dir or \`..']
+
+Installation directories:
+  --prefix=PREFIX         install architecture-independent files in PREFIX
+                          [$ac_default_prefix]
+  --exec-prefix=EPREFIX   install architecture-dependent files in EPREFIX
+                          [PREFIX]
+
+By default, \`make install' will install all the files in
+\`$ac_default_prefix/bin', \`$ac_default_prefix/lib' etc.  You can specify
+an installation prefix other than \`$ac_default_prefix' using \`--prefix',
+for instance \`--prefix=\$HOME'.
+
+For better control, use the options below.
+
+Fine tuning of the installation directories:
+  --bindir=DIR            user executables [EPREFIX/bin]
+  --sbindir=DIR           system admin executables [EPREFIX/sbin]
+  --libexecdir=DIR        program executables [EPREFIX/libexec]
+  --sysconfdir=DIR        read-only single-machine data [PREFIX/etc]
+  --sharedstatedir=DIR    modifiable architecture-independent data [PREFIX/com]
+  --localstatedir=DIR     modifiable single-machine data [PREFIX/var]
+  --libdir=DIR            object code libraries [EPREFIX/lib]
+  --includedir=DIR        C header files [PREFIX/include]
+  --oldincludedir=DIR     C header files for non-gcc [/usr/include]
+  --datarootdir=DIR       read-only arch.-independent data root [PREFIX/share]
+  --datadir=DIR           read-only architecture-independent data [DATAROOTDIR]
+  --infodir=DIR           info documentation [DATAROOTDIR/info]
+  --localedir=DIR         locale-dependent data [DATAROOTDIR/locale]
+  --mandir=DIR            man documentation [DATAROOTDIR/man]
+  --docdir=DIR            documentation root [DATAROOTDIR/doc/bluez]
+  --htmldir=DIR           html documentation [DOCDIR]
+  --dvidir=DIR            dvi documentation [DOCDIR]
+  --pdfdir=DIR            pdf documentation [DOCDIR]
+  --psdir=DIR             ps documentation [DOCDIR]
+_ACEOF
+
+  cat <<\_ACEOF
+
+Program names:
+  --program-prefix=PREFIX            prepend PREFIX to installed program names
+  --program-suffix=SUFFIX            append SUFFIX to installed program names
+  --program-transform-name=PROGRAM   run sed PROGRAM on installed program names
+
+System types:
+  --build=BUILD     configure for building on BUILD [guessed]
+  --host=HOST       cross-compile to build programs to run on HOST [BUILD]
+_ACEOF
+fi
+
+if test -n "$ac_init_help"; then
+  case $ac_init_help in
+     short | recursive ) echo "Configuration of bluez 5.17:";;
+   esac
+  cat <<\_ACEOF
+
+Optional Features:
+  --disable-option-checking  ignore unrecognized --enable/--with options
+  --disable-FEATURE       do not include FEATURE (same as --enable-FEATURE=no)
+  --enable-FEATURE[=ARG]  include FEATURE [ARG=yes]
+  --enable-silent-rules   less verbose build output (undo: "make V=1")
+  --disable-silent-rules  verbose build output (undo: "make V=0")
+  --enable-dependency-tracking
+                          do not reject slow dependency extractors
+  --disable-dependency-tracking
+                          speeds up one-time build
+  --enable-maintainer-mode
+                          enable make rules and dependencies not useful (and
+                          sometimes confusing) to the casual installer
+  --enable-static[=PKGS]  build static libraries [default=no]
+  --enable-shared[=PKGS]  build shared libraries [default=yes]
+  --enable-fast-install[=PKGS]
+                          optimize for fast installation [default=yes]
+  --disable-libtool-lock  avoid locking (might break parallel builds)
+  --disable-optimization  disable code optimization through compiler
+  --enable-debug          enable compiling with debugging information
+  --enable-pie            enable position independent executables flag
+  --enable-threads        enable threading support
+  --enable-library        install Bluetooth library
+  --enable-test           enable test/example scripts
+  --disable-tools         disable Bluetooth tools
+  --disable-monitor       disable Bluetooth monitor
+  --disable-udev          disable udev device support
+  --disable-cups          disable CUPS printer support
+  --disable-obex          disable OBEX profile support
+  --disable-client        disable command line client
+  --disable-systemd       disable systemd integration
+  --disable-datafiles     do not install configuration and data files
+  --enable-experimental   enable experimental plugins (SAP, NFC, ...)
+  --enable-sixaxis        enable sixaxis plugin
+  --enable-android        enable BlueZ for Android
+
+Optional Packages:
+  --with-PACKAGE[=ARG]    use PACKAGE [ARG=yes]
+  --without-PACKAGE       do not use PACKAGE (same as --with-PACKAGE=no)
+  --with-pic[=PKGS]       try to use only PIC/non-PIC objects [default=use
+                          both]
+  --with-gnu-ld           assume the C compiler uses GNU ld [default=no]
+  --with-sysroot=DIR Search for dependent libraries within DIR
+                        (or the compiler's sysroot if not specified).
+  --with-dbusconfdir=DIR  path to D-Bus configuration directory
+  --with-dbussystembusdir=DIR
+                          path to D-Bus system bus services directory
+  --with-dbussessionbusdir=DIR
+                          path to D-Bus session bus services directory
+  --with-udevdir=DIR      path to udev directory
+  --with-systemdsystemunitdir=DIR
+                          path to systemd system unit directory
+  --with-systemduserunitdir=DIR
+                          path to systemd user unit directory
+
+Some influential environment variables:
+  CC          C compiler command
+  CFLAGS      C compiler flags
+  LDFLAGS     linker flags, e.g. -L<lib dir> if you have libraries in a
+              nonstandard directory <lib dir>
+  LIBS        libraries to pass to the linker, e.g. -l<library>
+  CPPFLAGS    (Objective) C/C++ preprocessor flags, e.g. -I<include dir> if
+              you have headers in a nonstandard directory <include dir>
+  CPP         C preprocessor
+  PKG_CONFIG  path to pkg-config utility
+  PKG_CONFIG_PATH
+              directories to add to pkg-config's search path
+  PKG_CONFIG_LIBDIR
+              path overriding pkg-config's built-in search path
+  GLIB_CFLAGS C compiler flags for GLIB, overriding pkg-config
+  GLIB_LIBS   linker flags for GLIB, overriding pkg-config
+  GTHREAD_CFLAGS
+              C compiler flags for GTHREAD, overriding pkg-config
+  GTHREAD_LIBS
+              linker flags for GTHREAD, overriding pkg-config
+  DBUS_CFLAGS C compiler flags for DBUS, overriding pkg-config
+  DBUS_LIBS   linker flags for DBUS, overriding pkg-config
+  UDEV_CFLAGS C compiler flags for UDEV, overriding pkg-config
+  UDEV_LIBS   linker flags for UDEV, overriding pkg-config
+  ICAL_CFLAGS C compiler flags for ICAL, overriding pkg-config
+  ICAL_LIBS   linker flags for ICAL, overriding pkg-config
+  SBC_CFLAGS  C compiler flags for SBC, overriding pkg-config
+  SBC_LIBS    linker flags for SBC, overriding pkg-config
+
+Use these variables to override the choices made by `configure' or to help
+it to find libraries and programs with nonstandard names/locations.
+
+Report bugs to the package provider.
+_ACEOF
+ac_status=$?
+fi
+
+if test "$ac_init_help" = "recursive"; then
+  # If there are subdirs, report their specific --help.
+  for ac_dir in : $ac_subdirs_all; do test "x$ac_dir" = x: && continue
+    test -d "$ac_dir" ||
+      { cd "$srcdir" && ac_pwd=`pwd` && srcdir=. && test -d "$ac_dir"; } ||
+      continue
+    ac_builddir=.
+
+case "$ac_dir" in
+.) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;;
+*)
+  ac_dir_suffix=/`$as_echo "$ac_dir" | sed 's|^\.[\\/]||'`
+  # A ".." for each directory in $ac_dir_suffix.
+  ac_top_builddir_sub=`$as_echo "$ac_dir_suffix" | sed 's|/[^\\/]*|/..|g;s|/||'`
+  case $ac_top_builddir_sub in
+  "") ac_top_builddir_sub=. ac_top_build_prefix= ;;
+  *)  ac_top_build_prefix=$ac_top_builddir_sub/ ;;
+  esac ;;
+esac
+ac_abs_top_builddir=$ac_pwd
+ac_abs_builddir=$ac_pwd$ac_dir_suffix
+# for backward compatibility:
+ac_top_builddir=$ac_top_build_prefix
+
+case $srcdir in
+  .)  # We are building in place.
+    ac_srcdir=.
+    ac_top_srcdir=$ac_top_builddir_sub
+    ac_abs_top_srcdir=$ac_pwd ;;
+  [\\/]* | ?:[\\/]* )  # Absolute name.
+    ac_srcdir=$srcdir$ac_dir_suffix;
+    ac_top_srcdir=$srcdir
+    ac_abs_top_srcdir=$srcdir ;;
+  *) # Relative name.
+    ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix
+    ac_top_srcdir=$ac_top_build_prefix$srcdir
+    ac_abs_top_srcdir=$ac_pwd/$srcdir ;;
+esac
+ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix
+
+    cd "$ac_dir" || { ac_status=$?; continue; }
+    # Check for guested configure.
+    if test -f "$ac_srcdir/configure.gnu"; then
+      echo &&
+      $SHELL "$ac_srcdir/configure.gnu" --help=recursive
+    elif test -f "$ac_srcdir/configure"; then
+      echo &&
+      $SHELL "$ac_srcdir/configure" --help=recursive
+    else
+      $as_echo "$as_me: WARNING: no configuration information is in $ac_dir" >&2
+    fi || ac_status=$?
+    cd "$ac_pwd" || { ac_status=$?; break; }
+  done
+fi
+
+test -n "$ac_init_help" && exit $ac_status
+if $ac_init_version; then
+  cat <<\_ACEOF
+bluez configure 5.17
+generated by GNU Autoconf 2.69
+
+Copyright (C) 2012 Free Software Foundation, Inc.
+This configure script is free software; the Free Software Foundation
+gives unlimited permission to copy, distribute and modify it.
+_ACEOF
+  exit
+fi
+
+## ------------------------ ##
+## Autoconf initialization. ##
+## ------------------------ ##
+
+# ac_fn_c_try_compile LINENO
+# --------------------------
+# Try to compile conftest.$ac_ext, and return whether this succeeded.
+ac_fn_c_try_compile ()
+{
+  as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+  rm -f conftest.$ac_objext
+  if { { ac_try="$ac_compile"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+$as_echo "$ac_try_echo"; } >&5
+  (eval "$ac_compile") 2>conftest.err
+  ac_status=$?
+  if test -s conftest.err; then
+    grep -v '^ *+' conftest.err >conftest.er1
+    cat conftest.er1 >&5
+    mv -f conftest.er1 conftest.err
+  fi
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; } && {
+	 test -z "$ac_c_werror_flag" ||
+	 test ! -s conftest.err
+       } && test -s conftest.$ac_objext; then :
+  ac_retval=0
+else
+  $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+	ac_retval=1
+fi
+  eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
+  as_fn_set_status $ac_retval
+
+} # ac_fn_c_try_compile
+
+# ac_fn_c_try_cpp LINENO
+# ----------------------
+# Try to preprocess conftest.$ac_ext, and return whether this succeeded.
+ac_fn_c_try_cpp ()
+{
+  as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+  if { { ac_try="$ac_cpp conftest.$ac_ext"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+$as_echo "$ac_try_echo"; } >&5
+  (eval "$ac_cpp conftest.$ac_ext") 2>conftest.err
+  ac_status=$?
+  if test -s conftest.err; then
+    grep -v '^ *+' conftest.err >conftest.er1
+    cat conftest.er1 >&5
+    mv -f conftest.er1 conftest.err
+  fi
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; } > conftest.i && {
+	 test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" ||
+	 test ! -s conftest.err
+       }; then :
+  ac_retval=0
+else
+  $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+    ac_retval=1
+fi
+  eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
+  as_fn_set_status $ac_retval
+
+} # ac_fn_c_try_cpp
+
+# ac_fn_c_check_header_mongrel LINENO HEADER VAR INCLUDES
+# -------------------------------------------------------
+# Tests whether HEADER exists, giving a warning if it cannot be compiled using
+# the include files in INCLUDES and setting the cache variable VAR
+# accordingly.
+ac_fn_c_check_header_mongrel ()
+{
+  as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+  if eval \${$3+:} false; then :
+  { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5
+$as_echo_n "checking for $2... " >&6; }
+if eval \${$3+:} false; then :
+  $as_echo_n "(cached) " >&6
+fi
+eval ac_res=\$$3
+	       { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+else
+  # Is the header compilable?
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking $2 usability" >&5
+$as_echo_n "checking $2 usability... " >&6; }
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+$4
+#include <$2>
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+  ac_header_compiler=yes
+else
+  ac_header_compiler=no
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_header_compiler" >&5
+$as_echo "$ac_header_compiler" >&6; }
+
+# Is the header present?
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking $2 presence" >&5
+$as_echo_n "checking $2 presence... " >&6; }
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#include <$2>
+_ACEOF
+if ac_fn_c_try_cpp "$LINENO"; then :
+  ac_header_preproc=yes
+else
+  ac_header_preproc=no
+fi
+rm -f conftest.err conftest.i conftest.$ac_ext
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_header_preproc" >&5
+$as_echo "$ac_header_preproc" >&6; }
+
+# So?  What about this header?
+case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in #((
+  yes:no: )
+    { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: accepted by the compiler, rejected by the preprocessor!" >&5
+$as_echo "$as_me: WARNING: $2: accepted by the compiler, rejected by the preprocessor!" >&2;}
+    { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: proceeding with the compiler's result" >&5
+$as_echo "$as_me: WARNING: $2: proceeding with the compiler's result" >&2;}
+    ;;
+  no:yes:* )
+    { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: present but cannot be compiled" >&5
+$as_echo "$as_me: WARNING: $2: present but cannot be compiled" >&2;}
+    { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2:     check for missing prerequisite headers?" >&5
+$as_echo "$as_me: WARNING: $2:     check for missing prerequisite headers?" >&2;}
+    { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: see the Autoconf documentation" >&5
+$as_echo "$as_me: WARNING: $2: see the Autoconf documentation" >&2;}
+    { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2:     section \"Present But Cannot Be Compiled\"" >&5
+$as_echo "$as_me: WARNING: $2:     section \"Present But Cannot Be Compiled\"" >&2;}
+    { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: proceeding with the compiler's result" >&5
+$as_echo "$as_me: WARNING: $2: proceeding with the compiler's result" >&2;}
+    ;;
+esac
+  { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5
+$as_echo_n "checking for $2... " >&6; }
+if eval \${$3+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  eval "$3=\$ac_header_compiler"
+fi
+eval ac_res=\$$3
+	       { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+fi
+  eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
+
+} # ac_fn_c_check_header_mongrel
+
+# ac_fn_c_try_run LINENO
+# ----------------------
+# Try to link conftest.$ac_ext, and return whether this succeeded. Assumes
+# that executables *can* be run.
+ac_fn_c_try_run ()
+{
+  as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+  if { { ac_try="$ac_link"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+$as_echo "$ac_try_echo"; } >&5
+  (eval "$ac_link") 2>&5
+  ac_status=$?
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; } && { ac_try='./conftest$ac_exeext'
+  { { case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+$as_echo "$ac_try_echo"; } >&5
+  (eval "$ac_try") 2>&5
+  ac_status=$?
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; }; }; then :
+  ac_retval=0
+else
+  $as_echo "$as_me: program exited with status $ac_status" >&5
+       $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+       ac_retval=$ac_status
+fi
+  rm -rf conftest.dSYM conftest_ipa8_conftest.oo
+  eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
+  as_fn_set_status $ac_retval
+
+} # ac_fn_c_try_run
+
+# ac_fn_c_check_header_compile LINENO HEADER VAR INCLUDES
+# -------------------------------------------------------
+# Tests whether HEADER exists and can be compiled using the include files in
+# INCLUDES, setting the cache variable VAR accordingly.
+ac_fn_c_check_header_compile ()
+{
+  as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+  { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5
+$as_echo_n "checking for $2... " >&6; }
+if eval \${$3+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+$4
+#include <$2>
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+  eval "$3=yes"
+else
+  eval "$3=no"
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+eval ac_res=\$$3
+	       { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+  eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
+
+} # ac_fn_c_check_header_compile
+
+# ac_fn_c_try_link LINENO
+# -----------------------
+# Try to link conftest.$ac_ext, and return whether this succeeded.
+ac_fn_c_try_link ()
+{
+  as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+  rm -f conftest.$ac_objext conftest$ac_exeext
+  if { { ac_try="$ac_link"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+$as_echo "$ac_try_echo"; } >&5
+  (eval "$ac_link") 2>conftest.err
+  ac_status=$?
+  if test -s conftest.err; then
+    grep -v '^ *+' conftest.err >conftest.er1
+    cat conftest.er1 >&5
+    mv -f conftest.er1 conftest.err
+  fi
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; } && {
+	 test -z "$ac_c_werror_flag" ||
+	 test ! -s conftest.err
+       } && test -s conftest$ac_exeext && {
+	 test "$cross_compiling" = yes ||
+	 test -x conftest$ac_exeext
+       }; then :
+  ac_retval=0
+else
+  $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+	ac_retval=1
+fi
+  # Delete the IPA/IPO (Inter Procedural Analysis/Optimization) information
+  # created by the PGI compiler (conftest_ipa8_conftest.oo), as it would
+  # interfere with the next link command; also delete a directory that is
+  # left behind by Apple's compiler.  We do this before executing the actions.
+  rm -rf conftest.dSYM conftest_ipa8_conftest.oo
+  eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
+  as_fn_set_status $ac_retval
+
+} # ac_fn_c_try_link
+
+# ac_fn_c_check_func LINENO FUNC VAR
+# ----------------------------------
+# Tests whether FUNC exists, setting the cache variable VAR accordingly
+ac_fn_c_check_func ()
+{
+  as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+  { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5
+$as_echo_n "checking for $2... " >&6; }
+if eval \${$3+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+/* Define $2 to an innocuous variant, in case <limits.h> declares $2.
+   For example, HP-UX 11i <limits.h> declares gettimeofday.  */
+#define $2 innocuous_$2
+
+/* System header to define __stub macros and hopefully few prototypes,
+    which can conflict with char $2 (); below.
+    Prefer <limits.h> to <assert.h> if __STDC__ is defined, since
+    <limits.h> exists even on freestanding compilers.  */
+
+#ifdef __STDC__
+# include <limits.h>
+#else
+# include <assert.h>
+#endif
+
+#undef $2
+
+/* Override any GCC internal prototype to avoid an error.
+   Use char because int might match the return type of a GCC
+   builtin and then its argument prototype would still apply.  */
+#ifdef __cplusplus
+extern "C"
+#endif
+char $2 ();
+/* The GNU C library defines this for functions which it implements
+    to always fail with ENOSYS.  Some functions are actually named
+    something starting with __ and the normal name is an alias.  */
+#if defined __stub_$2 || defined __stub___$2
+choke me
+#endif
+
+int
+main ()
+{
+return $2 ();
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+  eval "$3=yes"
+else
+  eval "$3=no"
+fi
+rm -f core conftest.err conftest.$ac_objext \
+    conftest$ac_exeext conftest.$ac_ext
+fi
+eval ac_res=\$$3
+	       { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+  eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
+
+} # ac_fn_c_check_func
+cat >config.log <<_ACEOF
+This file contains any messages produced by compilers while
+running configure, to aid debugging if configure makes a mistake.
+
+It was created by bluez $as_me 5.17, which was
+generated by GNU Autoconf 2.69.  Invocation command line was
+
+  $ $0 $@
+
+_ACEOF
+exec 5>>config.log
+{
+cat <<_ASUNAME
+## --------- ##
+## Platform. ##
+## --------- ##
+
+hostname = `(hostname || uname -n) 2>/dev/null | sed 1q`
+uname -m = `(uname -m) 2>/dev/null || echo unknown`
+uname -r = `(uname -r) 2>/dev/null || echo unknown`
+uname -s = `(uname -s) 2>/dev/null || echo unknown`
+uname -v = `(uname -v) 2>/dev/null || echo unknown`
+
+/usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null || echo unknown`
+/bin/uname -X     = `(/bin/uname -X) 2>/dev/null     || echo unknown`
+
+/bin/arch              = `(/bin/arch) 2>/dev/null              || echo unknown`
+/usr/bin/arch -k       = `(/usr/bin/arch -k) 2>/dev/null       || echo unknown`
+/usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null || echo unknown`
+/usr/bin/hostinfo      = `(/usr/bin/hostinfo) 2>/dev/null      || echo unknown`
+/bin/machine           = `(/bin/machine) 2>/dev/null           || echo unknown`
+/usr/bin/oslevel       = `(/usr/bin/oslevel) 2>/dev/null       || echo unknown`
+/bin/universe          = `(/bin/universe) 2>/dev/null          || echo unknown`
+
+_ASUNAME
+
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    $as_echo "PATH: $as_dir"
+  done
+IFS=$as_save_IFS
+
+} >&5
+
+cat >&5 <<_ACEOF
+
+
+## ----------- ##
+## Core tests. ##
+## ----------- ##
+
+_ACEOF
+
+
+# Keep a trace of the command line.
+# Strip out --no-create and --no-recursion so they do not pile up.
+# Strip out --silent because we don't want to record it for future runs.
+# Also quote any args containing shell meta-characters.
+# Make two passes to allow for proper duplicate-argument suppression.
+ac_configure_args=
+ac_configure_args0=
+ac_configure_args1=
+ac_must_keep_next=false
+for ac_pass in 1 2
+do
+  for ac_arg
+  do
+    case $ac_arg in
+    -no-create | --no-c* | -n | -no-recursion | --no-r*) continue ;;
+    -q | -quiet | --quiet | --quie | --qui | --qu | --q \
+    | -silent | --silent | --silen | --sile | --sil)
+      continue ;;
+    *\'*)
+      ac_arg=`$as_echo "$ac_arg" | sed "s/'/'\\\\\\\\''/g"` ;;
+    esac
+    case $ac_pass in
+    1) as_fn_append ac_configure_args0 " '$ac_arg'" ;;
+    2)
+      as_fn_append ac_configure_args1 " '$ac_arg'"
+      if test $ac_must_keep_next = true; then
+	ac_must_keep_next=false # Got value, back to normal.
+      else
+	case $ac_arg in
+	  *=* | --config-cache | -C | -disable-* | --disable-* \
+	  | -enable-* | --enable-* | -gas | --g* | -nfp | --nf* \
+	  | -q | -quiet | --q* | -silent | --sil* | -v | -verb* \
+	  | -with-* | --with-* | -without-* | --without-* | --x)
+	    case "$ac_configure_args0 " in
+	      "$ac_configure_args1"*" '$ac_arg' "* ) continue ;;
+	    esac
+	    ;;
+	  -* ) ac_must_keep_next=true ;;
+	esac
+      fi
+      as_fn_append ac_configure_args " '$ac_arg'"
+      ;;
+    esac
+  done
+done
+{ ac_configure_args0=; unset ac_configure_args0;}
+{ ac_configure_args1=; unset ac_configure_args1;}
+
+# When interrupted or exit'd, cleanup temporary files, and complete
+# config.log.  We remove comments because anyway the quotes in there
+# would cause problems or look ugly.
+# WARNING: Use '\'' to represent an apostrophe within the trap.
+# WARNING: Do not start the trap code with a newline, due to a FreeBSD 4.0 bug.
+trap 'exit_status=$?
+  # Save into config.log some information that might help in debugging.
+  {
+    echo
+
+    $as_echo "## ---------------- ##
+## Cache variables. ##
+## ---------------- ##"
+    echo
+    # The following way of writing the cache mishandles newlines in values,
+(
+  for ac_var in `(set) 2>&1 | sed -n '\''s/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'\''`; do
+    eval ac_val=\$$ac_var
+    case $ac_val in #(
+    *${as_nl}*)
+      case $ac_var in #(
+      *_cv_*) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: cache variable $ac_var contains a newline" >&5
+$as_echo "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;;
+      esac
+      case $ac_var in #(
+      _ | IFS | as_nl) ;; #(
+      BASH_ARGV | BASH_SOURCE) eval $ac_var= ;; #(
+      *) { eval $ac_var=; unset $ac_var;} ;;
+      esac ;;
+    esac
+  done
+  (set) 2>&1 |
+    case $as_nl`(ac_space='\'' '\''; set) 2>&1` in #(
+    *${as_nl}ac_space=\ *)
+      sed -n \
+	"s/'\''/'\''\\\\'\'''\''/g;
+	  s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\''\\2'\''/p"
+      ;; #(
+    *)
+      sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p"
+      ;;
+    esac |
+    sort
+)
+    echo
+
+    $as_echo "## ----------------- ##
+## Output variables. ##
+## ----------------- ##"
+    echo
+    for ac_var in $ac_subst_vars
+    do
+      eval ac_val=\$$ac_var
+      case $ac_val in
+      *\'\''*) ac_val=`$as_echo "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;;
+      esac
+      $as_echo "$ac_var='\''$ac_val'\''"
+    done | sort
+    echo
+
+    if test -n "$ac_subst_files"; then
+      $as_echo "## ------------------- ##
+## File substitutions. ##
+## ------------------- ##"
+      echo
+      for ac_var in $ac_subst_files
+      do
+	eval ac_val=\$$ac_var
+	case $ac_val in
+	*\'\''*) ac_val=`$as_echo "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;;
+	esac
+	$as_echo "$ac_var='\''$ac_val'\''"
+      done | sort
+      echo
+    fi
+
+    if test -s confdefs.h; then
+      $as_echo "## ----------- ##
+## confdefs.h. ##
+## ----------- ##"
+      echo
+      cat confdefs.h
+      echo
+    fi
+    test "$ac_signal" != 0 &&
+      $as_echo "$as_me: caught signal $ac_signal"
+    $as_echo "$as_me: exit $exit_status"
+  } >&5
+  rm -f core *.core core.conftest.* &&
+    rm -f -r conftest* confdefs* conf$$* $ac_clean_files &&
+    exit $exit_status
+' 0
+for ac_signal in 1 2 13 15; do
+  trap 'ac_signal='$ac_signal'; as_fn_exit 1' $ac_signal
+done
+ac_signal=0
+
+# confdefs.h avoids OS command line length limits that DEFS can exceed.
+rm -f -r conftest* confdefs.h
+
+$as_echo "/* confdefs.h */" > confdefs.h
+
+# Predefined preprocessor variables.
+
+cat >>confdefs.h <<_ACEOF
+#define PACKAGE_NAME "$PACKAGE_NAME"
+_ACEOF
+
+cat >>confdefs.h <<_ACEOF
+#define PACKAGE_TARNAME "$PACKAGE_TARNAME"
+_ACEOF
+
+cat >>confdefs.h <<_ACEOF
+#define PACKAGE_VERSION "$PACKAGE_VERSION"
+_ACEOF
+
+cat >>confdefs.h <<_ACEOF
+#define PACKAGE_STRING "$PACKAGE_STRING"
+_ACEOF
+
+cat >>confdefs.h <<_ACEOF
+#define PACKAGE_BUGREPORT "$PACKAGE_BUGREPORT"
+_ACEOF
+
+cat >>confdefs.h <<_ACEOF
+#define PACKAGE_URL "$PACKAGE_URL"
+_ACEOF
+
+
+# Let the site file select an alternate cache file if it wants to.
+# Prefer an explicitly selected file to automatically selected ones.
+ac_site_file1=NONE
+ac_site_file2=NONE
+if test -n "$CONFIG_SITE"; then
+  # We do not want a PATH search for config.site.
+  case $CONFIG_SITE in #((
+    -*)  ac_site_file1=./$CONFIG_SITE;;
+    */*) ac_site_file1=$CONFIG_SITE;;
+    *)   ac_site_file1=./$CONFIG_SITE;;
+  esac
+elif test "x$prefix" != xNONE; then
+  ac_site_file1=$prefix/share/config.site
+  ac_site_file2=$prefix/etc/config.site
+else
+  ac_site_file1=$ac_default_prefix/share/config.site
+  ac_site_file2=$ac_default_prefix/etc/config.site
+fi
+for ac_site_file in "$ac_site_file1" "$ac_site_file2"
+do
+  test "x$ac_site_file" = xNONE && continue
+  if test /dev/null != "$ac_site_file" && test -r "$ac_site_file"; then
+    { $as_echo "$as_me:${as_lineno-$LINENO}: loading site script $ac_site_file" >&5
+$as_echo "$as_me: loading site script $ac_site_file" >&6;}
+    sed 's/^/| /' "$ac_site_file" >&5
+    . "$ac_site_file" \
+      || { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error $? "failed to load site script $ac_site_file
+See \`config.log' for more details" "$LINENO" 5; }
+  fi
+done
+
+if test -r "$cache_file"; then
+  # Some versions of bash will fail to source /dev/null (special files
+  # actually), so we avoid doing that.  DJGPP emulates it as a regular file.
+  if test /dev/null != "$cache_file" && test -f "$cache_file"; then
+    { $as_echo "$as_me:${as_lineno-$LINENO}: loading cache $cache_file" >&5
+$as_echo "$as_me: loading cache $cache_file" >&6;}
+    case $cache_file in
+      [\\/]* | ?:[\\/]* ) . "$cache_file";;
+      *)                      . "./$cache_file";;
+    esac
+  fi
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: creating cache $cache_file" >&5
+$as_echo "$as_me: creating cache $cache_file" >&6;}
+  >$cache_file
+fi
+
+# Check that the precious variables saved in the cache have kept the same
+# value.
+ac_cache_corrupted=false
+for ac_var in $ac_precious_vars; do
+  eval ac_old_set=\$ac_cv_env_${ac_var}_set
+  eval ac_new_set=\$ac_env_${ac_var}_set
+  eval ac_old_val=\$ac_cv_env_${ac_var}_value
+  eval ac_new_val=\$ac_env_${ac_var}_value
+  case $ac_old_set,$ac_new_set in
+    set,)
+      { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&5
+$as_echo "$as_me: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&2;}
+      ac_cache_corrupted=: ;;
+    ,set)
+      { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' was not set in the previous run" >&5
+$as_echo "$as_me: error: \`$ac_var' was not set in the previous run" >&2;}
+      ac_cache_corrupted=: ;;
+    ,);;
+    *)
+      if test "x$ac_old_val" != "x$ac_new_val"; then
+	# differences in whitespace do not lead to failure.
+	ac_old_val_w=`echo x $ac_old_val`
+	ac_new_val_w=`echo x $ac_new_val`
+	if test "$ac_old_val_w" != "$ac_new_val_w"; then
+	  { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' has changed since the previous run:" >&5
+$as_echo "$as_me: error: \`$ac_var' has changed since the previous run:" >&2;}
+	  ac_cache_corrupted=:
+	else
+	  { $as_echo "$as_me:${as_lineno-$LINENO}: warning: ignoring whitespace changes in \`$ac_var' since the previous run:" >&5
+$as_echo "$as_me: warning: ignoring whitespace changes in \`$ac_var' since the previous run:" >&2;}
+	  eval $ac_var=\$ac_old_val
+	fi
+	{ $as_echo "$as_me:${as_lineno-$LINENO}:   former value:  \`$ac_old_val'" >&5
+$as_echo "$as_me:   former value:  \`$ac_old_val'" >&2;}
+	{ $as_echo "$as_me:${as_lineno-$LINENO}:   current value: \`$ac_new_val'" >&5
+$as_echo "$as_me:   current value: \`$ac_new_val'" >&2;}
+      fi;;
+  esac
+  # Pass precious variables to config.status.
+  if test "$ac_new_set" = set; then
+    case $ac_new_val in
+    *\'*) ac_arg=$ac_var=`$as_echo "$ac_new_val" | sed "s/'/'\\\\\\\\''/g"` ;;
+    *) ac_arg=$ac_var=$ac_new_val ;;
+    esac
+    case " $ac_configure_args " in
+      *" '$ac_arg' "*) ;; # Avoid dups.  Use of quotes ensures accuracy.
+      *) as_fn_append ac_configure_args " '$ac_arg'" ;;
+    esac
+  fi
+done
+if $ac_cache_corrupted; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+  { $as_echo "$as_me:${as_lineno-$LINENO}: error: changes in the environment can compromise the build" >&5
+$as_echo "$as_me: error: changes in the environment can compromise the build" >&2;}
+  as_fn_error $? "run \`make distclean' and/or \`rm $cache_file' and start over" "$LINENO" 5
+fi
+## -------------------- ##
+## Main body of script. ##
+## -------------------- ##
+
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+
+
+
+am__api_version='1.13'
+
+ac_aux_dir=
+for ac_dir in "$srcdir" "$srcdir/.." "$srcdir/../.."; do
+  if test -f "$ac_dir/install-sh"; then
+    ac_aux_dir=$ac_dir
+    ac_install_sh="$ac_aux_dir/install-sh -c"
+    break
+  elif test -f "$ac_dir/install.sh"; then
+    ac_aux_dir=$ac_dir
+    ac_install_sh="$ac_aux_dir/install.sh -c"
+    break
+  elif test -f "$ac_dir/shtool"; then
+    ac_aux_dir=$ac_dir
+    ac_install_sh="$ac_aux_dir/shtool install -c"
+    break
+  fi
+done
+if test -z "$ac_aux_dir"; then
+  as_fn_error $? "cannot find install-sh, install.sh, or shtool in \"$srcdir\" \"$srcdir/..\" \"$srcdir/../..\"" "$LINENO" 5
+fi
+
+# These three variables are undocumented and unsupported,
+# and are intended to be withdrawn in a future Autoconf release.
+# They can cause serious problems if a builder's source tree is in a directory
+# whose full name contains unusual characters.
+ac_config_guess="$SHELL $ac_aux_dir/config.guess"  # Please don't use this var.
+ac_config_sub="$SHELL $ac_aux_dir/config.sub"  # Please don't use this var.
+ac_configure="$SHELL $ac_aux_dir/configure"  # Please don't use this var.
+
+
+# Find a good install program.  We prefer a C program (faster),
+# so one script is as good as another.  But avoid the broken or
+# incompatible versions:
+# SysV /etc/install, /usr/sbin/install
+# SunOS /usr/etc/install
+# IRIX /sbin/install
+# AIX /bin/install
+# AmigaOS /C/install, which installs bootblocks on floppy discs
+# AIX 4 /usr/bin/installbsd, which doesn't work without a -g flag
+# AFS /usr/afsws/bin/install, which mishandles nonexistent args
+# SVR4 /usr/ucb/install, which tries to use the nonexistent group "staff"
+# OS/2's system install, which has a completely different semantic
+# ./install, which can be erroneously created by make from ./install.sh.
+# Reject install programs that cannot install multiple files.
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for a BSD-compatible install" >&5
+$as_echo_n "checking for a BSD-compatible install... " >&6; }
+if test -z "$INSTALL"; then
+if ${ac_cv_path_install+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    # Account for people who put trailing slashes in PATH elements.
+case $as_dir/ in #((
+  ./ | .// | /[cC]/* | \
+  /etc/* | /usr/sbin/* | /usr/etc/* | /sbin/* | /usr/afsws/bin/* | \
+  ?:[\\/]os2[\\/]install[\\/]* | ?:[\\/]OS2[\\/]INSTALL[\\/]* | \
+  /usr/ucb/* ) ;;
+  *)
+    # OSF1 and SCO ODT 3.0 have their own names for install.
+    # Don't use installbsd from OSF since it installs stuff as root
+    # by default.
+    for ac_prog in ginstall scoinst install; do
+      for ac_exec_ext in '' $ac_executable_extensions; do
+	if as_fn_executable_p "$as_dir/$ac_prog$ac_exec_ext"; then
+	  if test $ac_prog = install &&
+	    grep dspmsg "$as_dir/$ac_prog$ac_exec_ext" >/dev/null 2>&1; then
+	    # AIX install.  It has an incompatible calling convention.
+	    :
+	  elif test $ac_prog = install &&
+	    grep pwplus "$as_dir/$ac_prog$ac_exec_ext" >/dev/null 2>&1; then
+	    # program-specific install script used by HP pwplus--don't use.
+	    :
+	  else
+	    rm -rf conftest.one conftest.two conftest.dir
+	    echo one > conftest.one
+	    echo two > conftest.two
+	    mkdir conftest.dir
+	    if "$as_dir/$ac_prog$ac_exec_ext" -c conftest.one conftest.two "`pwd`/conftest.dir" &&
+	      test -s conftest.one && test -s conftest.two &&
+	      test -s conftest.dir/conftest.one &&
+	      test -s conftest.dir/conftest.two
+	    then
+	      ac_cv_path_install="$as_dir/$ac_prog$ac_exec_ext -c"
+	      break 3
+	    fi
+	  fi
+	fi
+      done
+    done
+    ;;
+esac
+
+  done
+IFS=$as_save_IFS
+
+rm -rf conftest.one conftest.two conftest.dir
+
+fi
+  if test "${ac_cv_path_install+set}" = set; then
+    INSTALL=$ac_cv_path_install
+  else
+    # As a last resort, use the slow shell script.  Don't cache a
+    # value for INSTALL within a source directory, because that will
+    # break other packages using the cache if that directory is
+    # removed, or if the value is a relative name.
+    INSTALL=$ac_install_sh
+  fi
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $INSTALL" >&5
+$as_echo "$INSTALL" >&6; }
+
+# Use test -z because SunOS4 sh mishandles braces in ${var-val}.
+# It thinks the first close brace ends the variable substitution.
+test -z "$INSTALL_PROGRAM" && INSTALL_PROGRAM='${INSTALL}'
+
+test -z "$INSTALL_SCRIPT" && INSTALL_SCRIPT='${INSTALL}'
+
+test -z "$INSTALL_DATA" && INSTALL_DATA='${INSTALL} -m 644'
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether build environment is sane" >&5
+$as_echo_n "checking whether build environment is sane... " >&6; }
+# Reject unsafe characters in $srcdir or the absolute working directory
+# name.  Accept space and tab only in the latter.
+am_lf='
+'
+case `pwd` in
+  *[\\\"\#\$\&\'\`$am_lf]*)
+    as_fn_error $? "unsafe absolute working directory name" "$LINENO" 5;;
+esac
+case $srcdir in
+  *[\\\"\#\$\&\'\`$am_lf\ \	]*)
+    as_fn_error $? "unsafe srcdir value: '$srcdir'" "$LINENO" 5;;
+esac
+
+# Do 'set' in a subshell so we don't clobber the current shell's
+# arguments.  Must try -L first in case configure is actually a
+# symlink; some systems play weird games with the mod time of symlinks
+# (eg FreeBSD returns the mod time of the symlink's containing
+# directory).
+if (
+   am_has_slept=no
+   for am_try in 1 2; do
+     echo "timestamp, slept: $am_has_slept" > conftest.file
+     set X `ls -Lt "$srcdir/configure" conftest.file 2> /dev/null`
+     if test "$*" = "X"; then
+	# -L didn't work.
+	set X `ls -t "$srcdir/configure" conftest.file`
+     fi
+     if test "$*" != "X $srcdir/configure conftest.file" \
+	&& test "$*" != "X conftest.file $srcdir/configure"; then
+
+	# If neither matched, then we have a broken ls.  This can happen
+	# if, for instance, CONFIG_SHELL is bash and it inherits a
+	# broken ls alias from the environment.  This has actually
+	# happened.  Such a system could not be considered "sane".
+	as_fn_error $? "ls -t appears to fail.  Make sure there is not a broken
+  alias in your environment" "$LINENO" 5
+     fi
+     if test "$2" = conftest.file || test $am_try -eq 2; then
+       break
+     fi
+     # Just in case.
+     sleep 1
+     am_has_slept=yes
+   done
+   test "$2" = conftest.file
+   )
+then
+   # Ok.
+   :
+else
+   as_fn_error $? "newly created file is older than distributed files!
+Check your system clock" "$LINENO" 5
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+# If we didn't sleep, we still need to ensure time stamps of config.status and
+# generated files are strictly newer.
+am_sleep_pid=
+if grep 'slept: no' conftest.file >/dev/null 2>&1; then
+  ( sleep 1 ) &
+  am_sleep_pid=$!
+fi
+
+rm -f conftest.file
+
+test "$program_prefix" != NONE &&
+  program_transform_name="s&^&$program_prefix&;$program_transform_name"
+# Use a double $ so make ignores it.
+test "$program_suffix" != NONE &&
+  program_transform_name="s&\$&$program_suffix&;$program_transform_name"
+# Double any \ or $.
+# By default was `s,x,x', remove it if useless.
+ac_script='s/[\\$]/&&/g;s/;s,x,x,$//'
+program_transform_name=`$as_echo "$program_transform_name" | sed "$ac_script"`
+
+# expand $ac_aux_dir to an absolute path
+am_aux_dir=`cd $ac_aux_dir && pwd`
+
+if test x"${MISSING+set}" != xset; then
+  case $am_aux_dir in
+  *\ * | *\	*)
+    MISSING="\${SHELL} \"$am_aux_dir/missing\"" ;;
+  *)
+    MISSING="\${SHELL} $am_aux_dir/missing" ;;
+  esac
+fi
+# Use eval to expand $SHELL
+if eval "$MISSING --is-lightweight"; then
+  am_missing_run="$MISSING "
+else
+  am_missing_run=
+  { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: 'missing' script is too old or missing" >&5
+$as_echo "$as_me: WARNING: 'missing' script is too old or missing" >&2;}
+fi
+
+if test x"${install_sh}" != xset; then
+  case $am_aux_dir in
+  *\ * | *\	*)
+    install_sh="\${SHELL} '$am_aux_dir/install-sh'" ;;
+  *)
+    install_sh="\${SHELL} $am_aux_dir/install-sh"
+  esac
+fi
+
+# Installed binaries are usually stripped using 'strip' when the user
+# run "make install-strip".  However 'strip' might not be the right
+# tool to use in cross-compilation environments, therefore Automake
+# will honor the 'STRIP' environment variable to overrule this program.
+if test "$cross_compiling" != no; then
+  if test -n "$ac_tool_prefix"; then
+  # Extract the first word of "${ac_tool_prefix}strip", so it can be a program name with args.
+set dummy ${ac_tool_prefix}strip; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_STRIP+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  if test -n "$STRIP"; then
+  ac_cv_prog_STRIP="$STRIP" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    ac_cv_prog_STRIP="${ac_tool_prefix}strip"
+    $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
+
+fi
+fi
+STRIP=$ac_cv_prog_STRIP
+if test -n "$STRIP"; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $STRIP" >&5
+$as_echo "$STRIP" >&6; }
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+fi
+if test -z "$ac_cv_prog_STRIP"; then
+  ac_ct_STRIP=$STRIP
+  # Extract the first word of "strip", so it can be a program name with args.
+set dummy strip; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_ac_ct_STRIP+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  if test -n "$ac_ct_STRIP"; then
+  ac_cv_prog_ac_ct_STRIP="$ac_ct_STRIP" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    ac_cv_prog_ac_ct_STRIP="strip"
+    $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
+
+fi
+fi
+ac_ct_STRIP=$ac_cv_prog_ac_ct_STRIP
+if test -n "$ac_ct_STRIP"; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_STRIP" >&5
+$as_echo "$ac_ct_STRIP" >&6; }
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+  if test "x$ac_ct_STRIP" = x; then
+    STRIP=":"
+  else
+    case $cross_compiling:$ac_tool_warned in
+yes:)
+{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
+$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+ac_tool_warned=yes ;;
+esac
+    STRIP=$ac_ct_STRIP
+  fi
+else
+  STRIP="$ac_cv_prog_STRIP"
+fi
+
+fi
+INSTALL_STRIP_PROGRAM="\$(install_sh) -c -s"
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for a thread-safe mkdir -p" >&5
+$as_echo_n "checking for a thread-safe mkdir -p... " >&6; }
+if test -z "$MKDIR_P"; then
+  if ${ac_cv_path_mkdir+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH$PATH_SEPARATOR/opt/sfw/bin
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    for ac_prog in mkdir gmkdir; do
+	 for ac_exec_ext in '' $ac_executable_extensions; do
+	   as_fn_executable_p "$as_dir/$ac_prog$ac_exec_ext" || continue
+	   case `"$as_dir/$ac_prog$ac_exec_ext" --version 2>&1` in #(
+	     'mkdir (GNU coreutils) '* | \
+	     'mkdir (coreutils) '* | \
+	     'mkdir (fileutils) '4.1*)
+	       ac_cv_path_mkdir=$as_dir/$ac_prog$ac_exec_ext
+	       break 3;;
+	   esac
+	 done
+       done
+  done
+IFS=$as_save_IFS
+
+fi
+
+  test -d ./--version && rmdir ./--version
+  if test "${ac_cv_path_mkdir+set}" = set; then
+    MKDIR_P="$ac_cv_path_mkdir -p"
+  else
+    # As a last resort, use the slow shell script.  Don't cache a
+    # value for MKDIR_P within a source directory, because that will
+    # break other packages using the cache if that directory is
+    # removed, or if the value is a relative name.
+    MKDIR_P="$ac_install_sh -d"
+  fi
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $MKDIR_P" >&5
+$as_echo "$MKDIR_P" >&6; }
+
+for ac_prog in gawk mawk nawk awk
+do
+  # Extract the first word of "$ac_prog", so it can be a program name with args.
+set dummy $ac_prog; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_AWK+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  if test -n "$AWK"; then
+  ac_cv_prog_AWK="$AWK" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    ac_cv_prog_AWK="$ac_prog"
+    $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
+
+fi
+fi
+AWK=$ac_cv_prog_AWK
+if test -n "$AWK"; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $AWK" >&5
+$as_echo "$AWK" >&6; }
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+  test -n "$AWK" && break
+done
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether ${MAKE-make} sets \$(MAKE)" >&5
+$as_echo_n "checking whether ${MAKE-make} sets \$(MAKE)... " >&6; }
+set x ${MAKE-make}
+ac_make=`$as_echo "$2" | sed 's/+/p/g; s/[^a-zA-Z0-9_]/_/g'`
+if eval \${ac_cv_prog_make_${ac_make}_set+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  cat >conftest.make <<\_ACEOF
+SHELL = /bin/sh
+all:
+	@echo '@@@%%%=$(MAKE)=@@@%%%'
+_ACEOF
+# GNU make sometimes prints "make[1]: Entering ...", which would confuse us.
+case `${MAKE-make} -f conftest.make 2>/dev/null` in
+  *@@@%%%=?*=@@@%%%*)
+    eval ac_cv_prog_make_${ac_make}_set=yes;;
+  *)
+    eval ac_cv_prog_make_${ac_make}_set=no;;
+esac
+rm -f conftest.make
+fi
+if eval test \$ac_cv_prog_make_${ac_make}_set = yes; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+  SET_MAKE=
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+  SET_MAKE="MAKE=${MAKE-make}"
+fi
+
+rm -rf .tst 2>/dev/null
+mkdir .tst 2>/dev/null
+if test -d .tst; then
+  am__leading_dot=.
+else
+  am__leading_dot=_
+fi
+rmdir .tst 2>/dev/null
+
+# Check whether --enable-silent-rules was given.
+if test "${enable_silent_rules+set}" = set; then :
+  enableval=$enable_silent_rules;
+fi
+
+case $enable_silent_rules in # (((
+  yes) AM_DEFAULT_VERBOSITY=0;;
+   no) AM_DEFAULT_VERBOSITY=1;;
+    *) AM_DEFAULT_VERBOSITY=1;;
+esac
+am_make=${MAKE-make}
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $am_make supports nested variables" >&5
+$as_echo_n "checking whether $am_make supports nested variables... " >&6; }
+if ${am_cv_make_support_nested_variables+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  if $as_echo 'TRUE=$(BAR$(V))
+BAR0=false
+BAR1=true
+V=1
+am__doit:
+	@$(TRUE)
+.PHONY: am__doit' | $am_make -f - >/dev/null 2>&1; then
+  am_cv_make_support_nested_variables=yes
+else
+  am_cv_make_support_nested_variables=no
+fi
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $am_cv_make_support_nested_variables" >&5
+$as_echo "$am_cv_make_support_nested_variables" >&6; }
+if test $am_cv_make_support_nested_variables = yes; then
+    AM_V='$(V)'
+  AM_DEFAULT_V='$(AM_DEFAULT_VERBOSITY)'
+else
+  AM_V=$AM_DEFAULT_VERBOSITY
+  AM_DEFAULT_V=$AM_DEFAULT_VERBOSITY
+fi
+AM_BACKSLASH='\'
+
+if test "`cd $srcdir && pwd`" != "`pwd`"; then
+  # Use -I$(srcdir) only when $(srcdir) != ., so that make's output
+  # is not polluted with repeated "-I."
+  am__isrc=' -I$(srcdir)'
+  # test to see if srcdir already configured
+  if test -f $srcdir/config.status; then
+    as_fn_error $? "source directory already configured; run \"make distclean\" there first" "$LINENO" 5
+  fi
+fi
+
+# test whether we have cygpath
+if test -z "$CYGPATH_W"; then
+  if (cygpath --version) >/dev/null 2>/dev/null; then
+    CYGPATH_W='cygpath -w'
+  else
+    CYGPATH_W=echo
+  fi
+fi
+
+
+# Define the identity of the package.
+ PACKAGE='bluez'
+ VERSION='5.17'
+
+
+cat >>confdefs.h <<_ACEOF
+#define PACKAGE "$PACKAGE"
+_ACEOF
+
+
+cat >>confdefs.h <<_ACEOF
+#define VERSION "$VERSION"
+_ACEOF
+
+# Some tools Automake needs.
+
+ACLOCAL=${ACLOCAL-"${am_missing_run}aclocal-${am__api_version}"}
+
+
+AUTOCONF=${AUTOCONF-"${am_missing_run}autoconf"}
+
+
+AUTOMAKE=${AUTOMAKE-"${am_missing_run}automake-${am__api_version}"}
+
+
+AUTOHEADER=${AUTOHEADER-"${am_missing_run}autoheader"}
+
+
+MAKEINFO=${MAKEINFO-"${am_missing_run}makeinfo"}
+
+# For better backward compatibility.  To be removed once Automake 1.9.x
+# dies out for good.  For more background, see:
+# <http://lists.gnu.org/archive/html/automake/2012-07/msg00001.html>
+# <http://lists.gnu.org/archive/html/automake/2012-07/msg00014.html>
+mkdir_p='$(MKDIR_P)'
+
+# We need awk for the "check" target.  The system "awk" is bad on
+# some platforms.
+# Always define AMTAR for backward compatibility.  Yes, it's still used
+# in the wild :-(  We should find a proper way to deprecate it ...
+AMTAR='$${TAR-tar}'
+
+
+# We'll loop over all known methods to create a tar archive until one works.
+_am_tools='gnutar  pax cpio none'
+
+
+
+  { $as_echo "$as_me:${as_lineno-$LINENO}: checking how to create a pax tar archive" >&5
+$as_echo_n "checking how to create a pax tar archive... " >&6; }
+
+  # Go ahead even if we have the value already cached.  We do so because we
+  # need to set the values for the 'am__tar' and 'am__untar' variables.
+  _am_tools=${am_cv_prog_tar_pax-$_am_tools}
+
+  for _am_tool in $_am_tools; do
+    case $_am_tool in
+    gnutar)
+      for _am_tar in tar gnutar gtar; do
+        { echo "$as_me:$LINENO: $_am_tar --version" >&5
+   ($_am_tar --version) >&5 2>&5
+   ac_status=$?
+   echo "$as_me:$LINENO: \$? = $ac_status" >&5
+   (exit $ac_status); } && break
+      done
+      am__tar="$_am_tar --format=posix -chf - "'"$$tardir"'
+      am__tar_="$_am_tar --format=posix -chf - "'"$tardir"'
+      am__untar="$_am_tar -xf -"
+      ;;
+    plaintar)
+      # Must skip GNU tar: if it does not support --format= it doesn't create
+      # ustar tarball either.
+      (tar --version) >/dev/null 2>&1 && continue
+      am__tar='tar chf - "$$tardir"'
+      am__tar_='tar chf - "$tardir"'
+      am__untar='tar xf -'
+      ;;
+    pax)
+      am__tar='pax -L -x pax -w "$$tardir"'
+      am__tar_='pax -L -x pax -w "$tardir"'
+      am__untar='pax -r'
+      ;;
+    cpio)
+      am__tar='find "$$tardir" -print | cpio -o -H pax -L'
+      am__tar_='find "$tardir" -print | cpio -o -H pax -L'
+      am__untar='cpio -i -H pax -d'
+      ;;
+    none)
+      am__tar=false
+      am__tar_=false
+      am__untar=false
+      ;;
+    esac
+
+    # If the value was cached, stop now.  We just wanted to have am__tar
+    # and am__untar set.
+    test -n "${am_cv_prog_tar_pax}" && break
+
+    # tar/untar a dummy directory, and stop if the command works.
+    rm -rf conftest.dir
+    mkdir conftest.dir
+    echo GrepMe > conftest.dir/file
+    { echo "$as_me:$LINENO: tardir=conftest.dir && eval $am__tar_ >conftest.tar" >&5
+   (tardir=conftest.dir && eval $am__tar_ >conftest.tar) >&5 2>&5
+   ac_status=$?
+   echo "$as_me:$LINENO: \$? = $ac_status" >&5
+   (exit $ac_status); }
+    rm -rf conftest.dir
+    if test -s conftest.tar; then
+      { echo "$as_me:$LINENO: $am__untar <conftest.tar" >&5
+   ($am__untar <conftest.tar) >&5 2>&5
+   ac_status=$?
+   echo "$as_me:$LINENO: \$? = $ac_status" >&5
+   (exit $ac_status); }
+      { echo "$as_me:$LINENO: cat conftest.dir/file" >&5
+   (cat conftest.dir/file) >&5 2>&5
+   ac_status=$?
+   echo "$as_me:$LINENO: \$? = $ac_status" >&5
+   (exit $ac_status); }
+      grep GrepMe conftest.dir/file >/dev/null 2>&1 && break
+    fi
+  done
+  rm -rf conftest.dir
+
+  if ${am_cv_prog_tar_pax+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  am_cv_prog_tar_pax=$_am_tool
+fi
+
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $am_cv_prog_tar_pax" >&5
+$as_echo "$am_cv_prog_tar_pax" >&6; }
+
+
+
+
+
+
+ac_config_headers="$ac_config_headers config.h"
+
+DEPDIR="${am__leading_dot}deps"
+
+ac_config_commands="$ac_config_commands depfiles"
+
+
+am_make=${MAKE-make}
+cat > confinc << 'END'
+am__doit:
+	@echo this is the am__doit target
+.PHONY: am__doit
+END
+# If we don't find an include directive, just comment out the code.
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for style of include used by $am_make" >&5
+$as_echo_n "checking for style of include used by $am_make... " >&6; }
+am__include="#"
+am__quote=
+_am_result=none
+# First try GNU make style include.
+echo "include confinc" > confmf
+# Ignore all kinds of additional output from 'make'.
+case `$am_make -s -f confmf 2> /dev/null` in #(
+*the\ am__doit\ target*)
+  am__include=include
+  am__quote=
+  _am_result=GNU
+  ;;
+esac
+# Now try BSD make style include.
+if test "$am__include" = "#"; then
+   echo '.include "confinc"' > confmf
+   case `$am_make -s -f confmf 2> /dev/null` in #(
+   *the\ am__doit\ target*)
+     am__include=.include
+     am__quote="\""
+     _am_result=BSD
+     ;;
+   esac
+fi
+
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $_am_result" >&5
+$as_echo "$_am_result" >&6; }
+rm -f confinc confmf
+
+# Check whether --enable-dependency-tracking was given.
+if test "${enable_dependency_tracking+set}" = set; then :
+  enableval=$enable_dependency_tracking;
+fi
+
+if test "x$enable_dependency_tracking" != xno; then
+  am_depcomp="$ac_aux_dir/depcomp"
+  AMDEPBACKSLASH='\'
+  am__nodep='_no'
+fi
+ if test "x$enable_dependency_tracking" != xno; then
+  AMDEP_TRUE=
+  AMDEP_FALSE='#'
+else
+  AMDEP_TRUE='#'
+  AMDEP_FALSE=
+fi
+
+
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+if test -n "$ac_tool_prefix"; then
+  # Extract the first word of "${ac_tool_prefix}gcc", so it can be a program name with args.
+set dummy ${ac_tool_prefix}gcc; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_CC+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  if test -n "$CC"; then
+  ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    ac_cv_prog_CC="${ac_tool_prefix}gcc"
+    $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
+
+fi
+fi
+CC=$ac_cv_prog_CC
+if test -n "$CC"; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5
+$as_echo "$CC" >&6; }
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+fi
+if test -z "$ac_cv_prog_CC"; then
+  ac_ct_CC=$CC
+  # Extract the first word of "gcc", so it can be a program name with args.
+set dummy gcc; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_ac_ct_CC+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  if test -n "$ac_ct_CC"; then
+  ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    ac_cv_prog_ac_ct_CC="gcc"
+    $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
+
+fi
+fi
+ac_ct_CC=$ac_cv_prog_ac_ct_CC
+if test -n "$ac_ct_CC"; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5
+$as_echo "$ac_ct_CC" >&6; }
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+  if test "x$ac_ct_CC" = x; then
+    CC=""
+  else
+    case $cross_compiling:$ac_tool_warned in
+yes:)
+{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
+$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+ac_tool_warned=yes ;;
+esac
+    CC=$ac_ct_CC
+  fi
+else
+  CC="$ac_cv_prog_CC"
+fi
+
+if test -z "$CC"; then
+          if test -n "$ac_tool_prefix"; then
+    # Extract the first word of "${ac_tool_prefix}cc", so it can be a program name with args.
+set dummy ${ac_tool_prefix}cc; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_CC+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  if test -n "$CC"; then
+  ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    ac_cv_prog_CC="${ac_tool_prefix}cc"
+    $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
+
+fi
+fi
+CC=$ac_cv_prog_CC
+if test -n "$CC"; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5
+$as_echo "$CC" >&6; }
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+  fi
+fi
+if test -z "$CC"; then
+  # Extract the first word of "cc", so it can be a program name with args.
+set dummy cc; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_CC+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  if test -n "$CC"; then
+  ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+  ac_prog_rejected=no
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    if test "$as_dir/$ac_word$ac_exec_ext" = "/usr/ucb/cc"; then
+       ac_prog_rejected=yes
+       continue
+     fi
+    ac_cv_prog_CC="cc"
+    $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
+
+if test $ac_prog_rejected = yes; then
+  # We found a bogon in the path, so make sure we never use it.
+  set dummy $ac_cv_prog_CC
+  shift
+  if test $# != 0; then
+    # We chose a different compiler from the bogus one.
+    # However, it has the same basename, so the bogon will be chosen
+    # first if we set CC to just the basename; use the full file name.
+    shift
+    ac_cv_prog_CC="$as_dir/$ac_word${1+' '}$@"
+  fi
+fi
+fi
+fi
+CC=$ac_cv_prog_CC
+if test -n "$CC"; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5
+$as_echo "$CC" >&6; }
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+fi
+if test -z "$CC"; then
+  if test -n "$ac_tool_prefix"; then
+  for ac_prog in cl.exe
+  do
+    # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args.
+set dummy $ac_tool_prefix$ac_prog; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_CC+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  if test -n "$CC"; then
+  ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    ac_cv_prog_CC="$ac_tool_prefix$ac_prog"
+    $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
+
+fi
+fi
+CC=$ac_cv_prog_CC
+if test -n "$CC"; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5
+$as_echo "$CC" >&6; }
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+    test -n "$CC" && break
+  done
+fi
+if test -z "$CC"; then
+  ac_ct_CC=$CC
+  for ac_prog in cl.exe
+do
+  # Extract the first word of "$ac_prog", so it can be a program name with args.
+set dummy $ac_prog; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_ac_ct_CC+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  if test -n "$ac_ct_CC"; then
+  ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    ac_cv_prog_ac_ct_CC="$ac_prog"
+    $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
+
+fi
+fi
+ac_ct_CC=$ac_cv_prog_ac_ct_CC
+if test -n "$ac_ct_CC"; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5
+$as_echo "$ac_ct_CC" >&6; }
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+  test -n "$ac_ct_CC" && break
+done
+
+  if test "x$ac_ct_CC" = x; then
+    CC=""
+  else
+    case $cross_compiling:$ac_tool_warned in
+yes:)
+{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
+$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+ac_tool_warned=yes ;;
+esac
+    CC=$ac_ct_CC
+  fi
+fi
+
+fi
+
+
+test -z "$CC" && { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error $? "no acceptable C compiler found in \$PATH
+See \`config.log' for more details" "$LINENO" 5; }
+
+# Provide some information about the compiler.
+$as_echo "$as_me:${as_lineno-$LINENO}: checking for C compiler version" >&5
+set X $ac_compile
+ac_compiler=$2
+for ac_option in --version -v -V -qversion; do
+  { { ac_try="$ac_compiler $ac_option >&5"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+$as_echo "$ac_try_echo"; } >&5
+  (eval "$ac_compiler $ac_option >&5") 2>conftest.err
+  ac_status=$?
+  if test -s conftest.err; then
+    sed '10a\
+... rest of stderr output deleted ...
+         10q' conftest.err >conftest.er1
+    cat conftest.er1 >&5
+  fi
+  rm -f conftest.er1 conftest.err
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; }
+done
+
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+ac_clean_files_save=$ac_clean_files
+ac_clean_files="$ac_clean_files a.out a.out.dSYM a.exe b.out"
+# Try to create an executable without -o first, disregard a.out.
+# It will help us diagnose broken compilers, and finding out an intuition
+# of exeext.
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the C compiler works" >&5
+$as_echo_n "checking whether the C compiler works... " >&6; }
+ac_link_default=`$as_echo "$ac_link" | sed 's/ -o *conftest[^ ]*//'`
+
+# The possible output files:
+ac_files="a.out conftest.exe conftest a.exe a_out.exe b.out conftest.*"
+
+ac_rmfiles=
+for ac_file in $ac_files
+do
+  case $ac_file in
+    *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;;
+    * ) ac_rmfiles="$ac_rmfiles $ac_file";;
+  esac
+done
+rm -f $ac_rmfiles
+
+if { { ac_try="$ac_link_default"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+$as_echo "$ac_try_echo"; } >&5
+  (eval "$ac_link_default") 2>&5
+  ac_status=$?
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; }; then :
+  # Autoconf-2.13 could set the ac_cv_exeext variable to `no'.
+# So ignore a value of `no', otherwise this would lead to `EXEEXT = no'
+# in a Makefile.  We should not override ac_cv_exeext if it was cached,
+# so that the user can short-circuit this test for compilers unknown to
+# Autoconf.
+for ac_file in $ac_files ''
+do
+  test -f "$ac_file" || continue
+  case $ac_file in
+    *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj )
+	;;
+    [ab].out )
+	# We found the default executable, but exeext='' is most
+	# certainly right.
+	break;;
+    *.* )
+	if test "${ac_cv_exeext+set}" = set && test "$ac_cv_exeext" != no;
+	then :; else
+	   ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'`
+	fi
+	# We set ac_cv_exeext here because the later test for it is not
+	# safe: cross compilers may not add the suffix if given an `-o'
+	# argument, so we may need to know it at that point already.
+	# Even if this section looks crufty: it has the advantage of
+	# actually working.
+	break;;
+    * )
+	break;;
+  esac
+done
+test "$ac_cv_exeext" = no && ac_cv_exeext=
+
+else
+  ac_file=''
+fi
+if test -z "$ac_file"; then :
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+$as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+{ { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error 77 "C compiler cannot create executables
+See \`config.log' for more details" "$LINENO" 5; }
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for C compiler default output file name" >&5
+$as_echo_n "checking for C compiler default output file name... " >&6; }
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_file" >&5
+$as_echo "$ac_file" >&6; }
+ac_exeext=$ac_cv_exeext
+
+rm -f -r a.out a.out.dSYM a.exe conftest$ac_cv_exeext b.out
+ac_clean_files=$ac_clean_files_save
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for suffix of executables" >&5
+$as_echo_n "checking for suffix of executables... " >&6; }
+if { { ac_try="$ac_link"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+$as_echo "$ac_try_echo"; } >&5
+  (eval "$ac_link") 2>&5
+  ac_status=$?
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; }; then :
+  # If both `conftest.exe' and `conftest' are `present' (well, observable)
+# catch `conftest.exe'.  For instance with Cygwin, `ls conftest' will
+# work properly (i.e., refer to `conftest.exe'), while it won't with
+# `rm'.
+for ac_file in conftest.exe conftest conftest.*; do
+  test -f "$ac_file" || continue
+  case $ac_file in
+    *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;;
+    *.* ) ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'`
+	  break;;
+    * ) break;;
+  esac
+done
+else
+  { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error $? "cannot compute suffix of executables: cannot compile and link
+See \`config.log' for more details" "$LINENO" 5; }
+fi
+rm -f conftest conftest$ac_cv_exeext
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_exeext" >&5
+$as_echo "$ac_cv_exeext" >&6; }
+
+rm -f conftest.$ac_ext
+EXEEXT=$ac_cv_exeext
+ac_exeext=$EXEEXT
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#include <stdio.h>
+int
+main ()
+{
+FILE *f = fopen ("conftest.out", "w");
+ return ferror (f) || fclose (f) != 0;
+
+  ;
+  return 0;
+}
+_ACEOF
+ac_clean_files="$ac_clean_files conftest.out"
+# Check that the compiler produces executables we can run.  If not, either
+# the compiler is broken, or we cross compile.
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are cross compiling" >&5
+$as_echo_n "checking whether we are cross compiling... " >&6; }
+if test "$cross_compiling" != yes; then
+  { { ac_try="$ac_link"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+$as_echo "$ac_try_echo"; } >&5
+  (eval "$ac_link") 2>&5
+  ac_status=$?
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; }
+  if { ac_try='./conftest$ac_cv_exeext'
+  { { case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+$as_echo "$ac_try_echo"; } >&5
+  (eval "$ac_try") 2>&5
+  ac_status=$?
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; }; }; then
+    cross_compiling=no
+  else
+    if test "$cross_compiling" = maybe; then
+	cross_compiling=yes
+    else
+	{ { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error $? "cannot run C compiled programs.
+If you meant to cross compile, use \`--host'.
+See \`config.log' for more details" "$LINENO" 5; }
+    fi
+  fi
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $cross_compiling" >&5
+$as_echo "$cross_compiling" >&6; }
+
+rm -f conftest.$ac_ext conftest$ac_cv_exeext conftest.out
+ac_clean_files=$ac_clean_files_save
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for suffix of object files" >&5
+$as_echo_n "checking for suffix of object files... " >&6; }
+if ${ac_cv_objext+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.o conftest.obj
+if { { ac_try="$ac_compile"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+$as_echo "$ac_try_echo"; } >&5
+  (eval "$ac_compile") 2>&5
+  ac_status=$?
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; }; then :
+  for ac_file in conftest.o conftest.obj conftest.*; do
+  test -f "$ac_file" || continue;
+  case $ac_file in
+    *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM ) ;;
+    *) ac_cv_objext=`expr "$ac_file" : '.*\.\(.*\)'`
+       break;;
+  esac
+done
+else
+  $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+{ { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error $? "cannot compute suffix of object files: cannot compile
+See \`config.log' for more details" "$LINENO" 5; }
+fi
+rm -f conftest.$ac_cv_objext conftest.$ac_ext
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_objext" >&5
+$as_echo "$ac_cv_objext" >&6; }
+OBJEXT=$ac_cv_objext
+ac_objext=$OBJEXT
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are using the GNU C compiler" >&5
+$as_echo_n "checking whether we are using the GNU C compiler... " >&6; }
+if ${ac_cv_c_compiler_gnu+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+int
+main ()
+{
+#ifndef __GNUC__
+       choke me
+#endif
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+  ac_compiler_gnu=yes
+else
+  ac_compiler_gnu=no
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+ac_cv_c_compiler_gnu=$ac_compiler_gnu
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_compiler_gnu" >&5
+$as_echo "$ac_cv_c_compiler_gnu" >&6; }
+if test $ac_compiler_gnu = yes; then
+  GCC=yes
+else
+  GCC=
+fi
+ac_test_CFLAGS=${CFLAGS+set}
+ac_save_CFLAGS=$CFLAGS
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CC accepts -g" >&5
+$as_echo_n "checking whether $CC accepts -g... " >&6; }
+if ${ac_cv_prog_cc_g+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  ac_save_c_werror_flag=$ac_c_werror_flag
+   ac_c_werror_flag=yes
+   ac_cv_prog_cc_g=no
+   CFLAGS="-g"
+   cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+  ac_cv_prog_cc_g=yes
+else
+  CFLAGS=""
+      cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+
+else
+  ac_c_werror_flag=$ac_save_c_werror_flag
+	 CFLAGS="-g"
+	 cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+  ac_cv_prog_cc_g=yes
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+   ac_c_werror_flag=$ac_save_c_werror_flag
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_g" >&5
+$as_echo "$ac_cv_prog_cc_g" >&6; }
+if test "$ac_test_CFLAGS" = set; then
+  CFLAGS=$ac_save_CFLAGS
+elif test $ac_cv_prog_cc_g = yes; then
+  if test "$GCC" = yes; then
+    CFLAGS="-g -O2"
+  else
+    CFLAGS="-g"
+  fi
+else
+  if test "$GCC" = yes; then
+    CFLAGS="-O2"
+  else
+    CFLAGS=
+  fi
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $CC option to accept ISO C89" >&5
+$as_echo_n "checking for $CC option to accept ISO C89... " >&6; }
+if ${ac_cv_prog_cc_c89+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  ac_cv_prog_cc_c89=no
+ac_save_CC=$CC
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#include <stdarg.h>
+#include <stdio.h>
+struct stat;
+/* Most of the following tests are stolen from RCS 5.7's src/conf.sh.  */
+struct buf { int x; };
+FILE * (*rcsopen) (struct buf *, struct stat *, int);
+static char *e (p, i)
+     char **p;
+     int i;
+{
+  return p[i];
+}
+static char *f (char * (*g) (char **, int), char **p, ...)
+{
+  char *s;
+  va_list v;
+  va_start (v,p);
+  s = g (p, va_arg (v,int));
+  va_end (v);
+  return s;
+}
+
+/* OSF 4.0 Compaq cc is some sort of almost-ANSI by default.  It has
+   function prototypes and stuff, but not '\xHH' hex character constants.
+   These don't provoke an error unfortunately, instead are silently treated
+   as 'x'.  The following induces an error, until -std is added to get
+   proper ANSI mode.  Curiously '\x00'!='x' always comes out true, for an
+   array size at least.  It's necessary to write '\x00'==0 to get something
+   that's true only with -std.  */
+int osf4_cc_array ['\x00' == 0 ? 1 : -1];
+
+/* IBM C 6 for AIX is almost-ANSI by default, but it replaces macro parameters
+   inside strings and character constants.  */
+#define FOO(x) 'x'
+int xlc6_cc_array[FOO(a) == 'x' ? 1 : -1];
+
+int test (int i, double x);
+struct s1 {int (*f) (int a);};
+struct s2 {int (*f) (double a);};
+int pairnames (int, char **, FILE *(*)(struct buf *, struct stat *, int), int, int);
+int argc;
+char **argv;
+int
+main ()
+{
+return f (e, argv, 0) != argv[0]  ||  f (e, argv, 1) != argv[1];
+  ;
+  return 0;
+}
+_ACEOF
+for ac_arg in '' -qlanglvl=extc89 -qlanglvl=ansi -std \
+	-Ae "-Aa -D_HPUX_SOURCE" "-Xc -D__EXTENSIONS__"
+do
+  CC="$ac_save_CC $ac_arg"
+  if ac_fn_c_try_compile "$LINENO"; then :
+  ac_cv_prog_cc_c89=$ac_arg
+fi
+rm -f core conftest.err conftest.$ac_objext
+  test "x$ac_cv_prog_cc_c89" != "xno" && break
+done
+rm -f conftest.$ac_ext
+CC=$ac_save_CC
+
+fi
+# AC_CACHE_VAL
+case "x$ac_cv_prog_cc_c89" in
+  x)
+    { $as_echo "$as_me:${as_lineno-$LINENO}: result: none needed" >&5
+$as_echo "none needed" >&6; } ;;
+  xno)
+    { $as_echo "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5
+$as_echo "unsupported" >&6; } ;;
+  *)
+    CC="$CC $ac_cv_prog_cc_c89"
+    { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c89" >&5
+$as_echo "$ac_cv_prog_cc_c89" >&6; } ;;
+esac
+if test "x$ac_cv_prog_cc_c89" != xno; then :
+
+fi
+
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+
+depcc="$CC"   am_compiler_list=
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking dependency style of $depcc" >&5
+$as_echo_n "checking dependency style of $depcc... " >&6; }
+if ${am_cv_CC_dependencies_compiler_type+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  if test -z "$AMDEP_TRUE" && test -f "$am_depcomp"; then
+  # We make a subdir and do the tests there.  Otherwise we can end up
+  # making bogus files that we don't know about and never remove.  For
+  # instance it was reported that on HP-UX the gcc test will end up
+  # making a dummy file named 'D' -- because '-MD' means "put the output
+  # in D".
+  rm -rf conftest.dir
+  mkdir conftest.dir
+  # Copy depcomp to subdir because otherwise we won't find it if we're
+  # using a relative directory.
+  cp "$am_depcomp" conftest.dir
+  cd conftest.dir
+  # We will build objects and dependencies in a subdirectory because
+  # it helps to detect inapplicable dependency modes.  For instance
+  # both Tru64's cc and ICC support -MD to output dependencies as a
+  # side effect of compilation, but ICC will put the dependencies in
+  # the current directory while Tru64 will put them in the object
+  # directory.
+  mkdir sub
+
+  am_cv_CC_dependencies_compiler_type=none
+  if test "$am_compiler_list" = ""; then
+     am_compiler_list=`sed -n 's/^#*\([a-zA-Z0-9]*\))$/\1/p' < ./depcomp`
+  fi
+  am__universal=false
+  case " $depcc " in #(
+     *\ -arch\ *\ -arch\ *) am__universal=true ;;
+     esac
+
+  for depmode in $am_compiler_list; do
+    # Setup a source with many dependencies, because some compilers
+    # like to wrap large dependency lists on column 80 (with \), and
+    # we should not choose a depcomp mode which is confused by this.
+    #
+    # We need to recreate these files for each test, as the compiler may
+    # overwrite some of them when testing with obscure command lines.
+    # This happens at least with the AIX C compiler.
+    : > sub/conftest.c
+    for i in 1 2 3 4 5 6; do
+      echo '#include "conftst'$i'.h"' >> sub/conftest.c
+      # Using ": > sub/conftst$i.h" creates only sub/conftst1.h with
+      # Solaris 10 /bin/sh.
+      echo '/* dummy */' > sub/conftst$i.h
+    done
+    echo "${am__include} ${am__quote}sub/conftest.Po${am__quote}" > confmf
+
+    # We check with '-c' and '-o' for the sake of the "dashmstdout"
+    # mode.  It turns out that the SunPro C++ compiler does not properly
+    # handle '-M -o', and we need to detect this.  Also, some Intel
+    # versions had trouble with output in subdirs.
+    am__obj=sub/conftest.${OBJEXT-o}
+    am__minus_obj="-o $am__obj"
+    case $depmode in
+    gcc)
+      # This depmode causes a compiler race in universal mode.
+      test "$am__universal" = false || continue
+      ;;
+    nosideeffect)
+      # After this tag, mechanisms are not by side-effect, so they'll
+      # only be used when explicitly requested.
+      if test "x$enable_dependency_tracking" = xyes; then
+	continue
+      else
+	break
+      fi
+      ;;
+    msvc7 | msvc7msys | msvisualcpp | msvcmsys)
+      # This compiler won't grok '-c -o', but also, the minuso test has
+      # not run yet.  These depmodes are late enough in the game, and
+      # so weak that their functioning should not be impacted.
+      am__obj=conftest.${OBJEXT-o}
+      am__minus_obj=
+      ;;
+    none) break ;;
+    esac
+    if depmode=$depmode \
+       source=sub/conftest.c object=$am__obj \
+       depfile=sub/conftest.Po tmpdepfile=sub/conftest.TPo \
+       $SHELL ./depcomp $depcc -c $am__minus_obj sub/conftest.c \
+         >/dev/null 2>conftest.err &&
+       grep sub/conftst1.h sub/conftest.Po > /dev/null 2>&1 &&
+       grep sub/conftst6.h sub/conftest.Po > /dev/null 2>&1 &&
+       grep $am__obj sub/conftest.Po > /dev/null 2>&1 &&
+       ${MAKE-make} -s -f confmf > /dev/null 2>&1; then
+      # icc doesn't choke on unknown options, it will just issue warnings
+      # or remarks (even with -Werror).  So we grep stderr for any message
+      # that says an option was ignored or not supported.
+      # When given -MP, icc 7.0 and 7.1 complain thusly:
+      #   icc: Command line warning: ignoring option '-M'; no argument required
+      # The diagnosis changed in icc 8.0:
+      #   icc: Command line remark: option '-MP' not supported
+      if (grep 'ignoring option' conftest.err ||
+          grep 'not supported' conftest.err) >/dev/null 2>&1; then :; else
+        am_cv_CC_dependencies_compiler_type=$depmode
+        break
+      fi
+    fi
+  done
+
+  cd ..
+  rm -rf conftest.dir
+else
+  am_cv_CC_dependencies_compiler_type=none
+fi
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $am_cv_CC_dependencies_compiler_type" >&5
+$as_echo "$am_cv_CC_dependencies_compiler_type" >&6; }
+CCDEPMODE=depmode=$am_cv_CC_dependencies_compiler_type
+
+ if
+  test "x$enable_dependency_tracking" != xno \
+  && test "$am_cv_CC_dependencies_compiler_type" = gcc3; then
+  am__fastdepCC_TRUE=
+  am__fastdepCC_FALSE='#'
+else
+  am__fastdepCC_TRUE='#'
+  am__fastdepCC_FALSE=
+fi
+
+
+
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking how to run the C preprocessor" >&5
+$as_echo_n "checking how to run the C preprocessor... " >&6; }
+# On Suns, sometimes $CPP names a directory.
+if test -n "$CPP" && test -d "$CPP"; then
+  CPP=
+fi
+if test -z "$CPP"; then
+  if ${ac_cv_prog_CPP+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+      # Double quotes because CPP needs to be expanded
+    for CPP in "$CC -E" "$CC -E -traditional-cpp" "/lib/cpp"
+    do
+      ac_preproc_ok=false
+for ac_c_preproc_warn_flag in '' yes
+do
+  # Use a header file that comes with gcc, so configuring glibc
+  # with a fresh cross-compiler works.
+  # Prefer <limits.h> to <assert.h> if __STDC__ is defined, since
+  # <limits.h> exists even on freestanding compilers.
+  # On the NeXT, cc -E runs the code through the compiler's parser,
+  # not just through cpp. "Syntax error" is here to catch this case.
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#ifdef __STDC__
+# include <limits.h>
+#else
+# include <assert.h>
+#endif
+		     Syntax error
+_ACEOF
+if ac_fn_c_try_cpp "$LINENO"; then :
+
+else
+  # Broken: fails on valid input.
+continue
+fi
+rm -f conftest.err conftest.i conftest.$ac_ext
+
+  # OK, works on sane cases.  Now check whether nonexistent headers
+  # can be detected and how.
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#include <ac_nonexistent.h>
+_ACEOF
+if ac_fn_c_try_cpp "$LINENO"; then :
+  # Broken: success on invalid input.
+continue
+else
+  # Passes both tests.
+ac_preproc_ok=:
+break
+fi
+rm -f conftest.err conftest.i conftest.$ac_ext
+
+done
+# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped.
+rm -f conftest.i conftest.err conftest.$ac_ext
+if $ac_preproc_ok; then :
+  break
+fi
+
+    done
+    ac_cv_prog_CPP=$CPP
+
+fi
+  CPP=$ac_cv_prog_CPP
+else
+  ac_cv_prog_CPP=$CPP
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $CPP" >&5
+$as_echo "$CPP" >&6; }
+ac_preproc_ok=false
+for ac_c_preproc_warn_flag in '' yes
+do
+  # Use a header file that comes with gcc, so configuring glibc
+  # with a fresh cross-compiler works.
+  # Prefer <limits.h> to <assert.h> if __STDC__ is defined, since
+  # <limits.h> exists even on freestanding compilers.
+  # On the NeXT, cc -E runs the code through the compiler's parser,
+  # not just through cpp. "Syntax error" is here to catch this case.
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#ifdef __STDC__
+# include <limits.h>
+#else
+# include <assert.h>
+#endif
+		     Syntax error
+_ACEOF
+if ac_fn_c_try_cpp "$LINENO"; then :
+
+else
+  # Broken: fails on valid input.
+continue
+fi
+rm -f conftest.err conftest.i conftest.$ac_ext
+
+  # OK, works on sane cases.  Now check whether nonexistent headers
+  # can be detected and how.
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#include <ac_nonexistent.h>
+_ACEOF
+if ac_fn_c_try_cpp "$LINENO"; then :
+  # Broken: success on invalid input.
+continue
+else
+  # Passes both tests.
+ac_preproc_ok=:
+break
+fi
+rm -f conftest.err conftest.i conftest.$ac_ext
+
+done
+# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped.
+rm -f conftest.i conftest.err conftest.$ac_ext
+if $ac_preproc_ok; then :
+
+else
+  { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error $? "C preprocessor \"$CPP\" fails sanity check
+See \`config.log' for more details" "$LINENO" 5; }
+fi
+
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for grep that handles long lines and -e" >&5
+$as_echo_n "checking for grep that handles long lines and -e... " >&6; }
+if ${ac_cv_path_GREP+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  if test -z "$GREP"; then
+  ac_path_GREP_found=false
+  # Loop through the user's path and test for each of PROGNAME-LIST
+  as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    for ac_prog in grep ggrep; do
+    for ac_exec_ext in '' $ac_executable_extensions; do
+      ac_path_GREP="$as_dir/$ac_prog$ac_exec_ext"
+      as_fn_executable_p "$ac_path_GREP" || continue
+# Check for GNU ac_path_GREP and select it if it is found.
+  # Check for GNU $ac_path_GREP
+case `"$ac_path_GREP" --version 2>&1` in
+*GNU*)
+  ac_cv_path_GREP="$ac_path_GREP" ac_path_GREP_found=:;;
+*)
+  ac_count=0
+  $as_echo_n 0123456789 >"conftest.in"
+  while :
+  do
+    cat "conftest.in" "conftest.in" >"conftest.tmp"
+    mv "conftest.tmp" "conftest.in"
+    cp "conftest.in" "conftest.nl"
+    $as_echo 'GREP' >> "conftest.nl"
+    "$ac_path_GREP" -e 'GREP$' -e '-(cannot match)-' < "conftest.nl" >"conftest.out" 2>/dev/null || break
+    diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break
+    as_fn_arith $ac_count + 1 && ac_count=$as_val
+    if test $ac_count -gt ${ac_path_GREP_max-0}; then
+      # Best one so far, save it but keep looking for a better one
+      ac_cv_path_GREP="$ac_path_GREP"
+      ac_path_GREP_max=$ac_count
+    fi
+    # 10*(2^10) chars as input seems more than enough
+    test $ac_count -gt 10 && break
+  done
+  rm -f conftest.in conftest.tmp conftest.nl conftest.out;;
+esac
+
+      $ac_path_GREP_found && break 3
+    done
+  done
+  done
+IFS=$as_save_IFS
+  if test -z "$ac_cv_path_GREP"; then
+    as_fn_error $? "no acceptable grep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" "$LINENO" 5
+  fi
+else
+  ac_cv_path_GREP=$GREP
+fi
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_GREP" >&5
+$as_echo "$ac_cv_path_GREP" >&6; }
+ GREP="$ac_cv_path_GREP"
+
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for egrep" >&5
+$as_echo_n "checking for egrep... " >&6; }
+if ${ac_cv_path_EGREP+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  if echo a | $GREP -E '(a|b)' >/dev/null 2>&1
+   then ac_cv_path_EGREP="$GREP -E"
+   else
+     if test -z "$EGREP"; then
+  ac_path_EGREP_found=false
+  # Loop through the user's path and test for each of PROGNAME-LIST
+  as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    for ac_prog in egrep; do
+    for ac_exec_ext in '' $ac_executable_extensions; do
+      ac_path_EGREP="$as_dir/$ac_prog$ac_exec_ext"
+      as_fn_executable_p "$ac_path_EGREP" || continue
+# Check for GNU ac_path_EGREP and select it if it is found.
+  # Check for GNU $ac_path_EGREP
+case `"$ac_path_EGREP" --version 2>&1` in
+*GNU*)
+  ac_cv_path_EGREP="$ac_path_EGREP" ac_path_EGREP_found=:;;
+*)
+  ac_count=0
+  $as_echo_n 0123456789 >"conftest.in"
+  while :
+  do
+    cat "conftest.in" "conftest.in" >"conftest.tmp"
+    mv "conftest.tmp" "conftest.in"
+    cp "conftest.in" "conftest.nl"
+    $as_echo 'EGREP' >> "conftest.nl"
+    "$ac_path_EGREP" 'EGREP$' < "conftest.nl" >"conftest.out" 2>/dev/null || break
+    diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break
+    as_fn_arith $ac_count + 1 && ac_count=$as_val
+    if test $ac_count -gt ${ac_path_EGREP_max-0}; then
+      # Best one so far, save it but keep looking for a better one
+      ac_cv_path_EGREP="$ac_path_EGREP"
+      ac_path_EGREP_max=$ac_count
+    fi
+    # 10*(2^10) chars as input seems more than enough
+    test $ac_count -gt 10 && break
+  done
+  rm -f conftest.in conftest.tmp conftest.nl conftest.out;;
+esac
+
+      $ac_path_EGREP_found && break 3
+    done
+  done
+  done
+IFS=$as_save_IFS
+  if test -z "$ac_cv_path_EGREP"; then
+    as_fn_error $? "no acceptable egrep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" "$LINENO" 5
+  fi
+else
+  ac_cv_path_EGREP=$EGREP
+fi
+
+   fi
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_EGREP" >&5
+$as_echo "$ac_cv_path_EGREP" >&6; }
+ EGREP="$ac_cv_path_EGREP"
+
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for ANSI C header files" >&5
+$as_echo_n "checking for ANSI C header files... " >&6; }
+if ${ac_cv_header_stdc+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <float.h>
+
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+  ac_cv_header_stdc=yes
+else
+  ac_cv_header_stdc=no
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+
+if test $ac_cv_header_stdc = yes; then
+  # SunOS 4.x string.h does not declare mem*, contrary to ANSI.
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#include <string.h>
+
+_ACEOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+  $EGREP "memchr" >/dev/null 2>&1; then :
+
+else
+  ac_cv_header_stdc=no
+fi
+rm -f conftest*
+
+fi
+
+if test $ac_cv_header_stdc = yes; then
+  # ISC 2.0.2 stdlib.h does not declare free, contrary to ANSI.
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#include <stdlib.h>
+
+_ACEOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+  $EGREP "free" >/dev/null 2>&1; then :
+
+else
+  ac_cv_header_stdc=no
+fi
+rm -f conftest*
+
+fi
+
+if test $ac_cv_header_stdc = yes; then
+  # /bin/cc in Irix-4.0.5 gets non-ANSI ctype macros unless using -ansi.
+  if test "$cross_compiling" = yes; then :
+  :
+else
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#include <ctype.h>
+#include <stdlib.h>
+#if ((' ' & 0x0FF) == 0x020)
+# define ISLOWER(c) ('a' <= (c) && (c) <= 'z')
+# define TOUPPER(c) (ISLOWER(c) ? 'A' + ((c) - 'a') : (c))
+#else
+# define ISLOWER(c) \
+		   (('a' <= (c) && (c) <= 'i') \
+		     || ('j' <= (c) && (c) <= 'r') \
+		     || ('s' <= (c) && (c) <= 'z'))
+# define TOUPPER(c) (ISLOWER(c) ? ((c) | 0x40) : (c))
+#endif
+
+#define XOR(e, f) (((e) && !(f)) || (!(e) && (f)))
+int
+main ()
+{
+  int i;
+  for (i = 0; i < 256; i++)
+    if (XOR (islower (i), ISLOWER (i))
+	|| toupper (i) != TOUPPER (i))
+      return 2;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_run "$LINENO"; then :
+
+else
+  ac_cv_header_stdc=no
+fi
+rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \
+  conftest.$ac_objext conftest.beam conftest.$ac_ext
+fi
+
+fi
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_header_stdc" >&5
+$as_echo "$ac_cv_header_stdc" >&6; }
+if test $ac_cv_header_stdc = yes; then
+
+$as_echo "#define STDC_HEADERS 1" >>confdefs.h
+
+fi
+
+# On IRIX 5.3, sys/types and inttypes.h are conflicting.
+for ac_header in sys/types.h sys/stat.h stdlib.h string.h memory.h strings.h \
+		  inttypes.h stdint.h unistd.h
+do :
+  as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh`
+ac_fn_c_check_header_compile "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default
+"
+if eval test \"x\$"$as_ac_Header"\" = x"yes"; then :
+  cat >>confdefs.h <<_ACEOF
+#define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1
+_ACEOF
+
+fi
+
+done
+
+
+
+  ac_fn_c_check_header_mongrel "$LINENO" "minix/config.h" "ac_cv_header_minix_config_h" "$ac_includes_default"
+if test "x$ac_cv_header_minix_config_h" = xyes; then :
+  MINIX=yes
+else
+  MINIX=
+fi
+
+
+  if test "$MINIX" = yes; then
+
+$as_echo "#define _POSIX_SOURCE 1" >>confdefs.h
+
+
+$as_echo "#define _POSIX_1_SOURCE 2" >>confdefs.h
+
+
+$as_echo "#define _MINIX 1" >>confdefs.h
+
+  fi
+
+
+  { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether it is safe to define __EXTENSIONS__" >&5
+$as_echo_n "checking whether it is safe to define __EXTENSIONS__... " >&6; }
+if ${ac_cv_safe_to_define___extensions__+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+#         define __EXTENSIONS__ 1
+          $ac_includes_default
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+  ac_cv_safe_to_define___extensions__=yes
+else
+  ac_cv_safe_to_define___extensions__=no
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_safe_to_define___extensions__" >&5
+$as_echo "$ac_cv_safe_to_define___extensions__" >&6; }
+  test $ac_cv_safe_to_define___extensions__ = yes &&
+    $as_echo "#define __EXTENSIONS__ 1" >>confdefs.h
+
+  $as_echo "#define _ALL_SOURCE 1" >>confdefs.h
+
+  $as_echo "#define _GNU_SOURCE 1" >>confdefs.h
+
+  $as_echo "#define _POSIX_PTHREAD_SEMANTICS 1" >>confdefs.h
+
+  $as_echo "#define _TANDEM_SOURCE 1" >>confdefs.h
+
+
+
+# Check whether --enable-silent-rules was given.
+if test "${enable_silent_rules+set}" = set; then :
+  enableval=$enable_silent_rules;
+fi
+
+case $enable_silent_rules in # (((
+  yes) AM_DEFAULT_VERBOSITY=0;;
+   no) AM_DEFAULT_VERBOSITY=1;;
+    *) AM_DEFAULT_VERBOSITY=0;;
+esac
+am_make=${MAKE-make}
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $am_make supports nested variables" >&5
+$as_echo_n "checking whether $am_make supports nested variables... " >&6; }
+if ${am_cv_make_support_nested_variables+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  if $as_echo 'TRUE=$(BAR$(V))
+BAR0=false
+BAR1=true
+V=1
+am__doit:
+	@$(TRUE)
+.PHONY: am__doit' | $am_make -f - >/dev/null 2>&1; then
+  am_cv_make_support_nested_variables=yes
+else
+  am_cv_make_support_nested_variables=no
+fi
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $am_cv_make_support_nested_variables" >&5
+$as_echo "$am_cv_make_support_nested_variables" >&6; }
+if test $am_cv_make_support_nested_variables = yes; then
+    AM_V='$(V)'
+  AM_DEFAULT_V='$(AM_DEFAULT_VERBOSITY)'
+else
+  AM_V=$AM_DEFAULT_VERBOSITY
+  AM_DEFAULT_V=$AM_DEFAULT_VERBOSITY
+fi
+AM_BACKSLASH='\'
+
+
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to enable maintainer-specific portions of Makefiles" >&5
+$as_echo_n "checking whether to enable maintainer-specific portions of Makefiles... " >&6; }
+    # Check whether --enable-maintainer-mode was given.
+if test "${enable_maintainer_mode+set}" = set; then :
+  enableval=$enable_maintainer_mode; USE_MAINTAINER_MODE=$enableval
+else
+  USE_MAINTAINER_MODE=no
+fi
+
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $USE_MAINTAINER_MODE" >&5
+$as_echo "$USE_MAINTAINER_MODE" >&6; }
+   if test $USE_MAINTAINER_MODE = yes; then
+  MAINTAINER_MODE_TRUE=
+  MAINTAINER_MODE_FALSE='#'
+else
+  MAINTAINER_MODE_TRUE='#'
+  MAINTAINER_MODE_FALSE=
+fi
+
+  MAINT=$MAINTAINER_MODE_TRUE
+
+
+
+
+
+
+
+
+
+
+
+
+if test "x$ac_cv_env_PKG_CONFIG_set" != "xset"; then
+	if test -n "$ac_tool_prefix"; then
+  # Extract the first word of "${ac_tool_prefix}pkg-config", so it can be a program name with args.
+set dummy ${ac_tool_prefix}pkg-config; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_path_PKG_CONFIG+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  case $PKG_CONFIG in
+  [\\/]* | ?:[\\/]*)
+  ac_cv_path_PKG_CONFIG="$PKG_CONFIG" # Let the user override the test with a path.
+  ;;
+  *)
+  as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    ac_cv_path_PKG_CONFIG="$as_dir/$ac_word$ac_exec_ext"
+    $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
+
+  ;;
+esac
+fi
+PKG_CONFIG=$ac_cv_path_PKG_CONFIG
+if test -n "$PKG_CONFIG"; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $PKG_CONFIG" >&5
+$as_echo "$PKG_CONFIG" >&6; }
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+fi
+if test -z "$ac_cv_path_PKG_CONFIG"; then
+  ac_pt_PKG_CONFIG=$PKG_CONFIG
+  # Extract the first word of "pkg-config", so it can be a program name with args.
+set dummy pkg-config; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_path_ac_pt_PKG_CONFIG+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  case $ac_pt_PKG_CONFIG in
+  [\\/]* | ?:[\\/]*)
+  ac_cv_path_ac_pt_PKG_CONFIG="$ac_pt_PKG_CONFIG" # Let the user override the test with a path.
+  ;;
+  *)
+  as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    ac_cv_path_ac_pt_PKG_CONFIG="$as_dir/$ac_word$ac_exec_ext"
+    $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
+
+  ;;
+esac
+fi
+ac_pt_PKG_CONFIG=$ac_cv_path_ac_pt_PKG_CONFIG
+if test -n "$ac_pt_PKG_CONFIG"; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_pt_PKG_CONFIG" >&5
+$as_echo "$ac_pt_PKG_CONFIG" >&6; }
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+  if test "x$ac_pt_PKG_CONFIG" = x; then
+    PKG_CONFIG=""
+  else
+    case $cross_compiling:$ac_tool_warned in
+yes:)
+{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
+$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+ac_tool_warned=yes ;;
+esac
+    PKG_CONFIG=$ac_pt_PKG_CONFIG
+  fi
+else
+  PKG_CONFIG="$ac_cv_path_PKG_CONFIG"
+fi
+
+fi
+if test -n "$PKG_CONFIG"; then
+	_pkg_min_version=0.9.0
+	{ $as_echo "$as_me:${as_lineno-$LINENO}: checking pkg-config is at least version $_pkg_min_version" >&5
+$as_echo_n "checking pkg-config is at least version $_pkg_min_version... " >&6; }
+	if $PKG_CONFIG --atleast-pkgconfig-version $_pkg_min_version; then
+		{ $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+	else
+		{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+		PKG_CONFIG=""
+	fi
+fi
+
+
+	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 -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
+	WARNING_CFLAGS=$with_cflags
+
+
+
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for C/C++ restrict keyword" >&5
+$as_echo_n "checking for C/C++ restrict keyword... " >&6; }
+if ${ac_cv_c_restrict+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  ac_cv_c_restrict=no
+   # The order here caters to the fact that C++ does not require restrict.
+   for ac_kw in __restrict __restrict__ _Restrict restrict; do
+     cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+typedef int * int_ptr;
+	int foo (int_ptr $ac_kw ip) {
+	return ip[0];
+       }
+int
+main ()
+{
+int s[1];
+	int * $ac_kw t = s;
+	t[0] = 0;
+	return foo(t)
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+  ac_cv_c_restrict=$ac_kw
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+     test "$ac_cv_c_restrict" != no && break
+   done
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_restrict" >&5
+$as_echo "$ac_cv_c_restrict" >&6; }
+
+ case $ac_cv_c_restrict in
+   restrict) ;;
+   no) $as_echo "#define restrict /**/" >>confdefs.h
+ ;;
+   *)  cat >>confdefs.h <<_ACEOF
+#define restrict $ac_cv_c_restrict
+_ACEOF
+ ;;
+ esac
+
+
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+if test -n "$ac_tool_prefix"; then
+  # Extract the first word of "${ac_tool_prefix}gcc", so it can be a program name with args.
+set dummy ${ac_tool_prefix}gcc; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_CC+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  if test -n "$CC"; then
+  ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    ac_cv_prog_CC="${ac_tool_prefix}gcc"
+    $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
+
+fi
+fi
+CC=$ac_cv_prog_CC
+if test -n "$CC"; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5
+$as_echo "$CC" >&6; }
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+fi
+if test -z "$ac_cv_prog_CC"; then
+  ac_ct_CC=$CC
+  # Extract the first word of "gcc", so it can be a program name with args.
+set dummy gcc; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_ac_ct_CC+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  if test -n "$ac_ct_CC"; then
+  ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    ac_cv_prog_ac_ct_CC="gcc"
+    $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
+
+fi
+fi
+ac_ct_CC=$ac_cv_prog_ac_ct_CC
+if test -n "$ac_ct_CC"; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5
+$as_echo "$ac_ct_CC" >&6; }
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+  if test "x$ac_ct_CC" = x; then
+    CC=""
+  else
+    case $cross_compiling:$ac_tool_warned in
+yes:)
+{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
+$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+ac_tool_warned=yes ;;
+esac
+    CC=$ac_ct_CC
+  fi
+else
+  CC="$ac_cv_prog_CC"
+fi
+
+if test -z "$CC"; then
+          if test -n "$ac_tool_prefix"; then
+    # Extract the first word of "${ac_tool_prefix}cc", so it can be a program name with args.
+set dummy ${ac_tool_prefix}cc; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_CC+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  if test -n "$CC"; then
+  ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    ac_cv_prog_CC="${ac_tool_prefix}cc"
+    $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
+
+fi
+fi
+CC=$ac_cv_prog_CC
+if test -n "$CC"; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5
+$as_echo "$CC" >&6; }
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+  fi
+fi
+if test -z "$CC"; then
+  # Extract the first word of "cc", so it can be a program name with args.
+set dummy cc; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_CC+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  if test -n "$CC"; then
+  ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+  ac_prog_rejected=no
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    if test "$as_dir/$ac_word$ac_exec_ext" = "/usr/ucb/cc"; then
+       ac_prog_rejected=yes
+       continue
+     fi
+    ac_cv_prog_CC="cc"
+    $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
+
+if test $ac_prog_rejected = yes; then
+  # We found a bogon in the path, so make sure we never use it.
+  set dummy $ac_cv_prog_CC
+  shift
+  if test $# != 0; then
+    # We chose a different compiler from the bogus one.
+    # However, it has the same basename, so the bogon will be chosen
+    # first if we set CC to just the basename; use the full file name.
+    shift
+    ac_cv_prog_CC="$as_dir/$ac_word${1+' '}$@"
+  fi
+fi
+fi
+fi
+CC=$ac_cv_prog_CC
+if test -n "$CC"; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5
+$as_echo "$CC" >&6; }
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+fi
+if test -z "$CC"; then
+  if test -n "$ac_tool_prefix"; then
+  for ac_prog in cl.exe
+  do
+    # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args.
+set dummy $ac_tool_prefix$ac_prog; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_CC+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  if test -n "$CC"; then
+  ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    ac_cv_prog_CC="$ac_tool_prefix$ac_prog"
+    $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
+
+fi
+fi
+CC=$ac_cv_prog_CC
+if test -n "$CC"; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5
+$as_echo "$CC" >&6; }
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+    test -n "$CC" && break
+  done
+fi
+if test -z "$CC"; then
+  ac_ct_CC=$CC
+  for ac_prog in cl.exe
+do
+  # Extract the first word of "$ac_prog", so it can be a program name with args.
+set dummy $ac_prog; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_ac_ct_CC+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  if test -n "$ac_ct_CC"; then
+  ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    ac_cv_prog_ac_ct_CC="$ac_prog"
+    $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
+
+fi
+fi
+ac_ct_CC=$ac_cv_prog_ac_ct_CC
+if test -n "$ac_ct_CC"; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5
+$as_echo "$ac_ct_CC" >&6; }
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+  test -n "$ac_ct_CC" && break
+done
+
+  if test "x$ac_ct_CC" = x; then
+    CC=""
+  else
+    case $cross_compiling:$ac_tool_warned in
+yes:)
+{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
+$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+ac_tool_warned=yes ;;
+esac
+    CC=$ac_ct_CC
+  fi
+fi
+
+fi
+
+
+test -z "$CC" && { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error $? "no acceptable C compiler found in \$PATH
+See \`config.log' for more details" "$LINENO" 5; }
+
+# Provide some information about the compiler.
+$as_echo "$as_me:${as_lineno-$LINENO}: checking for C compiler version" >&5
+set X $ac_compile
+ac_compiler=$2
+for ac_option in --version -v -V -qversion; do
+  { { ac_try="$ac_compiler $ac_option >&5"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+$as_echo "$ac_try_echo"; } >&5
+  (eval "$ac_compiler $ac_option >&5") 2>conftest.err
+  ac_status=$?
+  if test -s conftest.err; then
+    sed '10a\
+... rest of stderr output deleted ...
+         10q' conftest.err >conftest.er1
+    cat conftest.er1 >&5
+  fi
+  rm -f conftest.er1 conftest.err
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; }
+done
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are using the GNU C compiler" >&5
+$as_echo_n "checking whether we are using the GNU C compiler... " >&6; }
+if ${ac_cv_c_compiler_gnu+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+int
+main ()
+{
+#ifndef __GNUC__
+       choke me
+#endif
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+  ac_compiler_gnu=yes
+else
+  ac_compiler_gnu=no
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+ac_cv_c_compiler_gnu=$ac_compiler_gnu
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_compiler_gnu" >&5
+$as_echo "$ac_cv_c_compiler_gnu" >&6; }
+if test $ac_compiler_gnu = yes; then
+  GCC=yes
+else
+  GCC=
+fi
+ac_test_CFLAGS=${CFLAGS+set}
+ac_save_CFLAGS=$CFLAGS
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CC accepts -g" >&5
+$as_echo_n "checking whether $CC accepts -g... " >&6; }
+if ${ac_cv_prog_cc_g+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  ac_save_c_werror_flag=$ac_c_werror_flag
+   ac_c_werror_flag=yes
+   ac_cv_prog_cc_g=no
+   CFLAGS="-g"
+   cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+  ac_cv_prog_cc_g=yes
+else
+  CFLAGS=""
+      cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+
+else
+  ac_c_werror_flag=$ac_save_c_werror_flag
+	 CFLAGS="-g"
+	 cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+  ac_cv_prog_cc_g=yes
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+   ac_c_werror_flag=$ac_save_c_werror_flag
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_g" >&5
+$as_echo "$ac_cv_prog_cc_g" >&6; }
+if test "$ac_test_CFLAGS" = set; then
+  CFLAGS=$ac_save_CFLAGS
+elif test $ac_cv_prog_cc_g = yes; then
+  if test "$GCC" = yes; then
+    CFLAGS="-g -O2"
+  else
+    CFLAGS="-g"
+  fi
+else
+  if test "$GCC" = yes; then
+    CFLAGS="-O2"
+  else
+    CFLAGS=
+  fi
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $CC option to accept ISO C89" >&5
+$as_echo_n "checking for $CC option to accept ISO C89... " >&6; }
+if ${ac_cv_prog_cc_c89+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  ac_cv_prog_cc_c89=no
+ac_save_CC=$CC
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#include <stdarg.h>
+#include <stdio.h>
+struct stat;
+/* Most of the following tests are stolen from RCS 5.7's src/conf.sh.  */
+struct buf { int x; };
+FILE * (*rcsopen) (struct buf *, struct stat *, int);
+static char *e (p, i)
+     char **p;
+     int i;
+{
+  return p[i];
+}
+static char *f (char * (*g) (char **, int), char **p, ...)
+{
+  char *s;
+  va_list v;
+  va_start (v,p);
+  s = g (p, va_arg (v,int));
+  va_end (v);
+  return s;
+}
+
+/* OSF 4.0 Compaq cc is some sort of almost-ANSI by default.  It has
+   function prototypes and stuff, but not '\xHH' hex character constants.
+   These don't provoke an error unfortunately, instead are silently treated
+   as 'x'.  The following induces an error, until -std is added to get
+   proper ANSI mode.  Curiously '\x00'!='x' always comes out true, for an
+   array size at least.  It's necessary to write '\x00'==0 to get something
+   that's true only with -std.  */
+int osf4_cc_array ['\x00' == 0 ? 1 : -1];
+
+/* IBM C 6 for AIX is almost-ANSI by default, but it replaces macro parameters
+   inside strings and character constants.  */
+#define FOO(x) 'x'
+int xlc6_cc_array[FOO(a) == 'x' ? 1 : -1];
+
+int test (int i, double x);
+struct s1 {int (*f) (int a);};
+struct s2 {int (*f) (double a);};
+int pairnames (int, char **, FILE *(*)(struct buf *, struct stat *, int), int, int);
+int argc;
+char **argv;
+int
+main ()
+{
+return f (e, argv, 0) != argv[0]  ||  f (e, argv, 1) != argv[1];
+  ;
+  return 0;
+}
+_ACEOF
+for ac_arg in '' -qlanglvl=extc89 -qlanglvl=ansi -std \
+	-Ae "-Aa -D_HPUX_SOURCE" "-Xc -D__EXTENSIONS__"
+do
+  CC="$ac_save_CC $ac_arg"
+  if ac_fn_c_try_compile "$LINENO"; then :
+  ac_cv_prog_cc_c89=$ac_arg
+fi
+rm -f core conftest.err conftest.$ac_objext
+  test "x$ac_cv_prog_cc_c89" != "xno" && break
+done
+rm -f conftest.$ac_ext
+CC=$ac_save_CC
+
+fi
+# AC_CACHE_VAL
+case "x$ac_cv_prog_cc_c89" in
+  x)
+    { $as_echo "$as_me:${as_lineno-$LINENO}: result: none needed" >&5
+$as_echo "none needed" >&6; } ;;
+  xno)
+    { $as_echo "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5
+$as_echo "unsupported" >&6; } ;;
+  *)
+    CC="$CC $ac_cv_prog_cc_c89"
+    { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c89" >&5
+$as_echo "$ac_cv_prog_cc_c89" >&6; } ;;
+esac
+if test "x$ac_cv_prog_cc_c89" != xno; then :
+
+fi
+
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+
+depcc="$CC"   am_compiler_list=
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking dependency style of $depcc" >&5
+$as_echo_n "checking dependency style of $depcc... " >&6; }
+if ${am_cv_CC_dependencies_compiler_type+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  if test -z "$AMDEP_TRUE" && test -f "$am_depcomp"; then
+  # We make a subdir and do the tests there.  Otherwise we can end up
+  # making bogus files that we don't know about and never remove.  For
+  # instance it was reported that on HP-UX the gcc test will end up
+  # making a dummy file named 'D' -- because '-MD' means "put the output
+  # in D".
+  rm -rf conftest.dir
+  mkdir conftest.dir
+  # Copy depcomp to subdir because otherwise we won't find it if we're
+  # using a relative directory.
+  cp "$am_depcomp" conftest.dir
+  cd conftest.dir
+  # We will build objects and dependencies in a subdirectory because
+  # it helps to detect inapplicable dependency modes.  For instance
+  # both Tru64's cc and ICC support -MD to output dependencies as a
+  # side effect of compilation, but ICC will put the dependencies in
+  # the current directory while Tru64 will put them in the object
+  # directory.
+  mkdir sub
+
+  am_cv_CC_dependencies_compiler_type=none
+  if test "$am_compiler_list" = ""; then
+     am_compiler_list=`sed -n 's/^#*\([a-zA-Z0-9]*\))$/\1/p' < ./depcomp`
+  fi
+  am__universal=false
+  case " $depcc " in #(
+     *\ -arch\ *\ -arch\ *) am__universal=true ;;
+     esac
+
+  for depmode in $am_compiler_list; do
+    # Setup a source with many dependencies, because some compilers
+    # like to wrap large dependency lists on column 80 (with \), and
+    # we should not choose a depcomp mode which is confused by this.
+    #
+    # We need to recreate these files for each test, as the compiler may
+    # overwrite some of them when testing with obscure command lines.
+    # This happens at least with the AIX C compiler.
+    : > sub/conftest.c
+    for i in 1 2 3 4 5 6; do
+      echo '#include "conftst'$i'.h"' >> sub/conftest.c
+      # Using ": > sub/conftst$i.h" creates only sub/conftst1.h with
+      # Solaris 10 /bin/sh.
+      echo '/* dummy */' > sub/conftst$i.h
+    done
+    echo "${am__include} ${am__quote}sub/conftest.Po${am__quote}" > confmf
+
+    # We check with '-c' and '-o' for the sake of the "dashmstdout"
+    # mode.  It turns out that the SunPro C++ compiler does not properly
+    # handle '-M -o', and we need to detect this.  Also, some Intel
+    # versions had trouble with output in subdirs.
+    am__obj=sub/conftest.${OBJEXT-o}
+    am__minus_obj="-o $am__obj"
+    case $depmode in
+    gcc)
+      # This depmode causes a compiler race in universal mode.
+      test "$am__universal" = false || continue
+      ;;
+    nosideeffect)
+      # After this tag, mechanisms are not by side-effect, so they'll
+      # only be used when explicitly requested.
+      if test "x$enable_dependency_tracking" = xyes; then
+	continue
+      else
+	break
+      fi
+      ;;
+    msvc7 | msvc7msys | msvisualcpp | msvcmsys)
+      # This compiler won't grok '-c -o', but also, the minuso test has
+      # not run yet.  These depmodes are late enough in the game, and
+      # so weak that their functioning should not be impacted.
+      am__obj=conftest.${OBJEXT-o}
+      am__minus_obj=
+      ;;
+    none) break ;;
+    esac
+    if depmode=$depmode \
+       source=sub/conftest.c object=$am__obj \
+       depfile=sub/conftest.Po tmpdepfile=sub/conftest.TPo \
+       $SHELL ./depcomp $depcc -c $am__minus_obj sub/conftest.c \
+         >/dev/null 2>conftest.err &&
+       grep sub/conftst1.h sub/conftest.Po > /dev/null 2>&1 &&
+       grep sub/conftst6.h sub/conftest.Po > /dev/null 2>&1 &&
+       grep $am__obj sub/conftest.Po > /dev/null 2>&1 &&
+       ${MAKE-make} -s -f confmf > /dev/null 2>&1; then
+      # icc doesn't choke on unknown options, it will just issue warnings
+      # or remarks (even with -Werror).  So we grep stderr for any message
+      # that says an option was ignored or not supported.
+      # When given -MP, icc 7.0 and 7.1 complain thusly:
+      #   icc: Command line warning: ignoring option '-M'; no argument required
+      # The diagnosis changed in icc 8.0:
+      #   icc: Command line remark: option '-MP' not supported
+      if (grep 'ignoring option' conftest.err ||
+          grep 'not supported' conftest.err) >/dev/null 2>&1; then :; else
+        am_cv_CC_dependencies_compiler_type=$depmode
+        break
+      fi
+    fi
+  done
+
+  cd ..
+  rm -rf conftest.dir
+else
+  am_cv_CC_dependencies_compiler_type=none
+fi
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $am_cv_CC_dependencies_compiler_type" >&5
+$as_echo "$am_cv_CC_dependencies_compiler_type" >&6; }
+CCDEPMODE=depmode=$am_cv_CC_dependencies_compiler_type
+
+ if
+  test "x$enable_dependency_tracking" != xno \
+  && test "$am_cv_CC_dependencies_compiler_type" = gcc3; then
+  am__fastdepCC_TRUE=
+  am__fastdepCC_FALSE='#'
+else
+  am__fastdepCC_TRUE='#'
+  am__fastdepCC_FALSE=
+fi
+
+
+if test "x$CC" != xcc; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CC and cc understand -c and -o together" >&5
+$as_echo_n "checking whether $CC and cc understand -c and -o together... " >&6; }
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether cc understands -c and -o together" >&5
+$as_echo_n "checking whether cc understands -c and -o together... " >&6; }
+fi
+set dummy $CC; ac_cc=`$as_echo "$2" |
+		      sed 's/[^a-zA-Z0-9_]/_/g;s/^[0-9]/_/'`
+if eval \${ac_cv_prog_cc_${ac_cc}_c_o+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+# Make sure it works both with $CC and with simple cc.
+# We do the test twice because some compilers refuse to overwrite an
+# existing .o file with -o, though they will create one.
+ac_try='$CC -c conftest.$ac_ext -o conftest2.$ac_objext >&5'
+rm -f conftest2.*
+if { { case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+$as_echo "$ac_try_echo"; } >&5
+  (eval "$ac_try") 2>&5
+  ac_status=$?
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; } &&
+   test -f conftest2.$ac_objext && { { case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+$as_echo "$ac_try_echo"; } >&5
+  (eval "$ac_try") 2>&5
+  ac_status=$?
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; };
+then
+  eval ac_cv_prog_cc_${ac_cc}_c_o=yes
+  if test "x$CC" != xcc; then
+    # Test first that cc exists at all.
+    if { ac_try='cc -c conftest.$ac_ext >&5'
+  { { case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+$as_echo "$ac_try_echo"; } >&5
+  (eval "$ac_try") 2>&5
+  ac_status=$?
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; }; }; then
+      ac_try='cc -c conftest.$ac_ext -o conftest2.$ac_objext >&5'
+      rm -f conftest2.*
+      if { { case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+$as_echo "$ac_try_echo"; } >&5
+  (eval "$ac_try") 2>&5
+  ac_status=$?
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; } &&
+	 test -f conftest2.$ac_objext && { { case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+$as_echo "$ac_try_echo"; } >&5
+  (eval "$ac_try") 2>&5
+  ac_status=$?
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; };
+      then
+	# cc works too.
+	:
+      else
+	# cc exists but doesn't like -o.
+	eval ac_cv_prog_cc_${ac_cc}_c_o=no
+      fi
+    fi
+  fi
+else
+  eval ac_cv_prog_cc_${ac_cc}_c_o=no
+fi
+rm -f core conftest*
+
+fi
+if eval test \$ac_cv_prog_cc_${ac_cc}_c_o = yes; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+
+$as_echo "#define NO_MINUS_C_MINUS_O 1" >>confdefs.h
+
+fi
+
+# FIXME: we rely on the cache variable name because
+# there is no other way.
+set dummy $CC
+am_cc=`echo $2 | sed 's/[^a-zA-Z0-9_]/_/g;s/^[0-9]/_/'`
+eval am_t=\$ac_cv_prog_cc_${am_cc}_c_o
+if test "$am_t" != yes; then
+   # Losing compiler, so override with the script.
+   # FIXME: It is wrong to rewrite CC.
+   # But if we don't then we get into trouble of one sort or another.
+   # A longer-term fix would be to have automake use am__CC in this case,
+   # and then we could set am__CC="\$(top_srcdir)/compile \$(CC)"
+   CC="$am_aux_dir/compile $CC"
+fi
+
+
+
+	{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether ${CC-cc} accepts -fPIE" >&5
+$as_echo_n "checking whether ${CC-cc} accepts -fPIE... " >&6; }
+if ${ac_cv_prog_cc_pie+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+
+		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*
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_pie" >&5
+$as_echo "$ac_cv_prog_cc_pie" >&6; }
+
+
+
+
+
+
+
+# Check whether --enable-static was given.
+if test "${enable_static+set}" = set; then :
+  enableval=$enable_static; p=${PACKAGE-default}
+    case $enableval in
+    yes) enable_static=yes ;;
+    no) enable_static=no ;;
+    *)
+     enable_static=no
+      # Look at the argument we got.  We use all the common list separators.
+      lt_save_ifs="$IFS"; IFS="${IFS}$PATH_SEPARATOR,"
+      for pkg in $enableval; do
+	IFS="$lt_save_ifs"
+	if test "X$pkg" = "X$p"; then
+	  enable_static=yes
+	fi
+      done
+      IFS="$lt_save_ifs"
+      ;;
+    esac
+else
+  enable_static=no
+fi
+
+
+
+
+
+
+
+
+
+case `pwd` in
+  *\ * | *\	*)
+    { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: Libtool does not cope well with whitespace in \`pwd\`" >&5
+$as_echo "$as_me: WARNING: Libtool does not cope well with whitespace in \`pwd\`" >&2;} ;;
+esac
+
+
+
+macro_version='2.4.2'
+macro_revision='1.3337'
+
+
+
+
+
+
+
+
+
+
+
+
+
+ltmain="$ac_aux_dir/ltmain.sh"
+
+# Make sure we can run config.sub.
+$SHELL "$ac_aux_dir/config.sub" sun4 >/dev/null 2>&1 ||
+  as_fn_error $? "cannot run $SHELL $ac_aux_dir/config.sub" "$LINENO" 5
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking build system type" >&5
+$as_echo_n "checking build system type... " >&6; }
+if ${ac_cv_build+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  ac_build_alias=$build_alias
+test "x$ac_build_alias" = x &&
+  ac_build_alias=`$SHELL "$ac_aux_dir/config.guess"`
+test "x$ac_build_alias" = x &&
+  as_fn_error $? "cannot guess build type; you must specify one" "$LINENO" 5
+ac_cv_build=`$SHELL "$ac_aux_dir/config.sub" $ac_build_alias` ||
+  as_fn_error $? "$SHELL $ac_aux_dir/config.sub $ac_build_alias failed" "$LINENO" 5
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_build" >&5
+$as_echo "$ac_cv_build" >&6; }
+case $ac_cv_build in
+*-*-*) ;;
+*) as_fn_error $? "invalid value of canonical build" "$LINENO" 5;;
+esac
+build=$ac_cv_build
+ac_save_IFS=$IFS; IFS='-'
+set x $ac_cv_build
+shift
+build_cpu=$1
+build_vendor=$2
+shift; shift
+# Remember, the first character of IFS is used to create $*,
+# except with old shells:
+build_os=$*
+IFS=$ac_save_IFS
+case $build_os in *\ *) build_os=`echo "$build_os" | sed 's/ /-/g'`;; esac
+
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking host system type" >&5
+$as_echo_n "checking host system type... " >&6; }
+if ${ac_cv_host+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  if test "x$host_alias" = x; then
+  ac_cv_host=$ac_cv_build
+else
+  ac_cv_host=`$SHELL "$ac_aux_dir/config.sub" $host_alias` ||
+    as_fn_error $? "$SHELL $ac_aux_dir/config.sub $host_alias failed" "$LINENO" 5
+fi
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_host" >&5
+$as_echo "$ac_cv_host" >&6; }
+case $ac_cv_host in
+*-*-*) ;;
+*) as_fn_error $? "invalid value of canonical host" "$LINENO" 5;;
+esac
+host=$ac_cv_host
+ac_save_IFS=$IFS; IFS='-'
+set x $ac_cv_host
+shift
+host_cpu=$1
+host_vendor=$2
+shift; shift
+# Remember, the first character of IFS is used to create $*,
+# except with old shells:
+host_os=$*
+IFS=$ac_save_IFS
+case $host_os in *\ *) host_os=`echo "$host_os" | sed 's/ /-/g'`;; esac
+
+
+# Backslashify metacharacters that are still active within
+# double-quoted strings.
+sed_quote_subst='s/\(["`$\\]\)/\\\1/g'
+
+# Same as above, but do not quote variable references.
+double_quote_subst='s/\(["`\\]\)/\\\1/g'
+
+# Sed substitution to delay expansion of an escaped shell variable in a
+# double_quote_subst'ed string.
+delay_variable_subst='s/\\\\\\\\\\\$/\\\\\\$/g'
+
+# Sed substitution to delay expansion of an escaped single quote.
+delay_single_quote_subst='s/'\''/'\'\\\\\\\'\''/g'
+
+# Sed substitution to avoid accidental globbing in evaled expressions
+no_glob_subst='s/\*/\\\*/g'
+
+ECHO='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\'
+ECHO=$ECHO$ECHO$ECHO$ECHO$ECHO
+ECHO=$ECHO$ECHO$ECHO$ECHO$ECHO$ECHO
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking how to print strings" >&5
+$as_echo_n "checking how to print strings... " >&6; }
+# Test print first, because it will be a builtin if present.
+if test "X`( print -r -- -n ) 2>/dev/null`" = X-n && \
+   test "X`print -r -- $ECHO 2>/dev/null`" = "X$ECHO"; then
+  ECHO='print -r --'
+elif test "X`printf %s $ECHO 2>/dev/null`" = "X$ECHO"; then
+  ECHO='printf %s\n'
+else
+  # Use this function as a fallback that always works.
+  func_fallback_echo ()
+  {
+    eval 'cat <<_LTECHO_EOF
+$1
+_LTECHO_EOF'
+  }
+  ECHO='func_fallback_echo'
+fi
+
+# func_echo_all arg...
+# Invoke $ECHO with all args, space-separated.
+func_echo_all ()
+{
+    $ECHO ""
+}
+
+case "$ECHO" in
+  printf*) { $as_echo "$as_me:${as_lineno-$LINENO}: result: printf" >&5
+$as_echo "printf" >&6; } ;;
+  print*) { $as_echo "$as_me:${as_lineno-$LINENO}: result: print -r" >&5
+$as_echo "print -r" >&6; } ;;
+  *) { $as_echo "$as_me:${as_lineno-$LINENO}: result: cat" >&5
+$as_echo "cat" >&6; } ;;
+esac
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for a sed that does not truncate output" >&5
+$as_echo_n "checking for a sed that does not truncate output... " >&6; }
+if ${ac_cv_path_SED+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+            ac_script=s/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb/
+     for ac_i in 1 2 3 4 5 6 7; do
+       ac_script="$ac_script$as_nl$ac_script"
+     done
+     echo "$ac_script" 2>/dev/null | sed 99q >conftest.sed
+     { ac_script=; unset ac_script;}
+     if test -z "$SED"; then
+  ac_path_SED_found=false
+  # Loop through the user's path and test for each of PROGNAME-LIST
+  as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    for ac_prog in sed gsed; do
+    for ac_exec_ext in '' $ac_executable_extensions; do
+      ac_path_SED="$as_dir/$ac_prog$ac_exec_ext"
+      as_fn_executable_p "$ac_path_SED" || continue
+# Check for GNU ac_path_SED and select it if it is found.
+  # Check for GNU $ac_path_SED
+case `"$ac_path_SED" --version 2>&1` in
+*GNU*)
+  ac_cv_path_SED="$ac_path_SED" ac_path_SED_found=:;;
+*)
+  ac_count=0
+  $as_echo_n 0123456789 >"conftest.in"
+  while :
+  do
+    cat "conftest.in" "conftest.in" >"conftest.tmp"
+    mv "conftest.tmp" "conftest.in"
+    cp "conftest.in" "conftest.nl"
+    $as_echo '' >> "conftest.nl"
+    "$ac_path_SED" -f conftest.sed < "conftest.nl" >"conftest.out" 2>/dev/null || break
+    diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break
+    as_fn_arith $ac_count + 1 && ac_count=$as_val
+    if test $ac_count -gt ${ac_path_SED_max-0}; then
+      # Best one so far, save it but keep looking for a better one
+      ac_cv_path_SED="$ac_path_SED"
+      ac_path_SED_max=$ac_count
+    fi
+    # 10*(2^10) chars as input seems more than enough
+    test $ac_count -gt 10 && break
+  done
+  rm -f conftest.in conftest.tmp conftest.nl conftest.out;;
+esac
+
+      $ac_path_SED_found && break 3
+    done
+  done
+  done
+IFS=$as_save_IFS
+  if test -z "$ac_cv_path_SED"; then
+    as_fn_error $? "no acceptable sed could be found in \$PATH" "$LINENO" 5
+  fi
+else
+  ac_cv_path_SED=$SED
+fi
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_SED" >&5
+$as_echo "$ac_cv_path_SED" >&6; }
+ SED="$ac_cv_path_SED"
+  rm -f conftest.sed
+
+test -z "$SED" && SED=sed
+Xsed="$SED -e 1s/^X//"
+
+
+
+
+
+
+
+
+
+
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for fgrep" >&5
+$as_echo_n "checking for fgrep... " >&6; }
+if ${ac_cv_path_FGREP+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  if echo 'ab*c' | $GREP -F 'ab*c' >/dev/null 2>&1
+   then ac_cv_path_FGREP="$GREP -F"
+   else
+     if test -z "$FGREP"; then
+  ac_path_FGREP_found=false
+  # Loop through the user's path and test for each of PROGNAME-LIST
+  as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    for ac_prog in fgrep; do
+    for ac_exec_ext in '' $ac_executable_extensions; do
+      ac_path_FGREP="$as_dir/$ac_prog$ac_exec_ext"
+      as_fn_executable_p "$ac_path_FGREP" || continue
+# Check for GNU ac_path_FGREP and select it if it is found.
+  # Check for GNU $ac_path_FGREP
+case `"$ac_path_FGREP" --version 2>&1` in
+*GNU*)
+  ac_cv_path_FGREP="$ac_path_FGREP" ac_path_FGREP_found=:;;
+*)
+  ac_count=0
+  $as_echo_n 0123456789 >"conftest.in"
+  while :
+  do
+    cat "conftest.in" "conftest.in" >"conftest.tmp"
+    mv "conftest.tmp" "conftest.in"
+    cp "conftest.in" "conftest.nl"
+    $as_echo 'FGREP' >> "conftest.nl"
+    "$ac_path_FGREP" FGREP < "conftest.nl" >"conftest.out" 2>/dev/null || break
+    diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break
+    as_fn_arith $ac_count + 1 && ac_count=$as_val
+    if test $ac_count -gt ${ac_path_FGREP_max-0}; then
+      # Best one so far, save it but keep looking for a better one
+      ac_cv_path_FGREP="$ac_path_FGREP"
+      ac_path_FGREP_max=$ac_count
+    fi
+    # 10*(2^10) chars as input seems more than enough
+    test $ac_count -gt 10 && break
+  done
+  rm -f conftest.in conftest.tmp conftest.nl conftest.out;;
+esac
+
+      $ac_path_FGREP_found && break 3
+    done
+  done
+  done
+IFS=$as_save_IFS
+  if test -z "$ac_cv_path_FGREP"; then
+    as_fn_error $? "no acceptable fgrep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" "$LINENO" 5
+  fi
+else
+  ac_cv_path_FGREP=$FGREP
+fi
+
+   fi
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_FGREP" >&5
+$as_echo "$ac_cv_path_FGREP" >&6; }
+ FGREP="$ac_cv_path_FGREP"
+
+
+test -z "$GREP" && GREP=grep
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+# Check whether --with-gnu-ld was given.
+if test "${with_gnu_ld+set}" = set; then :
+  withval=$with_gnu_ld; test "$withval" = no || with_gnu_ld=yes
+else
+  with_gnu_ld=no
+fi
+
+ac_prog=ld
+if test "$GCC" = yes; then
+  # Check if gcc -print-prog-name=ld gives a path.
+  { $as_echo "$as_me:${as_lineno-$LINENO}: checking for ld used by $CC" >&5
+$as_echo_n "checking for ld used by $CC... " >&6; }
+  case $host in
+  *-*-mingw*)
+    # gcc leaves a trailing carriage return which upsets mingw
+    ac_prog=`($CC -print-prog-name=ld) 2>&5 | tr -d '\015'` ;;
+  *)
+    ac_prog=`($CC -print-prog-name=ld) 2>&5` ;;
+  esac
+  case $ac_prog in
+    # Accept absolute paths.
+    [\\/]* | ?:[\\/]*)
+      re_direlt='/[^/][^/]*/\.\./'
+      # Canonicalize the pathname of ld
+      ac_prog=`$ECHO "$ac_prog"| $SED 's%\\\\%/%g'`
+      while $ECHO "$ac_prog" | $GREP "$re_direlt" > /dev/null 2>&1; do
+	ac_prog=`$ECHO $ac_prog| $SED "s%$re_direlt%/%"`
+      done
+      test -z "$LD" && LD="$ac_prog"
+      ;;
+  "")
+    # If it fails, then pretend we aren't using GCC.
+    ac_prog=ld
+    ;;
+  *)
+    # If it is relative, then search for the first ld in PATH.
+    with_gnu_ld=unknown
+    ;;
+  esac
+elif test "$with_gnu_ld" = yes; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: checking for GNU ld" >&5
+$as_echo_n "checking for GNU ld... " >&6; }
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: checking for non-GNU ld" >&5
+$as_echo_n "checking for non-GNU ld... " >&6; }
+fi
+if ${lt_cv_path_LD+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  if test -z "$LD"; then
+  lt_save_ifs="$IFS"; IFS=$PATH_SEPARATOR
+  for ac_dir in $PATH; do
+    IFS="$lt_save_ifs"
+    test -z "$ac_dir" && ac_dir=.
+    if test -f "$ac_dir/$ac_prog" || test -f "$ac_dir/$ac_prog$ac_exeext"; then
+      lt_cv_path_LD="$ac_dir/$ac_prog"
+      # Check to see if the program is GNU ld.  I'd rather use --version,
+      # but apparently some variants of GNU ld only accept -v.
+      # Break only if it was the GNU/non-GNU ld that we prefer.
+      case `"$lt_cv_path_LD" -v 2>&1 </dev/null` in
+      *GNU* | *'with BFD'*)
+	test "$with_gnu_ld" != no && break
+	;;
+      *)
+	test "$with_gnu_ld" != yes && break
+	;;
+      esac
+    fi
+  done
+  IFS="$lt_save_ifs"
+else
+  lt_cv_path_LD="$LD" # Let the user override the test with a path.
+fi
+fi
+
+LD="$lt_cv_path_LD"
+if test -n "$LD"; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $LD" >&5
+$as_echo "$LD" >&6; }
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+test -z "$LD" && as_fn_error $? "no acceptable ld found in \$PATH" "$LINENO" 5
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking if the linker ($LD) is GNU ld" >&5
+$as_echo_n "checking if the linker ($LD) is GNU ld... " >&6; }
+if ${lt_cv_prog_gnu_ld+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  # I'd rather use --version here, but apparently some GNU lds only accept -v.
+case `$LD -v 2>&1 </dev/null` in
+*GNU* | *'with BFD'*)
+  lt_cv_prog_gnu_ld=yes
+  ;;
+*)
+  lt_cv_prog_gnu_ld=no
+  ;;
+esac
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_gnu_ld" >&5
+$as_echo "$lt_cv_prog_gnu_ld" >&6; }
+with_gnu_ld=$lt_cv_prog_gnu_ld
+
+
+
+
+
+
+
+
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for BSD- or MS-compatible name lister (nm)" >&5
+$as_echo_n "checking for BSD- or MS-compatible name lister (nm)... " >&6; }
+if ${lt_cv_path_NM+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  if test -n "$NM"; then
+  # Let the user override the test.
+  lt_cv_path_NM="$NM"
+else
+  lt_nm_to_check="${ac_tool_prefix}nm"
+  if test -n "$ac_tool_prefix" && test "$build" = "$host"; then
+    lt_nm_to_check="$lt_nm_to_check nm"
+  fi
+  for lt_tmp_nm in $lt_nm_to_check; do
+    lt_save_ifs="$IFS"; IFS=$PATH_SEPARATOR
+    for ac_dir in $PATH /usr/ccs/bin/elf /usr/ccs/bin /usr/ucb /bin; do
+      IFS="$lt_save_ifs"
+      test -z "$ac_dir" && ac_dir=.
+      tmp_nm="$ac_dir/$lt_tmp_nm"
+      if test -f "$tmp_nm" || test -f "$tmp_nm$ac_exeext" ; then
+	# Check to see if the nm accepts a BSD-compat flag.
+	# Adding the `sed 1q' prevents false positives on HP-UX, which says:
+	#   nm: unknown option "B" ignored
+	# Tru64's nm complains that /dev/null is an invalid object file
+	case `"$tmp_nm" -B /dev/null 2>&1 | sed '1q'` in
+	*/dev/null* | *'Invalid file or object type'*)
+	  lt_cv_path_NM="$tmp_nm -B"
+	  break
+	  ;;
+	*)
+	  case `"$tmp_nm" -p /dev/null 2>&1 | sed '1q'` in
+	  */dev/null*)
+	    lt_cv_path_NM="$tmp_nm -p"
+	    break
+	    ;;
+	  *)
+	    lt_cv_path_NM=${lt_cv_path_NM="$tmp_nm"} # keep the first match, but
+	    continue # so that we can try to find one that supports BSD flags
+	    ;;
+	  esac
+	  ;;
+	esac
+      fi
+    done
+    IFS="$lt_save_ifs"
+  done
+  : ${lt_cv_path_NM=no}
+fi
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_path_NM" >&5
+$as_echo "$lt_cv_path_NM" >&6; }
+if test "$lt_cv_path_NM" != "no"; then
+  NM="$lt_cv_path_NM"
+else
+  # Didn't find any BSD compatible name lister, look for dumpbin.
+  if test -n "$DUMPBIN"; then :
+    # Let the user override the test.
+  else
+    if test -n "$ac_tool_prefix"; then
+  for ac_prog in dumpbin "link -dump"
+  do
+    # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args.
+set dummy $ac_tool_prefix$ac_prog; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_DUMPBIN+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  if test -n "$DUMPBIN"; then
+  ac_cv_prog_DUMPBIN="$DUMPBIN" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    ac_cv_prog_DUMPBIN="$ac_tool_prefix$ac_prog"
+    $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
+
+fi
+fi
+DUMPBIN=$ac_cv_prog_DUMPBIN
+if test -n "$DUMPBIN"; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $DUMPBIN" >&5
+$as_echo "$DUMPBIN" >&6; }
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+    test -n "$DUMPBIN" && break
+  done
+fi
+if test -z "$DUMPBIN"; then
+  ac_ct_DUMPBIN=$DUMPBIN
+  for ac_prog in dumpbin "link -dump"
+do
+  # Extract the first word of "$ac_prog", so it can be a program name with args.
+set dummy $ac_prog; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_ac_ct_DUMPBIN+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  if test -n "$ac_ct_DUMPBIN"; then
+  ac_cv_prog_ac_ct_DUMPBIN="$ac_ct_DUMPBIN" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    ac_cv_prog_ac_ct_DUMPBIN="$ac_prog"
+    $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
+
+fi
+fi
+ac_ct_DUMPBIN=$ac_cv_prog_ac_ct_DUMPBIN
+if test -n "$ac_ct_DUMPBIN"; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_DUMPBIN" >&5
+$as_echo "$ac_ct_DUMPBIN" >&6; }
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+  test -n "$ac_ct_DUMPBIN" && break
+done
+
+  if test "x$ac_ct_DUMPBIN" = x; then
+    DUMPBIN=":"
+  else
+    case $cross_compiling:$ac_tool_warned in
+yes:)
+{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
+$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+ac_tool_warned=yes ;;
+esac
+    DUMPBIN=$ac_ct_DUMPBIN
+  fi
+fi
+
+    case `$DUMPBIN -symbols /dev/null 2>&1 | sed '1q'` in
+    *COFF*)
+      DUMPBIN="$DUMPBIN -symbols"
+      ;;
+    *)
+      DUMPBIN=:
+      ;;
+    esac
+  fi
+
+  if test "$DUMPBIN" != ":"; then
+    NM="$DUMPBIN"
+  fi
+fi
+test -z "$NM" && NM=nm
+
+
+
+
+
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking the name lister ($NM) interface" >&5
+$as_echo_n "checking the name lister ($NM) interface... " >&6; }
+if ${lt_cv_nm_interface+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  lt_cv_nm_interface="BSD nm"
+  echo "int some_variable = 0;" > conftest.$ac_ext
+  (eval echo "\"\$as_me:$LINENO: $ac_compile\"" >&5)
+  (eval "$ac_compile" 2>conftest.err)
+  cat conftest.err >&5
+  (eval echo "\"\$as_me:$LINENO: $NM \\\"conftest.$ac_objext\\\"\"" >&5)
+  (eval "$NM \"conftest.$ac_objext\"" 2>conftest.err > conftest.out)
+  cat conftest.err >&5
+  (eval echo "\"\$as_me:$LINENO: output\"" >&5)
+  cat conftest.out >&5
+  if $GREP 'External.*some_variable' conftest.out > /dev/null; then
+    lt_cv_nm_interface="MS dumpbin"
+  fi
+  rm -f conftest*
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_nm_interface" >&5
+$as_echo "$lt_cv_nm_interface" >&6; }
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether ln -s works" >&5
+$as_echo_n "checking whether ln -s works... " >&6; }
+LN_S=$as_ln_s
+if test "$LN_S" = "ln -s"; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no, using $LN_S" >&5
+$as_echo "no, using $LN_S" >&6; }
+fi
+
+# find the maximum length of command line arguments
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking the maximum length of command line arguments" >&5
+$as_echo_n "checking the maximum length of command line arguments... " >&6; }
+if ${lt_cv_sys_max_cmd_len+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+    i=0
+  teststring="ABCD"
+
+  case $build_os in
+  msdosdjgpp*)
+    # On DJGPP, this test can blow up pretty badly due to problems in libc
+    # (any single argument exceeding 2000 bytes causes a buffer overrun
+    # during glob expansion).  Even if it were fixed, the result of this
+    # check would be larger than it should be.
+    lt_cv_sys_max_cmd_len=12288;    # 12K is about right
+    ;;
+
+  gnu*)
+    # Under GNU Hurd, this test is not required because there is
+    # no limit to the length of command line arguments.
+    # Libtool will interpret -1 as no limit whatsoever
+    lt_cv_sys_max_cmd_len=-1;
+    ;;
+
+  cygwin* | mingw* | cegcc*)
+    # On Win9x/ME, this test blows up -- it succeeds, but takes
+    # about 5 minutes as the teststring grows exponentially.
+    # Worse, since 9x/ME are not pre-emptively multitasking,
+    # you end up with a "frozen" computer, even though with patience
+    # the test eventually succeeds (with a max line length of 256k).
+    # Instead, let's just punt: use the minimum linelength reported by
+    # all of the supported platforms: 8192 (on NT/2K/XP).
+    lt_cv_sys_max_cmd_len=8192;
+    ;;
+
+  mint*)
+    # On MiNT this can take a long time and run out of memory.
+    lt_cv_sys_max_cmd_len=8192;
+    ;;
+
+  amigaos*)
+    # On AmigaOS with pdksh, this test takes hours, literally.
+    # So we just punt and use a minimum line length of 8192.
+    lt_cv_sys_max_cmd_len=8192;
+    ;;
+
+  netbsd* | freebsd* | openbsd* | darwin* | dragonfly*)
+    # This has been around since 386BSD, at least.  Likely further.
+    if test -x /sbin/sysctl; then
+      lt_cv_sys_max_cmd_len=`/sbin/sysctl -n kern.argmax`
+    elif test -x /usr/sbin/sysctl; then
+      lt_cv_sys_max_cmd_len=`/usr/sbin/sysctl -n kern.argmax`
+    else
+      lt_cv_sys_max_cmd_len=65536	# usable default for all BSDs
+    fi
+    # And add a safety zone
+    lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \/ 4`
+    lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \* 3`
+    ;;
+
+  interix*)
+    # We know the value 262144 and hardcode it with a safety zone (like BSD)
+    lt_cv_sys_max_cmd_len=196608
+    ;;
+
+  os2*)
+    # The test takes a long time on OS/2.
+    lt_cv_sys_max_cmd_len=8192
+    ;;
+
+  osf*)
+    # Dr. Hans Ekkehard Plesser reports seeing a kernel panic running configure
+    # due to this test when exec_disable_arg_limit is 1 on Tru64. It is not
+    # nice to cause kernel panics so lets avoid the loop below.
+    # First set a reasonable default.
+    lt_cv_sys_max_cmd_len=16384
+    #
+    if test -x /sbin/sysconfig; then
+      case `/sbin/sysconfig -q proc exec_disable_arg_limit` in
+        *1*) lt_cv_sys_max_cmd_len=-1 ;;
+      esac
+    fi
+    ;;
+  sco3.2v5*)
+    lt_cv_sys_max_cmd_len=102400
+    ;;
+  sysv5* | sco5v6* | sysv4.2uw2*)
+    kargmax=`grep ARG_MAX /etc/conf/cf.d/stune 2>/dev/null`
+    if test -n "$kargmax"; then
+      lt_cv_sys_max_cmd_len=`echo $kargmax | sed 's/.*[	 ]//'`
+    else
+      lt_cv_sys_max_cmd_len=32768
+    fi
+    ;;
+  *)
+    lt_cv_sys_max_cmd_len=`(getconf ARG_MAX) 2> /dev/null`
+    if test -n "$lt_cv_sys_max_cmd_len" && \
+	test undefined != "$lt_cv_sys_max_cmd_len"; then
+      lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \/ 4`
+      lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \* 3`
+    else
+      # Make teststring a little bigger before we do anything with it.
+      # a 1K string should be a reasonable start.
+      for i in 1 2 3 4 5 6 7 8 ; do
+        teststring=$teststring$teststring
+      done
+      SHELL=${SHELL-${CONFIG_SHELL-/bin/sh}}
+      # If test is not a shell built-in, we'll probably end up computing a
+      # maximum length that is only half of the actual maximum length, but
+      # we can't tell.
+      while { test "X"`env echo "$teststring$teststring" 2>/dev/null` \
+	         = "X$teststring$teststring"; } >/dev/null 2>&1 &&
+	      test $i != 17 # 1/2 MB should be enough
+      do
+        i=`expr $i + 1`
+        teststring=$teststring$teststring
+      done
+      # Only check the string length outside the loop.
+      lt_cv_sys_max_cmd_len=`expr "X$teststring" : ".*" 2>&1`
+      teststring=
+      # Add a significant safety factor because C++ compilers can tack on
+      # massive amounts of additional arguments before passing them to the
+      # linker.  It appears as though 1/2 is a usable value.
+      lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \/ 2`
+    fi
+    ;;
+  esac
+
+fi
+
+if test -n $lt_cv_sys_max_cmd_len ; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_sys_max_cmd_len" >&5
+$as_echo "$lt_cv_sys_max_cmd_len" >&6; }
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: none" >&5
+$as_echo "none" >&6; }
+fi
+max_cmd_len=$lt_cv_sys_max_cmd_len
+
+
+
+
+
+
+: ${CP="cp -f"}
+: ${MV="mv -f"}
+: ${RM="rm -f"}
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the shell understands some XSI constructs" >&5
+$as_echo_n "checking whether the shell understands some XSI constructs... " >&6; }
+# Try some XSI features
+xsi_shell=no
+( _lt_dummy="a/b/c"
+  test "${_lt_dummy##*/},${_lt_dummy%/*},${_lt_dummy#??}"${_lt_dummy%"$_lt_dummy"}, \
+      = c,a/b,b/c, \
+    && eval 'test $(( 1 + 1 )) -eq 2 \
+    && test "${#_lt_dummy}" -eq 5' ) >/dev/null 2>&1 \
+  && xsi_shell=yes
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $xsi_shell" >&5
+$as_echo "$xsi_shell" >&6; }
+
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the shell understands \"+=\"" >&5
+$as_echo_n "checking whether the shell understands \"+=\"... " >&6; }
+lt_shell_append=no
+( foo=bar; set foo baz; eval "$1+=\$2" && test "$foo" = barbaz ) \
+    >/dev/null 2>&1 \
+  && lt_shell_append=yes
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_shell_append" >&5
+$as_echo "$lt_shell_append" >&6; }
+
+
+if ( (MAIL=60; unset MAIL) || exit) >/dev/null 2>&1; then
+  lt_unset=unset
+else
+  lt_unset=false
+fi
+
+
+
+
+
+# test EBCDIC or ASCII
+case `echo X|tr X '\101'` in
+ A) # ASCII based system
+    # \n is not interpreted correctly by Solaris 8 /usr/ucb/tr
+  lt_SP2NL='tr \040 \012'
+  lt_NL2SP='tr \015\012 \040\040'
+  ;;
+ *) # EBCDIC based system
+  lt_SP2NL='tr \100 \n'
+  lt_NL2SP='tr \r\n \100\100'
+  ;;
+esac
+
+
+
+
+
+
+
+
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking how to convert $build file names to $host format" >&5
+$as_echo_n "checking how to convert $build file names to $host format... " >&6; }
+if ${lt_cv_to_host_file_cmd+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  case $host in
+  *-*-mingw* )
+    case $build in
+      *-*-mingw* ) # actually msys
+        lt_cv_to_host_file_cmd=func_convert_file_msys_to_w32
+        ;;
+      *-*-cygwin* )
+        lt_cv_to_host_file_cmd=func_convert_file_cygwin_to_w32
+        ;;
+      * ) # otherwise, assume *nix
+        lt_cv_to_host_file_cmd=func_convert_file_nix_to_w32
+        ;;
+    esac
+    ;;
+  *-*-cygwin* )
+    case $build in
+      *-*-mingw* ) # actually msys
+        lt_cv_to_host_file_cmd=func_convert_file_msys_to_cygwin
+        ;;
+      *-*-cygwin* )
+        lt_cv_to_host_file_cmd=func_convert_file_noop
+        ;;
+      * ) # otherwise, assume *nix
+        lt_cv_to_host_file_cmd=func_convert_file_nix_to_cygwin
+        ;;
+    esac
+    ;;
+  * ) # unhandled hosts (and "normal" native builds)
+    lt_cv_to_host_file_cmd=func_convert_file_noop
+    ;;
+esac
+
+fi
+
+to_host_file_cmd=$lt_cv_to_host_file_cmd
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_to_host_file_cmd" >&5
+$as_echo "$lt_cv_to_host_file_cmd" >&6; }
+
+
+
+
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking how to convert $build file names to toolchain format" >&5
+$as_echo_n "checking how to convert $build file names to toolchain format... " >&6; }
+if ${lt_cv_to_tool_file_cmd+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  #assume ordinary cross tools, or native build.
+lt_cv_to_tool_file_cmd=func_convert_file_noop
+case $host in
+  *-*-mingw* )
+    case $build in
+      *-*-mingw* ) # actually msys
+        lt_cv_to_tool_file_cmd=func_convert_file_msys_to_w32
+        ;;
+    esac
+    ;;
+esac
+
+fi
+
+to_tool_file_cmd=$lt_cv_to_tool_file_cmd
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_to_tool_file_cmd" >&5
+$as_echo "$lt_cv_to_tool_file_cmd" >&6; }
+
+
+
+
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $LD option to reload object files" >&5
+$as_echo_n "checking for $LD option to reload object files... " >&6; }
+if ${lt_cv_ld_reload_flag+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  lt_cv_ld_reload_flag='-r'
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_ld_reload_flag" >&5
+$as_echo "$lt_cv_ld_reload_flag" >&6; }
+reload_flag=$lt_cv_ld_reload_flag
+case $reload_flag in
+"" | " "*) ;;
+*) reload_flag=" $reload_flag" ;;
+esac
+reload_cmds='$LD$reload_flag -o $output$reload_objs'
+case $host_os in
+  cygwin* | mingw* | pw32* | cegcc*)
+    if test "$GCC" != yes; then
+      reload_cmds=false
+    fi
+    ;;
+  darwin*)
+    if test "$GCC" = yes; then
+      reload_cmds='$LTCC $LTCFLAGS -nostdlib ${wl}-r -o $output$reload_objs'
+    else
+      reload_cmds='$LD$reload_flag -o $output$reload_objs'
+    fi
+    ;;
+esac
+
+
+
+
+
+
+
+
+
+if test -n "$ac_tool_prefix"; then
+  # Extract the first word of "${ac_tool_prefix}objdump", so it can be a program name with args.
+set dummy ${ac_tool_prefix}objdump; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_OBJDUMP+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  if test -n "$OBJDUMP"; then
+  ac_cv_prog_OBJDUMP="$OBJDUMP" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    ac_cv_prog_OBJDUMP="${ac_tool_prefix}objdump"
+    $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
+
+fi
+fi
+OBJDUMP=$ac_cv_prog_OBJDUMP
+if test -n "$OBJDUMP"; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $OBJDUMP" >&5
+$as_echo "$OBJDUMP" >&6; }
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+fi
+if test -z "$ac_cv_prog_OBJDUMP"; then
+  ac_ct_OBJDUMP=$OBJDUMP
+  # Extract the first word of "objdump", so it can be a program name with args.
+set dummy objdump; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_ac_ct_OBJDUMP+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  if test -n "$ac_ct_OBJDUMP"; then
+  ac_cv_prog_ac_ct_OBJDUMP="$ac_ct_OBJDUMP" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    ac_cv_prog_ac_ct_OBJDUMP="objdump"
+    $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
+
+fi
+fi
+ac_ct_OBJDUMP=$ac_cv_prog_ac_ct_OBJDUMP
+if test -n "$ac_ct_OBJDUMP"; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_OBJDUMP" >&5
+$as_echo "$ac_ct_OBJDUMP" >&6; }
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+  if test "x$ac_ct_OBJDUMP" = x; then
+    OBJDUMP="false"
+  else
+    case $cross_compiling:$ac_tool_warned in
+yes:)
+{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
+$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+ac_tool_warned=yes ;;
+esac
+    OBJDUMP=$ac_ct_OBJDUMP
+  fi
+else
+  OBJDUMP="$ac_cv_prog_OBJDUMP"
+fi
+
+test -z "$OBJDUMP" && OBJDUMP=objdump
+
+
+
+
+
+
+
+
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking how to recognize dependent libraries" >&5
+$as_echo_n "checking how to recognize dependent libraries... " >&6; }
+if ${lt_cv_deplibs_check_method+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  lt_cv_file_magic_cmd='$MAGIC_CMD'
+lt_cv_file_magic_test_file=
+lt_cv_deplibs_check_method='unknown'
+# Need to set the preceding variable on all platforms that support
+# interlibrary dependencies.
+# 'none' -- dependencies not supported.
+# `unknown' -- same as none, but documents that we really don't know.
+# 'pass_all' -- all dependencies passed with no checks.
+# 'test_compile' -- check by making test program.
+# 'file_magic [[regex]]' -- check by looking for files in library path
+# which responds to the $file_magic_cmd with a given extended regex.
+# If you have `file' or equivalent on your system and you're not sure
+# whether `pass_all' will *always* work, you probably want this one.
+
+case $host_os in
+aix[4-9]*)
+  lt_cv_deplibs_check_method=pass_all
+  ;;
+
+beos*)
+  lt_cv_deplibs_check_method=pass_all
+  ;;
+
+bsdi[45]*)
+  lt_cv_deplibs_check_method='file_magic ELF [0-9][0-9]*-bit [ML]SB (shared object|dynamic lib)'
+  lt_cv_file_magic_cmd='/usr/bin/file -L'
+  lt_cv_file_magic_test_file=/shlib/libc.so
+  ;;
+
+cygwin*)
+  # func_win32_libid is a shell function defined in ltmain.sh
+  lt_cv_deplibs_check_method='file_magic ^x86 archive import|^x86 DLL'
+  lt_cv_file_magic_cmd='func_win32_libid'
+  ;;
+
+mingw* | pw32*)
+  # Base MSYS/MinGW do not provide the 'file' command needed by
+  # func_win32_libid shell function, so use a weaker test based on 'objdump',
+  # unless we find 'file', for example because we are cross-compiling.
+  # func_win32_libid assumes BSD nm, so disallow it if using MS dumpbin.
+  if ( test "$lt_cv_nm_interface" = "BSD nm" && file / ) >/dev/null 2>&1; then
+    lt_cv_deplibs_check_method='file_magic ^x86 archive import|^x86 DLL'
+    lt_cv_file_magic_cmd='func_win32_libid'
+  else
+    # Keep this pattern in sync with the one in func_win32_libid.
+    lt_cv_deplibs_check_method='file_magic file format (pei*-i386(.*architecture: i386)?|pe-arm-wince|pe-x86-64)'
+    lt_cv_file_magic_cmd='$OBJDUMP -f'
+  fi
+  ;;
+
+cegcc*)
+  # use the weaker test based on 'objdump'. See mingw*.
+  lt_cv_deplibs_check_method='file_magic file format pe-arm-.*little(.*architecture: arm)?'
+  lt_cv_file_magic_cmd='$OBJDUMP -f'
+  ;;
+
+darwin* | rhapsody*)
+  lt_cv_deplibs_check_method=pass_all
+  ;;
+
+freebsd* | dragonfly*)
+  if echo __ELF__ | $CC -E - | $GREP __ELF__ > /dev/null; then
+    case $host_cpu in
+    i*86 )
+      # Not sure whether the presence of OpenBSD here was a mistake.
+      # Let's accept both of them until this is cleared up.
+      lt_cv_deplibs_check_method='file_magic (FreeBSD|OpenBSD|DragonFly)/i[3-9]86 (compact )?demand paged shared library'
+      lt_cv_file_magic_cmd=/usr/bin/file
+      lt_cv_file_magic_test_file=`echo /usr/lib/libc.so.*`
+      ;;
+    esac
+  else
+    lt_cv_deplibs_check_method=pass_all
+  fi
+  ;;
+
+haiku*)
+  lt_cv_deplibs_check_method=pass_all
+  ;;
+
+hpux10.20* | hpux11*)
+  lt_cv_file_magic_cmd=/usr/bin/file
+  case $host_cpu in
+  ia64*)
+    lt_cv_deplibs_check_method='file_magic (s[0-9][0-9][0-9]|ELF-[0-9][0-9]) shared object file - IA64'
+    lt_cv_file_magic_test_file=/usr/lib/hpux32/libc.so
+    ;;
+  hppa*64*)
+    lt_cv_deplibs_check_method='file_magic (s[0-9][0-9][0-9]|ELF[ -][0-9][0-9])(-bit)?( [LM]SB)? shared object( file)?[, -]* PA-RISC [0-9]\.[0-9]'
+    lt_cv_file_magic_test_file=/usr/lib/pa20_64/libc.sl
+    ;;
+  *)
+    lt_cv_deplibs_check_method='file_magic (s[0-9][0-9][0-9]|PA-RISC[0-9]\.[0-9]) shared library'
+    lt_cv_file_magic_test_file=/usr/lib/libc.sl
+    ;;
+  esac
+  ;;
+
+interix[3-9]*)
+  # PIC code is broken on Interix 3.x, that's why |\.a not |_pic\.a here
+  lt_cv_deplibs_check_method='match_pattern /lib[^/]+(\.so|\.a)$'
+  ;;
+
+irix5* | irix6* | nonstopux*)
+  case $LD in
+  *-32|*"-32 ") libmagic=32-bit;;
+  *-n32|*"-n32 ") libmagic=N32;;
+  *-64|*"-64 ") libmagic=64-bit;;
+  *) libmagic=never-match;;
+  esac
+  lt_cv_deplibs_check_method=pass_all
+  ;;
+
+# This must be glibc/ELF.
+linux* | k*bsd*-gnu | kopensolaris*-gnu | gnu*)
+  lt_cv_deplibs_check_method=pass_all
+  ;;
+
+netbsd* | netbsdelf*-gnu)
+  if echo __ELF__ | $CC -E - | $GREP __ELF__ > /dev/null; then
+    lt_cv_deplibs_check_method='match_pattern /lib[^/]+(\.so\.[0-9]+\.[0-9]+|_pic\.a)$'
+  else
+    lt_cv_deplibs_check_method='match_pattern /lib[^/]+(\.so|_pic\.a)$'
+  fi
+  ;;
+
+newos6*)
+  lt_cv_deplibs_check_method='file_magic ELF [0-9][0-9]*-bit [ML]SB (executable|dynamic lib)'
+  lt_cv_file_magic_cmd=/usr/bin/file
+  lt_cv_file_magic_test_file=/usr/lib/libnls.so
+  ;;
+
+*nto* | *qnx*)
+  lt_cv_deplibs_check_method=pass_all
+  ;;
+
+openbsd*)
+  if test -z "`echo __ELF__ | $CC -E - | $GREP __ELF__`" || test "$host_os-$host_cpu" = "openbsd2.8-powerpc"; then
+    lt_cv_deplibs_check_method='match_pattern /lib[^/]+(\.so\.[0-9]+\.[0-9]+|\.so|_pic\.a)$'
+  else
+    lt_cv_deplibs_check_method='match_pattern /lib[^/]+(\.so\.[0-9]+\.[0-9]+|_pic\.a)$'
+  fi
+  ;;
+
+osf3* | osf4* | osf5*)
+  lt_cv_deplibs_check_method=pass_all
+  ;;
+
+rdos*)
+  lt_cv_deplibs_check_method=pass_all
+  ;;
+
+solaris*)
+  lt_cv_deplibs_check_method=pass_all
+  ;;
+
+sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX* | sysv4*uw2*)
+  lt_cv_deplibs_check_method=pass_all
+  ;;
+
+sysv4 | sysv4.3*)
+  case $host_vendor in
+  motorola)
+    lt_cv_deplibs_check_method='file_magic ELF [0-9][0-9]*-bit [ML]SB (shared object|dynamic lib) M[0-9][0-9]* Version [0-9]'
+    lt_cv_file_magic_test_file=`echo /usr/lib/libc.so*`
+    ;;
+  ncr)
+    lt_cv_deplibs_check_method=pass_all
+    ;;
+  sequent)
+    lt_cv_file_magic_cmd='/bin/file'
+    lt_cv_deplibs_check_method='file_magic ELF [0-9][0-9]*-bit [LM]SB (shared object|dynamic lib )'
+    ;;
+  sni)
+    lt_cv_file_magic_cmd='/bin/file'
+    lt_cv_deplibs_check_method="file_magic ELF [0-9][0-9]*-bit [LM]SB dynamic lib"
+    lt_cv_file_magic_test_file=/lib/libc.so
+    ;;
+  siemens)
+    lt_cv_deplibs_check_method=pass_all
+    ;;
+  pc)
+    lt_cv_deplibs_check_method=pass_all
+    ;;
+  esac
+  ;;
+
+tpf*)
+  lt_cv_deplibs_check_method=pass_all
+  ;;
+esac
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_deplibs_check_method" >&5
+$as_echo "$lt_cv_deplibs_check_method" >&6; }
+
+file_magic_glob=
+want_nocaseglob=no
+if test "$build" = "$host"; then
+  case $host_os in
+  mingw* | pw32*)
+    if ( shopt | grep nocaseglob ) >/dev/null 2>&1; then
+      want_nocaseglob=yes
+    else
+      file_magic_glob=`echo aAbBcCdDeEfFgGhHiIjJkKlLmMnNoOpPqQrRsStTuUvVwWxXyYzZ | $SED -e "s/\(..\)/s\/[\1]\/[\1]\/g;/g"`
+    fi
+    ;;
+  esac
+fi
+
+file_magic_cmd=$lt_cv_file_magic_cmd
+deplibs_check_method=$lt_cv_deplibs_check_method
+test -z "$deplibs_check_method" && deplibs_check_method=unknown
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+if test -n "$ac_tool_prefix"; then
+  # Extract the first word of "${ac_tool_prefix}dlltool", so it can be a program name with args.
+set dummy ${ac_tool_prefix}dlltool; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_DLLTOOL+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  if test -n "$DLLTOOL"; then
+  ac_cv_prog_DLLTOOL="$DLLTOOL" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    ac_cv_prog_DLLTOOL="${ac_tool_prefix}dlltool"
+    $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
+
+fi
+fi
+DLLTOOL=$ac_cv_prog_DLLTOOL
+if test -n "$DLLTOOL"; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $DLLTOOL" >&5
+$as_echo "$DLLTOOL" >&6; }
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+fi
+if test -z "$ac_cv_prog_DLLTOOL"; then
+  ac_ct_DLLTOOL=$DLLTOOL
+  # Extract the first word of "dlltool", so it can be a program name with args.
+set dummy dlltool; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_ac_ct_DLLTOOL+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  if test -n "$ac_ct_DLLTOOL"; then
+  ac_cv_prog_ac_ct_DLLTOOL="$ac_ct_DLLTOOL" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    ac_cv_prog_ac_ct_DLLTOOL="dlltool"
+    $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
+
+fi
+fi
+ac_ct_DLLTOOL=$ac_cv_prog_ac_ct_DLLTOOL
+if test -n "$ac_ct_DLLTOOL"; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_DLLTOOL" >&5
+$as_echo "$ac_ct_DLLTOOL" >&6; }
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+  if test "x$ac_ct_DLLTOOL" = x; then
+    DLLTOOL="false"
+  else
+    case $cross_compiling:$ac_tool_warned in
+yes:)
+{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
+$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+ac_tool_warned=yes ;;
+esac
+    DLLTOOL=$ac_ct_DLLTOOL
+  fi
+else
+  DLLTOOL="$ac_cv_prog_DLLTOOL"
+fi
+
+test -z "$DLLTOOL" && DLLTOOL=dlltool
+
+
+
+
+
+
+
+
+
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking how to associate runtime and link libraries" >&5
+$as_echo_n "checking how to associate runtime and link libraries... " >&6; }
+if ${lt_cv_sharedlib_from_linklib_cmd+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  lt_cv_sharedlib_from_linklib_cmd='unknown'
+
+case $host_os in
+cygwin* | mingw* | pw32* | cegcc*)
+  # two different shell functions defined in ltmain.sh
+  # decide which to use based on capabilities of $DLLTOOL
+  case `$DLLTOOL --help 2>&1` in
+  *--identify-strict*)
+    lt_cv_sharedlib_from_linklib_cmd=func_cygming_dll_for_implib
+    ;;
+  *)
+    lt_cv_sharedlib_from_linklib_cmd=func_cygming_dll_for_implib_fallback
+    ;;
+  esac
+  ;;
+*)
+  # fallback: assume linklib IS sharedlib
+  lt_cv_sharedlib_from_linklib_cmd="$ECHO"
+  ;;
+esac
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_sharedlib_from_linklib_cmd" >&5
+$as_echo "$lt_cv_sharedlib_from_linklib_cmd" >&6; }
+sharedlib_from_linklib_cmd=$lt_cv_sharedlib_from_linklib_cmd
+test -z "$sharedlib_from_linklib_cmd" && sharedlib_from_linklib_cmd=$ECHO
+
+
+
+
+
+
+
+if test -n "$ac_tool_prefix"; then
+  for ac_prog in ar
+  do
+    # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args.
+set dummy $ac_tool_prefix$ac_prog; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_AR+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  if test -n "$AR"; then
+  ac_cv_prog_AR="$AR" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    ac_cv_prog_AR="$ac_tool_prefix$ac_prog"
+    $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
+
+fi
+fi
+AR=$ac_cv_prog_AR
+if test -n "$AR"; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $AR" >&5
+$as_echo "$AR" >&6; }
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+    test -n "$AR" && break
+  done
+fi
+if test -z "$AR"; then
+  ac_ct_AR=$AR
+  for ac_prog in ar
+do
+  # Extract the first word of "$ac_prog", so it can be a program name with args.
+set dummy $ac_prog; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_ac_ct_AR+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  if test -n "$ac_ct_AR"; then
+  ac_cv_prog_ac_ct_AR="$ac_ct_AR" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    ac_cv_prog_ac_ct_AR="$ac_prog"
+    $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
+
+fi
+fi
+ac_ct_AR=$ac_cv_prog_ac_ct_AR
+if test -n "$ac_ct_AR"; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_AR" >&5
+$as_echo "$ac_ct_AR" >&6; }
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+  test -n "$ac_ct_AR" && break
+done
+
+  if test "x$ac_ct_AR" = x; then
+    AR="false"
+  else
+    case $cross_compiling:$ac_tool_warned in
+yes:)
+{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
+$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+ac_tool_warned=yes ;;
+esac
+    AR=$ac_ct_AR
+  fi
+fi
+
+: ${AR=ar}
+: ${AR_FLAGS=cru}
+
+
+
+
+
+
+
+
+
+
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for archiver @FILE support" >&5
+$as_echo_n "checking for archiver @FILE support... " >&6; }
+if ${lt_cv_ar_at_file+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  lt_cv_ar_at_file=no
+   cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+  echo conftest.$ac_objext > conftest.lst
+      lt_ar_try='$AR $AR_FLAGS libconftest.a @conftest.lst >&5'
+      { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$lt_ar_try\""; } >&5
+  (eval $lt_ar_try) 2>&5
+  ac_status=$?
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; }
+      if test "$ac_status" -eq 0; then
+	# Ensure the archiver fails upon bogus file names.
+	rm -f conftest.$ac_objext libconftest.a
+	{ { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$lt_ar_try\""; } >&5
+  (eval $lt_ar_try) 2>&5
+  ac_status=$?
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; }
+	if test "$ac_status" -ne 0; then
+          lt_cv_ar_at_file=@
+        fi
+      fi
+      rm -f conftest.* libconftest.a
+
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_ar_at_file" >&5
+$as_echo "$lt_cv_ar_at_file" >&6; }
+
+if test "x$lt_cv_ar_at_file" = xno; then
+  archiver_list_spec=
+else
+  archiver_list_spec=$lt_cv_ar_at_file
+fi
+
+
+
+
+
+
+
+if test -n "$ac_tool_prefix"; then
+  # Extract the first word of "${ac_tool_prefix}strip", so it can be a program name with args.
+set dummy ${ac_tool_prefix}strip; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_STRIP+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  if test -n "$STRIP"; then
+  ac_cv_prog_STRIP="$STRIP" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    ac_cv_prog_STRIP="${ac_tool_prefix}strip"
+    $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
+
+fi
+fi
+STRIP=$ac_cv_prog_STRIP
+if test -n "$STRIP"; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $STRIP" >&5
+$as_echo "$STRIP" >&6; }
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+fi
+if test -z "$ac_cv_prog_STRIP"; then
+  ac_ct_STRIP=$STRIP
+  # Extract the first word of "strip", so it can be a program name with args.
+set dummy strip; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_ac_ct_STRIP+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  if test -n "$ac_ct_STRIP"; then
+  ac_cv_prog_ac_ct_STRIP="$ac_ct_STRIP" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    ac_cv_prog_ac_ct_STRIP="strip"
+    $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
+
+fi
+fi
+ac_ct_STRIP=$ac_cv_prog_ac_ct_STRIP
+if test -n "$ac_ct_STRIP"; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_STRIP" >&5
+$as_echo "$ac_ct_STRIP" >&6; }
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+  if test "x$ac_ct_STRIP" = x; then
+    STRIP=":"
+  else
+    case $cross_compiling:$ac_tool_warned in
+yes:)
+{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
+$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+ac_tool_warned=yes ;;
+esac
+    STRIP=$ac_ct_STRIP
+  fi
+else
+  STRIP="$ac_cv_prog_STRIP"
+fi
+
+test -z "$STRIP" && STRIP=:
+
+
+
+
+
+
+if test -n "$ac_tool_prefix"; then
+  # Extract the first word of "${ac_tool_prefix}ranlib", so it can be a program name with args.
+set dummy ${ac_tool_prefix}ranlib; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_RANLIB+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  if test -n "$RANLIB"; then
+  ac_cv_prog_RANLIB="$RANLIB" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    ac_cv_prog_RANLIB="${ac_tool_prefix}ranlib"
+    $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
+
+fi
+fi
+RANLIB=$ac_cv_prog_RANLIB
+if test -n "$RANLIB"; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $RANLIB" >&5
+$as_echo "$RANLIB" >&6; }
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+fi
+if test -z "$ac_cv_prog_RANLIB"; then
+  ac_ct_RANLIB=$RANLIB
+  # Extract the first word of "ranlib", so it can be a program name with args.
+set dummy ranlib; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_ac_ct_RANLIB+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  if test -n "$ac_ct_RANLIB"; then
+  ac_cv_prog_ac_ct_RANLIB="$ac_ct_RANLIB" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    ac_cv_prog_ac_ct_RANLIB="ranlib"
+    $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
+
+fi
+fi
+ac_ct_RANLIB=$ac_cv_prog_ac_ct_RANLIB
+if test -n "$ac_ct_RANLIB"; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_RANLIB" >&5
+$as_echo "$ac_ct_RANLIB" >&6; }
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+  if test "x$ac_ct_RANLIB" = x; then
+    RANLIB=":"
+  else
+    case $cross_compiling:$ac_tool_warned in
+yes:)
+{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
+$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+ac_tool_warned=yes ;;
+esac
+    RANLIB=$ac_ct_RANLIB
+  fi
+else
+  RANLIB="$ac_cv_prog_RANLIB"
+fi
+
+test -z "$RANLIB" && RANLIB=:
+
+
+
+
+
+
+# Determine commands to create old-style static archives.
+old_archive_cmds='$AR $AR_FLAGS $oldlib$oldobjs'
+old_postinstall_cmds='chmod 644 $oldlib'
+old_postuninstall_cmds=
+
+if test -n "$RANLIB"; then
+  case $host_os in
+  openbsd*)
+    old_postinstall_cmds="$old_postinstall_cmds~\$RANLIB -t \$tool_oldlib"
+    ;;
+  *)
+    old_postinstall_cmds="$old_postinstall_cmds~\$RANLIB \$tool_oldlib"
+    ;;
+  esac
+  old_archive_cmds="$old_archive_cmds~\$RANLIB \$tool_oldlib"
+fi
+
+case $host_os in
+  darwin*)
+    lock_old_archive_extraction=yes ;;
+  *)
+    lock_old_archive_extraction=no ;;
+esac
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+# If no C compiler was specified, use CC.
+LTCC=${LTCC-"$CC"}
+
+# If no C compiler flags were specified, use CFLAGS.
+LTCFLAGS=${LTCFLAGS-"$CFLAGS"}
+
+# Allow CC to be a program name with arguments.
+compiler=$CC
+
+
+# Check for command to grab the raw symbol name followed by C symbol from nm.
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking command to parse $NM output from $compiler object" >&5
+$as_echo_n "checking command to parse $NM output from $compiler object... " >&6; }
+if ${lt_cv_sys_global_symbol_pipe+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+
+# These are sane defaults that work on at least a few old systems.
+# [They come from Ultrix.  What could be older than Ultrix?!! ;)]
+
+# Character class describing NM global symbol codes.
+symcode='[BCDEGRST]'
+
+# Regexp to match symbols that can be accessed directly from C.
+sympat='\([_A-Za-z][_A-Za-z0-9]*\)'
+
+# Define system-specific variables.
+case $host_os in
+aix*)
+  symcode='[BCDT]'
+  ;;
+cygwin* | mingw* | pw32* | cegcc*)
+  symcode='[ABCDGISTW]'
+  ;;
+hpux*)
+  if test "$host_cpu" = ia64; then
+    symcode='[ABCDEGRST]'
+  fi
+  ;;
+irix* | nonstopux*)
+  symcode='[BCDEGRST]'
+  ;;
+osf*)
+  symcode='[BCDEGQRST]'
+  ;;
+solaris*)
+  symcode='[BDRT]'
+  ;;
+sco3.2v5*)
+  symcode='[DT]'
+  ;;
+sysv4.2uw2*)
+  symcode='[DT]'
+  ;;
+sysv5* | sco5v6* | unixware* | OpenUNIX*)
+  symcode='[ABDT]'
+  ;;
+sysv4)
+  symcode='[DFNSTU]'
+  ;;
+esac
+
+# If we're using GNU nm, then use its standard symbol codes.
+case `$NM -V 2>&1` in
+*GNU* | *'with BFD'*)
+  symcode='[ABCDGIRSTW]' ;;
+esac
+
+# Transform an extracted symbol line into a proper C declaration.
+# Some systems (esp. on ia64) link data and code symbols differently,
+# so use this general approach.
+lt_cv_sys_global_symbol_to_cdecl="sed -n -e 's/^T .* \(.*\)$/extern int \1();/p' -e 's/^$symcode* .* \(.*\)$/extern char \1;/p'"
+
+# Transform an extracted symbol line into symbol name and symbol address
+lt_cv_sys_global_symbol_to_c_name_address="sed -n -e 's/^: \([^ ]*\)[ ]*$/  {\\\"\1\\\", (void *) 0},/p' -e 's/^$symcode* \([^ ]*\) \([^ ]*\)$/  {\"\2\", (void *) \&\2},/p'"
+lt_cv_sys_global_symbol_to_c_name_address_lib_prefix="sed -n -e 's/^: \([^ ]*\)[ ]*$/  {\\\"\1\\\", (void *) 0},/p' -e 's/^$symcode* \([^ ]*\) \(lib[^ ]*\)$/  {\"\2\", (void *) \&\2},/p' -e 's/^$symcode* \([^ ]*\) \([^ ]*\)$/  {\"lib\2\", (void *) \&\2},/p'"
+
+# Handle CRLF in mingw tool chain
+opt_cr=
+case $build_os in
+mingw*)
+  opt_cr=`$ECHO 'x\{0,1\}' | tr x '\015'` # option cr in regexp
+  ;;
+esac
+
+# Try without a prefix underscore, then with it.
+for ac_symprfx in "" "_"; do
+
+  # Transform symcode, sympat, and symprfx into a raw symbol and a C symbol.
+  symxfrm="\\1 $ac_symprfx\\2 \\2"
+
+  # Write the raw and C identifiers.
+  if test "$lt_cv_nm_interface" = "MS dumpbin"; then
+    # Fake it for dumpbin and say T for any non-static function
+    # and D for any global variable.
+    # Also find C++ and __fastcall symbols from MSVC++,
+    # which start with @ or ?.
+    lt_cv_sys_global_symbol_pipe="$AWK '"\
+"     {last_section=section; section=\$ 3};"\
+"     /^COFF SYMBOL TABLE/{for(i in hide) delete hide[i]};"\
+"     /Section length .*#relocs.*(pick any)/{hide[last_section]=1};"\
+"     \$ 0!~/External *\|/{next};"\
+"     / 0+ UNDEF /{next}; / UNDEF \([^|]\)*()/{next};"\
+"     {if(hide[section]) next};"\
+"     {f=0}; \$ 0~/\(\).*\|/{f=1}; {printf f ? \"T \" : \"D \"};"\
+"     {split(\$ 0, a, /\||\r/); split(a[2], s)};"\
+"     s[1]~/^[@?]/{print s[1], s[1]; next};"\
+"     s[1]~prfx {split(s[1],t,\"@\"); print t[1], substr(t[1],length(prfx))}"\
+"     ' prfx=^$ac_symprfx"
+  else
+    lt_cv_sys_global_symbol_pipe="sed -n -e 's/^.*[	 ]\($symcode$symcode*\)[	 ][	 ]*$ac_symprfx$sympat$opt_cr$/$symxfrm/p'"
+  fi
+  lt_cv_sys_global_symbol_pipe="$lt_cv_sys_global_symbol_pipe | sed '/ __gnu_lto/d'"
+
+  # Check to see that the pipe works correctly.
+  pipe_works=no
+
+  rm -f conftest*
+  cat > conftest.$ac_ext <<_LT_EOF
+#ifdef __cplusplus
+extern "C" {
+#endif
+char nm_test_var;
+void nm_test_func(void);
+void nm_test_func(void){}
+#ifdef __cplusplus
+}
+#endif
+int main(){nm_test_var='a';nm_test_func();return(0);}
+_LT_EOF
+
+  if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5
+  (eval $ac_compile) 2>&5
+  ac_status=$?
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; }; then
+    # Now try to grab the symbols.
+    nlist=conftest.nm
+    if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$NM conftest.$ac_objext \| "$lt_cv_sys_global_symbol_pipe" \> $nlist\""; } >&5
+  (eval $NM conftest.$ac_objext \| "$lt_cv_sys_global_symbol_pipe" \> $nlist) 2>&5
+  ac_status=$?
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; } && test -s "$nlist"; then
+      # Try sorting and uniquifying the output.
+      if sort "$nlist" | uniq > "$nlist"T; then
+	mv -f "$nlist"T "$nlist"
+      else
+	rm -f "$nlist"T
+      fi
+
+      # Make sure that we snagged all the symbols we need.
+      if $GREP ' nm_test_var$' "$nlist" >/dev/null; then
+	if $GREP ' nm_test_func$' "$nlist" >/dev/null; then
+	  cat <<_LT_EOF > conftest.$ac_ext
+/* Keep this code in sync between libtool.m4, ltmain, lt_system.h, and tests.  */
+#if defined(_WIN32) || defined(__CYGWIN__) || defined(_WIN32_WCE)
+/* DATA imports from DLLs on WIN32 con't be const, because runtime
+   relocations are performed -- see ld's documentation on pseudo-relocs.  */
+# define LT_DLSYM_CONST
+#elif defined(__osf__)
+/* This system does not cope well with relocations in const data.  */
+# define LT_DLSYM_CONST
+#else
+# define LT_DLSYM_CONST const
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+_LT_EOF
+	  # Now generate the symbol file.
+	  eval "$lt_cv_sys_global_symbol_to_cdecl"' < "$nlist" | $GREP -v main >> conftest.$ac_ext'
+
+	  cat <<_LT_EOF >> conftest.$ac_ext
+
+/* The mapping between symbol names and symbols.  */
+LT_DLSYM_CONST struct {
+  const char *name;
+  void       *address;
+}
+lt__PROGRAM__LTX_preloaded_symbols[] =
+{
+  { "@PROGRAM@", (void *) 0 },
+_LT_EOF
+	  $SED "s/^$symcode$symcode* \(.*\) \(.*\)$/  {\"\2\", (void *) \&\2},/" < "$nlist" | $GREP -v main >> conftest.$ac_ext
+	  cat <<\_LT_EOF >> conftest.$ac_ext
+  {0, (void *) 0}
+};
+
+/* This works around a problem in FreeBSD linker */
+#ifdef FREEBSD_WORKAROUND
+static const void *lt_preloaded_setup() {
+  return lt__PROGRAM__LTX_preloaded_symbols;
+}
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+_LT_EOF
+	  # Now try linking the two files.
+	  mv conftest.$ac_objext conftstm.$ac_objext
+	  lt_globsym_save_LIBS=$LIBS
+	  lt_globsym_save_CFLAGS=$CFLAGS
+	  LIBS="conftstm.$ac_objext"
+	  CFLAGS="$CFLAGS$lt_prog_compiler_no_builtin_flag"
+	  if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_link\""; } >&5
+  (eval $ac_link) 2>&5
+  ac_status=$?
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; } && test -s conftest${ac_exeext}; then
+	    pipe_works=yes
+	  fi
+	  LIBS=$lt_globsym_save_LIBS
+	  CFLAGS=$lt_globsym_save_CFLAGS
+	else
+	  echo "cannot find nm_test_func in $nlist" >&5
+	fi
+      else
+	echo "cannot find nm_test_var in $nlist" >&5
+      fi
+    else
+      echo "cannot run $lt_cv_sys_global_symbol_pipe" >&5
+    fi
+  else
+    echo "$progname: failed program was:" >&5
+    cat conftest.$ac_ext >&5
+  fi
+  rm -rf conftest* conftst*
+
+  # Do not use the global_symbol_pipe unless it works.
+  if test "$pipe_works" = yes; then
+    break
+  else
+    lt_cv_sys_global_symbol_pipe=
+  fi
+done
+
+fi
+
+if test -z "$lt_cv_sys_global_symbol_pipe"; then
+  lt_cv_sys_global_symbol_to_cdecl=
+fi
+if test -z "$lt_cv_sys_global_symbol_pipe$lt_cv_sys_global_symbol_to_cdecl"; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: failed" >&5
+$as_echo "failed" >&6; }
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: ok" >&5
+$as_echo "ok" >&6; }
+fi
+
+# Response file support.
+if test "$lt_cv_nm_interface" = "MS dumpbin"; then
+  nm_file_list_spec='@'
+elif $NM --help 2>/dev/null | grep '[@]FILE' >/dev/null; then
+  nm_file_list_spec='@'
+fi
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for sysroot" >&5
+$as_echo_n "checking for sysroot... " >&6; }
+
+# Check whether --with-sysroot was given.
+if test "${with_sysroot+set}" = set; then :
+  withval=$with_sysroot;
+else
+  with_sysroot=no
+fi
+
+
+lt_sysroot=
+case ${with_sysroot} in #(
+ yes)
+   if test "$GCC" = yes; then
+     lt_sysroot=`$CC --print-sysroot 2>/dev/null`
+   fi
+   ;; #(
+ /*)
+   lt_sysroot=`echo "$with_sysroot" | sed -e "$sed_quote_subst"`
+   ;; #(
+ no|'')
+   ;; #(
+ *)
+   { $as_echo "$as_me:${as_lineno-$LINENO}: result: ${with_sysroot}" >&5
+$as_echo "${with_sysroot}" >&6; }
+   as_fn_error $? "The sysroot must be an absolute path." "$LINENO" 5
+   ;;
+esac
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: ${lt_sysroot:-no}" >&5
+$as_echo "${lt_sysroot:-no}" >&6; }
+
+
+
+
+
+# Check whether --enable-libtool-lock was given.
+if test "${enable_libtool_lock+set}" = set; then :
+  enableval=$enable_libtool_lock;
+fi
+
+test "x$enable_libtool_lock" != xno && enable_libtool_lock=yes
+
+# Some flags need to be propagated to the compiler or linker for good
+# libtool support.
+case $host in
+ia64-*-hpux*)
+  # Find out which ABI we are using.
+  echo 'int i;' > conftest.$ac_ext
+  if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5
+  (eval $ac_compile) 2>&5
+  ac_status=$?
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; }; then
+    case `/usr/bin/file conftest.$ac_objext` in
+      *ELF-32*)
+	HPUX_IA64_MODE="32"
+	;;
+      *ELF-64*)
+	HPUX_IA64_MODE="64"
+	;;
+    esac
+  fi
+  rm -rf conftest*
+  ;;
+*-*-irix6*)
+  # Find out which ABI we are using.
+  echo '#line '$LINENO' "configure"' > conftest.$ac_ext
+  if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5
+  (eval $ac_compile) 2>&5
+  ac_status=$?
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; }; then
+    if test "$lt_cv_prog_gnu_ld" = yes; then
+      case `/usr/bin/file conftest.$ac_objext` in
+	*32-bit*)
+	  LD="${LD-ld} -melf32bsmip"
+	  ;;
+	*N32*)
+	  LD="${LD-ld} -melf32bmipn32"
+	  ;;
+	*64-bit*)
+	  LD="${LD-ld} -melf64bmip"
+	;;
+      esac
+    else
+      case `/usr/bin/file conftest.$ac_objext` in
+	*32-bit*)
+	  LD="${LD-ld} -32"
+	  ;;
+	*N32*)
+	  LD="${LD-ld} -n32"
+	  ;;
+	*64-bit*)
+	  LD="${LD-ld} -64"
+	  ;;
+      esac
+    fi
+  fi
+  rm -rf conftest*
+  ;;
+
+x86_64-*kfreebsd*-gnu|x86_64-*linux*|ppc*-*linux*|powerpc*-*linux*| \
+s390*-*linux*|s390*-*tpf*|sparc*-*linux*)
+  # Find out which ABI we are using.
+  echo 'int i;' > conftest.$ac_ext
+  if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5
+  (eval $ac_compile) 2>&5
+  ac_status=$?
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; }; then
+    case `/usr/bin/file conftest.o` in
+      *32-bit*)
+	case $host in
+	  x86_64-*kfreebsd*-gnu)
+	    LD="${LD-ld} -m elf_i386_fbsd"
+	    ;;
+	  x86_64-*linux*)
+	    case `/usr/bin/file conftest.o` in
+	      *x86-64*)
+		LD="${LD-ld} -m elf32_x86_64"
+		;;
+	      *)
+		LD="${LD-ld} -m elf_i386"
+		;;
+	    esac
+	    ;;
+	  ppc64-*linux*|powerpc64-*linux*)
+	    LD="${LD-ld} -m elf32ppclinux"
+	    ;;
+	  s390x-*linux*)
+	    LD="${LD-ld} -m elf_s390"
+	    ;;
+	  sparc64-*linux*)
+	    LD="${LD-ld} -m elf32_sparc"
+	    ;;
+	esac
+	;;
+      *64-bit*)
+	case $host in
+	  x86_64-*kfreebsd*-gnu)
+	    LD="${LD-ld} -m elf_x86_64_fbsd"
+	    ;;
+	  x86_64-*linux*)
+	    LD="${LD-ld} -m elf_x86_64"
+	    ;;
+	  ppc*-*linux*|powerpc*-*linux*)
+	    LD="${LD-ld} -m elf64ppc"
+	    ;;
+	  s390*-*linux*|s390*-*tpf*)
+	    LD="${LD-ld} -m elf64_s390"
+	    ;;
+	  sparc*-*linux*)
+	    LD="${LD-ld} -m elf64_sparc"
+	    ;;
+	esac
+	;;
+    esac
+  fi
+  rm -rf conftest*
+  ;;
+
+*-*-sco3.2v5*)
+  # On SCO OpenServer 5, we need -belf to get full-featured binaries.
+  SAVE_CFLAGS="$CFLAGS"
+  CFLAGS="$CFLAGS -belf"
+  { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the C compiler needs -belf" >&5
+$as_echo_n "checking whether the C compiler needs -belf... " >&6; }
+if ${lt_cv_cc_needs_belf+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+
+     cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+  lt_cv_cc_needs_belf=yes
+else
+  lt_cv_cc_needs_belf=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+    conftest$ac_exeext conftest.$ac_ext
+     ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_cc_needs_belf" >&5
+$as_echo "$lt_cv_cc_needs_belf" >&6; }
+  if test x"$lt_cv_cc_needs_belf" != x"yes"; then
+    # this is probably gcc 2.8.0, egcs 1.0 or newer; no need for -belf
+    CFLAGS="$SAVE_CFLAGS"
+  fi
+  ;;
+*-*solaris*)
+  # Find out which ABI we are using.
+  echo 'int i;' > conftest.$ac_ext
+  if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5
+  (eval $ac_compile) 2>&5
+  ac_status=$?
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; }; then
+    case `/usr/bin/file conftest.o` in
+    *64-bit*)
+      case $lt_cv_prog_gnu_ld in
+      yes*)
+        case $host in
+        i?86-*-solaris*)
+          LD="${LD-ld} -m elf_x86_64"
+          ;;
+        sparc*-*-solaris*)
+          LD="${LD-ld} -m elf64_sparc"
+          ;;
+        esac
+        # GNU ld 2.21 introduced _sol2 emulations.  Use them if available.
+        if ${LD-ld} -V | grep _sol2 >/dev/null 2>&1; then
+          LD="${LD-ld}_sol2"
+        fi
+        ;;
+      *)
+	if ${LD-ld} -64 -r -o conftest2.o conftest.o >/dev/null 2>&1; then
+	  LD="${LD-ld} -64"
+	fi
+	;;
+      esac
+      ;;
+    esac
+  fi
+  rm -rf conftest*
+  ;;
+esac
+
+need_locks="$enable_libtool_lock"
+
+if test -n "$ac_tool_prefix"; then
+  # Extract the first word of "${ac_tool_prefix}mt", so it can be a program name with args.
+set dummy ${ac_tool_prefix}mt; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_MANIFEST_TOOL+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  if test -n "$MANIFEST_TOOL"; then
+  ac_cv_prog_MANIFEST_TOOL="$MANIFEST_TOOL" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    ac_cv_prog_MANIFEST_TOOL="${ac_tool_prefix}mt"
+    $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
+
+fi
+fi
+MANIFEST_TOOL=$ac_cv_prog_MANIFEST_TOOL
+if test -n "$MANIFEST_TOOL"; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $MANIFEST_TOOL" >&5
+$as_echo "$MANIFEST_TOOL" >&6; }
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+fi
+if test -z "$ac_cv_prog_MANIFEST_TOOL"; then
+  ac_ct_MANIFEST_TOOL=$MANIFEST_TOOL
+  # Extract the first word of "mt", so it can be a program name with args.
+set dummy mt; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_ac_ct_MANIFEST_TOOL+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  if test -n "$ac_ct_MANIFEST_TOOL"; then
+  ac_cv_prog_ac_ct_MANIFEST_TOOL="$ac_ct_MANIFEST_TOOL" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    ac_cv_prog_ac_ct_MANIFEST_TOOL="mt"
+    $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
+
+fi
+fi
+ac_ct_MANIFEST_TOOL=$ac_cv_prog_ac_ct_MANIFEST_TOOL
+if test -n "$ac_ct_MANIFEST_TOOL"; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_MANIFEST_TOOL" >&5
+$as_echo "$ac_ct_MANIFEST_TOOL" >&6; }
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+  if test "x$ac_ct_MANIFEST_TOOL" = x; then
+    MANIFEST_TOOL=":"
+  else
+    case $cross_compiling:$ac_tool_warned in
+yes:)
+{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
+$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+ac_tool_warned=yes ;;
+esac
+    MANIFEST_TOOL=$ac_ct_MANIFEST_TOOL
+  fi
+else
+  MANIFEST_TOOL="$ac_cv_prog_MANIFEST_TOOL"
+fi
+
+test -z "$MANIFEST_TOOL" && MANIFEST_TOOL=mt
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking if $MANIFEST_TOOL is a manifest tool" >&5
+$as_echo_n "checking if $MANIFEST_TOOL is a manifest tool... " >&6; }
+if ${lt_cv_path_mainfest_tool+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  lt_cv_path_mainfest_tool=no
+  echo "$as_me:$LINENO: $MANIFEST_TOOL '-?'" >&5
+  $MANIFEST_TOOL '-?' 2>conftest.err > conftest.out
+  cat conftest.err >&5
+  if $GREP 'Manifest Tool' conftest.out > /dev/null; then
+    lt_cv_path_mainfest_tool=yes
+  fi
+  rm -f conftest*
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_path_mainfest_tool" >&5
+$as_echo "$lt_cv_path_mainfest_tool" >&6; }
+if test "x$lt_cv_path_mainfest_tool" != xyes; then
+  MANIFEST_TOOL=:
+fi
+
+
+
+
+
+
+  case $host_os in
+    rhapsody* | darwin*)
+    if test -n "$ac_tool_prefix"; then
+  # Extract the first word of "${ac_tool_prefix}dsymutil", so it can be a program name with args.
+set dummy ${ac_tool_prefix}dsymutil; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_DSYMUTIL+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  if test -n "$DSYMUTIL"; then
+  ac_cv_prog_DSYMUTIL="$DSYMUTIL" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    ac_cv_prog_DSYMUTIL="${ac_tool_prefix}dsymutil"
+    $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
+
+fi
+fi
+DSYMUTIL=$ac_cv_prog_DSYMUTIL
+if test -n "$DSYMUTIL"; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $DSYMUTIL" >&5
+$as_echo "$DSYMUTIL" >&6; }
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+fi
+if test -z "$ac_cv_prog_DSYMUTIL"; then
+  ac_ct_DSYMUTIL=$DSYMUTIL
+  # Extract the first word of "dsymutil", so it can be a program name with args.
+set dummy dsymutil; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_ac_ct_DSYMUTIL+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  if test -n "$ac_ct_DSYMUTIL"; then
+  ac_cv_prog_ac_ct_DSYMUTIL="$ac_ct_DSYMUTIL" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    ac_cv_prog_ac_ct_DSYMUTIL="dsymutil"
+    $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
+
+fi
+fi
+ac_ct_DSYMUTIL=$ac_cv_prog_ac_ct_DSYMUTIL
+if test -n "$ac_ct_DSYMUTIL"; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_DSYMUTIL" >&5
+$as_echo "$ac_ct_DSYMUTIL" >&6; }
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+  if test "x$ac_ct_DSYMUTIL" = x; then
+    DSYMUTIL=":"
+  else
+    case $cross_compiling:$ac_tool_warned in
+yes:)
+{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
+$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+ac_tool_warned=yes ;;
+esac
+    DSYMUTIL=$ac_ct_DSYMUTIL
+  fi
+else
+  DSYMUTIL="$ac_cv_prog_DSYMUTIL"
+fi
+
+    if test -n "$ac_tool_prefix"; then
+  # Extract the first word of "${ac_tool_prefix}nmedit", so it can be a program name with args.
+set dummy ${ac_tool_prefix}nmedit; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_NMEDIT+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  if test -n "$NMEDIT"; then
+  ac_cv_prog_NMEDIT="$NMEDIT" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    ac_cv_prog_NMEDIT="${ac_tool_prefix}nmedit"
+    $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
+
+fi
+fi
+NMEDIT=$ac_cv_prog_NMEDIT
+if test -n "$NMEDIT"; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $NMEDIT" >&5
+$as_echo "$NMEDIT" >&6; }
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+fi
+if test -z "$ac_cv_prog_NMEDIT"; then
+  ac_ct_NMEDIT=$NMEDIT
+  # Extract the first word of "nmedit", so it can be a program name with args.
+set dummy nmedit; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_ac_ct_NMEDIT+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  if test -n "$ac_ct_NMEDIT"; then
+  ac_cv_prog_ac_ct_NMEDIT="$ac_ct_NMEDIT" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    ac_cv_prog_ac_ct_NMEDIT="nmedit"
+    $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
+
+fi
+fi
+ac_ct_NMEDIT=$ac_cv_prog_ac_ct_NMEDIT
+if test -n "$ac_ct_NMEDIT"; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_NMEDIT" >&5
+$as_echo "$ac_ct_NMEDIT" >&6; }
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+  if test "x$ac_ct_NMEDIT" = x; then
+    NMEDIT=":"
+  else
+    case $cross_compiling:$ac_tool_warned in
+yes:)
+{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
+$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+ac_tool_warned=yes ;;
+esac
+    NMEDIT=$ac_ct_NMEDIT
+  fi
+else
+  NMEDIT="$ac_cv_prog_NMEDIT"
+fi
+
+    if test -n "$ac_tool_prefix"; then
+  # Extract the first word of "${ac_tool_prefix}lipo", so it can be a program name with args.
+set dummy ${ac_tool_prefix}lipo; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_LIPO+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  if test -n "$LIPO"; then
+  ac_cv_prog_LIPO="$LIPO" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    ac_cv_prog_LIPO="${ac_tool_prefix}lipo"
+    $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
+
+fi
+fi
+LIPO=$ac_cv_prog_LIPO
+if test -n "$LIPO"; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $LIPO" >&5
+$as_echo "$LIPO" >&6; }
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+fi
+if test -z "$ac_cv_prog_LIPO"; then
+  ac_ct_LIPO=$LIPO
+  # Extract the first word of "lipo", so it can be a program name with args.
+set dummy lipo; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_ac_ct_LIPO+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  if test -n "$ac_ct_LIPO"; then
+  ac_cv_prog_ac_ct_LIPO="$ac_ct_LIPO" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    ac_cv_prog_ac_ct_LIPO="lipo"
+    $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
+
+fi
+fi
+ac_ct_LIPO=$ac_cv_prog_ac_ct_LIPO
+if test -n "$ac_ct_LIPO"; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_LIPO" >&5
+$as_echo "$ac_ct_LIPO" >&6; }
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+  if test "x$ac_ct_LIPO" = x; then
+    LIPO=":"
+  else
+    case $cross_compiling:$ac_tool_warned in
+yes:)
+{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
+$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+ac_tool_warned=yes ;;
+esac
+    LIPO=$ac_ct_LIPO
+  fi
+else
+  LIPO="$ac_cv_prog_LIPO"
+fi
+
+    if test -n "$ac_tool_prefix"; then
+  # Extract the first word of "${ac_tool_prefix}otool", so it can be a program name with args.
+set dummy ${ac_tool_prefix}otool; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_OTOOL+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  if test -n "$OTOOL"; then
+  ac_cv_prog_OTOOL="$OTOOL" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    ac_cv_prog_OTOOL="${ac_tool_prefix}otool"
+    $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
+
+fi
+fi
+OTOOL=$ac_cv_prog_OTOOL
+if test -n "$OTOOL"; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $OTOOL" >&5
+$as_echo "$OTOOL" >&6; }
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+fi
+if test -z "$ac_cv_prog_OTOOL"; then
+  ac_ct_OTOOL=$OTOOL
+  # Extract the first word of "otool", so it can be a program name with args.
+set dummy otool; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_ac_ct_OTOOL+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  if test -n "$ac_ct_OTOOL"; then
+  ac_cv_prog_ac_ct_OTOOL="$ac_ct_OTOOL" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    ac_cv_prog_ac_ct_OTOOL="otool"
+    $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
+
+fi
+fi
+ac_ct_OTOOL=$ac_cv_prog_ac_ct_OTOOL
+if test -n "$ac_ct_OTOOL"; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_OTOOL" >&5
+$as_echo "$ac_ct_OTOOL" >&6; }
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+  if test "x$ac_ct_OTOOL" = x; then
+    OTOOL=":"
+  else
+    case $cross_compiling:$ac_tool_warned in
+yes:)
+{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
+$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+ac_tool_warned=yes ;;
+esac
+    OTOOL=$ac_ct_OTOOL
+  fi
+else
+  OTOOL="$ac_cv_prog_OTOOL"
+fi
+
+    if test -n "$ac_tool_prefix"; then
+  # Extract the first word of "${ac_tool_prefix}otool64", so it can be a program name with args.
+set dummy ${ac_tool_prefix}otool64; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_OTOOL64+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  if test -n "$OTOOL64"; then
+  ac_cv_prog_OTOOL64="$OTOOL64" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    ac_cv_prog_OTOOL64="${ac_tool_prefix}otool64"
+    $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
+
+fi
+fi
+OTOOL64=$ac_cv_prog_OTOOL64
+if test -n "$OTOOL64"; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $OTOOL64" >&5
+$as_echo "$OTOOL64" >&6; }
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+fi
+if test -z "$ac_cv_prog_OTOOL64"; then
+  ac_ct_OTOOL64=$OTOOL64
+  # Extract the first word of "otool64", so it can be a program name with args.
+set dummy otool64; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_ac_ct_OTOOL64+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  if test -n "$ac_ct_OTOOL64"; then
+  ac_cv_prog_ac_ct_OTOOL64="$ac_ct_OTOOL64" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    ac_cv_prog_ac_ct_OTOOL64="otool64"
+    $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
+
+fi
+fi
+ac_ct_OTOOL64=$ac_cv_prog_ac_ct_OTOOL64
+if test -n "$ac_ct_OTOOL64"; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_OTOOL64" >&5
+$as_echo "$ac_ct_OTOOL64" >&6; }
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+  if test "x$ac_ct_OTOOL64" = x; then
+    OTOOL64=":"
+  else
+    case $cross_compiling:$ac_tool_warned in
+yes:)
+{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
+$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+ac_tool_warned=yes ;;
+esac
+    OTOOL64=$ac_ct_OTOOL64
+  fi
+else
+  OTOOL64="$ac_cv_prog_OTOOL64"
+fi
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+    { $as_echo "$as_me:${as_lineno-$LINENO}: checking for -single_module linker flag" >&5
+$as_echo_n "checking for -single_module linker flag... " >&6; }
+if ${lt_cv_apple_cc_single_mod+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  lt_cv_apple_cc_single_mod=no
+      if test -z "${LT_MULTI_MODULE}"; then
+	# By default we will add the -single_module flag. You can override
+	# by either setting the environment variable LT_MULTI_MODULE
+	# non-empty at configure time, or by adding -multi_module to the
+	# link flags.
+	rm -rf libconftest.dylib*
+	echo "int foo(void){return 1;}" > conftest.c
+	echo "$LTCC $LTCFLAGS $LDFLAGS -o libconftest.dylib \
+-dynamiclib -Wl,-single_module conftest.c" >&5
+	$LTCC $LTCFLAGS $LDFLAGS -o libconftest.dylib \
+	  -dynamiclib -Wl,-single_module conftest.c 2>conftest.err
+        _lt_result=$?
+	# If there is a non-empty error log, and "single_module"
+	# appears in it, assume the flag caused a linker warning
+        if test -s conftest.err && $GREP single_module conftest.err; then
+	  cat conftest.err >&5
+	# Otherwise, if the output was created with a 0 exit code from
+	# the compiler, it worked.
+	elif test -f libconftest.dylib && test $_lt_result -eq 0; then
+	  lt_cv_apple_cc_single_mod=yes
+	else
+	  cat conftest.err >&5
+	fi
+	rm -rf libconftest.dylib*
+	rm -f conftest.*
+      fi
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_apple_cc_single_mod" >&5
+$as_echo "$lt_cv_apple_cc_single_mod" >&6; }
+
+    { $as_echo "$as_me:${as_lineno-$LINENO}: checking for -exported_symbols_list linker flag" >&5
+$as_echo_n "checking for -exported_symbols_list linker flag... " >&6; }
+if ${lt_cv_ld_exported_symbols_list+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  lt_cv_ld_exported_symbols_list=no
+      save_LDFLAGS=$LDFLAGS
+      echo "_main" > conftest.sym
+      LDFLAGS="$LDFLAGS -Wl,-exported_symbols_list,conftest.sym"
+      cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+  lt_cv_ld_exported_symbols_list=yes
+else
+  lt_cv_ld_exported_symbols_list=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+    conftest$ac_exeext conftest.$ac_ext
+	LDFLAGS="$save_LDFLAGS"
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_ld_exported_symbols_list" >&5
+$as_echo "$lt_cv_ld_exported_symbols_list" >&6; }
+
+    { $as_echo "$as_me:${as_lineno-$LINENO}: checking for -force_load linker flag" >&5
+$as_echo_n "checking for -force_load linker flag... " >&6; }
+if ${lt_cv_ld_force_load+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  lt_cv_ld_force_load=no
+      cat > conftest.c << _LT_EOF
+int forced_loaded() { return 2;}
+_LT_EOF
+      echo "$LTCC $LTCFLAGS -c -o conftest.o conftest.c" >&5
+      $LTCC $LTCFLAGS -c -o conftest.o conftest.c 2>&5
+      echo "$AR cru libconftest.a conftest.o" >&5
+      $AR cru libconftest.a conftest.o 2>&5
+      echo "$RANLIB libconftest.a" >&5
+      $RANLIB libconftest.a 2>&5
+      cat > conftest.c << _LT_EOF
+int main() { return 0;}
+_LT_EOF
+      echo "$LTCC $LTCFLAGS $LDFLAGS -o conftest conftest.c -Wl,-force_load,./libconftest.a" >&5
+      $LTCC $LTCFLAGS $LDFLAGS -o conftest conftest.c -Wl,-force_load,./libconftest.a 2>conftest.err
+      _lt_result=$?
+      if test -s conftest.err && $GREP force_load conftest.err; then
+	cat conftest.err >&5
+      elif test -f conftest && test $_lt_result -eq 0 && $GREP forced_load conftest >/dev/null 2>&1 ; then
+	lt_cv_ld_force_load=yes
+      else
+	cat conftest.err >&5
+      fi
+        rm -f conftest.err libconftest.a conftest conftest.c
+        rm -rf conftest.dSYM
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_ld_force_load" >&5
+$as_echo "$lt_cv_ld_force_load" >&6; }
+    case $host_os in
+    rhapsody* | darwin1.[012])
+      _lt_dar_allow_undefined='${wl}-undefined ${wl}suppress' ;;
+    darwin1.*)
+      _lt_dar_allow_undefined='${wl}-flat_namespace ${wl}-undefined ${wl}suppress' ;;
+    darwin*) # darwin 5.x on
+      # if running on 10.5 or later, the deployment target defaults
+      # to the OS version, if on x86, and 10.4, the deployment
+      # target defaults to 10.4. Don't you love it?
+      case ${MACOSX_DEPLOYMENT_TARGET-10.0},$host in
+	10.0,*86*-darwin8*|10.0,*-darwin[91]*)
+	  _lt_dar_allow_undefined='${wl}-undefined ${wl}dynamic_lookup' ;;
+	10.[012]*)
+	  _lt_dar_allow_undefined='${wl}-flat_namespace ${wl}-undefined ${wl}suppress' ;;
+	10.*)
+	  _lt_dar_allow_undefined='${wl}-undefined ${wl}dynamic_lookup' ;;
+      esac
+    ;;
+  esac
+    if test "$lt_cv_apple_cc_single_mod" = "yes"; then
+      _lt_dar_single_mod='$single_module'
+    fi
+    if test "$lt_cv_ld_exported_symbols_list" = "yes"; then
+      _lt_dar_export_syms=' ${wl}-exported_symbols_list,$output_objdir/${libname}-symbols.expsym'
+    else
+      _lt_dar_export_syms='~$NMEDIT -s $output_objdir/${libname}-symbols.expsym ${lib}'
+    fi
+    if test "$DSYMUTIL" != ":" && test "$lt_cv_ld_force_load" = "no"; then
+      _lt_dsymutil='~$DSYMUTIL $lib || :'
+    else
+      _lt_dsymutil=
+    fi
+    ;;
+  esac
+
+for ac_header in dlfcn.h
+do :
+  ac_fn_c_check_header_compile "$LINENO" "dlfcn.h" "ac_cv_header_dlfcn_h" "$ac_includes_default
+"
+if test "x$ac_cv_header_dlfcn_h" = xyes; then :
+  cat >>confdefs.h <<_ACEOF
+#define HAVE_DLFCN_H 1
+_ACEOF
+
+fi
+
+done
+
+
+
+
+
+# Set options
+
+
+
+        enable_dlopen=no
+
+
+  enable_win32_dll=no
+
+
+            # Check whether --enable-shared was given.
+if test "${enable_shared+set}" = set; then :
+  enableval=$enable_shared; p=${PACKAGE-default}
+    case $enableval in
+    yes) enable_shared=yes ;;
+    no) enable_shared=no ;;
+    *)
+      enable_shared=no
+      # Look at the argument we got.  We use all the common list separators.
+      lt_save_ifs="$IFS"; IFS="${IFS}$PATH_SEPARATOR,"
+      for pkg in $enableval; do
+	IFS="$lt_save_ifs"
+	if test "X$pkg" = "X$p"; then
+	  enable_shared=yes
+	fi
+      done
+      IFS="$lt_save_ifs"
+      ;;
+    esac
+else
+  enable_shared=yes
+fi
+
+
+
+
+
+
+
+
+
+
+
+# Check whether --with-pic was given.
+if test "${with_pic+set}" = set; then :
+  withval=$with_pic; lt_p=${PACKAGE-default}
+    case $withval in
+    yes|no) pic_mode=$withval ;;
+    *)
+      pic_mode=default
+      # Look at the argument we got.  We use all the common list separators.
+      lt_save_ifs="$IFS"; IFS="${IFS}$PATH_SEPARATOR,"
+      for lt_pkg in $withval; do
+	IFS="$lt_save_ifs"
+	if test "X$lt_pkg" = "X$lt_p"; then
+	  pic_mode=yes
+	fi
+      done
+      IFS="$lt_save_ifs"
+      ;;
+    esac
+else
+  pic_mode=default
+fi
+
+
+test -z "$pic_mode" && pic_mode=default
+
+
+
+
+
+
+
+  # Check whether --enable-fast-install was given.
+if test "${enable_fast_install+set}" = set; then :
+  enableval=$enable_fast_install; p=${PACKAGE-default}
+    case $enableval in
+    yes) enable_fast_install=yes ;;
+    no) enable_fast_install=no ;;
+    *)
+      enable_fast_install=no
+      # Look at the argument we got.  We use all the common list separators.
+      lt_save_ifs="$IFS"; IFS="${IFS}$PATH_SEPARATOR,"
+      for pkg in $enableval; do
+	IFS="$lt_save_ifs"
+	if test "X$pkg" = "X$p"; then
+	  enable_fast_install=yes
+	fi
+      done
+      IFS="$lt_save_ifs"
+      ;;
+    esac
+else
+  enable_fast_install=yes
+fi
+
+
+
+
+
+
+
+
+
+
+
+# This can be used to rebuild libtool when needed
+LIBTOOL_DEPS="$ltmain"
+
+# Always use our own libtool.
+LIBTOOL='$(SHELL) $(top_builddir)/libtool'
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+test -z "$LN_S" && LN_S="ln -s"
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+if test -n "${ZSH_VERSION+set}" ; then
+   setopt NO_GLOB_SUBST
+fi
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for objdir" >&5
+$as_echo_n "checking for objdir... " >&6; }
+if ${lt_cv_objdir+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  rm -f .libs 2>/dev/null
+mkdir .libs 2>/dev/null
+if test -d .libs; then
+  lt_cv_objdir=.libs
+else
+  # MS-DOS does not allow filenames that begin with a dot.
+  lt_cv_objdir=_libs
+fi
+rmdir .libs 2>/dev/null
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_objdir" >&5
+$as_echo "$lt_cv_objdir" >&6; }
+objdir=$lt_cv_objdir
+
+
+
+
+
+cat >>confdefs.h <<_ACEOF
+#define LT_OBJDIR "$lt_cv_objdir/"
+_ACEOF
+
+
+
+
+case $host_os in
+aix3*)
+  # AIX sometimes has problems with the GCC collect2 program.  For some
+  # reason, if we set the COLLECT_NAMES environment variable, the problems
+  # vanish in a puff of smoke.
+  if test "X${COLLECT_NAMES+set}" != Xset; then
+    COLLECT_NAMES=
+    export COLLECT_NAMES
+  fi
+  ;;
+esac
+
+# Global variables:
+ofile=libtool
+can_build_shared=yes
+
+# All known linkers require a `.a' archive for static linking (except MSVC,
+# which needs '.lib').
+libext=a
+
+with_gnu_ld="$lt_cv_prog_gnu_ld"
+
+old_CC="$CC"
+old_CFLAGS="$CFLAGS"
+
+# Set sane defaults for various variables
+test -z "$CC" && CC=cc
+test -z "$LTCC" && LTCC=$CC
+test -z "$LTCFLAGS" && LTCFLAGS=$CFLAGS
+test -z "$LD" && LD=ld
+test -z "$ac_objext" && ac_objext=o
+
+for cc_temp in $compiler""; do
+  case $cc_temp in
+    compile | *[\\/]compile | ccache | *[\\/]ccache ) ;;
+    distcc | *[\\/]distcc | purify | *[\\/]purify ) ;;
+    \-*) ;;
+    *) break;;
+  esac
+done
+cc_basename=`$ECHO "$cc_temp" | $SED "s%.*/%%; s%^$host_alias-%%"`
+
+
+# Only perform the check for file, if the check method requires it
+test -z "$MAGIC_CMD" && MAGIC_CMD=file
+case $deplibs_check_method in
+file_magic*)
+  if test "$file_magic_cmd" = '$MAGIC_CMD'; then
+    { $as_echo "$as_me:${as_lineno-$LINENO}: checking for ${ac_tool_prefix}file" >&5
+$as_echo_n "checking for ${ac_tool_prefix}file... " >&6; }
+if ${lt_cv_path_MAGIC_CMD+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  case $MAGIC_CMD in
+[\\/*] |  ?:[\\/]*)
+  lt_cv_path_MAGIC_CMD="$MAGIC_CMD" # Let the user override the test with a path.
+  ;;
+*)
+  lt_save_MAGIC_CMD="$MAGIC_CMD"
+  lt_save_ifs="$IFS"; IFS=$PATH_SEPARATOR
+  ac_dummy="/usr/bin$PATH_SEPARATOR$PATH"
+  for ac_dir in $ac_dummy; do
+    IFS="$lt_save_ifs"
+    test -z "$ac_dir" && ac_dir=.
+    if test -f $ac_dir/${ac_tool_prefix}file; then
+      lt_cv_path_MAGIC_CMD="$ac_dir/${ac_tool_prefix}file"
+      if test -n "$file_magic_test_file"; then
+	case $deplibs_check_method in
+	"file_magic "*)
+	  file_magic_regex=`expr "$deplibs_check_method" : "file_magic \(.*\)"`
+	  MAGIC_CMD="$lt_cv_path_MAGIC_CMD"
+	  if eval $file_magic_cmd \$file_magic_test_file 2> /dev/null |
+	    $EGREP "$file_magic_regex" > /dev/null; then
+	    :
+	  else
+	    cat <<_LT_EOF 1>&2
+
+*** Warning: the command libtool uses to detect shared libraries,
+*** $file_magic_cmd, produces output that libtool cannot recognize.
+*** The result is that libtool may fail to recognize shared libraries
+*** as such.  This will affect the creation of libtool libraries that
+*** depend on shared libraries, but programs linked with such libtool
+*** libraries will work regardless of this problem.  Nevertheless, you
+*** may want to report the problem to your system manager and/or to
+*** bug-libtool@gnu.org
+
+_LT_EOF
+	  fi ;;
+	esac
+      fi
+      break
+    fi
+  done
+  IFS="$lt_save_ifs"
+  MAGIC_CMD="$lt_save_MAGIC_CMD"
+  ;;
+esac
+fi
+
+MAGIC_CMD="$lt_cv_path_MAGIC_CMD"
+if test -n "$MAGIC_CMD"; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $MAGIC_CMD" >&5
+$as_echo "$MAGIC_CMD" >&6; }
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+
+
+
+if test -z "$lt_cv_path_MAGIC_CMD"; then
+  if test -n "$ac_tool_prefix"; then
+    { $as_echo "$as_me:${as_lineno-$LINENO}: checking for file" >&5
+$as_echo_n "checking for file... " >&6; }
+if ${lt_cv_path_MAGIC_CMD+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  case $MAGIC_CMD in
+[\\/*] |  ?:[\\/]*)
+  lt_cv_path_MAGIC_CMD="$MAGIC_CMD" # Let the user override the test with a path.
+  ;;
+*)
+  lt_save_MAGIC_CMD="$MAGIC_CMD"
+  lt_save_ifs="$IFS"; IFS=$PATH_SEPARATOR
+  ac_dummy="/usr/bin$PATH_SEPARATOR$PATH"
+  for ac_dir in $ac_dummy; do
+    IFS="$lt_save_ifs"
+    test -z "$ac_dir" && ac_dir=.
+    if test -f $ac_dir/file; then
+      lt_cv_path_MAGIC_CMD="$ac_dir/file"
+      if test -n "$file_magic_test_file"; then
+	case $deplibs_check_method in
+	"file_magic "*)
+	  file_magic_regex=`expr "$deplibs_check_method" : "file_magic \(.*\)"`
+	  MAGIC_CMD="$lt_cv_path_MAGIC_CMD"
+	  if eval $file_magic_cmd \$file_magic_test_file 2> /dev/null |
+	    $EGREP "$file_magic_regex" > /dev/null; then
+	    :
+	  else
+	    cat <<_LT_EOF 1>&2
+
+*** Warning: the command libtool uses to detect shared libraries,
+*** $file_magic_cmd, produces output that libtool cannot recognize.
+*** The result is that libtool may fail to recognize shared libraries
+*** as such.  This will affect the creation of libtool libraries that
+*** depend on shared libraries, but programs linked with such libtool
+*** libraries will work regardless of this problem.  Nevertheless, you
+*** may want to report the problem to your system manager and/or to
+*** bug-libtool@gnu.org
+
+_LT_EOF
+	  fi ;;
+	esac
+      fi
+      break
+    fi
+  done
+  IFS="$lt_save_ifs"
+  MAGIC_CMD="$lt_save_MAGIC_CMD"
+  ;;
+esac
+fi
+
+MAGIC_CMD="$lt_cv_path_MAGIC_CMD"
+if test -n "$MAGIC_CMD"; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $MAGIC_CMD" >&5
+$as_echo "$MAGIC_CMD" >&6; }
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+  else
+    MAGIC_CMD=:
+  fi
+fi
+
+  fi
+  ;;
+esac
+
+# Use C for the default configuration in the libtool script
+
+lt_save_CC="$CC"
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+
+
+# Source file extension for C test sources.
+ac_ext=c
+
+# Object file extension for compiled C test sources.
+objext=o
+objext=$objext
+
+# Code to be used in simple compile tests
+lt_simple_compile_test_code="int some_variable = 0;"
+
+# Code to be used in simple link tests
+lt_simple_link_test_code='int main(){return(0);}'
+
+
+
+
+
+
+
+# If no C compiler was specified, use CC.
+LTCC=${LTCC-"$CC"}
+
+# If no C compiler flags were specified, use CFLAGS.
+LTCFLAGS=${LTCFLAGS-"$CFLAGS"}
+
+# Allow CC to be a program name with arguments.
+compiler=$CC
+
+# Save the default compiler, since it gets overwritten when the other
+# tags are being tested, and _LT_TAGVAR(compiler, []) is a NOP.
+compiler_DEFAULT=$CC
+
+# save warnings/boilerplate of simple test code
+ac_outfile=conftest.$ac_objext
+echo "$lt_simple_compile_test_code" >conftest.$ac_ext
+eval "$ac_compile" 2>&1 >/dev/null | $SED '/^$/d; /^ *+/d' >conftest.err
+_lt_compiler_boilerplate=`cat conftest.err`
+$RM conftest*
+
+ac_outfile=conftest.$ac_objext
+echo "$lt_simple_link_test_code" >conftest.$ac_ext
+eval "$ac_link" 2>&1 >/dev/null | $SED '/^$/d; /^ *+/d' >conftest.err
+_lt_linker_boilerplate=`cat conftest.err`
+$RM -r conftest*
+
+
+if test -n "$compiler"; then
+
+lt_prog_compiler_no_builtin_flag=
+
+if test "$GCC" = yes; then
+  case $cc_basename in
+  nvcc*)
+    lt_prog_compiler_no_builtin_flag=' -Xcompiler -fno-builtin' ;;
+  *)
+    lt_prog_compiler_no_builtin_flag=' -fno-builtin' ;;
+  esac
+
+  { $as_echo "$as_me:${as_lineno-$LINENO}: checking if $compiler supports -fno-rtti -fno-exceptions" >&5
+$as_echo_n "checking if $compiler supports -fno-rtti -fno-exceptions... " >&6; }
+if ${lt_cv_prog_compiler_rtti_exceptions+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  lt_cv_prog_compiler_rtti_exceptions=no
+   ac_outfile=conftest.$ac_objext
+   echo "$lt_simple_compile_test_code" > conftest.$ac_ext
+   lt_compiler_flag="-fno-rtti -fno-exceptions"
+   # Insert the option either (1) after the last *FLAGS variable, or
+   # (2) before a word containing "conftest.", or (3) at the end.
+   # Note that $ac_compile itself does not contain backslashes and begins
+   # with a dollar sign (not a hyphen), so the echo should work correctly.
+   # The option is referenced via a variable to avoid confusing sed.
+   lt_compile=`echo "$ac_compile" | $SED \
+   -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \
+   -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \
+   -e 's:$: $lt_compiler_flag:'`
+   (eval echo "\"\$as_me:$LINENO: $lt_compile\"" >&5)
+   (eval "$lt_compile" 2>conftest.err)
+   ac_status=$?
+   cat conftest.err >&5
+   echo "$as_me:$LINENO: \$? = $ac_status" >&5
+   if (exit $ac_status) && test -s "$ac_outfile"; then
+     # The compiler can only warn and ignore the option if not recognized
+     # So say no if there are warnings other than the usual output.
+     $ECHO "$_lt_compiler_boilerplate" | $SED '/^$/d' >conftest.exp
+     $SED '/^$/d; /^ *+/d' conftest.err >conftest.er2
+     if test ! -s conftest.er2 || diff conftest.exp conftest.er2 >/dev/null; then
+       lt_cv_prog_compiler_rtti_exceptions=yes
+     fi
+   fi
+   $RM conftest*
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_rtti_exceptions" >&5
+$as_echo "$lt_cv_prog_compiler_rtti_exceptions" >&6; }
+
+if test x"$lt_cv_prog_compiler_rtti_exceptions" = xyes; then
+    lt_prog_compiler_no_builtin_flag="$lt_prog_compiler_no_builtin_flag -fno-rtti -fno-exceptions"
+else
+    :
+fi
+
+fi
+
+
+
+
+
+
+  lt_prog_compiler_wl=
+lt_prog_compiler_pic=
+lt_prog_compiler_static=
+
+
+  if test "$GCC" = yes; then
+    lt_prog_compiler_wl='-Wl,'
+    lt_prog_compiler_static='-static'
+
+    case $host_os in
+      aix*)
+      # All AIX code is PIC.
+      if test "$host_cpu" = ia64; then
+	# AIX 5 now supports IA64 processor
+	lt_prog_compiler_static='-Bstatic'
+      fi
+      ;;
+
+    amigaos*)
+      case $host_cpu in
+      powerpc)
+            # see comment about AmigaOS4 .so support
+            lt_prog_compiler_pic='-fPIC'
+        ;;
+      m68k)
+            # FIXME: we need at least 68020 code to build shared libraries, but
+            # adding the `-m68020' flag to GCC prevents building anything better,
+            # like `-m68040'.
+            lt_prog_compiler_pic='-m68020 -resident32 -malways-restore-a4'
+        ;;
+      esac
+      ;;
+
+    beos* | irix5* | irix6* | nonstopux* | osf3* | osf4* | osf5*)
+      # PIC is the default for these OSes.
+      ;;
+
+    mingw* | cygwin* | pw32* | os2* | cegcc*)
+      # This hack is so that the source file can tell whether it is being
+      # built for inclusion in a dll (and should export symbols for example).
+      # Although the cygwin gcc ignores -fPIC, still need this for old-style
+      # (--disable-auto-import) libraries
+      lt_prog_compiler_pic='-DDLL_EXPORT'
+      ;;
+
+    darwin* | rhapsody*)
+      # PIC is the default on this platform
+      # Common symbols not allowed in MH_DYLIB files
+      lt_prog_compiler_pic='-fno-common'
+      ;;
+
+    haiku*)
+      # PIC is the default for Haiku.
+      # The "-static" flag exists, but is broken.
+      lt_prog_compiler_static=
+      ;;
+
+    hpux*)
+      # PIC is the default for 64-bit PA HP-UX, but not for 32-bit
+      # PA HP-UX.  On IA64 HP-UX, PIC is the default but the pic flag
+      # sets the default TLS model and affects inlining.
+      case $host_cpu in
+      hppa*64*)
+	# +Z the default
+	;;
+      *)
+	lt_prog_compiler_pic='-fPIC'
+	;;
+      esac
+      ;;
+
+    interix[3-9]*)
+      # Interix 3.x gcc -fpic/-fPIC options generate broken code.
+      # Instead, we relocate shared libraries at runtime.
+      ;;
+
+    msdosdjgpp*)
+      # Just because we use GCC doesn't mean we suddenly get shared libraries
+      # on systems that don't support them.
+      lt_prog_compiler_can_build_shared=no
+      enable_shared=no
+      ;;
+
+    *nto* | *qnx*)
+      # QNX uses GNU C++, but need to define -shared option too, otherwise
+      # it will coredump.
+      lt_prog_compiler_pic='-fPIC -shared'
+      ;;
+
+    sysv4*MP*)
+      if test -d /usr/nec; then
+	lt_prog_compiler_pic=-Kconform_pic
+      fi
+      ;;
+
+    *)
+      lt_prog_compiler_pic='-fPIC'
+      ;;
+    esac
+
+    case $cc_basename in
+    nvcc*) # Cuda Compiler Driver 2.2
+      lt_prog_compiler_wl='-Xlinker '
+      if test -n "$lt_prog_compiler_pic"; then
+        lt_prog_compiler_pic="-Xcompiler $lt_prog_compiler_pic"
+      fi
+      ;;
+    esac
+  else
+    # PORTME Check for flag to pass linker flags through the system compiler.
+    case $host_os in
+    aix*)
+      lt_prog_compiler_wl='-Wl,'
+      if test "$host_cpu" = ia64; then
+	# AIX 5 now supports IA64 processor
+	lt_prog_compiler_static='-Bstatic'
+      else
+	lt_prog_compiler_static='-bnso -bI:/lib/syscalls.exp'
+      fi
+      ;;
+
+    mingw* | cygwin* | pw32* | os2* | cegcc*)
+      # This hack is so that the source file can tell whether it is being
+      # built for inclusion in a dll (and should export symbols for example).
+      lt_prog_compiler_pic='-DDLL_EXPORT'
+      ;;
+
+    hpux9* | hpux10* | hpux11*)
+      lt_prog_compiler_wl='-Wl,'
+      # PIC is the default for IA64 HP-UX and 64-bit HP-UX, but
+      # not for PA HP-UX.
+      case $host_cpu in
+      hppa*64*|ia64*)
+	# +Z the default
+	;;
+      *)
+	lt_prog_compiler_pic='+Z'
+	;;
+      esac
+      # Is there a better lt_prog_compiler_static that works with the bundled CC?
+      lt_prog_compiler_static='${wl}-a ${wl}archive'
+      ;;
+
+    irix5* | irix6* | nonstopux*)
+      lt_prog_compiler_wl='-Wl,'
+      # PIC (with -KPIC) is the default.
+      lt_prog_compiler_static='-non_shared'
+      ;;
+
+    linux* | k*bsd*-gnu | kopensolaris*-gnu | gnu*)
+      case $cc_basename in
+      # old Intel for x86_64 which still supported -KPIC.
+      ecc*)
+	lt_prog_compiler_wl='-Wl,'
+	lt_prog_compiler_pic='-KPIC'
+	lt_prog_compiler_static='-static'
+        ;;
+      # icc used to be incompatible with GCC.
+      # ICC 10 doesn't accept -KPIC any more.
+      icc* | ifort*)
+	lt_prog_compiler_wl='-Wl,'
+	lt_prog_compiler_pic='-fPIC'
+	lt_prog_compiler_static='-static'
+        ;;
+      # Lahey Fortran 8.1.
+      lf95*)
+	lt_prog_compiler_wl='-Wl,'
+	lt_prog_compiler_pic='--shared'
+	lt_prog_compiler_static='--static'
+	;;
+      nagfor*)
+	# NAG Fortran compiler
+	lt_prog_compiler_wl='-Wl,-Wl,,'
+	lt_prog_compiler_pic='-PIC'
+	lt_prog_compiler_static='-Bstatic'
+	;;
+      pgcc* | pgf77* | pgf90* | pgf95* | pgfortran*)
+        # Portland Group compilers (*not* the Pentium gcc compiler,
+	# which looks to be a dead project)
+	lt_prog_compiler_wl='-Wl,'
+	lt_prog_compiler_pic='-fpic'
+	lt_prog_compiler_static='-Bstatic'
+        ;;
+      ccc*)
+        lt_prog_compiler_wl='-Wl,'
+        # All Alpha code is PIC.
+        lt_prog_compiler_static='-non_shared'
+        ;;
+      xl* | bgxl* | bgf* | mpixl*)
+	# IBM XL C 8.0/Fortran 10.1, 11.1 on PPC and BlueGene
+	lt_prog_compiler_wl='-Wl,'
+	lt_prog_compiler_pic='-qpic'
+	lt_prog_compiler_static='-qstaticlink'
+	;;
+      *)
+	case `$CC -V 2>&1 | sed 5q` in
+	*Sun\ Ceres\ Fortran* | *Sun*Fortran*\ [1-7].* | *Sun*Fortran*\ 8.[0-3]*)
+	  # Sun Fortran 8.3 passes all unrecognized flags to the linker
+	  lt_prog_compiler_pic='-KPIC'
+	  lt_prog_compiler_static='-Bstatic'
+	  lt_prog_compiler_wl=''
+	  ;;
+	*Sun\ F* | *Sun*Fortran*)
+	  lt_prog_compiler_pic='-KPIC'
+	  lt_prog_compiler_static='-Bstatic'
+	  lt_prog_compiler_wl='-Qoption ld '
+	  ;;
+	*Sun\ C*)
+	  # Sun C 5.9
+	  lt_prog_compiler_pic='-KPIC'
+	  lt_prog_compiler_static='-Bstatic'
+	  lt_prog_compiler_wl='-Wl,'
+	  ;;
+        *Intel*\ [CF]*Compiler*)
+	  lt_prog_compiler_wl='-Wl,'
+	  lt_prog_compiler_pic='-fPIC'
+	  lt_prog_compiler_static='-static'
+	  ;;
+	*Portland\ Group*)
+	  lt_prog_compiler_wl='-Wl,'
+	  lt_prog_compiler_pic='-fpic'
+	  lt_prog_compiler_static='-Bstatic'
+	  ;;
+	esac
+	;;
+      esac
+      ;;
+
+    newsos6)
+      lt_prog_compiler_pic='-KPIC'
+      lt_prog_compiler_static='-Bstatic'
+      ;;
+
+    *nto* | *qnx*)
+      # QNX uses GNU C++, but need to define -shared option too, otherwise
+      # it will coredump.
+      lt_prog_compiler_pic='-fPIC -shared'
+      ;;
+
+    osf3* | osf4* | osf5*)
+      lt_prog_compiler_wl='-Wl,'
+      # All OSF/1 code is PIC.
+      lt_prog_compiler_static='-non_shared'
+      ;;
+
+    rdos*)
+      lt_prog_compiler_static='-non_shared'
+      ;;
+
+    solaris*)
+      lt_prog_compiler_pic='-KPIC'
+      lt_prog_compiler_static='-Bstatic'
+      case $cc_basename in
+      f77* | f90* | f95* | sunf77* | sunf90* | sunf95*)
+	lt_prog_compiler_wl='-Qoption ld ';;
+      *)
+	lt_prog_compiler_wl='-Wl,';;
+      esac
+      ;;
+
+    sunos4*)
+      lt_prog_compiler_wl='-Qoption ld '
+      lt_prog_compiler_pic='-PIC'
+      lt_prog_compiler_static='-Bstatic'
+      ;;
+
+    sysv4 | sysv4.2uw2* | sysv4.3*)
+      lt_prog_compiler_wl='-Wl,'
+      lt_prog_compiler_pic='-KPIC'
+      lt_prog_compiler_static='-Bstatic'
+      ;;
+
+    sysv4*MP*)
+      if test -d /usr/nec ;then
+	lt_prog_compiler_pic='-Kconform_pic'
+	lt_prog_compiler_static='-Bstatic'
+      fi
+      ;;
+
+    sysv5* | unixware* | sco3.2v5* | sco5v6* | OpenUNIX*)
+      lt_prog_compiler_wl='-Wl,'
+      lt_prog_compiler_pic='-KPIC'
+      lt_prog_compiler_static='-Bstatic'
+      ;;
+
+    unicos*)
+      lt_prog_compiler_wl='-Wl,'
+      lt_prog_compiler_can_build_shared=no
+      ;;
+
+    uts4*)
+      lt_prog_compiler_pic='-pic'
+      lt_prog_compiler_static='-Bstatic'
+      ;;
+
+    *)
+      lt_prog_compiler_can_build_shared=no
+      ;;
+    esac
+  fi
+
+case $host_os in
+  # For platforms which do not support PIC, -DPIC is meaningless:
+  *djgpp*)
+    lt_prog_compiler_pic=
+    ;;
+  *)
+    lt_prog_compiler_pic="$lt_prog_compiler_pic -DPIC"
+    ;;
+esac
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $compiler option to produce PIC" >&5
+$as_echo_n "checking for $compiler option to produce PIC... " >&6; }
+if ${lt_cv_prog_compiler_pic+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  lt_cv_prog_compiler_pic=$lt_prog_compiler_pic
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_pic" >&5
+$as_echo "$lt_cv_prog_compiler_pic" >&6; }
+lt_prog_compiler_pic=$lt_cv_prog_compiler_pic
+
+#
+# Check to make sure the PIC flag actually works.
+#
+if test -n "$lt_prog_compiler_pic"; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: checking if $compiler PIC flag $lt_prog_compiler_pic works" >&5
+$as_echo_n "checking if $compiler PIC flag $lt_prog_compiler_pic works... " >&6; }
+if ${lt_cv_prog_compiler_pic_works+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  lt_cv_prog_compiler_pic_works=no
+   ac_outfile=conftest.$ac_objext
+   echo "$lt_simple_compile_test_code" > conftest.$ac_ext
+   lt_compiler_flag="$lt_prog_compiler_pic -DPIC"
+   # Insert the option either (1) after the last *FLAGS variable, or
+   # (2) before a word containing "conftest.", or (3) at the end.
+   # Note that $ac_compile itself does not contain backslashes and begins
+   # with a dollar sign (not a hyphen), so the echo should work correctly.
+   # The option is referenced via a variable to avoid confusing sed.
+   lt_compile=`echo "$ac_compile" | $SED \
+   -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \
+   -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \
+   -e 's:$: $lt_compiler_flag:'`
+   (eval echo "\"\$as_me:$LINENO: $lt_compile\"" >&5)
+   (eval "$lt_compile" 2>conftest.err)
+   ac_status=$?
+   cat conftest.err >&5
+   echo "$as_me:$LINENO: \$? = $ac_status" >&5
+   if (exit $ac_status) && test -s "$ac_outfile"; then
+     # The compiler can only warn and ignore the option if not recognized
+     # So say no if there are warnings other than the usual output.
+     $ECHO "$_lt_compiler_boilerplate" | $SED '/^$/d' >conftest.exp
+     $SED '/^$/d; /^ *+/d' conftest.err >conftest.er2
+     if test ! -s conftest.er2 || diff conftest.exp conftest.er2 >/dev/null; then
+       lt_cv_prog_compiler_pic_works=yes
+     fi
+   fi
+   $RM conftest*
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_pic_works" >&5
+$as_echo "$lt_cv_prog_compiler_pic_works" >&6; }
+
+if test x"$lt_cv_prog_compiler_pic_works" = xyes; then
+    case $lt_prog_compiler_pic in
+     "" | " "*) ;;
+     *) lt_prog_compiler_pic=" $lt_prog_compiler_pic" ;;
+     esac
+else
+    lt_prog_compiler_pic=
+     lt_prog_compiler_can_build_shared=no
+fi
+
+fi
+
+
+
+
+
+
+
+
+
+
+
+#
+# Check to make sure the static flag actually works.
+#
+wl=$lt_prog_compiler_wl eval lt_tmp_static_flag=\"$lt_prog_compiler_static\"
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking if $compiler static flag $lt_tmp_static_flag works" >&5
+$as_echo_n "checking if $compiler static flag $lt_tmp_static_flag works... " >&6; }
+if ${lt_cv_prog_compiler_static_works+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  lt_cv_prog_compiler_static_works=no
+   save_LDFLAGS="$LDFLAGS"
+   LDFLAGS="$LDFLAGS $lt_tmp_static_flag"
+   echo "$lt_simple_link_test_code" > conftest.$ac_ext
+   if (eval $ac_link 2>conftest.err) && test -s conftest$ac_exeext; then
+     # The linker can only warn and ignore the option if not recognized
+     # So say no if there are warnings
+     if test -s conftest.err; then
+       # Append any errors to the config.log.
+       cat conftest.err 1>&5
+       $ECHO "$_lt_linker_boilerplate" | $SED '/^$/d' > conftest.exp
+       $SED '/^$/d; /^ *+/d' conftest.err >conftest.er2
+       if diff conftest.exp conftest.er2 >/dev/null; then
+         lt_cv_prog_compiler_static_works=yes
+       fi
+     else
+       lt_cv_prog_compiler_static_works=yes
+     fi
+   fi
+   $RM -r conftest*
+   LDFLAGS="$save_LDFLAGS"
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_static_works" >&5
+$as_echo "$lt_cv_prog_compiler_static_works" >&6; }
+
+if test x"$lt_cv_prog_compiler_static_works" = xyes; then
+    :
+else
+    lt_prog_compiler_static=
+fi
+
+
+
+
+
+
+
+  { $as_echo "$as_me:${as_lineno-$LINENO}: checking if $compiler supports -c -o file.$ac_objext" >&5
+$as_echo_n "checking if $compiler supports -c -o file.$ac_objext... " >&6; }
+if ${lt_cv_prog_compiler_c_o+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  lt_cv_prog_compiler_c_o=no
+   $RM -r conftest 2>/dev/null
+   mkdir conftest
+   cd conftest
+   mkdir out
+   echo "$lt_simple_compile_test_code" > conftest.$ac_ext
+
+   lt_compiler_flag="-o out/conftest2.$ac_objext"
+   # Insert the option either (1) after the last *FLAGS variable, or
+   # (2) before a word containing "conftest.", or (3) at the end.
+   # Note that $ac_compile itself does not contain backslashes and begins
+   # with a dollar sign (not a hyphen), so the echo should work correctly.
+   lt_compile=`echo "$ac_compile" | $SED \
+   -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \
+   -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \
+   -e 's:$: $lt_compiler_flag:'`
+   (eval echo "\"\$as_me:$LINENO: $lt_compile\"" >&5)
+   (eval "$lt_compile" 2>out/conftest.err)
+   ac_status=$?
+   cat out/conftest.err >&5
+   echo "$as_me:$LINENO: \$? = $ac_status" >&5
+   if (exit $ac_status) && test -s out/conftest2.$ac_objext
+   then
+     # The compiler can only warn and ignore the option if not recognized
+     # So say no if there are warnings
+     $ECHO "$_lt_compiler_boilerplate" | $SED '/^$/d' > out/conftest.exp
+     $SED '/^$/d; /^ *+/d' out/conftest.err >out/conftest.er2
+     if test ! -s out/conftest.er2 || diff out/conftest.exp out/conftest.er2 >/dev/null; then
+       lt_cv_prog_compiler_c_o=yes
+     fi
+   fi
+   chmod u+w . 2>&5
+   $RM conftest*
+   # SGI C++ compiler will create directory out/ii_files/ for
+   # template instantiation
+   test -d out/ii_files && $RM out/ii_files/* && rmdir out/ii_files
+   $RM out/* && rmdir out
+   cd ..
+   $RM -r conftest
+   $RM conftest*
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_c_o" >&5
+$as_echo "$lt_cv_prog_compiler_c_o" >&6; }
+
+
+
+
+
+
+  { $as_echo "$as_me:${as_lineno-$LINENO}: checking if $compiler supports -c -o file.$ac_objext" >&5
+$as_echo_n "checking if $compiler supports -c -o file.$ac_objext... " >&6; }
+if ${lt_cv_prog_compiler_c_o+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  lt_cv_prog_compiler_c_o=no
+   $RM -r conftest 2>/dev/null
+   mkdir conftest
+   cd conftest
+   mkdir out
+   echo "$lt_simple_compile_test_code" > conftest.$ac_ext
+
+   lt_compiler_flag="-o out/conftest2.$ac_objext"
+   # Insert the option either (1) after the last *FLAGS variable, or
+   # (2) before a word containing "conftest.", or (3) at the end.
+   # Note that $ac_compile itself does not contain backslashes and begins
+   # with a dollar sign (not a hyphen), so the echo should work correctly.
+   lt_compile=`echo "$ac_compile" | $SED \
+   -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \
+   -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \
+   -e 's:$: $lt_compiler_flag:'`
+   (eval echo "\"\$as_me:$LINENO: $lt_compile\"" >&5)
+   (eval "$lt_compile" 2>out/conftest.err)
+   ac_status=$?
+   cat out/conftest.err >&5
+   echo "$as_me:$LINENO: \$? = $ac_status" >&5
+   if (exit $ac_status) && test -s out/conftest2.$ac_objext
+   then
+     # The compiler can only warn and ignore the option if not recognized
+     # So say no if there are warnings
+     $ECHO "$_lt_compiler_boilerplate" | $SED '/^$/d' > out/conftest.exp
+     $SED '/^$/d; /^ *+/d' out/conftest.err >out/conftest.er2
+     if test ! -s out/conftest.er2 || diff out/conftest.exp out/conftest.er2 >/dev/null; then
+       lt_cv_prog_compiler_c_o=yes
+     fi
+   fi
+   chmod u+w . 2>&5
+   $RM conftest*
+   # SGI C++ compiler will create directory out/ii_files/ for
+   # template instantiation
+   test -d out/ii_files && $RM out/ii_files/* && rmdir out/ii_files
+   $RM out/* && rmdir out
+   cd ..
+   $RM -r conftest
+   $RM conftest*
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_c_o" >&5
+$as_echo "$lt_cv_prog_compiler_c_o" >&6; }
+
+
+
+
+hard_links="nottested"
+if test "$lt_cv_prog_compiler_c_o" = no && test "$need_locks" != no; then
+  # do not overwrite the value of need_locks provided by the user
+  { $as_echo "$as_me:${as_lineno-$LINENO}: checking if we can lock with hard links" >&5
+$as_echo_n "checking if we can lock with hard links... " >&6; }
+  hard_links=yes
+  $RM conftest*
+  ln conftest.a conftest.b 2>/dev/null && hard_links=no
+  touch conftest.a
+  ln conftest.a conftest.b 2>&5 || hard_links=no
+  ln conftest.a conftest.b 2>/dev/null && hard_links=no
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $hard_links" >&5
+$as_echo "$hard_links" >&6; }
+  if test "$hard_links" = no; then
+    { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: \`$CC' does not support \`-c -o', so \`make -j' may be unsafe" >&5
+$as_echo "$as_me: WARNING: \`$CC' does not support \`-c -o', so \`make -j' may be unsafe" >&2;}
+    need_locks=warn
+  fi
+else
+  need_locks=no
+fi
+
+
+
+
+
+
+  { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the $compiler linker ($LD) supports shared libraries" >&5
+$as_echo_n "checking whether the $compiler linker ($LD) supports shared libraries... " >&6; }
+
+  runpath_var=
+  allow_undefined_flag=
+  always_export_symbols=no
+  archive_cmds=
+  archive_expsym_cmds=
+  compiler_needs_object=no
+  enable_shared_with_static_runtimes=no
+  export_dynamic_flag_spec=
+  export_symbols_cmds='$NM $libobjs $convenience | $global_symbol_pipe | $SED '\''s/.* //'\'' | sort | uniq > $export_symbols'
+  hardcode_automatic=no
+  hardcode_direct=no
+  hardcode_direct_absolute=no
+  hardcode_libdir_flag_spec=
+  hardcode_libdir_separator=
+  hardcode_minus_L=no
+  hardcode_shlibpath_var=unsupported
+  inherit_rpath=no
+  link_all_deplibs=unknown
+  module_cmds=
+  module_expsym_cmds=
+  old_archive_from_new_cmds=
+  old_archive_from_expsyms_cmds=
+  thread_safe_flag_spec=
+  whole_archive_flag_spec=
+  # include_expsyms should be a list of space-separated symbols to be *always*
+  # included in the symbol list
+  include_expsyms=
+  # exclude_expsyms can be an extended regexp of symbols to exclude
+  # it will be wrapped by ` (' and `)$', so one must not match beginning or
+  # end of line.  Example: `a|bc|.*d.*' will exclude the symbols `a' and `bc',
+  # as well as any symbol that contains `d'.
+  exclude_expsyms='_GLOBAL_OFFSET_TABLE_|_GLOBAL__F[ID]_.*'
+  # Although _GLOBAL_OFFSET_TABLE_ is a valid symbol C name, most a.out
+  # platforms (ab)use it in PIC code, but their linkers get confused if
+  # the symbol is explicitly referenced.  Since portable code cannot
+  # rely on this symbol name, it's probably fine to never include it in
+  # preloaded symbol tables.
+  # Exclude shared library initialization/finalization symbols.
+  extract_expsyms_cmds=
+
+  case $host_os in
+  cygwin* | mingw* | pw32* | cegcc*)
+    # FIXME: the MSVC++ port hasn't been tested in a loooong time
+    # When not using gcc, we currently assume that we are using
+    # Microsoft Visual C++.
+    if test "$GCC" != yes; then
+      with_gnu_ld=no
+    fi
+    ;;
+  interix*)
+    # we just hope/assume this is gcc and not c89 (= MSVC++)
+    with_gnu_ld=yes
+    ;;
+  openbsd*)
+    with_gnu_ld=no
+    ;;
+  linux* | k*bsd*-gnu | gnu*)
+    link_all_deplibs=no
+    ;;
+  esac
+
+  ld_shlibs=yes
+
+  # On some targets, GNU ld is compatible enough with the native linker
+  # that we're better off using the native interface for both.
+  lt_use_gnu_ld_interface=no
+  if test "$with_gnu_ld" = yes; then
+    case $host_os in
+      aix*)
+	# The AIX port of GNU ld has always aspired to compatibility
+	# with the native linker.  However, as the warning in the GNU ld
+	# block says, versions before 2.19.5* couldn't really create working
+	# shared libraries, regardless of the interface used.
+	case `$LD -v 2>&1` in
+	  *\ \(GNU\ Binutils\)\ 2.19.5*) ;;
+	  *\ \(GNU\ Binutils\)\ 2.[2-9]*) ;;
+	  *\ \(GNU\ Binutils\)\ [3-9]*) ;;
+	  *)
+	    lt_use_gnu_ld_interface=yes
+	    ;;
+	esac
+	;;
+      *)
+	lt_use_gnu_ld_interface=yes
+	;;
+    esac
+  fi
+
+  if test "$lt_use_gnu_ld_interface" = yes; then
+    # If archive_cmds runs LD, not CC, wlarc should be empty
+    wlarc='${wl}'
+
+    # Set some defaults for GNU ld with shared library support. These
+    # are reset later if shared libraries are not supported. Putting them
+    # here allows them to be overridden if necessary.
+    runpath_var=LD_RUN_PATH
+    hardcode_libdir_flag_spec='${wl}-rpath ${wl}$libdir'
+    export_dynamic_flag_spec='${wl}--export-dynamic'
+    # ancient GNU ld didn't support --whole-archive et. al.
+    if $LD --help 2>&1 | $GREP 'no-whole-archive' > /dev/null; then
+      whole_archive_flag_spec="$wlarc"'--whole-archive$convenience '"$wlarc"'--no-whole-archive'
+    else
+      whole_archive_flag_spec=
+    fi
+    supports_anon_versioning=no
+    case `$LD -v 2>&1` in
+      *GNU\ gold*) supports_anon_versioning=yes ;;
+      *\ [01].* | *\ 2.[0-9].* | *\ 2.10.*) ;; # catch versions < 2.11
+      *\ 2.11.93.0.2\ *) supports_anon_versioning=yes ;; # RH7.3 ...
+      *\ 2.11.92.0.12\ *) supports_anon_versioning=yes ;; # Mandrake 8.2 ...
+      *\ 2.11.*) ;; # other 2.11 versions
+      *) supports_anon_versioning=yes ;;
+    esac
+
+    # See if GNU ld supports shared libraries.
+    case $host_os in
+    aix[3-9]*)
+      # On AIX/PPC, the GNU linker is very broken
+      if test "$host_cpu" != ia64; then
+	ld_shlibs=no
+	cat <<_LT_EOF 1>&2
+
+*** Warning: the GNU linker, at least up to release 2.19, is reported
+*** to be unable to reliably create shared libraries on AIX.
+*** Therefore, libtool is disabling shared libraries support.  If you
+*** really care for shared libraries, you may want to install binutils
+*** 2.20 or above, or modify your PATH so that a non-GNU linker is found.
+*** You will then need to restart the configuration process.
+
+_LT_EOF
+      fi
+      ;;
+
+    amigaos*)
+      case $host_cpu in
+      powerpc)
+            # see comment about AmigaOS4 .so support
+            archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib'
+            archive_expsym_cmds=''
+        ;;
+      m68k)
+            archive_cmds='$RM $output_objdir/a2ixlibrary.data~$ECHO "#define NAME $libname" > $output_objdir/a2ixlibrary.data~$ECHO "#define LIBRARY_ID 1" >> $output_objdir/a2ixlibrary.data~$ECHO "#define VERSION $major" >> $output_objdir/a2ixlibrary.data~$ECHO "#define REVISION $revision" >> $output_objdir/a2ixlibrary.data~$AR $AR_FLAGS $lib $libobjs~$RANLIB $lib~(cd $output_objdir && a2ixlibrary -32)'
+            hardcode_libdir_flag_spec='-L$libdir'
+            hardcode_minus_L=yes
+        ;;
+      esac
+      ;;
+
+    beos*)
+      if $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then
+	allow_undefined_flag=unsupported
+	# Joseph Beckenbach <jrb3@best.com> says some releases of gcc
+	# support --undefined.  This deserves some investigation.  FIXME
+	archive_cmds='$CC -nostart $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib'
+      else
+	ld_shlibs=no
+      fi
+      ;;
+
+    cygwin* | mingw* | pw32* | cegcc*)
+      # _LT_TAGVAR(hardcode_libdir_flag_spec, ) is actually meaningless,
+      # as there is no search path for DLLs.
+      hardcode_libdir_flag_spec='-L$libdir'
+      export_dynamic_flag_spec='${wl}--export-all-symbols'
+      allow_undefined_flag=unsupported
+      always_export_symbols=no
+      enable_shared_with_static_runtimes=yes
+      export_symbols_cmds='$NM $libobjs $convenience | $global_symbol_pipe | $SED -e '\''/^[BCDGRS][ ]/s/.*[ ]\([^ ]*\)/\1 DATA/;s/^.*[ ]__nm__\([^ ]*\)[ ][^ ]*/\1 DATA/;/^I[ ]/d;/^[AITW][ ]/s/.* //'\'' | sort | uniq > $export_symbols'
+      exclude_expsyms='[_]+GLOBAL_OFFSET_TABLE_|[_]+GLOBAL__[FID]_.*|[_]+head_[A-Za-z0-9_]+_dll|[A-Za-z0-9_]+_dll_iname'
+
+      if $LD --help 2>&1 | $GREP 'auto-import' > /dev/null; then
+        archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags -o $output_objdir/$soname ${wl}--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib'
+	# If the export-symbols file already is a .def file (1st line
+	# is EXPORTS), use it as is; otherwise, prepend...
+	archive_expsym_cmds='if test "x`$SED 1q $export_symbols`" = xEXPORTS; then
+	  cp $export_symbols $output_objdir/$soname.def;
+	else
+	  echo EXPORTS > $output_objdir/$soname.def;
+	  cat $export_symbols >> $output_objdir/$soname.def;
+	fi~
+	$CC -shared $output_objdir/$soname.def $libobjs $deplibs $compiler_flags -o $output_objdir/$soname ${wl}--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib'
+      else
+	ld_shlibs=no
+      fi
+      ;;
+
+    haiku*)
+      archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib'
+      link_all_deplibs=yes
+      ;;
+
+    interix[3-9]*)
+      hardcode_direct=no
+      hardcode_shlibpath_var=no
+      hardcode_libdir_flag_spec='${wl}-rpath,$libdir'
+      export_dynamic_flag_spec='${wl}-E'
+      # Hack: On Interix 3.x, we cannot compile PIC because of a broken gcc.
+      # Instead, shared libraries are loaded at an image base (0x10000000 by
+      # default) and relocated if they conflict, which is a slow very memory
+      # consuming and fragmenting process.  To avoid this, we pick a random,
+      # 256 KiB-aligned image base between 0x50000000 and 0x6FFC0000 at link
+      # time.  Moving up from 0x10000000 also allows more sbrk(2) space.
+      archive_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-h,$soname ${wl}--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib'
+      archive_expsym_cmds='sed "s,^,_," $export_symbols >$output_objdir/$soname.expsym~$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-h,$soname ${wl}--retain-symbols-file,$output_objdir/$soname.expsym ${wl}--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib'
+      ;;
+
+    gnu* | linux* | tpf* | k*bsd*-gnu | kopensolaris*-gnu)
+      tmp_diet=no
+      if test "$host_os" = linux-dietlibc; then
+	case $cc_basename in
+	  diet\ *) tmp_diet=yes;;	# linux-dietlibc with static linking (!diet-dyn)
+	esac
+      fi
+      if $LD --help 2>&1 | $EGREP ': supported targets:.* elf' > /dev/null \
+	 && test "$tmp_diet" = no
+      then
+	tmp_addflag=' $pic_flag'
+	tmp_sharedflag='-shared'
+	case $cc_basename,$host_cpu in
+        pgcc*)				# Portland Group C compiler
+	  whole_archive_flag_spec='${wl}--whole-archive`for conv in $convenience\"\"; do test  -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` ${wl}--no-whole-archive'
+	  tmp_addflag=' $pic_flag'
+	  ;;
+	pgf77* | pgf90* | pgf95* | pgfortran*)
+					# Portland Group f77 and f90 compilers
+	  whole_archive_flag_spec='${wl}--whole-archive`for conv in $convenience\"\"; do test  -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` ${wl}--no-whole-archive'
+	  tmp_addflag=' $pic_flag -Mnomain' ;;
+	ecc*,ia64* | icc*,ia64*)	# Intel C compiler on ia64
+	  tmp_addflag=' -i_dynamic' ;;
+	efc*,ia64* | ifort*,ia64*)	# Intel Fortran compiler on ia64
+	  tmp_addflag=' -i_dynamic -nofor_main' ;;
+	ifc* | ifort*)			# Intel Fortran compiler
+	  tmp_addflag=' -nofor_main' ;;
+	lf95*)				# Lahey Fortran 8.1
+	  whole_archive_flag_spec=
+	  tmp_sharedflag='--shared' ;;
+	xl[cC]* | bgxl[cC]* | mpixl[cC]*) # IBM XL C 8.0 on PPC (deal with xlf below)
+	  tmp_sharedflag='-qmkshrobj'
+	  tmp_addflag= ;;
+	nvcc*)	# Cuda Compiler Driver 2.2
+	  whole_archive_flag_spec='${wl}--whole-archive`for conv in $convenience\"\"; do test  -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` ${wl}--no-whole-archive'
+	  compiler_needs_object=yes
+	  ;;
+	esac
+	case `$CC -V 2>&1 | sed 5q` in
+	*Sun\ C*)			# Sun C 5.9
+	  whole_archive_flag_spec='${wl}--whole-archive`new_convenience=; for conv in $convenience\"\"; do test -z \"$conv\" || new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` ${wl}--no-whole-archive'
+	  compiler_needs_object=yes
+	  tmp_sharedflag='-G' ;;
+	*Sun\ F*)			# Sun Fortran 8.3
+	  tmp_sharedflag='-G' ;;
+	esac
+	archive_cmds='$CC '"$tmp_sharedflag""$tmp_addflag"' $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib'
+
+        if test "x$supports_anon_versioning" = xyes; then
+          archive_expsym_cmds='echo "{ global:" > $output_objdir/$libname.ver~
+	    cat $export_symbols | sed -e "s/\(.*\)/\1;/" >> $output_objdir/$libname.ver~
+	    echo "local: *; };" >> $output_objdir/$libname.ver~
+	    $CC '"$tmp_sharedflag""$tmp_addflag"' $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-version-script ${wl}$output_objdir/$libname.ver -o $lib'
+        fi
+
+	case $cc_basename in
+	xlf* | bgf* | bgxlf* | mpixlf*)
+	  # IBM XL Fortran 10.1 on PPC cannot create shared libs itself
+	  whole_archive_flag_spec='--whole-archive$convenience --no-whole-archive'
+	  hardcode_libdir_flag_spec='${wl}-rpath ${wl}$libdir'
+	  archive_cmds='$LD -shared $libobjs $deplibs $linker_flags -soname $soname -o $lib'
+	  if test "x$supports_anon_versioning" = xyes; then
+	    archive_expsym_cmds='echo "{ global:" > $output_objdir/$libname.ver~
+	      cat $export_symbols | sed -e "s/\(.*\)/\1;/" >> $output_objdir/$libname.ver~
+	      echo "local: *; };" >> $output_objdir/$libname.ver~
+	      $LD -shared $libobjs $deplibs $linker_flags -soname $soname -version-script $output_objdir/$libname.ver -o $lib'
+	  fi
+	  ;;
+	esac
+      else
+        ld_shlibs=no
+      fi
+      ;;
+
+    netbsd* | netbsdelf*-gnu)
+      if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then
+	archive_cmds='$LD -Bshareable $libobjs $deplibs $linker_flags -o $lib'
+	wlarc=
+      else
+	archive_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib'
+	archive_expsym_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib'
+      fi
+      ;;
+
+    solaris*)
+      if $LD -v 2>&1 | $GREP 'BFD 2\.8' > /dev/null; then
+	ld_shlibs=no
+	cat <<_LT_EOF 1>&2
+
+*** Warning: The releases 2.8.* of the GNU linker cannot reliably
+*** create shared libraries on Solaris systems.  Therefore, libtool
+*** is disabling shared libraries support.  We urge you to upgrade GNU
+*** binutils to release 2.9.1 or newer.  Another option is to modify
+*** your PATH or compiler configuration so that the native linker is
+*** used, and then restart.
+
+_LT_EOF
+      elif $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then
+	archive_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib'
+	archive_expsym_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib'
+      else
+	ld_shlibs=no
+      fi
+      ;;
+
+    sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX*)
+      case `$LD -v 2>&1` in
+        *\ [01].* | *\ 2.[0-9].* | *\ 2.1[0-5].*)
+	ld_shlibs=no
+	cat <<_LT_EOF 1>&2
+
+*** Warning: Releases of the GNU linker prior to 2.16.91.0.3 can not
+*** reliably create shared libraries on SCO systems.  Therefore, libtool
+*** is disabling shared libraries support.  We urge you to upgrade GNU
+*** binutils to release 2.16.91.0.3 or newer.  Another option is to modify
+*** your PATH or compiler configuration so that the native linker is
+*** used, and then restart.
+
+_LT_EOF
+	;;
+	*)
+	  # For security reasons, it is highly recommended that you always
+	  # use absolute paths for naming shared libraries, and exclude the
+	  # DT_RUNPATH tag from executables and libraries.  But doing so
+	  # requires that you compile everything twice, which is a pain.
+	  if $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then
+	    hardcode_libdir_flag_spec='${wl}-rpath ${wl}$libdir'
+	    archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib'
+	    archive_expsym_cmds='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib'
+	  else
+	    ld_shlibs=no
+	  fi
+	;;
+      esac
+      ;;
+
+    sunos4*)
+      archive_cmds='$LD -assert pure-text -Bshareable -o $lib $libobjs $deplibs $linker_flags'
+      wlarc=
+      hardcode_direct=yes
+      hardcode_shlibpath_var=no
+      ;;
+
+    *)
+      if $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then
+	archive_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib'
+	archive_expsym_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib'
+      else
+	ld_shlibs=no
+      fi
+      ;;
+    esac
+
+    if test "$ld_shlibs" = no; then
+      runpath_var=
+      hardcode_libdir_flag_spec=
+      export_dynamic_flag_spec=
+      whole_archive_flag_spec=
+    fi
+  else
+    # PORTME fill in a description of your system's linker (not GNU ld)
+    case $host_os in
+    aix3*)
+      allow_undefined_flag=unsupported
+      always_export_symbols=yes
+      archive_expsym_cmds='$LD -o $output_objdir/$soname $libobjs $deplibs $linker_flags -bE:$export_symbols -T512 -H512 -bM:SRE~$AR $AR_FLAGS $lib $output_objdir/$soname'
+      # Note: this linker hardcodes the directories in LIBPATH if there
+      # are no directories specified by -L.
+      hardcode_minus_L=yes
+      if test "$GCC" = yes && test -z "$lt_prog_compiler_static"; then
+	# Neither direct hardcoding nor static linking is supported with a
+	# broken collect2.
+	hardcode_direct=unsupported
+      fi
+      ;;
+
+    aix[4-9]*)
+      if test "$host_cpu" = ia64; then
+	# On IA64, the linker does run time linking by default, so we don't
+	# have to do anything special.
+	aix_use_runtimelinking=no
+	exp_sym_flag='-Bexport'
+	no_entry_flag=""
+      else
+	# If we're using GNU nm, then we don't want the "-C" option.
+	# -C means demangle to AIX nm, but means don't demangle with GNU nm
+	# Also, AIX nm treats weak defined symbols like other global
+	# defined symbols, whereas GNU nm marks them as "W".
+	if $NM -V 2>&1 | $GREP 'GNU' > /dev/null; then
+	  export_symbols_cmds='$NM -Bpg $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B") || (\$ 2 == "W")) && (substr(\$ 3,1,1) != ".")) { print \$ 3 } }'\'' | sort -u > $export_symbols'
+	else
+	  export_symbols_cmds='$NM -BCpg $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B")) && (substr(\$ 3,1,1) != ".")) { print \$ 3 } }'\'' | sort -u > $export_symbols'
+	fi
+	aix_use_runtimelinking=no
+
+	# Test if we are trying to use run time linking or normal
+	# AIX style linking. If -brtl is somewhere in LDFLAGS, we
+	# need to do runtime linking.
+	case $host_os in aix4.[23]|aix4.[23].*|aix[5-9]*)
+	  for ld_flag in $LDFLAGS; do
+	  if (test $ld_flag = "-brtl" || test $ld_flag = "-Wl,-brtl"); then
+	    aix_use_runtimelinking=yes
+	    break
+	  fi
+	  done
+	  ;;
+	esac
+
+	exp_sym_flag='-bexport'
+	no_entry_flag='-bnoentry'
+      fi
+
+      # When large executables or shared objects are built, AIX ld can
+      # have problems creating the table of contents.  If linking a library
+      # or program results in "error TOC overflow" add -mminimal-toc to
+      # CXXFLAGS/CFLAGS for g++/gcc.  In the cases where that is not
+      # enough to fix the problem, add -Wl,-bbigtoc to LDFLAGS.
+
+      archive_cmds=''
+      hardcode_direct=yes
+      hardcode_direct_absolute=yes
+      hardcode_libdir_separator=':'
+      link_all_deplibs=yes
+      file_list_spec='${wl}-f,'
+
+      if test "$GCC" = yes; then
+	case $host_os in aix4.[012]|aix4.[012].*)
+	# We only want to do this on AIX 4.2 and lower, the check
+	# below for broken collect2 doesn't work under 4.3+
+	  collect2name=`${CC} -print-prog-name=collect2`
+	  if test -f "$collect2name" &&
+	   strings "$collect2name" | $GREP resolve_lib_name >/dev/null
+	  then
+	  # We have reworked collect2
+	  :
+	  else
+	  # We have old collect2
+	  hardcode_direct=unsupported
+	  # It fails to find uninstalled libraries when the uninstalled
+	  # path is not listed in the libpath.  Setting hardcode_minus_L
+	  # to unsupported forces relinking
+	  hardcode_minus_L=yes
+	  hardcode_libdir_flag_spec='-L$libdir'
+	  hardcode_libdir_separator=
+	  fi
+	  ;;
+	esac
+	shared_flag='-shared'
+	if test "$aix_use_runtimelinking" = yes; then
+	  shared_flag="$shared_flag "'${wl}-G'
+	fi
+	link_all_deplibs=no
+      else
+	# not using gcc
+	if test "$host_cpu" = ia64; then
+	# VisualAge C++, Version 5.5 for AIX 5L for IA-64, Beta 3 Release
+	# chokes on -Wl,-G. The following line is correct:
+	  shared_flag='-G'
+	else
+	  if test "$aix_use_runtimelinking" = yes; then
+	    shared_flag='${wl}-G'
+	  else
+	    shared_flag='${wl}-bM:SRE'
+	  fi
+	fi
+      fi
+
+      export_dynamic_flag_spec='${wl}-bexpall'
+      # It seems that -bexpall does not export symbols beginning with
+      # underscore (_), so it is better to generate a list of symbols to export.
+      always_export_symbols=yes
+      if test "$aix_use_runtimelinking" = yes; then
+	# Warning - without using the other runtime loading flags (-brtl),
+	# -berok will link without error, but may produce a broken library.
+	allow_undefined_flag='-berok'
+        # Determine the default libpath from the value encoded in an
+        # empty executable.
+        if test "${lt_cv_aix_libpath+set}" = set; then
+  aix_libpath=$lt_cv_aix_libpath
+else
+  if ${lt_cv_aix_libpath_+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+
+  lt_aix_libpath_sed='
+      /Import File Strings/,/^$/ {
+	  /^0/ {
+	      s/^0  *\([^ ]*\) *$/\1/
+	      p
+	  }
+      }'
+  lt_cv_aix_libpath_=`dump -H conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"`
+  # Check for a 64-bit object if we didn't find anything.
+  if test -z "$lt_cv_aix_libpath_"; then
+    lt_cv_aix_libpath_=`dump -HX64 conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"`
+  fi
+fi
+rm -f core conftest.err conftest.$ac_objext \
+    conftest$ac_exeext conftest.$ac_ext
+  if test -z "$lt_cv_aix_libpath_"; then
+    lt_cv_aix_libpath_="/usr/lib:/lib"
+  fi
+
+fi
+
+  aix_libpath=$lt_cv_aix_libpath_
+fi
+
+        hardcode_libdir_flag_spec='${wl}-blibpath:$libdir:'"$aix_libpath"
+        archive_expsym_cmds='$CC -o $output_objdir/$soname $libobjs $deplibs '"\${wl}$no_entry_flag"' $compiler_flags `if test "x${allow_undefined_flag}" != "x"; then func_echo_all "${wl}${allow_undefined_flag}"; else :; fi` '"\${wl}$exp_sym_flag:\$export_symbols $shared_flag"
+      else
+	if test "$host_cpu" = ia64; then
+	  hardcode_libdir_flag_spec='${wl}-R $libdir:/usr/lib:/lib'
+	  allow_undefined_flag="-z nodefs"
+	  archive_expsym_cmds="\$CC $shared_flag"' -o $output_objdir/$soname $libobjs $deplibs '"\${wl}$no_entry_flag"' $compiler_flags ${wl}${allow_undefined_flag} '"\${wl}$exp_sym_flag:\$export_symbols"
+	else
+	 # Determine the default libpath from the value encoded in an
+	 # empty executable.
+	 if test "${lt_cv_aix_libpath+set}" = set; then
+  aix_libpath=$lt_cv_aix_libpath
+else
+  if ${lt_cv_aix_libpath_+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+
+  lt_aix_libpath_sed='
+      /Import File Strings/,/^$/ {
+	  /^0/ {
+	      s/^0  *\([^ ]*\) *$/\1/
+	      p
+	  }
+      }'
+  lt_cv_aix_libpath_=`dump -H conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"`
+  # Check for a 64-bit object if we didn't find anything.
+  if test -z "$lt_cv_aix_libpath_"; then
+    lt_cv_aix_libpath_=`dump -HX64 conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"`
+  fi
+fi
+rm -f core conftest.err conftest.$ac_objext \
+    conftest$ac_exeext conftest.$ac_ext
+  if test -z "$lt_cv_aix_libpath_"; then
+    lt_cv_aix_libpath_="/usr/lib:/lib"
+  fi
+
+fi
+
+  aix_libpath=$lt_cv_aix_libpath_
+fi
+
+	 hardcode_libdir_flag_spec='${wl}-blibpath:$libdir:'"$aix_libpath"
+	  # Warning - without using the other run time loading flags,
+	  # -berok will link without error, but may produce a broken library.
+	  no_undefined_flag=' ${wl}-bernotok'
+	  allow_undefined_flag=' ${wl}-berok'
+	  if test "$with_gnu_ld" = yes; then
+	    # We only use this code for GNU lds that support --whole-archive.
+	    whole_archive_flag_spec='${wl}--whole-archive$convenience ${wl}--no-whole-archive'
+	  else
+	    # Exported symbols can be pulled into shared objects from archives
+	    whole_archive_flag_spec='$convenience'
+	  fi
+	  archive_cmds_need_lc=yes
+	  # This is similar to how AIX traditionally builds its shared libraries.
+	  archive_expsym_cmds="\$CC $shared_flag"' -o $output_objdir/$soname $libobjs $deplibs ${wl}-bnoentry $compiler_flags ${wl}-bE:$export_symbols${allow_undefined_flag}~$AR $AR_FLAGS $output_objdir/$libname$release.a $output_objdir/$soname'
+	fi
+      fi
+      ;;
+
+    amigaos*)
+      case $host_cpu in
+      powerpc)
+            # see comment about AmigaOS4 .so support
+            archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib'
+            archive_expsym_cmds=''
+        ;;
+      m68k)
+            archive_cmds='$RM $output_objdir/a2ixlibrary.data~$ECHO "#define NAME $libname" > $output_objdir/a2ixlibrary.data~$ECHO "#define LIBRARY_ID 1" >> $output_objdir/a2ixlibrary.data~$ECHO "#define VERSION $major" >> $output_objdir/a2ixlibrary.data~$ECHO "#define REVISION $revision" >> $output_objdir/a2ixlibrary.data~$AR $AR_FLAGS $lib $libobjs~$RANLIB $lib~(cd $output_objdir && a2ixlibrary -32)'
+            hardcode_libdir_flag_spec='-L$libdir'
+            hardcode_minus_L=yes
+        ;;
+      esac
+      ;;
+
+    bsdi[45]*)
+      export_dynamic_flag_spec=-rdynamic
+      ;;
+
+    cygwin* | mingw* | pw32* | cegcc*)
+      # When not using gcc, we currently assume that we are using
+      # Microsoft Visual C++.
+      # hardcode_libdir_flag_spec is actually meaningless, as there is
+      # no search path for DLLs.
+      case $cc_basename in
+      cl*)
+	# Native MSVC
+	hardcode_libdir_flag_spec=' '
+	allow_undefined_flag=unsupported
+	always_export_symbols=yes
+	file_list_spec='@'
+	# Tell ltmain to make .lib files, not .a files.
+	libext=lib
+	# Tell ltmain to make .dll files, not .so files.
+	shrext_cmds=".dll"
+	# FIXME: Setting linknames here is a bad hack.
+	archive_cmds='$CC -o $output_objdir/$soname $libobjs $compiler_flags $deplibs -Wl,-dll~linknames='
+	archive_expsym_cmds='if test "x`$SED 1q $export_symbols`" = xEXPORTS; then
+	    sed -n -e 's/\\\\\\\(.*\\\\\\\)/-link\\\ -EXPORT:\\\\\\\1/' -e '1\\\!p' < $export_symbols > $output_objdir/$soname.exp;
+	  else
+	    sed -e 's/\\\\\\\(.*\\\\\\\)/-link\\\ -EXPORT:\\\\\\\1/' < $export_symbols > $output_objdir/$soname.exp;
+	  fi~
+	  $CC -o $tool_output_objdir$soname $libobjs $compiler_flags $deplibs "@$tool_output_objdir$soname.exp" -Wl,-DLL,-IMPLIB:"$tool_output_objdir$libname.dll.lib"~
+	  linknames='
+	# The linker will not automatically build a static lib if we build a DLL.
+	# _LT_TAGVAR(old_archive_from_new_cmds, )='true'
+	enable_shared_with_static_runtimes=yes
+	exclude_expsyms='_NULL_IMPORT_DESCRIPTOR|_IMPORT_DESCRIPTOR_.*'
+	export_symbols_cmds='$NM $libobjs $convenience | $global_symbol_pipe | $SED -e '\''/^[BCDGRS][ ]/s/.*[ ]\([^ ]*\)/\1,DATA/'\'' | $SED -e '\''/^[AITW][ ]/s/.*[ ]//'\'' | sort | uniq > $export_symbols'
+	# Don't use ranlib
+	old_postinstall_cmds='chmod 644 $oldlib'
+	postlink_cmds='lt_outputfile="@OUTPUT@"~
+	  lt_tool_outputfile="@TOOL_OUTPUT@"~
+	  case $lt_outputfile in
+	    *.exe|*.EXE) ;;
+	    *)
+	      lt_outputfile="$lt_outputfile.exe"
+	      lt_tool_outputfile="$lt_tool_outputfile.exe"
+	      ;;
+	  esac~
+	  if test "$MANIFEST_TOOL" != ":" && test -f "$lt_outputfile.manifest"; then
+	    $MANIFEST_TOOL -manifest "$lt_tool_outputfile.manifest" -outputresource:"$lt_tool_outputfile" || exit 1;
+	    $RM "$lt_outputfile.manifest";
+	  fi'
+	;;
+      *)
+	# Assume MSVC wrapper
+	hardcode_libdir_flag_spec=' '
+	allow_undefined_flag=unsupported
+	# Tell ltmain to make .lib files, not .a files.
+	libext=lib
+	# Tell ltmain to make .dll files, not .so files.
+	shrext_cmds=".dll"
+	# FIXME: Setting linknames here is a bad hack.
+	archive_cmds='$CC -o $lib $libobjs $compiler_flags `func_echo_all "$deplibs" | $SED '\''s/ -lc$//'\''` -link -dll~linknames='
+	# The linker will automatically build a .lib file if we build a DLL.
+	old_archive_from_new_cmds='true'
+	# FIXME: Should let the user specify the lib program.
+	old_archive_cmds='lib -OUT:$oldlib$oldobjs$old_deplibs'
+	enable_shared_with_static_runtimes=yes
+	;;
+      esac
+      ;;
+
+    darwin* | rhapsody*)
+
+
+  archive_cmds_need_lc=no
+  hardcode_direct=no
+  hardcode_automatic=yes
+  hardcode_shlibpath_var=unsupported
+  if test "$lt_cv_ld_force_load" = "yes"; then
+    whole_archive_flag_spec='`for conv in $convenience\"\"; do test  -n \"$conv\" && new_convenience=\"$new_convenience ${wl}-force_load,$conv\"; done; func_echo_all \"$new_convenience\"`'
+
+  else
+    whole_archive_flag_spec=''
+  fi
+  link_all_deplibs=yes
+  allow_undefined_flag="$_lt_dar_allow_undefined"
+  case $cc_basename in
+     ifort*) _lt_dar_can_shared=yes ;;
+     *) _lt_dar_can_shared=$GCC ;;
+  esac
+  if test "$_lt_dar_can_shared" = "yes"; then
+    output_verbose_link_cmd=func_echo_all
+    archive_cmds="\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring $_lt_dar_single_mod${_lt_dsymutil}"
+    module_cmds="\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags${_lt_dsymutil}"
+    archive_expsym_cmds="sed 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring ${_lt_dar_single_mod}${_lt_dar_export_syms}${_lt_dsymutil}"
+    module_expsym_cmds="sed -e 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags${_lt_dar_export_syms}${_lt_dsymutil}"
+
+  else
+  ld_shlibs=no
+  fi
+
+      ;;
+
+    dgux*)
+      archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags'
+      hardcode_libdir_flag_spec='-L$libdir'
+      hardcode_shlibpath_var=no
+      ;;
+
+    # FreeBSD 2.2.[012] allows us to include c++rt0.o to get C++ constructor
+    # support.  Future versions do this automatically, but an explicit c++rt0.o
+    # does not break anything, and helps significantly (at the cost of a little
+    # extra space).
+    freebsd2.2*)
+      archive_cmds='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags /usr/lib/c++rt0.o'
+      hardcode_libdir_flag_spec='-R$libdir'
+      hardcode_direct=yes
+      hardcode_shlibpath_var=no
+      ;;
+
+    # Unfortunately, older versions of FreeBSD 2 do not have this feature.
+    freebsd2.*)
+      archive_cmds='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags'
+      hardcode_direct=yes
+      hardcode_minus_L=yes
+      hardcode_shlibpath_var=no
+      ;;
+
+    # FreeBSD 3 and greater uses gcc -shared to do shared libraries.
+    freebsd* | dragonfly*)
+      archive_cmds='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags'
+      hardcode_libdir_flag_spec='-R$libdir'
+      hardcode_direct=yes
+      hardcode_shlibpath_var=no
+      ;;
+
+    hpux9*)
+      if test "$GCC" = yes; then
+	archive_cmds='$RM $output_objdir/$soname~$CC -shared $pic_flag ${wl}+b ${wl}$install_libdir -o $output_objdir/$soname $libobjs $deplibs $compiler_flags~test $output_objdir/$soname = $lib || mv $output_objdir/$soname $lib'
+      else
+	archive_cmds='$RM $output_objdir/$soname~$LD -b +b $install_libdir -o $output_objdir/$soname $libobjs $deplibs $linker_flags~test $output_objdir/$soname = $lib || mv $output_objdir/$soname $lib'
+      fi
+      hardcode_libdir_flag_spec='${wl}+b ${wl}$libdir'
+      hardcode_libdir_separator=:
+      hardcode_direct=yes
+
+      # hardcode_minus_L: Not really in the search PATH,
+      # but as the default location of the library.
+      hardcode_minus_L=yes
+      export_dynamic_flag_spec='${wl}-E'
+      ;;
+
+    hpux10*)
+      if test "$GCC" = yes && test "$with_gnu_ld" = no; then
+	archive_cmds='$CC -shared $pic_flag ${wl}+h ${wl}$soname ${wl}+b ${wl}$install_libdir -o $lib $libobjs $deplibs $compiler_flags'
+      else
+	archive_cmds='$LD -b +h $soname +b $install_libdir -o $lib $libobjs $deplibs $linker_flags'
+      fi
+      if test "$with_gnu_ld" = no; then
+	hardcode_libdir_flag_spec='${wl}+b ${wl}$libdir'
+	hardcode_libdir_separator=:
+	hardcode_direct=yes
+	hardcode_direct_absolute=yes
+	export_dynamic_flag_spec='${wl}-E'
+	# hardcode_minus_L: Not really in the search PATH,
+	# but as the default location of the library.
+	hardcode_minus_L=yes
+      fi
+      ;;
+
+    hpux11*)
+      if test "$GCC" = yes && test "$with_gnu_ld" = no; then
+	case $host_cpu in
+	hppa*64*)
+	  archive_cmds='$CC -shared ${wl}+h ${wl}$soname -o $lib $libobjs $deplibs $compiler_flags'
+	  ;;
+	ia64*)
+	  archive_cmds='$CC -shared $pic_flag ${wl}+h ${wl}$soname ${wl}+nodefaultrpath -o $lib $libobjs $deplibs $compiler_flags'
+	  ;;
+	*)
+	  archive_cmds='$CC -shared $pic_flag ${wl}+h ${wl}$soname ${wl}+b ${wl}$install_libdir -o $lib $libobjs $deplibs $compiler_flags'
+	  ;;
+	esac
+      else
+	case $host_cpu in
+	hppa*64*)
+	  archive_cmds='$CC -b ${wl}+h ${wl}$soname -o $lib $libobjs $deplibs $compiler_flags'
+	  ;;
+	ia64*)
+	  archive_cmds='$CC -b ${wl}+h ${wl}$soname ${wl}+nodefaultrpath -o $lib $libobjs $deplibs $compiler_flags'
+	  ;;
+	*)
+
+	  # Older versions of the 11.00 compiler do not understand -b yet
+	  # (HP92453-01 A.11.01.20 doesn't, HP92453-01 B.11.X.35175-35176.GP does)
+	  { $as_echo "$as_me:${as_lineno-$LINENO}: checking if $CC understands -b" >&5
+$as_echo_n "checking if $CC understands -b... " >&6; }
+if ${lt_cv_prog_compiler__b+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  lt_cv_prog_compiler__b=no
+   save_LDFLAGS="$LDFLAGS"
+   LDFLAGS="$LDFLAGS -b"
+   echo "$lt_simple_link_test_code" > conftest.$ac_ext
+   if (eval $ac_link 2>conftest.err) && test -s conftest$ac_exeext; then
+     # The linker can only warn and ignore the option if not recognized
+     # So say no if there are warnings
+     if test -s conftest.err; then
+       # Append any errors to the config.log.
+       cat conftest.err 1>&5
+       $ECHO "$_lt_linker_boilerplate" | $SED '/^$/d' > conftest.exp
+       $SED '/^$/d; /^ *+/d' conftest.err >conftest.er2
+       if diff conftest.exp conftest.er2 >/dev/null; then
+         lt_cv_prog_compiler__b=yes
+       fi
+     else
+       lt_cv_prog_compiler__b=yes
+     fi
+   fi
+   $RM -r conftest*
+   LDFLAGS="$save_LDFLAGS"
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler__b" >&5
+$as_echo "$lt_cv_prog_compiler__b" >&6; }
+
+if test x"$lt_cv_prog_compiler__b" = xyes; then
+    archive_cmds='$CC -b ${wl}+h ${wl}$soname ${wl}+b ${wl}$install_libdir -o $lib $libobjs $deplibs $compiler_flags'
+else
+    archive_cmds='$LD -b +h $soname +b $install_libdir -o $lib $libobjs $deplibs $linker_flags'
+fi
+
+	  ;;
+	esac
+      fi
+      if test "$with_gnu_ld" = no; then
+	hardcode_libdir_flag_spec='${wl}+b ${wl}$libdir'
+	hardcode_libdir_separator=:
+
+	case $host_cpu in
+	hppa*64*|ia64*)
+	  hardcode_direct=no
+	  hardcode_shlibpath_var=no
+	  ;;
+	*)
+	  hardcode_direct=yes
+	  hardcode_direct_absolute=yes
+	  export_dynamic_flag_spec='${wl}-E'
+
+	  # hardcode_minus_L: Not really in the search PATH,
+	  # but as the default location of the library.
+	  hardcode_minus_L=yes
+	  ;;
+	esac
+      fi
+      ;;
+
+    irix5* | irix6* | nonstopux*)
+      if test "$GCC" = yes; then
+	archive_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-soname ${wl}$soname `test -n "$verstring" && func_echo_all "${wl}-set_version ${wl}$verstring"` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib'
+	# Try to use the -exported_symbol ld option, if it does not
+	# work, assume that -exports_file does not work either and
+	# implicitly export all symbols.
+	# This should be the same for all languages, so no per-tag cache variable.
+	{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the $host_os linker accepts -exported_symbol" >&5
+$as_echo_n "checking whether the $host_os linker accepts -exported_symbol... " >&6; }
+if ${lt_cv_irix_exported_symbol+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  save_LDFLAGS="$LDFLAGS"
+	   LDFLAGS="$LDFLAGS -shared ${wl}-exported_symbol ${wl}foo ${wl}-update_registry ${wl}/dev/null"
+	   cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+int foo (void) { return 0; }
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+  lt_cv_irix_exported_symbol=yes
+else
+  lt_cv_irix_exported_symbol=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+    conftest$ac_exeext conftest.$ac_ext
+           LDFLAGS="$save_LDFLAGS"
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_irix_exported_symbol" >&5
+$as_echo "$lt_cv_irix_exported_symbol" >&6; }
+	if test "$lt_cv_irix_exported_symbol" = yes; then
+          archive_expsym_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-soname ${wl}$soname `test -n "$verstring" && func_echo_all "${wl}-set_version ${wl}$verstring"` ${wl}-update_registry ${wl}${output_objdir}/so_locations ${wl}-exports_file ${wl}$export_symbols -o $lib'
+	fi
+      else
+	archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry ${output_objdir}/so_locations -o $lib'
+	archive_expsym_cmds='$CC -shared $libobjs $deplibs $compiler_flags -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry ${output_objdir}/so_locations -exports_file $export_symbols -o $lib'
+      fi
+      archive_cmds_need_lc='no'
+      hardcode_libdir_flag_spec='${wl}-rpath ${wl}$libdir'
+      hardcode_libdir_separator=:
+      inherit_rpath=yes
+      link_all_deplibs=yes
+      ;;
+
+    netbsd* | netbsdelf*-gnu)
+      if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then
+	archive_cmds='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags'  # a.out
+      else
+	archive_cmds='$LD -shared -o $lib $libobjs $deplibs $linker_flags'      # ELF
+      fi
+      hardcode_libdir_flag_spec='-R$libdir'
+      hardcode_direct=yes
+      hardcode_shlibpath_var=no
+      ;;
+
+    newsos6)
+      archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags'
+      hardcode_direct=yes
+      hardcode_libdir_flag_spec='${wl}-rpath ${wl}$libdir'
+      hardcode_libdir_separator=:
+      hardcode_shlibpath_var=no
+      ;;
+
+    *nto* | *qnx*)
+      ;;
+
+    openbsd*)
+      if test -f /usr/libexec/ld.so; then
+	hardcode_direct=yes
+	hardcode_shlibpath_var=no
+	hardcode_direct_absolute=yes
+	if test -z "`echo __ELF__ | $CC -E - | $GREP __ELF__`" || test "$host_os-$host_cpu" = "openbsd2.8-powerpc"; then
+	  archive_cmds='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags'
+	  archive_expsym_cmds='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags ${wl}-retain-symbols-file,$export_symbols'
+	  hardcode_libdir_flag_spec='${wl}-rpath,$libdir'
+	  export_dynamic_flag_spec='${wl}-E'
+	else
+	  case $host_os in
+	   openbsd[01].* | openbsd2.[0-7] | openbsd2.[0-7].*)
+	     archive_cmds='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags'
+	     hardcode_libdir_flag_spec='-R$libdir'
+	     ;;
+	   *)
+	     archive_cmds='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags'
+	     hardcode_libdir_flag_spec='${wl}-rpath,$libdir'
+	     ;;
+	  esac
+	fi
+      else
+	ld_shlibs=no
+      fi
+      ;;
+
+    os2*)
+      hardcode_libdir_flag_spec='-L$libdir'
+      hardcode_minus_L=yes
+      allow_undefined_flag=unsupported
+      archive_cmds='$ECHO "LIBRARY $libname INITINSTANCE" > $output_objdir/$libname.def~$ECHO "DESCRIPTION \"$libname\"" >> $output_objdir/$libname.def~echo DATA >> $output_objdir/$libname.def~echo " SINGLE NONSHARED" >> $output_objdir/$libname.def~echo EXPORTS >> $output_objdir/$libname.def~emxexp $libobjs >> $output_objdir/$libname.def~$CC -Zdll -Zcrtdll -o $lib $libobjs $deplibs $compiler_flags $output_objdir/$libname.def'
+      old_archive_from_new_cmds='emximp -o $output_objdir/$libname.a $output_objdir/$libname.def'
+      ;;
+
+    osf3*)
+      if test "$GCC" = yes; then
+	allow_undefined_flag=' ${wl}-expect_unresolved ${wl}\*'
+	archive_cmds='$CC -shared${allow_undefined_flag} $libobjs $deplibs $compiler_flags ${wl}-soname ${wl}$soname `test -n "$verstring" && func_echo_all "${wl}-set_version ${wl}$verstring"` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib'
+      else
+	allow_undefined_flag=' -expect_unresolved \*'
+	archive_cmds='$CC -shared${allow_undefined_flag} $libobjs $deplibs $compiler_flags -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry ${output_objdir}/so_locations -o $lib'
+      fi
+      archive_cmds_need_lc='no'
+      hardcode_libdir_flag_spec='${wl}-rpath ${wl}$libdir'
+      hardcode_libdir_separator=:
+      ;;
+
+    osf4* | osf5*)	# as osf3* with the addition of -msym flag
+      if test "$GCC" = yes; then
+	allow_undefined_flag=' ${wl}-expect_unresolved ${wl}\*'
+	archive_cmds='$CC -shared${allow_undefined_flag} $pic_flag $libobjs $deplibs $compiler_flags ${wl}-msym ${wl}-soname ${wl}$soname `test -n "$verstring" && func_echo_all "${wl}-set_version ${wl}$verstring"` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib'
+	hardcode_libdir_flag_spec='${wl}-rpath ${wl}$libdir'
+      else
+	allow_undefined_flag=' -expect_unresolved \*'
+	archive_cmds='$CC -shared${allow_undefined_flag} $libobjs $deplibs $compiler_flags -msym -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry ${output_objdir}/so_locations -o $lib'
+	archive_expsym_cmds='for i in `cat $export_symbols`; do printf "%s %s\\n" -exported_symbol "\$i" >> $lib.exp; done; printf "%s\\n" "-hidden">> $lib.exp~
+	$CC -shared${allow_undefined_flag} ${wl}-input ${wl}$lib.exp $compiler_flags $libobjs $deplibs -soname $soname `test -n "$verstring" && $ECHO "-set_version $verstring"` -update_registry ${output_objdir}/so_locations -o $lib~$RM $lib.exp'
+
+	# Both c and cxx compiler support -rpath directly
+	hardcode_libdir_flag_spec='-rpath $libdir'
+      fi
+      archive_cmds_need_lc='no'
+      hardcode_libdir_separator=:
+      ;;
+
+    solaris*)
+      no_undefined_flag=' -z defs'
+      if test "$GCC" = yes; then
+	wlarc='${wl}'
+	archive_cmds='$CC -shared $pic_flag ${wl}-z ${wl}text ${wl}-h ${wl}$soname -o $lib $libobjs $deplibs $compiler_flags'
+	archive_expsym_cmds='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~
+	  $CC -shared $pic_flag ${wl}-z ${wl}text ${wl}-M ${wl}$lib.exp ${wl}-h ${wl}$soname -o $lib $libobjs $deplibs $compiler_flags~$RM $lib.exp'
+      else
+	case `$CC -V 2>&1` in
+	*"Compilers 5.0"*)
+	  wlarc=''
+	  archive_cmds='$LD -G${allow_undefined_flag} -h $soname -o $lib $libobjs $deplibs $linker_flags'
+	  archive_expsym_cmds='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~
+	  $LD -G${allow_undefined_flag} -M $lib.exp -h $soname -o $lib $libobjs $deplibs $linker_flags~$RM $lib.exp'
+	  ;;
+	*)
+	  wlarc='${wl}'
+	  archive_cmds='$CC -G${allow_undefined_flag} -h $soname -o $lib $libobjs $deplibs $compiler_flags'
+	  archive_expsym_cmds='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~
+	  $CC -G${allow_undefined_flag} -M $lib.exp -h $soname -o $lib $libobjs $deplibs $compiler_flags~$RM $lib.exp'
+	  ;;
+	esac
+      fi
+      hardcode_libdir_flag_spec='-R$libdir'
+      hardcode_shlibpath_var=no
+      case $host_os in
+      solaris2.[0-5] | solaris2.[0-5].*) ;;
+      *)
+	# The compiler driver will combine and reorder linker options,
+	# but understands `-z linker_flag'.  GCC discards it without `$wl',
+	# but is careful enough not to reorder.
+	# Supported since Solaris 2.6 (maybe 2.5.1?)
+	if test "$GCC" = yes; then
+	  whole_archive_flag_spec='${wl}-z ${wl}allextract$convenience ${wl}-z ${wl}defaultextract'
+	else
+	  whole_archive_flag_spec='-z allextract$convenience -z defaultextract'
+	fi
+	;;
+      esac
+      link_all_deplibs=yes
+      ;;
+
+    sunos4*)
+      if test "x$host_vendor" = xsequent; then
+	# Use $CC to link under sequent, because it throws in some extra .o
+	# files that make .init and .fini sections work.
+	archive_cmds='$CC -G ${wl}-h $soname -o $lib $libobjs $deplibs $compiler_flags'
+      else
+	archive_cmds='$LD -assert pure-text -Bstatic -o $lib $libobjs $deplibs $linker_flags'
+      fi
+      hardcode_libdir_flag_spec='-L$libdir'
+      hardcode_direct=yes
+      hardcode_minus_L=yes
+      hardcode_shlibpath_var=no
+      ;;
+
+    sysv4)
+      case $host_vendor in
+	sni)
+	  archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags'
+	  hardcode_direct=yes # is this really true???
+	;;
+	siemens)
+	  ## LD is ld it makes a PLAMLIB
+	  ## CC just makes a GrossModule.
+	  archive_cmds='$LD -G -o $lib $libobjs $deplibs $linker_flags'
+	  reload_cmds='$CC -r -o $output$reload_objs'
+	  hardcode_direct=no
+        ;;
+	motorola)
+	  archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags'
+	  hardcode_direct=no #Motorola manual says yes, but my tests say they lie
+	;;
+      esac
+      runpath_var='LD_RUN_PATH'
+      hardcode_shlibpath_var=no
+      ;;
+
+    sysv4.3*)
+      archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags'
+      hardcode_shlibpath_var=no
+      export_dynamic_flag_spec='-Bexport'
+      ;;
+
+    sysv4*MP*)
+      if test -d /usr/nec; then
+	archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags'
+	hardcode_shlibpath_var=no
+	runpath_var=LD_RUN_PATH
+	hardcode_runpath_var=yes
+	ld_shlibs=yes
+      fi
+      ;;
+
+    sysv4*uw2* | sysv5OpenUNIX* | sysv5UnixWare7.[01].[10]* | unixware7* | sco3.2v5.0.[024]*)
+      no_undefined_flag='${wl}-z,text'
+      archive_cmds_need_lc=no
+      hardcode_shlibpath_var=no
+      runpath_var='LD_RUN_PATH'
+
+      if test "$GCC" = yes; then
+	archive_cmds='$CC -shared ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+	archive_expsym_cmds='$CC -shared ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+      else
+	archive_cmds='$CC -G ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+	archive_expsym_cmds='$CC -G ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+      fi
+      ;;
+
+    sysv5* | sco3.2v5* | sco5v6*)
+      # Note: We can NOT use -z defs as we might desire, because we do not
+      # link with -lc, and that would cause any symbols used from libc to
+      # always be unresolved, which means just about no library would
+      # ever link correctly.  If we're not using GNU ld we use -z text
+      # though, which does catch some bad symbols but isn't as heavy-handed
+      # as -z defs.
+      no_undefined_flag='${wl}-z,text'
+      allow_undefined_flag='${wl}-z,nodefs'
+      archive_cmds_need_lc=no
+      hardcode_shlibpath_var=no
+      hardcode_libdir_flag_spec='${wl}-R,$libdir'
+      hardcode_libdir_separator=':'
+      link_all_deplibs=yes
+      export_dynamic_flag_spec='${wl}-Bexport'
+      runpath_var='LD_RUN_PATH'
+
+      if test "$GCC" = yes; then
+	archive_cmds='$CC -shared ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+	archive_expsym_cmds='$CC -shared ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+      else
+	archive_cmds='$CC -G ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+	archive_expsym_cmds='$CC -G ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+      fi
+      ;;
+
+    uts4*)
+      archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags'
+      hardcode_libdir_flag_spec='-L$libdir'
+      hardcode_shlibpath_var=no
+      ;;
+
+    *)
+      ld_shlibs=no
+      ;;
+    esac
+
+    if test x$host_vendor = xsni; then
+      case $host in
+      sysv4 | sysv4.2uw2* | sysv4.3* | sysv5*)
+	export_dynamic_flag_spec='${wl}-Blargedynsym'
+	;;
+      esac
+    fi
+  fi
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ld_shlibs" >&5
+$as_echo "$ld_shlibs" >&6; }
+test "$ld_shlibs" = no && can_build_shared=no
+
+with_gnu_ld=$with_gnu_ld
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+#
+# Do we need to explicitly link libc?
+#
+case "x$archive_cmds_need_lc" in
+x|xyes)
+  # Assume -lc should be added
+  archive_cmds_need_lc=yes
+
+  if test "$enable_shared" = yes && test "$GCC" = yes; then
+    case $archive_cmds in
+    *'~'*)
+      # FIXME: we may have to deal with multi-command sequences.
+      ;;
+    '$CC '*)
+      # Test whether the compiler implicitly links with -lc since on some
+      # systems, -lgcc has to come before -lc. If gcc already passes -lc
+      # to ld, don't add -lc before -lgcc.
+      { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether -lc should be explicitly linked in" >&5
+$as_echo_n "checking whether -lc should be explicitly linked in... " >&6; }
+if ${lt_cv_archive_cmds_need_lc+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  $RM conftest*
+	echo "$lt_simple_compile_test_code" > conftest.$ac_ext
+
+	if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5
+  (eval $ac_compile) 2>&5
+  ac_status=$?
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; } 2>conftest.err; then
+	  soname=conftest
+	  lib=conftest
+	  libobjs=conftest.$ac_objext
+	  deplibs=
+	  wl=$lt_prog_compiler_wl
+	  pic_flag=$lt_prog_compiler_pic
+	  compiler_flags=-v
+	  linker_flags=-v
+	  verstring=
+	  output_objdir=.
+	  libname=conftest
+	  lt_save_allow_undefined_flag=$allow_undefined_flag
+	  allow_undefined_flag=
+	  if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$archive_cmds 2\>\&1 \| $GREP \" -lc \" \>/dev/null 2\>\&1\""; } >&5
+  (eval $archive_cmds 2\>\&1 \| $GREP \" -lc \" \>/dev/null 2\>\&1) 2>&5
+  ac_status=$?
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; }
+	  then
+	    lt_cv_archive_cmds_need_lc=no
+	  else
+	    lt_cv_archive_cmds_need_lc=yes
+	  fi
+	  allow_undefined_flag=$lt_save_allow_undefined_flag
+	else
+	  cat conftest.err 1>&5
+	fi
+	$RM conftest*
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_archive_cmds_need_lc" >&5
+$as_echo "$lt_cv_archive_cmds_need_lc" >&6; }
+      archive_cmds_need_lc=$lt_cv_archive_cmds_need_lc
+      ;;
+    esac
+  fi
+  ;;
+esac
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+  { $as_echo "$as_me:${as_lineno-$LINENO}: checking dynamic linker characteristics" >&5
+$as_echo_n "checking dynamic linker characteristics... " >&6; }
+
+if test "$GCC" = yes; then
+  case $host_os in
+    darwin*) lt_awk_arg="/^libraries:/,/LR/" ;;
+    *) lt_awk_arg="/^libraries:/" ;;
+  esac
+  case $host_os in
+    mingw* | cegcc*) lt_sed_strip_eq="s,=\([A-Za-z]:\),\1,g" ;;
+    *) lt_sed_strip_eq="s,=/,/,g" ;;
+  esac
+  lt_search_path_spec=`$CC -print-search-dirs | awk $lt_awk_arg | $SED -e "s/^libraries://" -e $lt_sed_strip_eq`
+  case $lt_search_path_spec in
+  *\;*)
+    # if the path contains ";" then we assume it to be the separator
+    # otherwise default to the standard path separator (i.e. ":") - it is
+    # assumed that no part of a normal pathname contains ";" but that should
+    # okay in the real world where ";" in dirpaths is itself problematic.
+    lt_search_path_spec=`$ECHO "$lt_search_path_spec" | $SED 's/;/ /g'`
+    ;;
+  *)
+    lt_search_path_spec=`$ECHO "$lt_search_path_spec" | $SED "s/$PATH_SEPARATOR/ /g"`
+    ;;
+  esac
+  # Ok, now we have the path, separated by spaces, we can step through it
+  # and add multilib dir if necessary.
+  lt_tmp_lt_search_path_spec=
+  lt_multi_os_dir=`$CC $CPPFLAGS $CFLAGS $LDFLAGS -print-multi-os-directory 2>/dev/null`
+  for lt_sys_path in $lt_search_path_spec; do
+    if test -d "$lt_sys_path/$lt_multi_os_dir"; then
+      lt_tmp_lt_search_path_spec="$lt_tmp_lt_search_path_spec $lt_sys_path/$lt_multi_os_dir"
+    else
+      test -d "$lt_sys_path" && \
+	lt_tmp_lt_search_path_spec="$lt_tmp_lt_search_path_spec $lt_sys_path"
+    fi
+  done
+  lt_search_path_spec=`$ECHO "$lt_tmp_lt_search_path_spec" | awk '
+BEGIN {RS=" "; FS="/|\n";} {
+  lt_foo="";
+  lt_count=0;
+  for (lt_i = NF; lt_i > 0; lt_i--) {
+    if ($lt_i != "" && $lt_i != ".") {
+      if ($lt_i == "..") {
+        lt_count++;
+      } else {
+        if (lt_count == 0) {
+          lt_foo="/" $lt_i lt_foo;
+        } else {
+          lt_count--;
+        }
+      }
+    }
+  }
+  if (lt_foo != "") { lt_freq[lt_foo]++; }
+  if (lt_freq[lt_foo] == 1) { print lt_foo; }
+}'`
+  # AWK program above erroneously prepends '/' to C:/dos/paths
+  # for these hosts.
+  case $host_os in
+    mingw* | cegcc*) lt_search_path_spec=`$ECHO "$lt_search_path_spec" |\
+      $SED 's,/\([A-Za-z]:\),\1,g'` ;;
+  esac
+  sys_lib_search_path_spec=`$ECHO "$lt_search_path_spec" | $lt_NL2SP`
+else
+  sys_lib_search_path_spec="/lib /usr/lib /usr/local/lib"
+fi
+library_names_spec=
+libname_spec='lib$name'
+soname_spec=
+shrext_cmds=".so"
+postinstall_cmds=
+postuninstall_cmds=
+finish_cmds=
+finish_eval=
+shlibpath_var=
+shlibpath_overrides_runpath=unknown
+version_type=none
+dynamic_linker="$host_os ld.so"
+sys_lib_dlsearch_path_spec="/lib /usr/lib"
+need_lib_prefix=unknown
+hardcode_into_libs=no
+
+# when you set need_version to no, make sure it does not cause -set_version
+# flags to be left without arguments
+need_version=unknown
+
+case $host_os in
+aix3*)
+  version_type=linux # correct to gnu/linux during the next big refactor
+  library_names_spec='${libname}${release}${shared_ext}$versuffix $libname.a'
+  shlibpath_var=LIBPATH
+
+  # AIX 3 has no versioning support, so we append a major version to the name.
+  soname_spec='${libname}${release}${shared_ext}$major'
+  ;;
+
+aix[4-9]*)
+  version_type=linux # correct to gnu/linux during the next big refactor
+  need_lib_prefix=no
+  need_version=no
+  hardcode_into_libs=yes
+  if test "$host_cpu" = ia64; then
+    # AIX 5 supports IA64
+    library_names_spec='${libname}${release}${shared_ext}$major ${libname}${release}${shared_ext}$versuffix $libname${shared_ext}'
+    shlibpath_var=LD_LIBRARY_PATH
+  else
+    # With GCC up to 2.95.x, collect2 would create an import file
+    # for dependence libraries.  The import file would start with
+    # the line `#! .'.  This would cause the generated library to
+    # depend on `.', always an invalid library.  This was fixed in
+    # development snapshots of GCC prior to 3.0.
+    case $host_os in
+      aix4 | aix4.[01] | aix4.[01].*)
+      if { echo '#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 97)'
+	   echo ' yes '
+	   echo '#endif'; } | ${CC} -E - | $GREP yes > /dev/null; then
+	:
+      else
+	can_build_shared=no
+      fi
+      ;;
+    esac
+    # AIX (on Power*) has no versioning support, so currently we can not hardcode correct
+    # soname into executable. Probably we can add versioning support to
+    # collect2, so additional links can be useful in future.
+    if test "$aix_use_runtimelinking" = yes; then
+      # If using run time linking (on AIX 4.2 or later) use lib<name>.so
+      # instead of lib<name>.a to let people know that these are not
+      # typical AIX shared libraries.
+      library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+    else
+      # We preserve .a as extension for shared libraries through AIX4.2
+      # and later when we are not doing run time linking.
+      library_names_spec='${libname}${release}.a $libname.a'
+      soname_spec='${libname}${release}${shared_ext}$major'
+    fi
+    shlibpath_var=LIBPATH
+  fi
+  ;;
+
+amigaos*)
+  case $host_cpu in
+  powerpc)
+    # Since July 2007 AmigaOS4 officially supports .so libraries.
+    # When compiling the executable, add -use-dynld -Lsobjs: to the compileline.
+    library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+    ;;
+  m68k)
+    library_names_spec='$libname.ixlibrary $libname.a'
+    # Create ${libname}_ixlibrary.a entries in /sys/libs.
+    finish_eval='for lib in `ls $libdir/*.ixlibrary 2>/dev/null`; do libname=`func_echo_all "$lib" | $SED '\''s%^.*/\([^/]*\)\.ixlibrary$%\1%'\''`; test $RM /sys/libs/${libname}_ixlibrary.a; $show "cd /sys/libs && $LN_S $lib ${libname}_ixlibrary.a"; cd /sys/libs && $LN_S $lib ${libname}_ixlibrary.a || exit 1; done'
+    ;;
+  esac
+  ;;
+
+beos*)
+  library_names_spec='${libname}${shared_ext}'
+  dynamic_linker="$host_os ld.so"
+  shlibpath_var=LIBRARY_PATH
+  ;;
+
+bsdi[45]*)
+  version_type=linux # correct to gnu/linux during the next big refactor
+  need_version=no
+  library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+  soname_spec='${libname}${release}${shared_ext}$major'
+  finish_cmds='PATH="\$PATH:/sbin" ldconfig $libdir'
+  shlibpath_var=LD_LIBRARY_PATH
+  sys_lib_search_path_spec="/shlib /usr/lib /usr/X11/lib /usr/contrib/lib /lib /usr/local/lib"
+  sys_lib_dlsearch_path_spec="/shlib /usr/lib /usr/local/lib"
+  # the default ld.so.conf also contains /usr/contrib/lib and
+  # /usr/X11R6/lib (/usr/X11 is a link to /usr/X11R6), but let us allow
+  # libtool to hard-code these into programs
+  ;;
+
+cygwin* | mingw* | pw32* | cegcc*)
+  version_type=windows
+  shrext_cmds=".dll"
+  need_version=no
+  need_lib_prefix=no
+
+  case $GCC,$cc_basename in
+  yes,*)
+    # gcc
+    library_names_spec='$libname.dll.a'
+    # DLL is installed to $(libdir)/../bin by postinstall_cmds
+    postinstall_cmds='base_file=`basename \${file}`~
+      dlpath=`$SHELL 2>&1 -c '\''. $dir/'\''\${base_file}'\''i; echo \$dlname'\''`~
+      dldir=$destdir/`dirname \$dlpath`~
+      test -d \$dldir || mkdir -p \$dldir~
+      $install_prog $dir/$dlname \$dldir/$dlname~
+      chmod a+x \$dldir/$dlname~
+      if test -n '\''$stripme'\'' && test -n '\''$striplib'\''; then
+        eval '\''$striplib \$dldir/$dlname'\'' || exit \$?;
+      fi'
+    postuninstall_cmds='dldll=`$SHELL 2>&1 -c '\''. $file; echo \$dlname'\''`~
+      dlpath=$dir/\$dldll~
+       $RM \$dlpath'
+    shlibpath_overrides_runpath=yes
+
+    case $host_os in
+    cygwin*)
+      # Cygwin DLLs use 'cyg' prefix rather than 'lib'
+      soname_spec='`echo ${libname} | sed -e 's/^lib/cyg/'``echo ${release} | $SED -e 's/[.]/-/g'`${versuffix}${shared_ext}'
+
+      sys_lib_search_path_spec="$sys_lib_search_path_spec /usr/lib/w32api"
+      ;;
+    mingw* | cegcc*)
+      # MinGW DLLs use traditional 'lib' prefix
+      soname_spec='${libname}`echo ${release} | $SED -e 's/[.]/-/g'`${versuffix}${shared_ext}'
+      ;;
+    pw32*)
+      # pw32 DLLs use 'pw' prefix rather than 'lib'
+      library_names_spec='`echo ${libname} | sed -e 's/^lib/pw/'``echo ${release} | $SED -e 's/[.]/-/g'`${versuffix}${shared_ext}'
+      ;;
+    esac
+    dynamic_linker='Win32 ld.exe'
+    ;;
+
+  *,cl*)
+    # Native MSVC
+    libname_spec='$name'
+    soname_spec='${libname}`echo ${release} | $SED -e 's/[.]/-/g'`${versuffix}${shared_ext}'
+    library_names_spec='${libname}.dll.lib'
+
+    case $build_os in
+    mingw*)
+      sys_lib_search_path_spec=
+      lt_save_ifs=$IFS
+      IFS=';'
+      for lt_path in $LIB
+      do
+        IFS=$lt_save_ifs
+        # Let DOS variable expansion print the short 8.3 style file name.
+        lt_path=`cd "$lt_path" 2>/dev/null && cmd //C "for %i in (".") do @echo %~si"`
+        sys_lib_search_path_spec="$sys_lib_search_path_spec $lt_path"
+      done
+      IFS=$lt_save_ifs
+      # Convert to MSYS style.
+      sys_lib_search_path_spec=`$ECHO "$sys_lib_search_path_spec" | sed -e 's|\\\\|/|g' -e 's| \\([a-zA-Z]\\):| /\\1|g' -e 's|^ ||'`
+      ;;
+    cygwin*)
+      # Convert to unix form, then to dos form, then back to unix form
+      # but this time dos style (no spaces!) so that the unix form looks
+      # like /cygdrive/c/PROGRA~1:/cygdr...
+      sys_lib_search_path_spec=`cygpath --path --unix "$LIB"`
+      sys_lib_search_path_spec=`cygpath --path --dos "$sys_lib_search_path_spec" 2>/dev/null`
+      sys_lib_search_path_spec=`cygpath --path --unix "$sys_lib_search_path_spec" | $SED -e "s/$PATH_SEPARATOR/ /g"`
+      ;;
+    *)
+      sys_lib_search_path_spec="$LIB"
+      if $ECHO "$sys_lib_search_path_spec" | $GREP ';[c-zC-Z]:/' >/dev/null; then
+        # It is most probably a Windows format PATH.
+        sys_lib_search_path_spec=`$ECHO "$sys_lib_search_path_spec" | $SED -e 's/;/ /g'`
+      else
+        sys_lib_search_path_spec=`$ECHO "$sys_lib_search_path_spec" | $SED -e "s/$PATH_SEPARATOR/ /g"`
+      fi
+      # FIXME: find the short name or the path components, as spaces are
+      # common. (e.g. "Program Files" -> "PROGRA~1")
+      ;;
+    esac
+
+    # DLL is installed to $(libdir)/../bin by postinstall_cmds
+    postinstall_cmds='base_file=`basename \${file}`~
+      dlpath=`$SHELL 2>&1 -c '\''. $dir/'\''\${base_file}'\''i; echo \$dlname'\''`~
+      dldir=$destdir/`dirname \$dlpath`~
+      test -d \$dldir || mkdir -p \$dldir~
+      $install_prog $dir/$dlname \$dldir/$dlname'
+    postuninstall_cmds='dldll=`$SHELL 2>&1 -c '\''. $file; echo \$dlname'\''`~
+      dlpath=$dir/\$dldll~
+       $RM \$dlpath'
+    shlibpath_overrides_runpath=yes
+    dynamic_linker='Win32 link.exe'
+    ;;
+
+  *)
+    # Assume MSVC wrapper
+    library_names_spec='${libname}`echo ${release} | $SED -e 's/[.]/-/g'`${versuffix}${shared_ext} $libname.lib'
+    dynamic_linker='Win32 ld.exe'
+    ;;
+  esac
+  # FIXME: first we should search . and the directory the executable is in
+  shlibpath_var=PATH
+  ;;
+
+darwin* | rhapsody*)
+  dynamic_linker="$host_os dyld"
+  version_type=darwin
+  need_lib_prefix=no
+  need_version=no
+  library_names_spec='${libname}${release}${major}$shared_ext ${libname}$shared_ext'
+  soname_spec='${libname}${release}${major}$shared_ext'
+  shlibpath_overrides_runpath=yes
+  shlibpath_var=DYLD_LIBRARY_PATH
+  shrext_cmds='`test .$module = .yes && echo .so || echo .dylib`'
+
+  sys_lib_search_path_spec="$sys_lib_search_path_spec /usr/local/lib"
+  sys_lib_dlsearch_path_spec='/usr/local/lib /lib /usr/lib'
+  ;;
+
+dgux*)
+  version_type=linux # correct to gnu/linux during the next big refactor
+  need_lib_prefix=no
+  need_version=no
+  library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname$shared_ext'
+  soname_spec='${libname}${release}${shared_ext}$major'
+  shlibpath_var=LD_LIBRARY_PATH
+  ;;
+
+freebsd* | dragonfly*)
+  # DragonFly does not have aout.  When/if they implement a new
+  # versioning mechanism, adjust this.
+  if test -x /usr/bin/objformat; then
+    objformat=`/usr/bin/objformat`
+  else
+    case $host_os in
+    freebsd[23].*) objformat=aout ;;
+    *) objformat=elf ;;
+    esac
+  fi
+  version_type=freebsd-$objformat
+  case $version_type in
+    freebsd-elf*)
+      library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext} $libname${shared_ext}'
+      need_version=no
+      need_lib_prefix=no
+      ;;
+    freebsd-*)
+      library_names_spec='${libname}${release}${shared_ext}$versuffix $libname${shared_ext}$versuffix'
+      need_version=yes
+      ;;
+  esac
+  shlibpath_var=LD_LIBRARY_PATH
+  case $host_os in
+  freebsd2.*)
+    shlibpath_overrides_runpath=yes
+    ;;
+  freebsd3.[01]* | freebsdelf3.[01]*)
+    shlibpath_overrides_runpath=yes
+    hardcode_into_libs=yes
+    ;;
+  freebsd3.[2-9]* | freebsdelf3.[2-9]* | \
+  freebsd4.[0-5] | freebsdelf4.[0-5] | freebsd4.1.1 | freebsdelf4.1.1)
+    shlibpath_overrides_runpath=no
+    hardcode_into_libs=yes
+    ;;
+  *) # from 4.6 on, and DragonFly
+    shlibpath_overrides_runpath=yes
+    hardcode_into_libs=yes
+    ;;
+  esac
+  ;;
+
+haiku*)
+  version_type=linux # correct to gnu/linux during the next big refactor
+  need_lib_prefix=no
+  need_version=no
+  dynamic_linker="$host_os runtime_loader"
+  library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}${major} ${libname}${shared_ext}'
+  soname_spec='${libname}${release}${shared_ext}$major'
+  shlibpath_var=LIBRARY_PATH
+  shlibpath_overrides_runpath=yes
+  sys_lib_dlsearch_path_spec='/boot/home/config/lib /boot/common/lib /boot/system/lib'
+  hardcode_into_libs=yes
+  ;;
+
+hpux9* | hpux10* | hpux11*)
+  # Give a soname corresponding to the major version so that dld.sl refuses to
+  # link against other versions.
+  version_type=sunos
+  need_lib_prefix=no
+  need_version=no
+  case $host_cpu in
+  ia64*)
+    shrext_cmds='.so'
+    hardcode_into_libs=yes
+    dynamic_linker="$host_os dld.so"
+    shlibpath_var=LD_LIBRARY_PATH
+    shlibpath_overrides_runpath=yes # Unless +noenvvar is specified.
+    library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+    soname_spec='${libname}${release}${shared_ext}$major'
+    if test "X$HPUX_IA64_MODE" = X32; then
+      sys_lib_search_path_spec="/usr/lib/hpux32 /usr/local/lib/hpux32 /usr/local/lib"
+    else
+      sys_lib_search_path_spec="/usr/lib/hpux64 /usr/local/lib/hpux64"
+    fi
+    sys_lib_dlsearch_path_spec=$sys_lib_search_path_spec
+    ;;
+  hppa*64*)
+    shrext_cmds='.sl'
+    hardcode_into_libs=yes
+    dynamic_linker="$host_os dld.sl"
+    shlibpath_var=LD_LIBRARY_PATH # How should we handle SHLIB_PATH
+    shlibpath_overrides_runpath=yes # Unless +noenvvar is specified.
+    library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+    soname_spec='${libname}${release}${shared_ext}$major'
+    sys_lib_search_path_spec="/usr/lib/pa20_64 /usr/ccs/lib/pa20_64"
+    sys_lib_dlsearch_path_spec=$sys_lib_search_path_spec
+    ;;
+  *)
+    shrext_cmds='.sl'
+    dynamic_linker="$host_os dld.sl"
+    shlibpath_var=SHLIB_PATH
+    shlibpath_overrides_runpath=no # +s is required to enable SHLIB_PATH
+    library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+    soname_spec='${libname}${release}${shared_ext}$major'
+    ;;
+  esac
+  # HP-UX runs *really* slowly unless shared libraries are mode 555, ...
+  postinstall_cmds='chmod 555 $lib'
+  # or fails outright, so override atomically:
+  install_override_mode=555
+  ;;
+
+interix[3-9]*)
+  version_type=linux # correct to gnu/linux during the next big refactor
+  need_lib_prefix=no
+  need_version=no
+  library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${shared_ext}'
+  soname_spec='${libname}${release}${shared_ext}$major'
+  dynamic_linker='Interix 3.x ld.so.1 (PE, like ELF)'
+  shlibpath_var=LD_LIBRARY_PATH
+  shlibpath_overrides_runpath=no
+  hardcode_into_libs=yes
+  ;;
+
+irix5* | irix6* | nonstopux*)
+  case $host_os in
+    nonstopux*) version_type=nonstopux ;;
+    *)
+	if test "$lt_cv_prog_gnu_ld" = yes; then
+		version_type=linux # correct to gnu/linux during the next big refactor
+	else
+		version_type=irix
+	fi ;;
+  esac
+  need_lib_prefix=no
+  need_version=no
+  soname_spec='${libname}${release}${shared_ext}$major'
+  library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${release}${shared_ext} $libname${shared_ext}'
+  case $host_os in
+  irix5* | nonstopux*)
+    libsuff= shlibsuff=
+    ;;
+  *)
+    case $LD in # libtool.m4 will add one of these switches to LD
+    *-32|*"-32 "|*-melf32bsmip|*"-melf32bsmip ")
+      libsuff= shlibsuff= libmagic=32-bit;;
+    *-n32|*"-n32 "|*-melf32bmipn32|*"-melf32bmipn32 ")
+      libsuff=32 shlibsuff=N32 libmagic=N32;;
+    *-64|*"-64 "|*-melf64bmip|*"-melf64bmip ")
+      libsuff=64 shlibsuff=64 libmagic=64-bit;;
+    *) libsuff= shlibsuff= libmagic=never-match;;
+    esac
+    ;;
+  esac
+  shlibpath_var=LD_LIBRARY${shlibsuff}_PATH
+  shlibpath_overrides_runpath=no
+  sys_lib_search_path_spec="/usr/lib${libsuff} /lib${libsuff} /usr/local/lib${libsuff}"
+  sys_lib_dlsearch_path_spec="/usr/lib${libsuff} /lib${libsuff}"
+  hardcode_into_libs=yes
+  ;;
+
+# No shared lib support for Linux oldld, aout, or coff.
+linux*oldld* | linux*aout* | linux*coff*)
+  dynamic_linker=no
+  ;;
+
+# This must be glibc/ELF.
+linux* | k*bsd*-gnu | kopensolaris*-gnu | gnu*)
+  version_type=linux # correct to gnu/linux during the next big refactor
+  need_lib_prefix=no
+  need_version=no
+  library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+  soname_spec='${libname}${release}${shared_ext}$major'
+  finish_cmds='PATH="\$PATH:/sbin" ldconfig -n $libdir'
+  shlibpath_var=LD_LIBRARY_PATH
+  shlibpath_overrides_runpath=no
+
+  # Some binutils ld are patched to set DT_RUNPATH
+  if ${lt_cv_shlibpath_overrides_runpath+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  lt_cv_shlibpath_overrides_runpath=no
+    save_LDFLAGS=$LDFLAGS
+    save_libdir=$libdir
+    eval "libdir=/foo; wl=\"$lt_prog_compiler_wl\"; \
+	 LDFLAGS=\"\$LDFLAGS $hardcode_libdir_flag_spec\""
+    cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+  if  ($OBJDUMP -p conftest$ac_exeext) 2>/dev/null | grep "RUNPATH.*$libdir" >/dev/null; then :
+  lt_cv_shlibpath_overrides_runpath=yes
+fi
+fi
+rm -f core conftest.err conftest.$ac_objext \
+    conftest$ac_exeext conftest.$ac_ext
+    LDFLAGS=$save_LDFLAGS
+    libdir=$save_libdir
+
+fi
+
+  shlibpath_overrides_runpath=$lt_cv_shlibpath_overrides_runpath
+
+  # This implies no fast_install, which is unacceptable.
+  # Some rework will be needed to allow for fast_install
+  # before this can be enabled.
+  hardcode_into_libs=yes
+
+  # Append ld.so.conf contents to the search path
+  if test -f /etc/ld.so.conf; then
+    lt_ld_extra=`awk '/^include / { system(sprintf("cd /etc; cat %s 2>/dev/null", \$2)); skip = 1; } { if (!skip) print \$0; skip = 0; }' < /etc/ld.so.conf | $SED -e 's/#.*//;/^[	 ]*hwcap[	 ]/d;s/[:,	]/ /g;s/=[^=]*$//;s/=[^= ]* / /g;s/"//g;/^$/d' | tr '\n' ' '`
+    sys_lib_dlsearch_path_spec="/lib /usr/lib $lt_ld_extra"
+  fi
+
+  # We used to test for /lib/ld.so.1 and disable shared libraries on
+  # powerpc, because MkLinux only supported shared libraries with the
+  # GNU dynamic linker.  Since this was broken with cross compilers,
+  # most powerpc-linux boxes support dynamic linking these days and
+  # people can always --disable-shared, the test was removed, and we
+  # assume the GNU/Linux dynamic linker is in use.
+  dynamic_linker='GNU/Linux ld.so'
+  ;;
+
+netbsdelf*-gnu)
+  version_type=linux
+  need_lib_prefix=no
+  need_version=no
+  library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${shared_ext}'
+  soname_spec='${libname}${release}${shared_ext}$major'
+  shlibpath_var=LD_LIBRARY_PATH
+  shlibpath_overrides_runpath=no
+  hardcode_into_libs=yes
+  dynamic_linker='NetBSD ld.elf_so'
+  ;;
+
+netbsd*)
+  version_type=sunos
+  need_lib_prefix=no
+  need_version=no
+  if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then
+    library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${shared_ext}$versuffix'
+    finish_cmds='PATH="\$PATH:/sbin" ldconfig -m $libdir'
+    dynamic_linker='NetBSD (a.out) ld.so'
+  else
+    library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${shared_ext}'
+    soname_spec='${libname}${release}${shared_ext}$major'
+    dynamic_linker='NetBSD ld.elf_so'
+  fi
+  shlibpath_var=LD_LIBRARY_PATH
+  shlibpath_overrides_runpath=yes
+  hardcode_into_libs=yes
+  ;;
+
+newsos6)
+  version_type=linux # correct to gnu/linux during the next big refactor
+  library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+  shlibpath_var=LD_LIBRARY_PATH
+  shlibpath_overrides_runpath=yes
+  ;;
+
+*nto* | *qnx*)
+  version_type=qnx
+  need_lib_prefix=no
+  need_version=no
+  library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+  soname_spec='${libname}${release}${shared_ext}$major'
+  shlibpath_var=LD_LIBRARY_PATH
+  shlibpath_overrides_runpath=no
+  hardcode_into_libs=yes
+  dynamic_linker='ldqnx.so'
+  ;;
+
+openbsd*)
+  version_type=sunos
+  sys_lib_dlsearch_path_spec="/usr/lib"
+  need_lib_prefix=no
+  # Some older versions of OpenBSD (3.3 at least) *do* need versioned libs.
+  case $host_os in
+    openbsd3.3 | openbsd3.3.*)	need_version=yes ;;
+    *)				need_version=no  ;;
+  esac
+  library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${shared_ext}$versuffix'
+  finish_cmds='PATH="\$PATH:/sbin" ldconfig -m $libdir'
+  shlibpath_var=LD_LIBRARY_PATH
+  if test -z "`echo __ELF__ | $CC -E - | $GREP __ELF__`" || test "$host_os-$host_cpu" = "openbsd2.8-powerpc"; then
+    case $host_os in
+      openbsd2.[89] | openbsd2.[89].*)
+	shlibpath_overrides_runpath=no
+	;;
+      *)
+	shlibpath_overrides_runpath=yes
+	;;
+      esac
+  else
+    shlibpath_overrides_runpath=yes
+  fi
+  ;;
+
+os2*)
+  libname_spec='$name'
+  shrext_cmds=".dll"
+  need_lib_prefix=no
+  library_names_spec='$libname${shared_ext} $libname.a'
+  dynamic_linker='OS/2 ld.exe'
+  shlibpath_var=LIBPATH
+  ;;
+
+osf3* | osf4* | osf5*)
+  version_type=osf
+  need_lib_prefix=no
+  need_version=no
+  soname_spec='${libname}${release}${shared_ext}$major'
+  library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+  shlibpath_var=LD_LIBRARY_PATH
+  sys_lib_search_path_spec="/usr/shlib /usr/ccs/lib /usr/lib/cmplrs/cc /usr/lib /usr/local/lib /var/shlib"
+  sys_lib_dlsearch_path_spec="$sys_lib_search_path_spec"
+  ;;
+
+rdos*)
+  dynamic_linker=no
+  ;;
+
+solaris*)
+  version_type=linux # correct to gnu/linux during the next big refactor
+  need_lib_prefix=no
+  need_version=no
+  library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+  soname_spec='${libname}${release}${shared_ext}$major'
+  shlibpath_var=LD_LIBRARY_PATH
+  shlibpath_overrides_runpath=yes
+  hardcode_into_libs=yes
+  # ldd complains unless libraries are executable
+  postinstall_cmds='chmod +x $lib'
+  ;;
+
+sunos4*)
+  version_type=sunos
+  library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${shared_ext}$versuffix'
+  finish_cmds='PATH="\$PATH:/usr/etc" ldconfig $libdir'
+  shlibpath_var=LD_LIBRARY_PATH
+  shlibpath_overrides_runpath=yes
+  if test "$with_gnu_ld" = yes; then
+    need_lib_prefix=no
+  fi
+  need_version=yes
+  ;;
+
+sysv4 | sysv4.3*)
+  version_type=linux # correct to gnu/linux during the next big refactor
+  library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+  soname_spec='${libname}${release}${shared_ext}$major'
+  shlibpath_var=LD_LIBRARY_PATH
+  case $host_vendor in
+    sni)
+      shlibpath_overrides_runpath=no
+      need_lib_prefix=no
+      runpath_var=LD_RUN_PATH
+      ;;
+    siemens)
+      need_lib_prefix=no
+      ;;
+    motorola)
+      need_lib_prefix=no
+      need_version=no
+      shlibpath_overrides_runpath=no
+      sys_lib_search_path_spec='/lib /usr/lib /usr/ccs/lib'
+      ;;
+  esac
+  ;;
+
+sysv4*MP*)
+  if test -d /usr/nec ;then
+    version_type=linux # correct to gnu/linux during the next big refactor
+    library_names_spec='$libname${shared_ext}.$versuffix $libname${shared_ext}.$major $libname${shared_ext}'
+    soname_spec='$libname${shared_ext}.$major'
+    shlibpath_var=LD_LIBRARY_PATH
+  fi
+  ;;
+
+sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX* | sysv4*uw2*)
+  version_type=freebsd-elf
+  need_lib_prefix=no
+  need_version=no
+  library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext} $libname${shared_ext}'
+  soname_spec='${libname}${release}${shared_ext}$major'
+  shlibpath_var=LD_LIBRARY_PATH
+  shlibpath_overrides_runpath=yes
+  hardcode_into_libs=yes
+  if test "$with_gnu_ld" = yes; then
+    sys_lib_search_path_spec='/usr/local/lib /usr/gnu/lib /usr/ccs/lib /usr/lib /lib'
+  else
+    sys_lib_search_path_spec='/usr/ccs/lib /usr/lib'
+    case $host_os in
+      sco3.2v5*)
+        sys_lib_search_path_spec="$sys_lib_search_path_spec /lib"
+	;;
+    esac
+  fi
+  sys_lib_dlsearch_path_spec='/usr/lib'
+  ;;
+
+tpf*)
+  # TPF is a cross-target only.  Preferred cross-host = GNU/Linux.
+  version_type=linux # correct to gnu/linux during the next big refactor
+  need_lib_prefix=no
+  need_version=no
+  library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+  shlibpath_var=LD_LIBRARY_PATH
+  shlibpath_overrides_runpath=no
+  hardcode_into_libs=yes
+  ;;
+
+uts4*)
+  version_type=linux # correct to gnu/linux during the next big refactor
+  library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+  soname_spec='${libname}${release}${shared_ext}$major'
+  shlibpath_var=LD_LIBRARY_PATH
+  ;;
+
+*)
+  dynamic_linker=no
+  ;;
+esac
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $dynamic_linker" >&5
+$as_echo "$dynamic_linker" >&6; }
+test "$dynamic_linker" = no && can_build_shared=no
+
+variables_saved_for_relink="PATH $shlibpath_var $runpath_var"
+if test "$GCC" = yes; then
+  variables_saved_for_relink="$variables_saved_for_relink GCC_EXEC_PREFIX COMPILER_PATH LIBRARY_PATH"
+fi
+
+if test "${lt_cv_sys_lib_search_path_spec+set}" = set; then
+  sys_lib_search_path_spec="$lt_cv_sys_lib_search_path_spec"
+fi
+if test "${lt_cv_sys_lib_dlsearch_path_spec+set}" = set; then
+  sys_lib_dlsearch_path_spec="$lt_cv_sys_lib_dlsearch_path_spec"
+fi
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+  { $as_echo "$as_me:${as_lineno-$LINENO}: checking how to hardcode library paths into programs" >&5
+$as_echo_n "checking how to hardcode library paths into programs... " >&6; }
+hardcode_action=
+if test -n "$hardcode_libdir_flag_spec" ||
+   test -n "$runpath_var" ||
+   test "X$hardcode_automatic" = "Xyes" ; then
+
+  # We can hardcode non-existent directories.
+  if test "$hardcode_direct" != no &&
+     # If the only mechanism to avoid hardcoding is shlibpath_var, we
+     # have to relink, otherwise we might link with an installed library
+     # when we should be linking with a yet-to-be-installed one
+     ## test "$_LT_TAGVAR(hardcode_shlibpath_var, )" != no &&
+     test "$hardcode_minus_L" != no; then
+    # Linking always hardcodes the temporary library directory.
+    hardcode_action=relink
+  else
+    # We can link without hardcoding, and we can hardcode nonexisting dirs.
+    hardcode_action=immediate
+  fi
+else
+  # We cannot hardcode anything, or else we can only hardcode existing
+  # directories.
+  hardcode_action=unsupported
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $hardcode_action" >&5
+$as_echo "$hardcode_action" >&6; }
+
+if test "$hardcode_action" = relink ||
+   test "$inherit_rpath" = yes; then
+  # Fast installation is not supported
+  enable_fast_install=no
+elif test "$shlibpath_overrides_runpath" = yes ||
+     test "$enable_shared" = no; then
+  # Fast installation is not necessary
+  enable_fast_install=needless
+fi
+
+
+
+
+
+
+  if test "x$enable_dlopen" != xyes; then
+  enable_dlopen=unknown
+  enable_dlopen_self=unknown
+  enable_dlopen_self_static=unknown
+else
+  lt_cv_dlopen=no
+  lt_cv_dlopen_libs=
+
+  case $host_os in
+  beos*)
+    lt_cv_dlopen="load_add_on"
+    lt_cv_dlopen_libs=
+    lt_cv_dlopen_self=yes
+    ;;
+
+  mingw* | pw32* | cegcc*)
+    lt_cv_dlopen="LoadLibrary"
+    lt_cv_dlopen_libs=
+    ;;
+
+  cygwin*)
+    lt_cv_dlopen="dlopen"
+    lt_cv_dlopen_libs=
+    ;;
+
+  darwin*)
+  # if libdl is installed we need to link against it
+    { $as_echo "$as_me:${as_lineno-$LINENO}: checking for dlopen in -ldl" >&5
+$as_echo_n "checking for dlopen in -ldl... " >&6; }
+if ${ac_cv_lib_dl_dlopen+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  ac_check_lib_save_LIBS=$LIBS
+LIBS="-ldl  $LIBS"
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+/* Override any GCC internal prototype to avoid an error.
+   Use char because int might match the return type of a GCC
+   builtin and then its argument prototype would still apply.  */
+#ifdef __cplusplus
+extern "C"
+#endif
+char dlopen ();
+int
+main ()
+{
+return dlopen ();
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+  ac_cv_lib_dl_dlopen=yes
+else
+  ac_cv_lib_dl_dlopen=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+    conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_dl_dlopen" >&5
+$as_echo "$ac_cv_lib_dl_dlopen" >&6; }
+if test "x$ac_cv_lib_dl_dlopen" = xyes; then :
+  lt_cv_dlopen="dlopen" lt_cv_dlopen_libs="-ldl"
+else
+
+    lt_cv_dlopen="dyld"
+    lt_cv_dlopen_libs=
+    lt_cv_dlopen_self=yes
+
+fi
+
+    ;;
+
+  *)
+    ac_fn_c_check_func "$LINENO" "shl_load" "ac_cv_func_shl_load"
+if test "x$ac_cv_func_shl_load" = xyes; then :
+  lt_cv_dlopen="shl_load"
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: checking for shl_load in -ldld" >&5
+$as_echo_n "checking for shl_load in -ldld... " >&6; }
+if ${ac_cv_lib_dld_shl_load+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  ac_check_lib_save_LIBS=$LIBS
+LIBS="-ldld  $LIBS"
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+/* Override any GCC internal prototype to avoid an error.
+   Use char because int might match the return type of a GCC
+   builtin and then its argument prototype would still apply.  */
+#ifdef __cplusplus
+extern "C"
+#endif
+char shl_load ();
+int
+main ()
+{
+return shl_load ();
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+  ac_cv_lib_dld_shl_load=yes
+else
+  ac_cv_lib_dld_shl_load=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+    conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_dld_shl_load" >&5
+$as_echo "$ac_cv_lib_dld_shl_load" >&6; }
+if test "x$ac_cv_lib_dld_shl_load" = xyes; then :
+  lt_cv_dlopen="shl_load" lt_cv_dlopen_libs="-ldld"
+else
+  ac_fn_c_check_func "$LINENO" "dlopen" "ac_cv_func_dlopen"
+if test "x$ac_cv_func_dlopen" = xyes; then :
+  lt_cv_dlopen="dlopen"
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: checking for dlopen in -ldl" >&5
+$as_echo_n "checking for dlopen in -ldl... " >&6; }
+if ${ac_cv_lib_dl_dlopen+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  ac_check_lib_save_LIBS=$LIBS
+LIBS="-ldl  $LIBS"
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+/* Override any GCC internal prototype to avoid an error.
+   Use char because int might match the return type of a GCC
+   builtin and then its argument prototype would still apply.  */
+#ifdef __cplusplus
+extern "C"
+#endif
+char dlopen ();
+int
+main ()
+{
+return dlopen ();
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+  ac_cv_lib_dl_dlopen=yes
+else
+  ac_cv_lib_dl_dlopen=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+    conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_dl_dlopen" >&5
+$as_echo "$ac_cv_lib_dl_dlopen" >&6; }
+if test "x$ac_cv_lib_dl_dlopen" = xyes; then :
+  lt_cv_dlopen="dlopen" lt_cv_dlopen_libs="-ldl"
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: checking for dlopen in -lsvld" >&5
+$as_echo_n "checking for dlopen in -lsvld... " >&6; }
+if ${ac_cv_lib_svld_dlopen+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  ac_check_lib_save_LIBS=$LIBS
+LIBS="-lsvld  $LIBS"
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+/* Override any GCC internal prototype to avoid an error.
+   Use char because int might match the return type of a GCC
+   builtin and then its argument prototype would still apply.  */
+#ifdef __cplusplus
+extern "C"
+#endif
+char dlopen ();
+int
+main ()
+{
+return dlopen ();
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+  ac_cv_lib_svld_dlopen=yes
+else
+  ac_cv_lib_svld_dlopen=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+    conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_svld_dlopen" >&5
+$as_echo "$ac_cv_lib_svld_dlopen" >&6; }
+if test "x$ac_cv_lib_svld_dlopen" = xyes; then :
+  lt_cv_dlopen="dlopen" lt_cv_dlopen_libs="-lsvld"
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: checking for dld_link in -ldld" >&5
+$as_echo_n "checking for dld_link in -ldld... " >&6; }
+if ${ac_cv_lib_dld_dld_link+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  ac_check_lib_save_LIBS=$LIBS
+LIBS="-ldld  $LIBS"
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+/* Override any GCC internal prototype to avoid an error.
+   Use char because int might match the return type of a GCC
+   builtin and then its argument prototype would still apply.  */
+#ifdef __cplusplus
+extern "C"
+#endif
+char dld_link ();
+int
+main ()
+{
+return dld_link ();
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+  ac_cv_lib_dld_dld_link=yes
+else
+  ac_cv_lib_dld_dld_link=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+    conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_dld_dld_link" >&5
+$as_echo "$ac_cv_lib_dld_dld_link" >&6; }
+if test "x$ac_cv_lib_dld_dld_link" = xyes; then :
+  lt_cv_dlopen="dld_link" lt_cv_dlopen_libs="-ldld"
+fi
+
+
+fi
+
+
+fi
+
+
+fi
+
+
+fi
+
+
+fi
+
+    ;;
+  esac
+
+  if test "x$lt_cv_dlopen" != xno; then
+    enable_dlopen=yes
+  else
+    enable_dlopen=no
+  fi
+
+  case $lt_cv_dlopen in
+  dlopen)
+    save_CPPFLAGS="$CPPFLAGS"
+    test "x$ac_cv_header_dlfcn_h" = xyes && CPPFLAGS="$CPPFLAGS -DHAVE_DLFCN_H"
+
+    save_LDFLAGS="$LDFLAGS"
+    wl=$lt_prog_compiler_wl eval LDFLAGS=\"\$LDFLAGS $export_dynamic_flag_spec\"
+
+    save_LIBS="$LIBS"
+    LIBS="$lt_cv_dlopen_libs $LIBS"
+
+    { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether a program can dlopen itself" >&5
+$as_echo_n "checking whether a program can dlopen itself... " >&6; }
+if ${lt_cv_dlopen_self+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  	  if test "$cross_compiling" = yes; then :
+  lt_cv_dlopen_self=cross
+else
+  lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2
+  lt_status=$lt_dlunknown
+  cat > conftest.$ac_ext <<_LT_EOF
+#line $LINENO "configure"
+#include "confdefs.h"
+
+#if HAVE_DLFCN_H
+#include <dlfcn.h>
+#endif
+
+#include <stdio.h>
+
+#ifdef RTLD_GLOBAL
+#  define LT_DLGLOBAL		RTLD_GLOBAL
+#else
+#  ifdef DL_GLOBAL
+#    define LT_DLGLOBAL		DL_GLOBAL
+#  else
+#    define LT_DLGLOBAL		0
+#  endif
+#endif
+
+/* We may have to define LT_DLLAZY_OR_NOW in the command line if we
+   find out it does not work in some platform. */
+#ifndef LT_DLLAZY_OR_NOW
+#  ifdef RTLD_LAZY
+#    define LT_DLLAZY_OR_NOW		RTLD_LAZY
+#  else
+#    ifdef DL_LAZY
+#      define LT_DLLAZY_OR_NOW		DL_LAZY
+#    else
+#      ifdef RTLD_NOW
+#        define LT_DLLAZY_OR_NOW	RTLD_NOW
+#      else
+#        ifdef DL_NOW
+#          define LT_DLLAZY_OR_NOW	DL_NOW
+#        else
+#          define LT_DLLAZY_OR_NOW	0
+#        endif
+#      endif
+#    endif
+#  endif
+#endif
+
+/* When -fvisbility=hidden is used, assume the code has been annotated
+   correspondingly for the symbols needed.  */
+#if defined(__GNUC__) && (((__GNUC__ == 3) && (__GNUC_MINOR__ >= 3)) || (__GNUC__ > 3))
+int fnord () __attribute__((visibility("default")));
+#endif
+
+int fnord () { return 42; }
+int main ()
+{
+  void *self = dlopen (0, LT_DLGLOBAL|LT_DLLAZY_OR_NOW);
+  int status = $lt_dlunknown;
+
+  if (self)
+    {
+      if (dlsym (self,"fnord"))       status = $lt_dlno_uscore;
+      else
+        {
+	  if (dlsym( self,"_fnord"))  status = $lt_dlneed_uscore;
+          else puts (dlerror ());
+	}
+      /* dlclose (self); */
+    }
+  else
+    puts (dlerror ());
+
+  return status;
+}
+_LT_EOF
+  if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_link\""; } >&5
+  (eval $ac_link) 2>&5
+  ac_status=$?
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; } && test -s conftest${ac_exeext} 2>/dev/null; then
+    (./conftest; exit; ) >&5 2>/dev/null
+    lt_status=$?
+    case x$lt_status in
+      x$lt_dlno_uscore) lt_cv_dlopen_self=yes ;;
+      x$lt_dlneed_uscore) lt_cv_dlopen_self=yes ;;
+      x$lt_dlunknown|x*) lt_cv_dlopen_self=no ;;
+    esac
+  else :
+    # compilation failed
+    lt_cv_dlopen_self=no
+  fi
+fi
+rm -fr conftest*
+
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_dlopen_self" >&5
+$as_echo "$lt_cv_dlopen_self" >&6; }
+
+    if test "x$lt_cv_dlopen_self" = xyes; then
+      wl=$lt_prog_compiler_wl eval LDFLAGS=\"\$LDFLAGS $lt_prog_compiler_static\"
+      { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether a statically linked program can dlopen itself" >&5
+$as_echo_n "checking whether a statically linked program can dlopen itself... " >&6; }
+if ${lt_cv_dlopen_self_static+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  	  if test "$cross_compiling" = yes; then :
+  lt_cv_dlopen_self_static=cross
+else
+  lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2
+  lt_status=$lt_dlunknown
+  cat > conftest.$ac_ext <<_LT_EOF
+#line $LINENO "configure"
+#include "confdefs.h"
+
+#if HAVE_DLFCN_H
+#include <dlfcn.h>
+#endif
+
+#include <stdio.h>
+
+#ifdef RTLD_GLOBAL
+#  define LT_DLGLOBAL		RTLD_GLOBAL
+#else
+#  ifdef DL_GLOBAL
+#    define LT_DLGLOBAL		DL_GLOBAL
+#  else
+#    define LT_DLGLOBAL		0
+#  endif
+#endif
+
+/* We may have to define LT_DLLAZY_OR_NOW in the command line if we
+   find out it does not work in some platform. */
+#ifndef LT_DLLAZY_OR_NOW
+#  ifdef RTLD_LAZY
+#    define LT_DLLAZY_OR_NOW		RTLD_LAZY
+#  else
+#    ifdef DL_LAZY
+#      define LT_DLLAZY_OR_NOW		DL_LAZY
+#    else
+#      ifdef RTLD_NOW
+#        define LT_DLLAZY_OR_NOW	RTLD_NOW
+#      else
+#        ifdef DL_NOW
+#          define LT_DLLAZY_OR_NOW	DL_NOW
+#        else
+#          define LT_DLLAZY_OR_NOW	0
+#        endif
+#      endif
+#    endif
+#  endif
+#endif
+
+/* When -fvisbility=hidden is used, assume the code has been annotated
+   correspondingly for the symbols needed.  */
+#if defined(__GNUC__) && (((__GNUC__ == 3) && (__GNUC_MINOR__ >= 3)) || (__GNUC__ > 3))
+int fnord () __attribute__((visibility("default")));
+#endif
+
+int fnord () { return 42; }
+int main ()
+{
+  void *self = dlopen (0, LT_DLGLOBAL|LT_DLLAZY_OR_NOW);
+  int status = $lt_dlunknown;
+
+  if (self)
+    {
+      if (dlsym (self,"fnord"))       status = $lt_dlno_uscore;
+      else
+        {
+	  if (dlsym( self,"_fnord"))  status = $lt_dlneed_uscore;
+          else puts (dlerror ());
+	}
+      /* dlclose (self); */
+    }
+  else
+    puts (dlerror ());
+
+  return status;
+}
+_LT_EOF
+  if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_link\""; } >&5
+  (eval $ac_link) 2>&5
+  ac_status=$?
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; } && test -s conftest${ac_exeext} 2>/dev/null; then
+    (./conftest; exit; ) >&5 2>/dev/null
+    lt_status=$?
+    case x$lt_status in
+      x$lt_dlno_uscore) lt_cv_dlopen_self_static=yes ;;
+      x$lt_dlneed_uscore) lt_cv_dlopen_self_static=yes ;;
+      x$lt_dlunknown|x*) lt_cv_dlopen_self_static=no ;;
+    esac
+  else :
+    # compilation failed
+    lt_cv_dlopen_self_static=no
+  fi
+fi
+rm -fr conftest*
+
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_dlopen_self_static" >&5
+$as_echo "$lt_cv_dlopen_self_static" >&6; }
+    fi
+
+    CPPFLAGS="$save_CPPFLAGS"
+    LDFLAGS="$save_LDFLAGS"
+    LIBS="$save_LIBS"
+    ;;
+  esac
+
+  case $lt_cv_dlopen_self in
+  yes|no) enable_dlopen_self=$lt_cv_dlopen_self ;;
+  *) enable_dlopen_self=unknown ;;
+  esac
+
+  case $lt_cv_dlopen_self_static in
+  yes|no) enable_dlopen_self_static=$lt_cv_dlopen_self_static ;;
+  *) enable_dlopen_self_static=unknown ;;
+  esac
+fi
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+striplib=
+old_striplib=
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether stripping libraries is possible" >&5
+$as_echo_n "checking whether stripping libraries is possible... " >&6; }
+if test -n "$STRIP" && $STRIP -V 2>&1 | $GREP "GNU strip" >/dev/null; then
+  test -z "$old_striplib" && old_striplib="$STRIP --strip-debug"
+  test -z "$striplib" && striplib="$STRIP --strip-unneeded"
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+else
+# FIXME - insert some real tests, host_os isn't really good enough
+  case $host_os in
+  darwin*)
+    if test -n "$STRIP" ; then
+      striplib="$STRIP -x"
+      old_striplib="$STRIP -S"
+      { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+    else
+      { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+    fi
+    ;;
+  *)
+    { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+    ;;
+  esac
+fi
+
+
+
+
+
+
+
+
+
+
+
+
+  # Report which library types will actually be built
+  { $as_echo "$as_me:${as_lineno-$LINENO}: checking if libtool supports shared libraries" >&5
+$as_echo_n "checking if libtool supports shared libraries... " >&6; }
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $can_build_shared" >&5
+$as_echo "$can_build_shared" >&6; }
+
+  { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to build shared libraries" >&5
+$as_echo_n "checking whether to build shared libraries... " >&6; }
+  test "$can_build_shared" = "no" && enable_shared=no
+
+  # On AIX, shared libraries and static libraries use the same namespace, and
+  # are all built from PIC.
+  case $host_os in
+  aix3*)
+    test "$enable_shared" = yes && enable_static=no
+    if test -n "$RANLIB"; then
+      archive_cmds="$archive_cmds~\$RANLIB \$lib"
+      postinstall_cmds='$RANLIB $lib'
+    fi
+    ;;
+
+  aix[4-9]*)
+    if test "$host_cpu" != ia64 && test "$aix_use_runtimelinking" = no ; then
+      test "$enable_shared" = yes && enable_static=no
+    fi
+    ;;
+  esac
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $enable_shared" >&5
+$as_echo "$enable_shared" >&6; }
+
+  { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to build static libraries" >&5
+$as_echo_n "checking whether to build static libraries... " >&6; }
+  # Make sure either enable_shared or enable_static is yes.
+  test "$enable_shared" = yes || enable_static=yes
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $enable_static" >&5
+$as_echo "$enable_static" >&6; }
+
+
+
+
+fi
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+
+CC="$lt_save_CC"
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+        ac_config_commands="$ac_config_commands libtool"
+
+
+
+
+# Only expand once:
+
+
+
+
+	misc_cflags=""
+	misc_ldflags=""
+	# Check whether --enable-optimization was given.
+if test "${enable_optimization+set}" = set; then :
+  enableval=$enable_optimization;
+		if (test "${enableval}" = "no"); then
+			misc_cflags="$misc_cflags -O0"
+		fi
+
+fi
+
+	# Check whether --enable-debug was given.
+if test "${enable_debug+set}" = set; then :
+  enableval=$enable_debug;
+		if (test "${enableval}" = "yes" &&
+				test "${ac_cv_prog_cc_g}" = "yes"); then
+			misc_cflags="$misc_cflags -g"
+		fi
+
+fi
+
+	# Check whether --enable-pie was given.
+if test "${enable_pie+set}" = set; then :
+  enableval=$enable_pie;
+		if (test "${enableval}" = "yes" &&
+				test "${ac_cv_prog_cc_pie}" = "yes"); then
+			misc_cflags="$misc_cflags -fPIC"
+			misc_ldflags="$misc_ldflags -pie"
+		fi
+
+fi
+
+	MISC_CFLAGS=$misc_cflags
+
+	MISC_LDFLAGS=$misc_ldflags
+
+
+
+# Check whether --enable-threads was given.
+if test "${enable_threads+set}" = set; then :
+  enableval=$enable_threads; enable_threads=${enableval}
+fi
+
+
+ac_fn_c_check_func "$LINENO" "signalfd" "ac_cv_func_signalfd"
+if test "x$ac_cv_func_signalfd" = xyes; then :
+  dummy=yes
+else
+  as_fn_error $? "signalfd support is required" "$LINENO" 5
+fi
+
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for clock_gettime in -lrt" >&5
+$as_echo_n "checking for clock_gettime in -lrt... " >&6; }
+if ${ac_cv_lib_rt_clock_gettime+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  ac_check_lib_save_LIBS=$LIBS
+LIBS="-lrt  $LIBS"
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+/* Override any GCC internal prototype to avoid an error.
+   Use char because int might match the return type of a GCC
+   builtin and then its argument prototype would still apply.  */
+#ifdef __cplusplus
+extern "C"
+#endif
+char clock_gettime ();
+int
+main ()
+{
+return clock_gettime ();
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+  ac_cv_lib_rt_clock_gettime=yes
+else
+  ac_cv_lib_rt_clock_gettime=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+    conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_rt_clock_gettime" >&5
+$as_echo "$ac_cv_lib_rt_clock_gettime" >&6; }
+if test "x$ac_cv_lib_rt_clock_gettime" = xyes; then :
+  dummy=yes
+else
+  as_fn_error $? "realtime clock support is required" "$LINENO" 5
+fi
+
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for pthread_create in -lpthread" >&5
+$as_echo_n "checking for pthread_create in -lpthread... " >&6; }
+if ${ac_cv_lib_pthread_pthread_create+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  ac_check_lib_save_LIBS=$LIBS
+LIBS="-lpthread  $LIBS"
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+/* Override any GCC internal prototype to avoid an error.
+   Use char because int might match the return type of a GCC
+   builtin and then its argument prototype would still apply.  */
+#ifdef __cplusplus
+extern "C"
+#endif
+char pthread_create ();
+int
+main ()
+{
+return pthread_create ();
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+  ac_cv_lib_pthread_pthread_create=yes
+else
+  ac_cv_lib_pthread_pthread_create=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+    conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_pthread_pthread_create" >&5
+$as_echo "$ac_cv_lib_pthread_pthread_create" >&6; }
+if test "x$ac_cv_lib_pthread_pthread_create" = xyes; then :
+  dummy=yes
+else
+  as_fn_error $? "posix thread support is required" "$LINENO" 5
+fi
+
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for dlopen in -ldl" >&5
+$as_echo_n "checking for dlopen in -ldl... " >&6; }
+if ${ac_cv_lib_dl_dlopen+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  ac_check_lib_save_LIBS=$LIBS
+LIBS="-ldl  $LIBS"
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+/* Override any GCC internal prototype to avoid an error.
+   Use char because int might match the return type of a GCC
+   builtin and then its argument prototype would still apply.  */
+#ifdef __cplusplus
+extern "C"
+#endif
+char dlopen ();
+int
+main ()
+{
+return dlopen ();
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+  ac_cv_lib_dl_dlopen=yes
+else
+  ac_cv_lib_dl_dlopen=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+    conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_dl_dlopen" >&5
+$as_echo "$ac_cv_lib_dl_dlopen" >&6; }
+if test "x$ac_cv_lib_dl_dlopen" = xyes; then :
+  dummy=yes
+else
+  as_fn_error $? "dynamic linking loader is required" "$LINENO" 5
+fi
+
+
+
+pkg_failed=no
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for GLIB" >&5
+$as_echo_n "checking for GLIB... " >&6; }
+
+if test -n "$GLIB_CFLAGS"; then
+    pkg_cv_GLIB_CFLAGS="$GLIB_CFLAGS"
+ elif test -n "$PKG_CONFIG"; then
+    if test -n "$PKG_CONFIG" && \
+    { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"glib-2.0 >= 2.28\""; } >&5
+  ($PKG_CONFIG --exists --print-errors "glib-2.0 >= 2.28") 2>&5
+  ac_status=$?
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; }; then
+  pkg_cv_GLIB_CFLAGS=`$PKG_CONFIG --cflags "glib-2.0 >= 2.28" 2>/dev/null`
+		      test "x$?" != "x0" && pkg_failed=yes
+else
+  pkg_failed=yes
+fi
+ else
+    pkg_failed=untried
+fi
+if test -n "$GLIB_LIBS"; then
+    pkg_cv_GLIB_LIBS="$GLIB_LIBS"
+ elif test -n "$PKG_CONFIG"; then
+    if test -n "$PKG_CONFIG" && \
+    { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"glib-2.0 >= 2.28\""; } >&5
+  ($PKG_CONFIG --exists --print-errors "glib-2.0 >= 2.28") 2>&5
+  ac_status=$?
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; }; then
+  pkg_cv_GLIB_LIBS=`$PKG_CONFIG --libs "glib-2.0 >= 2.28" 2>/dev/null`
+		      test "x$?" != "x0" && pkg_failed=yes
+else
+  pkg_failed=yes
+fi
+ else
+    pkg_failed=untried
+fi
+
+
+
+if test $pkg_failed = yes; then
+   	{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+
+if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then
+        _pkg_short_errors_supported=yes
+else
+        _pkg_short_errors_supported=no
+fi
+        if test $_pkg_short_errors_supported = yes; then
+	        GLIB_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "glib-2.0 >= 2.28" 2>&1`
+        else
+	        GLIB_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "glib-2.0 >= 2.28" 2>&1`
+        fi
+	# Put the nasty error message in config.log where it belongs
+	echo "$GLIB_PKG_ERRORS" >&5
+
+	as_fn_error $? "GLib >= 2.28 is required" "$LINENO" 5
+elif test $pkg_failed = untried; then
+     	{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+	as_fn_error $? "GLib >= 2.28 is required" "$LINENO" 5
+else
+	GLIB_CFLAGS=$pkg_cv_GLIB_CFLAGS
+	GLIB_LIBS=$pkg_cv_GLIB_LIBS
+        { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+	dummy=yes
+fi
+
+
+
+if (test "${enable_threads}" = "yes"); then
+
+$as_echo "#define NEED_THREADS 1" >>confdefs.h
+
+
+pkg_failed=no
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for GTHREAD" >&5
+$as_echo_n "checking for GTHREAD... " >&6; }
+
+if test -n "$GTHREAD_CFLAGS"; then
+    pkg_cv_GTHREAD_CFLAGS="$GTHREAD_CFLAGS"
+ elif test -n "$PKG_CONFIG"; then
+    if test -n "$PKG_CONFIG" && \
+    { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"gthread-2.0 >= 2.16\""; } >&5
+  ($PKG_CONFIG --exists --print-errors "gthread-2.0 >= 2.16") 2>&5
+  ac_status=$?
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; }; then
+  pkg_cv_GTHREAD_CFLAGS=`$PKG_CONFIG --cflags "gthread-2.0 >= 2.16" 2>/dev/null`
+		      test "x$?" != "x0" && pkg_failed=yes
+else
+  pkg_failed=yes
+fi
+ else
+    pkg_failed=untried
+fi
+if test -n "$GTHREAD_LIBS"; then
+    pkg_cv_GTHREAD_LIBS="$GTHREAD_LIBS"
+ elif test -n "$PKG_CONFIG"; then
+    if test -n "$PKG_CONFIG" && \
+    { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"gthread-2.0 >= 2.16\""; } >&5
+  ($PKG_CONFIG --exists --print-errors "gthread-2.0 >= 2.16") 2>&5
+  ac_status=$?
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; }; then
+  pkg_cv_GTHREAD_LIBS=`$PKG_CONFIG --libs "gthread-2.0 >= 2.16" 2>/dev/null`
+		      test "x$?" != "x0" && pkg_failed=yes
+else
+  pkg_failed=yes
+fi
+ else
+    pkg_failed=untried
+fi
+
+
+
+if test $pkg_failed = yes; then
+   	{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+
+if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then
+        _pkg_short_errors_supported=yes
+else
+        _pkg_short_errors_supported=no
+fi
+        if test $_pkg_short_errors_supported = yes; then
+	        GTHREAD_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "gthread-2.0 >= 2.16" 2>&1`
+        else
+	        GTHREAD_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "gthread-2.0 >= 2.16" 2>&1`
+        fi
+	# Put the nasty error message in config.log where it belongs
+	echo "$GTHREAD_PKG_ERRORS" >&5
+
+	as_fn_error $? "GThread >= 2.16 is required" "$LINENO" 5
+elif test $pkg_failed = untried; then
+     	{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+	as_fn_error $? "GThread >= 2.16 is required" "$LINENO" 5
+else
+	GTHREAD_CFLAGS=$pkg_cv_GTHREAD_CFLAGS
+	GTHREAD_LIBS=$pkg_cv_GTHREAD_LIBS
+        { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+	dummy=yes
+fi
+	GLIB_CFLAGS="$GLIB_CFLAGS $GTHREAD_CFLAGS"
+	GLIB_LIBS="$GLIB_LIBS $GTHREAD_LIBS"
+fi
+
+
+pkg_failed=no
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for DBUS" >&5
+$as_echo_n "checking for DBUS... " >&6; }
+
+if test -n "$DBUS_CFLAGS"; then
+    pkg_cv_DBUS_CFLAGS="$DBUS_CFLAGS"
+ elif test -n "$PKG_CONFIG"; then
+    if test -n "$PKG_CONFIG" && \
+    { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"dbus-1 >= 1.6\""; } >&5
+  ($PKG_CONFIG --exists --print-errors "dbus-1 >= 1.6") 2>&5
+  ac_status=$?
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; }; then
+  pkg_cv_DBUS_CFLAGS=`$PKG_CONFIG --cflags "dbus-1 >= 1.6" 2>/dev/null`
+		      test "x$?" != "x0" && pkg_failed=yes
+else
+  pkg_failed=yes
+fi
+ else
+    pkg_failed=untried
+fi
+if test -n "$DBUS_LIBS"; then
+    pkg_cv_DBUS_LIBS="$DBUS_LIBS"
+ elif test -n "$PKG_CONFIG"; then
+    if test -n "$PKG_CONFIG" && \
+    { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"dbus-1 >= 1.6\""; } >&5
+  ($PKG_CONFIG --exists --print-errors "dbus-1 >= 1.6") 2>&5
+  ac_status=$?
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; }; then
+  pkg_cv_DBUS_LIBS=`$PKG_CONFIG --libs "dbus-1 >= 1.6" 2>/dev/null`
+		      test "x$?" != "x0" && pkg_failed=yes
+else
+  pkg_failed=yes
+fi
+ else
+    pkg_failed=untried
+fi
+
+
+
+if test $pkg_failed = yes; then
+   	{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+
+if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then
+        _pkg_short_errors_supported=yes
+else
+        _pkg_short_errors_supported=no
+fi
+        if test $_pkg_short_errors_supported = yes; then
+	        DBUS_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "dbus-1 >= 1.6" 2>&1`
+        else
+	        DBUS_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "dbus-1 >= 1.6" 2>&1`
+        fi
+	# Put the nasty error message in config.log where it belongs
+	echo "$DBUS_PKG_ERRORS" >&5
+
+	as_fn_error $? "D-Bus >= 1.6 is required" "$LINENO" 5
+elif test $pkg_failed = untried; then
+     	{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+	as_fn_error $? "D-Bus >= 1.6 is required" "$LINENO" 5
+else
+	DBUS_CFLAGS=$pkg_cv_DBUS_CFLAGS
+	DBUS_LIBS=$pkg_cv_DBUS_LIBS
+        { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+	dummy=yes
+fi
+
+
+
+
+# Check whether --with-dbusconfdir was given.
+if test "${with_dbusconfdir+set}" = set; then :
+  withval=$with_dbusconfdir; path_dbusconfdir=${withval}
+fi
+
+if (test -z "${path_dbusconfdir}"); then
+	{ $as_echo "$as_me:${as_lineno-$LINENO}: checking D-Bus configuration directory" >&5
+$as_echo_n "checking D-Bus configuration directory... " >&6; }
+	path_dbusconfdir="`$PKG_CONFIG --variable=sysconfdir dbus-1`"
+	if (test -z "${path_dbusconfdir}"); then
+		as_fn_error $? "D-Bus configuration directory is required" "$LINENO" 5
+	fi
+	{ $as_echo "$as_me:${as_lineno-$LINENO}: result: ${path_dbusconfdir}" >&5
+$as_echo "${path_dbusconfdir}" >&6; }
+fi
+DBUS_CONFDIR=${path_dbusconfdir}
+
+
+
+# Check whether --with-dbussystembusdir was given.
+if test "${with_dbussystembusdir+set}" = set; then :
+  withval=$with_dbussystembusdir; path_dbussystembusdir=${withval}
+fi
+
+if (test -z "${path_dbussystembusdir}"); then
+	{ $as_echo "$as_me:${as_lineno-$LINENO}: checking D-Bus system bus services dir" >&5
+$as_echo_n "checking D-Bus system bus services dir... " >&6; }
+	path_dbussystembusdir="`$PKG_CONFIG --variable=system_bus_services_dir dbus-1`"
+	if (test -z "${path_dbussystembusdir}"); then
+		as_fn_error $? "D-Bus system bus services directory is required" "$LINENO" 5
+	fi
+	{ $as_echo "$as_me:${as_lineno-$LINENO}: result: ${path_dbussystembusdir}" >&5
+$as_echo "${path_dbussystembusdir}" >&6; }
+fi
+DBUS_SYSTEMBUSDIR=${path_dbussystembusdir}
+
+
+
+# Check whether --with-dbussessionbusdir was given.
+if test "${with_dbussessionbusdir+set}" = set; then :
+  withval=$with_dbussessionbusdir; path_dbussessionbusdir=${withval}
+fi
+
+if (test -z "${path_dbussessionbusdir}"); then
+	{ $as_echo "$as_me:${as_lineno-$LINENO}: checking D-Bus session bus services dir" >&5
+$as_echo_n "checking D-Bus session bus services dir... " >&6; }
+	path_dbussessionbusdir="`$PKG_CONFIG --variable=session_bus_services_dir dbus-1`"
+	if (test -z "${path_dbussessionbusdir}"); then
+		as_fn_error $? "D-Bus session bus services directory is required" "$LINENO" 5
+	fi
+	{ $as_echo "$as_me:${as_lineno-$LINENO}: result: ${path_dbussessionbusdir}" >&5
+$as_echo "${path_dbussessionbusdir}" >&6; }
+fi
+DBUS_SESSIONBUSDIR=${path_dbussessionbusdir}
+
+
+# Check whether --enable-library was given.
+if test "${enable_library+set}" = set; then :
+  enableval=$enable_library; enable_library=${enableval}
+fi
+
+ if test "${enable_library}" = "yes"; then
+  LIBRARY_TRUE=
+  LIBRARY_FALSE='#'
+else
+  LIBRARY_TRUE='#'
+  LIBRARY_FALSE=
+fi
+
+
+# Check whether --enable-test was given.
+if test "${enable_test+set}" = set; then :
+  enableval=$enable_test; enable_test=${enableval}
+fi
+
+ if test "${enable_test}" = "yes"; then
+  TEST_TRUE=
+  TEST_FALSE='#'
+else
+  TEST_TRUE='#'
+  TEST_FALSE=
+fi
+
+
+# Check whether --enable-tools was given.
+if test "${enable_tools+set}" = set; then :
+  enableval=$enable_tools; enable_tools=${enableval}
+fi
+
+ if test "${enable_tools}" != "no"; then
+  TOOLS_TRUE=
+  TOOLS_FALSE='#'
+else
+  TOOLS_TRUE='#'
+  TOOLS_FALSE=
+fi
+
+
+# Check whether --enable-monitor was given.
+if test "${enable_monitor+set}" = set; then :
+  enableval=$enable_monitor; enable_monitor=${enableval}
+fi
+
+ if test "${enable_monitor}" != "no"; then
+  MONITOR_TRUE=
+  MONITOR_FALSE='#'
+else
+  MONITOR_TRUE='#'
+  MONITOR_FALSE=
+fi
+
+
+# Check whether --enable-udev was given.
+if test "${enable_udev+set}" = set; then :
+  enableval=$enable_udev; enable_udev=${enableval}
+fi
+
+if (test "${enable_tools}" != "no" && test "${enable_udev}" != "no"); then
+
+pkg_failed=no
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for UDEV" >&5
+$as_echo_n "checking for UDEV... " >&6; }
+
+if test -n "$UDEV_CFLAGS"; then
+    pkg_cv_UDEV_CFLAGS="$UDEV_CFLAGS"
+ elif test -n "$PKG_CONFIG"; then
+    if test -n "$PKG_CONFIG" && \
+    { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"libudev >= 143\""; } >&5
+  ($PKG_CONFIG --exists --print-errors "libudev >= 143") 2>&5
+  ac_status=$?
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; }; then
+  pkg_cv_UDEV_CFLAGS=`$PKG_CONFIG --cflags "libudev >= 143" 2>/dev/null`
+		      test "x$?" != "x0" && pkg_failed=yes
+else
+  pkg_failed=yes
+fi
+ else
+    pkg_failed=untried
+fi
+if test -n "$UDEV_LIBS"; then
+    pkg_cv_UDEV_LIBS="$UDEV_LIBS"
+ elif test -n "$PKG_CONFIG"; then
+    if test -n "$PKG_CONFIG" && \
+    { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"libudev >= 143\""; } >&5
+  ($PKG_CONFIG --exists --print-errors "libudev >= 143") 2>&5
+  ac_status=$?
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; }; then
+  pkg_cv_UDEV_LIBS=`$PKG_CONFIG --libs "libudev >= 143" 2>/dev/null`
+		      test "x$?" != "x0" && pkg_failed=yes
+else
+  pkg_failed=yes
+fi
+ else
+    pkg_failed=untried
+fi
+
+
+
+if test $pkg_failed = yes; then
+   	{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+
+if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then
+        _pkg_short_errors_supported=yes
+else
+        _pkg_short_errors_supported=no
+fi
+        if test $_pkg_short_errors_supported = yes; then
+	        UDEV_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "libudev >= 143" 2>&1`
+        else
+	        UDEV_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "libudev >= 143" 2>&1`
+        fi
+	# Put the nasty error message in config.log where it belongs
+	echo "$UDEV_PKG_ERRORS" >&5
+
+	as_fn_error $? "libudev >= 143 is required" "$LINENO" 5
+elif test $pkg_failed = untried; then
+     	{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+	as_fn_error $? "libudev >= 143 is required" "$LINENO" 5
+else
+	UDEV_CFLAGS=$pkg_cv_UDEV_CFLAGS
+	UDEV_LIBS=$pkg_cv_UDEV_LIBS
+        { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+	dummy=yes
+fi
+
+
+	{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for udev_hwdb_new in -ludev" >&5
+$as_echo_n "checking for udev_hwdb_new in -ludev... " >&6; }
+if ${ac_cv_lib_udev_udev_hwdb_new+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  ac_check_lib_save_LIBS=$LIBS
+LIBS="-ludev  $LIBS"
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+/* Override any GCC internal prototype to avoid an error.
+   Use char because int might match the return type of a GCC
+   builtin and then its argument prototype would still apply.  */
+#ifdef __cplusplus
+extern "C"
+#endif
+char udev_hwdb_new ();
+int
+main ()
+{
+return udev_hwdb_new ();
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+  ac_cv_lib_udev_udev_hwdb_new=yes
+else
+  ac_cv_lib_udev_udev_hwdb_new=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+    conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_udev_udev_hwdb_new" >&5
+$as_echo "$ac_cv_lib_udev_udev_hwdb_new" >&6; }
+if test "x$ac_cv_lib_udev_udev_hwdb_new" = xyes; then :
+
+$as_echo "#define HAVE_UDEV_HWDB_NEW 1" >>confdefs.h
+
+fi
+
+fi
+ if test "${enable_udev}" != "no"; then
+  UDEV_TRUE=
+  UDEV_FALSE='#'
+else
+  UDEV_TRUE='#'
+  UDEV_FALSE=
+fi
+
+
+
+# Check whether --with-udevdir was given.
+if test "${with_udevdir+set}" = set; then :
+  withval=$with_udevdir; path_udevdir=${withval}
+fi
+
+if (test "${enable_udev}" != "no" && test -z "${path_udevdir}"); then
+	{ $as_echo "$as_me:${as_lineno-$LINENO}: checking udev directory" >&5
+$as_echo_n "checking udev directory... " >&6; }
+	path_udevdir="`$PKG_CONFIG --variable=udevdir udev`"
+	if (test -z "${path_udevdir}"); then
+		as_fn_error $? "udev directory is required" "$LINENO" 5
+	fi
+	{ $as_echo "$as_me:${as_lineno-$LINENO}: result: ${path_udevdir}" >&5
+$as_echo "${path_udevdir}" >&6; }
+fi
+UDEV_DIR=${path_udevdir}
+
+
+ if test "${enable_tools}" != "no" &&
+						test "${enable_udev}" != "no"; then
+  HID2HCI_TRUE=
+  HID2HCI_FALSE='#'
+else
+  HID2HCI_TRUE='#'
+  HID2HCI_FALSE=
+fi
+
+
+# Check whether --enable-cups was given.
+if test "${enable_cups+set}" = set; then :
+  enableval=$enable_cups; enable_cups=${enableval}
+fi
+
+ if test "${enable_cups}" != "no"; then
+  CUPS_TRUE=
+  CUPS_FALSE='#'
+else
+  CUPS_TRUE='#'
+  CUPS_FALSE=
+fi
+
+
+# Check whether --enable-obex was given.
+if test "${enable_obex+set}" = set; then :
+  enableval=$enable_obex; enable_obex=${enableval}
+fi
+
+if (test "${enable_obex}" != "no"); then
+
+pkg_failed=no
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for ICAL" >&5
+$as_echo_n "checking for ICAL... " >&6; }
+
+if test -n "$ICAL_CFLAGS"; then
+    pkg_cv_ICAL_CFLAGS="$ICAL_CFLAGS"
+ elif test -n "$PKG_CONFIG"; then
+    if test -n "$PKG_CONFIG" && \
+    { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"libical\""; } >&5
+  ($PKG_CONFIG --exists --print-errors "libical") 2>&5
+  ac_status=$?
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; }; then
+  pkg_cv_ICAL_CFLAGS=`$PKG_CONFIG --cflags "libical" 2>/dev/null`
+		      test "x$?" != "x0" && pkg_failed=yes
+else
+  pkg_failed=yes
+fi
+ else
+    pkg_failed=untried
+fi
+if test -n "$ICAL_LIBS"; then
+    pkg_cv_ICAL_LIBS="$ICAL_LIBS"
+ elif test -n "$PKG_CONFIG"; then
+    if test -n "$PKG_CONFIG" && \
+    { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"libical\""; } >&5
+  ($PKG_CONFIG --exists --print-errors "libical") 2>&5
+  ac_status=$?
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; }; then
+  pkg_cv_ICAL_LIBS=`$PKG_CONFIG --libs "libical" 2>/dev/null`
+		      test "x$?" != "x0" && pkg_failed=yes
+else
+  pkg_failed=yes
+fi
+ else
+    pkg_failed=untried
+fi
+
+
+
+if test $pkg_failed = yes; then
+   	{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+
+if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then
+        _pkg_short_errors_supported=yes
+else
+        _pkg_short_errors_supported=no
+fi
+        if test $_pkg_short_errors_supported = yes; then
+	        ICAL_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "libical" 2>&1`
+        else
+	        ICAL_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "libical" 2>&1`
+        fi
+	# Put the nasty error message in config.log where it belongs
+	echo "$ICAL_PKG_ERRORS" >&5
+
+	as_fn_error $? "libical is required" "$LINENO" 5
+elif test $pkg_failed = untried; then
+     	{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+	as_fn_error $? "libical is required" "$LINENO" 5
+else
+	ICAL_CFLAGS=$pkg_cv_ICAL_CFLAGS
+	ICAL_LIBS=$pkg_cv_ICAL_LIBS
+        { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+	dummy=yes
+fi
+
+
+fi
+ if test "${enable_obex}" != "no"; then
+  OBEX_TRUE=
+  OBEX_FALSE='#'
+else
+  OBEX_TRUE='#'
+  OBEX_FALSE=
+fi
+
+
+# Check whether --enable-client was given.
+if test "${enable_client+set}" = set; then :
+  enableval=$enable_client; enable_client=${enableval}
+fi
+
+ if test "${enable_client}" != "no"; then
+  CLIENT_TRUE=
+  CLIENT_FALSE='#'
+else
+  CLIENT_TRUE='#'
+  CLIENT_FALSE=
+fi
+
+
+if (test "${enable_client}" != "no"); then
+        for ac_header in readline/readline.h
+do :
+  ac_fn_c_check_header_mongrel "$LINENO" "readline/readline.h" "ac_cv_header_readline_readline_h" "$ac_includes_default"
+if test "x$ac_cv_header_readline_readline_h" = xyes; then :
+  cat >>confdefs.h <<_ACEOF
+#define HAVE_READLINE_READLINE_H 1
+_ACEOF
+ enable_readline=yes
+else
+  as_fn_error $? "readline header files are required" "$LINENO" 5
+fi
+
+done
+
+fi
+ if test "${enable_readline}" = "yes"; then
+  READLINE_TRUE=
+  READLINE_FALSE='#'
+else
+  READLINE_TRUE='#'
+  READLINE_FALSE=
+fi
+
+
+# Check whether --enable-systemd was given.
+if test "${enable_systemd+set}" = set; then :
+  enableval=$enable_systemd; enable_systemd=${enableval}
+fi
+
+ if test "${enable_systemd}" != "no"; then
+  SYSTEMD_TRUE=
+  SYSTEMD_FALSE='#'
+else
+  SYSTEMD_TRUE='#'
+  SYSTEMD_FALSE=
+fi
+
+
+
+# Check whether --with-systemdsystemunitdir was given.
+if test "${with_systemdsystemunitdir+set}" = set; then :
+  withval=$with_systemdsystemunitdir; path_systemunitdir=${withval}
+fi
+
+if (test "${enable_systemd}" != "no" && test -z "${path_systemunitdir}"); then
+	{ $as_echo "$as_me:${as_lineno-$LINENO}: checking systemd system unit dir" >&5
+$as_echo_n "checking systemd system unit dir... " >&6; }
+	path_systemunitdir="`$PKG_CONFIG --variable=systemdsystemunitdir systemd`"
+	if (test -z "${path_systemunitdir}"); then
+		as_fn_error $? "systemd system unit directory is required" "$LINENO" 5
+	fi
+	{ $as_echo "$as_me:${as_lineno-$LINENO}: result: ${path_systemunitdir}" >&5
+$as_echo "${path_systemunitdir}" >&6; }
+fi
+SYSTEMD_SYSTEMUNITDIR=${path_systemunitdir}
+
+
+
+# Check whether --with-systemduserunitdir was given.
+if test "${with_systemduserunitdir+set}" = set; then :
+  withval=$with_systemduserunitdir; path_userunitdir=${withval}
+fi
+
+if (test "${enable_systemd}" != "no" && test -z "${path_userunitdir}"); then
+	{ $as_echo "$as_me:${as_lineno-$LINENO}: checking systemd user unit dir" >&5
+$as_echo_n "checking systemd user unit dir... " >&6; }
+	path_userunitdir="`$PKG_CONFIG --variable=systemduserunitdir systemd`"
+	if (test -z "${path_userunitdir}"); then
+		as_fn_error $? "systemd user unit directory is required" "$LINENO" 5
+	fi
+	{ $as_echo "$as_me:${as_lineno-$LINENO}: result: ${path_userunitdir}" >&5
+$as_echo "${path_userunitdir}" >&6; }
+fi
+SYSTEMD_USERUNITDIR=${path_userunitdir}
+
+
+# Check whether --enable-datafiles was given.
+if test "${enable_datafiles+set}" = set; then :
+  enableval=$enable_datafiles; enable_datafiles=${enableval}
+fi
+
+ if test "${enable_datafiles}" != "no"; then
+  DATAFILES_TRUE=
+  DATAFILES_FALSE='#'
+else
+  DATAFILES_TRUE='#'
+  DATAFILES_FALSE=
+fi
+
+
+# Check whether --enable-experimental was given.
+if test "${enable_experimental+set}" = set; then :
+  enableval=$enable_experimental; enable_experimental=${enableval}
+fi
+
+ if test "${enable_experimental}" = "yes"; then
+  EXPERIMENTAL_TRUE=
+  EXPERIMENTAL_FALSE='#'
+else
+  EXPERIMENTAL_TRUE='#'
+  EXPERIMENTAL_FALSE=
+fi
+
+
+# Check whether --enable-sixaxis was given.
+if test "${enable_sixaxis+set}" = set; then :
+  enableval=$enable_sixaxis; enable_sixaxis=${enableval}
+fi
+
+ if test "${enable_sixaxis}" = "yes" &&
+					 test "${enable_udev}" != "no"; then
+  SIXAXIS_TRUE=
+  SIXAXIS_FALSE='#'
+else
+  SIXAXIS_TRUE='#'
+  SIXAXIS_FALSE=
+fi
+
+
+if (test "${prefix}" = "NONE"); then
+		if (test "$localstatedir" = '${prefix}/var'); then
+		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
+
+cat >>confdefs.h <<_ACEOF
+#define STORAGEDIR "${storagedir}"
+_ACEOF
+
+
+if (test "$sysconfdir" = '${prefix}/etc'); then
+	configdir="${prefix}/etc/bluetooth"
+else
+	configdir="${sysconfdir}/bluetooth"
+fi
+
+cat >>confdefs.h <<_ACEOF
+#define CONFIGDIR "${configdir}"
+_ACEOF
+
+CONFIGDIR="${configdir}"
+
+
+# Check whether --enable-android was given.
+if test "${enable_android+set}" = set; then :
+  enableval=$enable_android; enable_android=${enableval}
+fi
+
+ if test "${enable_android}" = "yes"; then
+  ANDROID_TRUE=
+  ANDROID_FALSE='#'
+else
+  ANDROID_TRUE='#'
+  ANDROID_FALSE=
+fi
+
+
+if (test "${enable_android}" = "yes"); then
+
+pkg_failed=no
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for SBC" >&5
+$as_echo_n "checking for SBC... " >&6; }
+
+if test -n "$SBC_CFLAGS"; then
+    pkg_cv_SBC_CFLAGS="$SBC_CFLAGS"
+ elif test -n "$PKG_CONFIG"; then
+    if test -n "$PKG_CONFIG" && \
+    { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"sbc >= 1.2\""; } >&5
+  ($PKG_CONFIG --exists --print-errors "sbc >= 1.2") 2>&5
+  ac_status=$?
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; }; then
+  pkg_cv_SBC_CFLAGS=`$PKG_CONFIG --cflags "sbc >= 1.2" 2>/dev/null`
+		      test "x$?" != "x0" && pkg_failed=yes
+else
+  pkg_failed=yes
+fi
+ else
+    pkg_failed=untried
+fi
+if test -n "$SBC_LIBS"; then
+    pkg_cv_SBC_LIBS="$SBC_LIBS"
+ elif test -n "$PKG_CONFIG"; then
+    if test -n "$PKG_CONFIG" && \
+    { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"sbc >= 1.2\""; } >&5
+  ($PKG_CONFIG --exists --print-errors "sbc >= 1.2") 2>&5
+  ac_status=$?
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; }; then
+  pkg_cv_SBC_LIBS=`$PKG_CONFIG --libs "sbc >= 1.2" 2>/dev/null`
+		      test "x$?" != "x0" && pkg_failed=yes
+else
+  pkg_failed=yes
+fi
+ else
+    pkg_failed=untried
+fi
+
+
+
+if test $pkg_failed = yes; then
+   	{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+
+if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then
+        _pkg_short_errors_supported=yes
+else
+        _pkg_short_errors_supported=no
+fi
+        if test $_pkg_short_errors_supported = yes; then
+	        SBC_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "sbc >= 1.2" 2>&1`
+        else
+	        SBC_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "sbc >= 1.2" 2>&1`
+        fi
+	# Put the nasty error message in config.log where it belongs
+	echo "$SBC_PKG_ERRORS" >&5
+
+	as_fn_error $? "SBC library >= 1.2 is required" "$LINENO" 5
+elif test $pkg_failed = untried; then
+     	{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+	as_fn_error $? "SBC library >= 1.2 is required" "$LINENO" 5
+else
+	SBC_CFLAGS=$pkg_cv_SBC_CFLAGS
+	SBC_LIBS=$pkg_cv_SBC_LIBS
+        { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+	dummy=yes
+fi
+
+
+fi
+
+
+cat >>confdefs.h <<_ACEOF
+#define ANDROID_STORAGEDIR "${storagedir}/android"
+_ACEOF
+
+
+ac_config_files="$ac_config_files Makefile src/bluetoothd.8 lib/bluez.pc"
+
+cat >confcache <<\_ACEOF
+# This file is a shell script that caches the results of configure
+# tests run on this system so they can be shared between configure
+# scripts and configure runs, see configure's option --config-cache.
+# It is not useful on other systems.  If it contains results you don't
+# want to keep, you may remove or edit it.
+#
+# config.status only pays attention to the cache file if you give it
+# the --recheck option to rerun configure.
+#
+# `ac_cv_env_foo' variables (set or unset) will be overridden when
+# loading this file, other *unset* `ac_cv_foo' will be assigned the
+# following values.
+
+_ACEOF
+
+# The following way of writing the cache mishandles newlines in values,
+# but we know of no workaround that is simple, portable, and efficient.
+# So, we kill variables containing newlines.
+# Ultrix sh set writes to stderr and can't be redirected directly,
+# and sets the high bit in the cache file unless we assign to the vars.
+(
+  for ac_var in `(set) 2>&1 | sed -n 's/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'`; do
+    eval ac_val=\$$ac_var
+    case $ac_val in #(
+    *${as_nl}*)
+      case $ac_var in #(
+      *_cv_*) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: cache variable $ac_var contains a newline" >&5
+$as_echo "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;;
+      esac
+      case $ac_var in #(
+      _ | IFS | as_nl) ;; #(
+      BASH_ARGV | BASH_SOURCE) eval $ac_var= ;; #(
+      *) { eval $ac_var=; unset $ac_var;} ;;
+      esac ;;
+    esac
+  done
+
+  (set) 2>&1 |
+    case $as_nl`(ac_space=' '; set) 2>&1` in #(
+    *${as_nl}ac_space=\ *)
+      # `set' does not quote correctly, so add quotes: double-quote
+      # substitution turns \\\\ into \\, and sed turns \\ into \.
+      sed -n \
+	"s/'/'\\\\''/g;
+	  s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\\2'/p"
+      ;; #(
+    *)
+      # `set' quotes correctly as required by POSIX, so do not add quotes.
+      sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p"
+      ;;
+    esac |
+    sort
+) |
+  sed '
+     /^ac_cv_env_/b end
+     t clear
+     :clear
+     s/^\([^=]*\)=\(.*[{}].*\)$/test "${\1+set}" = set || &/
+     t end
+     s/^\([^=]*\)=\(.*\)$/\1=${\1=\2}/
+     :end' >>confcache
+if diff "$cache_file" confcache >/dev/null 2>&1; then :; else
+  if test -w "$cache_file"; then
+    if test "x$cache_file" != "x/dev/null"; then
+      { $as_echo "$as_me:${as_lineno-$LINENO}: updating cache $cache_file" >&5
+$as_echo "$as_me: updating cache $cache_file" >&6;}
+      if test ! -f "$cache_file" || test -h "$cache_file"; then
+	cat confcache >"$cache_file"
+      else
+        case $cache_file in #(
+        */* | ?:*)
+	  mv -f confcache "$cache_file"$$ &&
+	  mv -f "$cache_file"$$ "$cache_file" ;; #(
+        *)
+	  mv -f confcache "$cache_file" ;;
+	esac
+      fi
+    fi
+  else
+    { $as_echo "$as_me:${as_lineno-$LINENO}: not updating unwritable cache $cache_file" >&5
+$as_echo "$as_me: not updating unwritable cache $cache_file" >&6;}
+  fi
+fi
+rm -f confcache
+
+test "x$prefix" = xNONE && prefix=$ac_default_prefix
+# Let make expand exec_prefix.
+test "x$exec_prefix" = xNONE && exec_prefix='${prefix}'
+
+DEFS=-DHAVE_CONFIG_H
+
+ac_libobjs=
+ac_ltlibobjs=
+U=
+for ac_i in : $LIBOBJS; do test "x$ac_i" = x: && continue
+  # 1. Remove the extension, and $U if already installed.
+  ac_script='s/\$U\././;s/\.o$//;s/\.obj$//'
+  ac_i=`$as_echo "$ac_i" | sed "$ac_script"`
+  # 2. Prepend LIBOBJDIR.  When used with automake>=1.10 LIBOBJDIR
+  #    will be set to the directory where LIBOBJS objects are built.
+  as_fn_append ac_libobjs " \${LIBOBJDIR}$ac_i\$U.$ac_objext"
+  as_fn_append ac_ltlibobjs " \${LIBOBJDIR}$ac_i"'$U.lo'
+done
+LIBOBJS=$ac_libobjs
+
+LTLIBOBJS=$ac_ltlibobjs
+
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking that generated files are newer than configure" >&5
+$as_echo_n "checking that generated files are newer than configure... " >&6; }
+   if test -n "$am_sleep_pid"; then
+     # Hide warnings about reused PIDs.
+     wait $am_sleep_pid 2>/dev/null
+   fi
+   { $as_echo "$as_me:${as_lineno-$LINENO}: result: done" >&5
+$as_echo "done" >&6; }
+ if test -n "$EXEEXT"; then
+  am__EXEEXT_TRUE=
+  am__EXEEXT_FALSE='#'
+else
+  am__EXEEXT_TRUE='#'
+  am__EXEEXT_FALSE=
+fi
+
+if test -z "${AMDEP_TRUE}" && test -z "${AMDEP_FALSE}"; then
+  as_fn_error $? "conditional \"AMDEP\" was never defined.
+Usually this means the macro was only invoked conditionally." "$LINENO" 5
+fi
+if test -z "${am__fastdepCC_TRUE}" && test -z "${am__fastdepCC_FALSE}"; then
+  as_fn_error $? "conditional \"am__fastdepCC\" was never defined.
+Usually this means the macro was only invoked conditionally." "$LINENO" 5
+fi
+if test -z "${MAINTAINER_MODE_TRUE}" && test -z "${MAINTAINER_MODE_FALSE}"; then
+  as_fn_error $? "conditional \"MAINTAINER_MODE\" was never defined.
+Usually this means the macro was only invoked conditionally." "$LINENO" 5
+fi
+if test -z "${am__fastdepCC_TRUE}" && test -z "${am__fastdepCC_FALSE}"; then
+  as_fn_error $? "conditional \"am__fastdepCC\" was never defined.
+Usually this means the macro was only invoked conditionally." "$LINENO" 5
+fi
+if test -z "${LIBRARY_TRUE}" && test -z "${LIBRARY_FALSE}"; then
+  as_fn_error $? "conditional \"LIBRARY\" was never defined.
+Usually this means the macro was only invoked conditionally." "$LINENO" 5
+fi
+if test -z "${TEST_TRUE}" && test -z "${TEST_FALSE}"; then
+  as_fn_error $? "conditional \"TEST\" was never defined.
+Usually this means the macro was only invoked conditionally." "$LINENO" 5
+fi
+if test -z "${TOOLS_TRUE}" && test -z "${TOOLS_FALSE}"; then
+  as_fn_error $? "conditional \"TOOLS\" was never defined.
+Usually this means the macro was only invoked conditionally." "$LINENO" 5
+fi
+if test -z "${MONITOR_TRUE}" && test -z "${MONITOR_FALSE}"; then
+  as_fn_error $? "conditional \"MONITOR\" was never defined.
+Usually this means the macro was only invoked conditionally." "$LINENO" 5
+fi
+if test -z "${UDEV_TRUE}" && test -z "${UDEV_FALSE}"; then
+  as_fn_error $? "conditional \"UDEV\" was never defined.
+Usually this means the macro was only invoked conditionally." "$LINENO" 5
+fi
+if test -z "${HID2HCI_TRUE}" && test -z "${HID2HCI_FALSE}"; then
+  as_fn_error $? "conditional \"HID2HCI\" was never defined.
+Usually this means the macro was only invoked conditionally." "$LINENO" 5
+fi
+if test -z "${CUPS_TRUE}" && test -z "${CUPS_FALSE}"; then
+  as_fn_error $? "conditional \"CUPS\" was never defined.
+Usually this means the macro was only invoked conditionally." "$LINENO" 5
+fi
+if test -z "${OBEX_TRUE}" && test -z "${OBEX_FALSE}"; then
+  as_fn_error $? "conditional \"OBEX\" was never defined.
+Usually this means the macro was only invoked conditionally." "$LINENO" 5
+fi
+if test -z "${CLIENT_TRUE}" && test -z "${CLIENT_FALSE}"; then
+  as_fn_error $? "conditional \"CLIENT\" was never defined.
+Usually this means the macro was only invoked conditionally." "$LINENO" 5
+fi
+if test -z "${READLINE_TRUE}" && test -z "${READLINE_FALSE}"; then
+  as_fn_error $? "conditional \"READLINE\" was never defined.
+Usually this means the macro was only invoked conditionally." "$LINENO" 5
+fi
+if test -z "${SYSTEMD_TRUE}" && test -z "${SYSTEMD_FALSE}"; then
+  as_fn_error $? "conditional \"SYSTEMD\" was never defined.
+Usually this means the macro was only invoked conditionally." "$LINENO" 5
+fi
+if test -z "${DATAFILES_TRUE}" && test -z "${DATAFILES_FALSE}"; then
+  as_fn_error $? "conditional \"DATAFILES\" was never defined.
+Usually this means the macro was only invoked conditionally." "$LINENO" 5
+fi
+if test -z "${EXPERIMENTAL_TRUE}" && test -z "${EXPERIMENTAL_FALSE}"; then
+  as_fn_error $? "conditional \"EXPERIMENTAL\" was never defined.
+Usually this means the macro was only invoked conditionally." "$LINENO" 5
+fi
+if test -z "${SIXAXIS_TRUE}" && test -z "${SIXAXIS_FALSE}"; then
+  as_fn_error $? "conditional \"SIXAXIS\" was never defined.
+Usually this means the macro was only invoked conditionally." "$LINENO" 5
+fi
+if test -z "${ANDROID_TRUE}" && test -z "${ANDROID_FALSE}"; then
+  as_fn_error $? "conditional \"ANDROID\" was never defined.
+Usually this means the macro was only invoked conditionally." "$LINENO" 5
+fi
+
+: "${CONFIG_STATUS=./config.status}"
+ac_write_fail=0
+ac_clean_files_save=$ac_clean_files
+ac_clean_files="$ac_clean_files $CONFIG_STATUS"
+{ $as_echo "$as_me:${as_lineno-$LINENO}: creating $CONFIG_STATUS" >&5
+$as_echo "$as_me: creating $CONFIG_STATUS" >&6;}
+as_write_fail=0
+cat >$CONFIG_STATUS <<_ASEOF || as_write_fail=1
+#! $SHELL
+# Generated by $as_me.
+# Run this file to recreate the current configuration.
+# Compiler output produced by configure, useful for debugging
+# configure, is in config.log if it exists.
+
+debug=false
+ac_cs_recheck=false
+ac_cs_silent=false
+
+SHELL=\${CONFIG_SHELL-$SHELL}
+export SHELL
+_ASEOF
+cat >>$CONFIG_STATUS <<\_ASEOF || as_write_fail=1
+## -------------------- ##
+## M4sh Initialization. ##
+## -------------------- ##
+
+# Be more Bourne compatible
+DUALCASE=1; export DUALCASE # for MKS sh
+if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then :
+  emulate sh
+  NULLCMD=:
+  # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which
+  # is contrary to our usage.  Disable this feature.
+  alias -g '${1+"$@"}'='"$@"'
+  setopt NO_GLOB_SUBST
+else
+  case `(set -o) 2>/dev/null` in #(
+  *posix*) :
+    set -o posix ;; #(
+  *) :
+     ;;
+esac
+fi
+
+
+as_nl='
+'
+export as_nl
+# Printing a long string crashes Solaris 7 /usr/bin/printf.
+as_echo='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\'
+as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo
+as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo$as_echo
+# Prefer a ksh shell builtin over an external printf program on Solaris,
+# but without wasting forks for bash or zsh.
+if test -z "$BASH_VERSION$ZSH_VERSION" \
+    && (test "X`print -r -- $as_echo`" = "X$as_echo") 2>/dev/null; then
+  as_echo='print -r --'
+  as_echo_n='print -rn --'
+elif (test "X`printf %s $as_echo`" = "X$as_echo") 2>/dev/null; then
+  as_echo='printf %s\n'
+  as_echo_n='printf %s'
+else
+  if test "X`(/usr/ucb/echo -n -n $as_echo) 2>/dev/null`" = "X-n $as_echo"; then
+    as_echo_body='eval /usr/ucb/echo -n "$1$as_nl"'
+    as_echo_n='/usr/ucb/echo -n'
+  else
+    as_echo_body='eval expr "X$1" : "X\\(.*\\)"'
+    as_echo_n_body='eval
+      arg=$1;
+      case $arg in #(
+      *"$as_nl"*)
+	expr "X$arg" : "X\\(.*\\)$as_nl";
+	arg=`expr "X$arg" : ".*$as_nl\\(.*\\)"`;;
+      esac;
+      expr "X$arg" : "X\\(.*\\)" | tr -d "$as_nl"
+    '
+    export as_echo_n_body
+    as_echo_n='sh -c $as_echo_n_body as_echo'
+  fi
+  export as_echo_body
+  as_echo='sh -c $as_echo_body as_echo'
+fi
+
+# The user is always right.
+if test "${PATH_SEPARATOR+set}" != set; then
+  PATH_SEPARATOR=:
+  (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && {
+    (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 ||
+      PATH_SEPARATOR=';'
+  }
+fi
+
+
+# IFS
+# We need space, tab and new line, in precisely that order.  Quoting is
+# there to prevent editors from complaining about space-tab.
+# (If _AS_PATH_WALK were called with IFS unset, it would disable word
+# splitting by setting IFS to empty value.)
+IFS=" ""	$as_nl"
+
+# Find who we are.  Look in the path if we contain no directory separator.
+as_myself=
+case $0 in #((
+  *[\\/]* ) as_myself=$0 ;;
+  *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break
+  done
+IFS=$as_save_IFS
+
+     ;;
+esac
+# We did not find ourselves, most probably we were run as `sh COMMAND'
+# in which case we are not to be found in the path.
+if test "x$as_myself" = x; then
+  as_myself=$0
+fi
+if test ! -f "$as_myself"; then
+  $as_echo "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2
+  exit 1
+fi
+
+# Unset variables that we do not need and which cause bugs (e.g. in
+# pre-3.0 UWIN ksh).  But do not cause bugs in bash 2.01; the "|| exit 1"
+# suppresses any "Segmentation fault" message there.  '((' could
+# trigger a bug in pdksh 5.2.14.
+for as_var in BASH_ENV ENV MAIL MAILPATH
+do eval test x\${$as_var+set} = xset \
+  && ( (unset $as_var) || exit 1) >/dev/null 2>&1 && unset $as_var || :
+done
+PS1='$ '
+PS2='> '
+PS4='+ '
+
+# NLS nuisances.
+LC_ALL=C
+export LC_ALL
+LANGUAGE=C
+export LANGUAGE
+
+# CDPATH.
+(unset CDPATH) >/dev/null 2>&1 && unset CDPATH
+
+
+# as_fn_error STATUS ERROR [LINENO LOG_FD]
+# ----------------------------------------
+# Output "`basename $0`: error: ERROR" to stderr. If LINENO and LOG_FD are
+# provided, also output the error to LOG_FD, referencing LINENO. Then exit the
+# script with STATUS, using 1 if that was 0.
+as_fn_error ()
+{
+  as_status=$1; test $as_status -eq 0 && as_status=1
+  if test "$4"; then
+    as_lineno=${as_lineno-"$3"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+    $as_echo "$as_me:${as_lineno-$LINENO}: error: $2" >&$4
+  fi
+  $as_echo "$as_me: error: $2" >&2
+  as_fn_exit $as_status
+} # as_fn_error
+
+
+# as_fn_set_status STATUS
+# -----------------------
+# Set $? to STATUS, without forking.
+as_fn_set_status ()
+{
+  return $1
+} # as_fn_set_status
+
+# as_fn_exit STATUS
+# -----------------
+# Exit the shell with STATUS, even in a "trap 0" or "set -e" context.
+as_fn_exit ()
+{
+  set +e
+  as_fn_set_status $1
+  exit $1
+} # as_fn_exit
+
+# as_fn_unset VAR
+# ---------------
+# Portably unset VAR.
+as_fn_unset ()
+{
+  { eval $1=; unset $1;}
+}
+as_unset=as_fn_unset
+# as_fn_append VAR VALUE
+# ----------------------
+# Append the text in VALUE to the end of the definition contained in VAR. Take
+# advantage of any shell optimizations that allow amortized linear growth over
+# repeated appends, instead of the typical quadratic growth present in naive
+# implementations.
+if (eval "as_var=1; as_var+=2; test x\$as_var = x12") 2>/dev/null; then :
+  eval 'as_fn_append ()
+  {
+    eval $1+=\$2
+  }'
+else
+  as_fn_append ()
+  {
+    eval $1=\$$1\$2
+  }
+fi # as_fn_append
+
+# as_fn_arith ARG...
+# ------------------
+# Perform arithmetic evaluation on the ARGs, and store the result in the
+# global $as_val. Take advantage of shells that can avoid forks. The arguments
+# must be portable across $(()) and expr.
+if (eval "test \$(( 1 + 1 )) = 2") 2>/dev/null; then :
+  eval 'as_fn_arith ()
+  {
+    as_val=$(( $* ))
+  }'
+else
+  as_fn_arith ()
+  {
+    as_val=`expr "$@" || test $? -eq 1`
+  }
+fi # as_fn_arith
+
+
+if expr a : '\(a\)' >/dev/null 2>&1 &&
+   test "X`expr 00001 : '.*\(...\)'`" = X001; then
+  as_expr=expr
+else
+  as_expr=false
+fi
+
+if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then
+  as_basename=basename
+else
+  as_basename=false
+fi
+
+if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then
+  as_dirname=dirname
+else
+  as_dirname=false
+fi
+
+as_me=`$as_basename -- "$0" ||
+$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \
+	 X"$0" : 'X\(//\)$' \| \
+	 X"$0" : 'X\(/\)' \| . 2>/dev/null ||
+$as_echo X/"$0" |
+    sed '/^.*\/\([^/][^/]*\)\/*$/{
+	    s//\1/
+	    q
+	  }
+	  /^X\/\(\/\/\)$/{
+	    s//\1/
+	    q
+	  }
+	  /^X\/\(\/\).*/{
+	    s//\1/
+	    q
+	  }
+	  s/.*/./; q'`
+
+# Avoid depending upon Character Ranges.
+as_cr_letters='abcdefghijklmnopqrstuvwxyz'
+as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ'
+as_cr_Letters=$as_cr_letters$as_cr_LETTERS
+as_cr_digits='0123456789'
+as_cr_alnum=$as_cr_Letters$as_cr_digits
+
+ECHO_C= ECHO_N= ECHO_T=
+case `echo -n x` in #(((((
+-n*)
+  case `echo 'xy\c'` in
+  *c*) ECHO_T='	';;	# ECHO_T is single tab character.
+  xy)  ECHO_C='\c';;
+  *)   echo `echo ksh88 bug on AIX 6.1` > /dev/null
+       ECHO_T='	';;
+  esac;;
+*)
+  ECHO_N='-n';;
+esac
+
+rm -f conf$$ conf$$.exe conf$$.file
+if test -d conf$$.dir; then
+  rm -f conf$$.dir/conf$$.file
+else
+  rm -f conf$$.dir
+  mkdir conf$$.dir 2>/dev/null
+fi
+if (echo >conf$$.file) 2>/dev/null; then
+  if ln -s conf$$.file conf$$ 2>/dev/null; then
+    as_ln_s='ln -s'
+    # ... but there are two gotchas:
+    # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail.
+    # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable.
+    # In both cases, we have to default to `cp -pR'.
+    ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe ||
+      as_ln_s='cp -pR'
+  elif ln conf$$.file conf$$ 2>/dev/null; then
+    as_ln_s=ln
+  else
+    as_ln_s='cp -pR'
+  fi
+else
+  as_ln_s='cp -pR'
+fi
+rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file
+rmdir conf$$.dir 2>/dev/null
+
+
+# as_fn_mkdir_p
+# -------------
+# Create "$as_dir" as a directory, including parents if necessary.
+as_fn_mkdir_p ()
+{
+
+  case $as_dir in #(
+  -*) as_dir=./$as_dir;;
+  esac
+  test -d "$as_dir" || eval $as_mkdir_p || {
+    as_dirs=
+    while :; do
+      case $as_dir in #(
+      *\'*) as_qdir=`$as_echo "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #'(
+      *) as_qdir=$as_dir;;
+      esac
+      as_dirs="'$as_qdir' $as_dirs"
+      as_dir=`$as_dirname -- "$as_dir" ||
+$as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+	 X"$as_dir" : 'X\(//\)[^/]' \| \
+	 X"$as_dir" : 'X\(//\)$' \| \
+	 X"$as_dir" : 'X\(/\)' \| . 2>/dev/null ||
+$as_echo X"$as_dir" |
+    sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{
+	    s//\1/
+	    q
+	  }
+	  /^X\(\/\/\)[^/].*/{
+	    s//\1/
+	    q
+	  }
+	  /^X\(\/\/\)$/{
+	    s//\1/
+	    q
+	  }
+	  /^X\(\/\).*/{
+	    s//\1/
+	    q
+	  }
+	  s/.*/./; q'`
+      test -d "$as_dir" && break
+    done
+    test -z "$as_dirs" || eval "mkdir $as_dirs"
+  } || test -d "$as_dir" || as_fn_error $? "cannot create directory $as_dir"
+
+
+} # as_fn_mkdir_p
+if mkdir -p . 2>/dev/null; then
+  as_mkdir_p='mkdir -p "$as_dir"'
+else
+  test -d ./-p && rmdir ./-p
+  as_mkdir_p=false
+fi
+
+
+# as_fn_executable_p FILE
+# -----------------------
+# Test if FILE is an executable regular file.
+as_fn_executable_p ()
+{
+  test -f "$1" && test -x "$1"
+} # as_fn_executable_p
+as_test_x='test -x'
+as_executable_p=as_fn_executable_p
+
+# Sed expression to map a string onto a valid CPP name.
+as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'"
+
+# Sed expression to map a string onto a valid variable name.
+as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'"
+
+
+exec 6>&1
+## ----------------------------------- ##
+## Main body of $CONFIG_STATUS script. ##
+## ----------------------------------- ##
+_ASEOF
+test $as_write_fail = 0 && chmod +x $CONFIG_STATUS || ac_write_fail=1
+
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+# Save the log message, to keep $0 and so on meaningful, and to
+# report actual input values of CONFIG_FILES etc. instead of their
+# values after options handling.
+ac_log="
+This file was extended by bluez $as_me 5.17, which was
+generated by GNU Autoconf 2.69.  Invocation command line was
+
+  CONFIG_FILES    = $CONFIG_FILES
+  CONFIG_HEADERS  = $CONFIG_HEADERS
+  CONFIG_LINKS    = $CONFIG_LINKS
+  CONFIG_COMMANDS = $CONFIG_COMMANDS
+  $ $0 $@
+
+on `(hostname || uname -n) 2>/dev/null | sed 1q`
+"
+
+_ACEOF
+
+case $ac_config_files in *"
+"*) set x $ac_config_files; shift; ac_config_files=$*;;
+esac
+
+case $ac_config_headers in *"
+"*) set x $ac_config_headers; shift; ac_config_headers=$*;;
+esac
+
+
+cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+# Files that config.status was made for.
+config_files="$ac_config_files"
+config_headers="$ac_config_headers"
+config_commands="$ac_config_commands"
+
+_ACEOF
+
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+ac_cs_usage="\
+\`$as_me' instantiates files and other configuration actions
+from templates according to the current configuration.  Unless the files
+and actions are specified as TAGs, all are instantiated by default.
+
+Usage: $0 [OPTION]... [TAG]...
+
+  -h, --help       print this help, then exit
+  -V, --version    print version number and configuration settings, then exit
+      --config     print configuration, then exit
+  -q, --quiet, --silent
+                   do not print progress messages
+  -d, --debug      don't remove temporary files
+      --recheck    update $as_me by reconfiguring in the same conditions
+      --file=FILE[:TEMPLATE]
+                   instantiate the configuration file FILE
+      --header=FILE[:TEMPLATE]
+                   instantiate the configuration header FILE
+
+Configuration files:
+$config_files
+
+Configuration headers:
+$config_headers
+
+Configuration commands:
+$config_commands
+
+Report bugs to the package provider."
+
+_ACEOF
+cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`"
+ac_cs_version="\\
+bluez config.status 5.17
+configured by $0, generated by GNU Autoconf 2.69,
+  with options \\"\$ac_cs_config\\"
+
+Copyright (C) 2012 Free Software Foundation, Inc.
+This config.status script is free software; the Free Software Foundation
+gives unlimited permission to copy, distribute and modify it."
+
+ac_pwd='$ac_pwd'
+srcdir='$srcdir'
+INSTALL='$INSTALL'
+MKDIR_P='$MKDIR_P'
+AWK='$AWK'
+test -n "\$AWK" || AWK=awk
+_ACEOF
+
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+# The default lists apply if the user does not specify any file.
+ac_need_defaults=:
+while test $# != 0
+do
+  case $1 in
+  --*=?*)
+    ac_option=`expr "X$1" : 'X\([^=]*\)='`
+    ac_optarg=`expr "X$1" : 'X[^=]*=\(.*\)'`
+    ac_shift=:
+    ;;
+  --*=)
+    ac_option=`expr "X$1" : 'X\([^=]*\)='`
+    ac_optarg=
+    ac_shift=:
+    ;;
+  *)
+    ac_option=$1
+    ac_optarg=$2
+    ac_shift=shift
+    ;;
+  esac
+
+  case $ac_option in
+  # Handling of the options.
+  -recheck | --recheck | --rechec | --reche | --rech | --rec | --re | --r)
+    ac_cs_recheck=: ;;
+  --version | --versio | --versi | --vers | --ver | --ve | --v | -V )
+    $as_echo "$ac_cs_version"; exit ;;
+  --config | --confi | --conf | --con | --co | --c )
+    $as_echo "$ac_cs_config"; exit ;;
+  --debug | --debu | --deb | --de | --d | -d )
+    debug=: ;;
+  --file | --fil | --fi | --f )
+    $ac_shift
+    case $ac_optarg in
+    *\'*) ac_optarg=`$as_echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` ;;
+    '') as_fn_error $? "missing file argument" ;;
+    esac
+    as_fn_append CONFIG_FILES " '$ac_optarg'"
+    ac_need_defaults=false;;
+  --header | --heade | --head | --hea )
+    $ac_shift
+    case $ac_optarg in
+    *\'*) ac_optarg=`$as_echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` ;;
+    esac
+    as_fn_append CONFIG_HEADERS " '$ac_optarg'"
+    ac_need_defaults=false;;
+  --he | --h)
+    # Conflict between --help and --header
+    as_fn_error $? "ambiguous option: \`$1'
+Try \`$0 --help' for more information.";;
+  --help | --hel | -h )
+    $as_echo "$ac_cs_usage"; exit ;;
+  -q | -quiet | --quiet | --quie | --qui | --qu | --q \
+  | -silent | --silent | --silen | --sile | --sil | --si | --s)
+    ac_cs_silent=: ;;
+
+  # This is an error.
+  -*) as_fn_error $? "unrecognized option: \`$1'
+Try \`$0 --help' for more information." ;;
+
+  *) as_fn_append ac_config_targets " $1"
+     ac_need_defaults=false ;;
+
+  esac
+  shift
+done
+
+ac_configure_extra_args=
+
+if $ac_cs_silent; then
+  exec 6>/dev/null
+  ac_configure_extra_args="$ac_configure_extra_args --silent"
+fi
+
+_ACEOF
+cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+if \$ac_cs_recheck; then
+  set X $SHELL '$0' $ac_configure_args \$ac_configure_extra_args --no-create --no-recursion
+  shift
+  \$as_echo "running CONFIG_SHELL=$SHELL \$*" >&6
+  CONFIG_SHELL='$SHELL'
+  export CONFIG_SHELL
+  exec "\$@"
+fi
+
+_ACEOF
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+exec 5>>config.log
+{
+  echo
+  sed 'h;s/./-/g;s/^.../## /;s/...$/ ##/;p;x;p;x' <<_ASBOX
+## Running $as_me. ##
+_ASBOX
+  $as_echo "$ac_log"
+} >&5
+
+_ACEOF
+cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+#
+# INIT-COMMANDS
+#
+AMDEP_TRUE="$AMDEP_TRUE" ac_aux_dir="$ac_aux_dir"
+
+
+# The HP-UX ksh and POSIX shell print the target directory to stdout
+# if CDPATH is set.
+(unset CDPATH) >/dev/null 2>&1 && unset CDPATH
+
+sed_quote_subst='$sed_quote_subst'
+double_quote_subst='$double_quote_subst'
+delay_variable_subst='$delay_variable_subst'
+enable_static='`$ECHO "$enable_static" | $SED "$delay_single_quote_subst"`'
+macro_version='`$ECHO "$macro_version" | $SED "$delay_single_quote_subst"`'
+macro_revision='`$ECHO "$macro_revision" | $SED "$delay_single_quote_subst"`'
+enable_shared='`$ECHO "$enable_shared" | $SED "$delay_single_quote_subst"`'
+pic_mode='`$ECHO "$pic_mode" | $SED "$delay_single_quote_subst"`'
+enable_fast_install='`$ECHO "$enable_fast_install" | $SED "$delay_single_quote_subst"`'
+SHELL='`$ECHO "$SHELL" | $SED "$delay_single_quote_subst"`'
+ECHO='`$ECHO "$ECHO" | $SED "$delay_single_quote_subst"`'
+PATH_SEPARATOR='`$ECHO "$PATH_SEPARATOR" | $SED "$delay_single_quote_subst"`'
+host_alias='`$ECHO "$host_alias" | $SED "$delay_single_quote_subst"`'
+host='`$ECHO "$host" | $SED "$delay_single_quote_subst"`'
+host_os='`$ECHO "$host_os" | $SED "$delay_single_quote_subst"`'
+build_alias='`$ECHO "$build_alias" | $SED "$delay_single_quote_subst"`'
+build='`$ECHO "$build" | $SED "$delay_single_quote_subst"`'
+build_os='`$ECHO "$build_os" | $SED "$delay_single_quote_subst"`'
+SED='`$ECHO "$SED" | $SED "$delay_single_quote_subst"`'
+Xsed='`$ECHO "$Xsed" | $SED "$delay_single_quote_subst"`'
+GREP='`$ECHO "$GREP" | $SED "$delay_single_quote_subst"`'
+EGREP='`$ECHO "$EGREP" | $SED "$delay_single_quote_subst"`'
+FGREP='`$ECHO "$FGREP" | $SED "$delay_single_quote_subst"`'
+LD='`$ECHO "$LD" | $SED "$delay_single_quote_subst"`'
+NM='`$ECHO "$NM" | $SED "$delay_single_quote_subst"`'
+LN_S='`$ECHO "$LN_S" | $SED "$delay_single_quote_subst"`'
+max_cmd_len='`$ECHO "$max_cmd_len" | $SED "$delay_single_quote_subst"`'
+ac_objext='`$ECHO "$ac_objext" | $SED "$delay_single_quote_subst"`'
+exeext='`$ECHO "$exeext" | $SED "$delay_single_quote_subst"`'
+lt_unset='`$ECHO "$lt_unset" | $SED "$delay_single_quote_subst"`'
+lt_SP2NL='`$ECHO "$lt_SP2NL" | $SED "$delay_single_quote_subst"`'
+lt_NL2SP='`$ECHO "$lt_NL2SP" | $SED "$delay_single_quote_subst"`'
+lt_cv_to_host_file_cmd='`$ECHO "$lt_cv_to_host_file_cmd" | $SED "$delay_single_quote_subst"`'
+lt_cv_to_tool_file_cmd='`$ECHO "$lt_cv_to_tool_file_cmd" | $SED "$delay_single_quote_subst"`'
+reload_flag='`$ECHO "$reload_flag" | $SED "$delay_single_quote_subst"`'
+reload_cmds='`$ECHO "$reload_cmds" | $SED "$delay_single_quote_subst"`'
+OBJDUMP='`$ECHO "$OBJDUMP" | $SED "$delay_single_quote_subst"`'
+deplibs_check_method='`$ECHO "$deplibs_check_method" | $SED "$delay_single_quote_subst"`'
+file_magic_cmd='`$ECHO "$file_magic_cmd" | $SED "$delay_single_quote_subst"`'
+file_magic_glob='`$ECHO "$file_magic_glob" | $SED "$delay_single_quote_subst"`'
+want_nocaseglob='`$ECHO "$want_nocaseglob" | $SED "$delay_single_quote_subst"`'
+DLLTOOL='`$ECHO "$DLLTOOL" | $SED "$delay_single_quote_subst"`'
+sharedlib_from_linklib_cmd='`$ECHO "$sharedlib_from_linklib_cmd" | $SED "$delay_single_quote_subst"`'
+AR='`$ECHO "$AR" | $SED "$delay_single_quote_subst"`'
+AR_FLAGS='`$ECHO "$AR_FLAGS" | $SED "$delay_single_quote_subst"`'
+archiver_list_spec='`$ECHO "$archiver_list_spec" | $SED "$delay_single_quote_subst"`'
+STRIP='`$ECHO "$STRIP" | $SED "$delay_single_quote_subst"`'
+RANLIB='`$ECHO "$RANLIB" | $SED "$delay_single_quote_subst"`'
+old_postinstall_cmds='`$ECHO "$old_postinstall_cmds" | $SED "$delay_single_quote_subst"`'
+old_postuninstall_cmds='`$ECHO "$old_postuninstall_cmds" | $SED "$delay_single_quote_subst"`'
+old_archive_cmds='`$ECHO "$old_archive_cmds" | $SED "$delay_single_quote_subst"`'
+lock_old_archive_extraction='`$ECHO "$lock_old_archive_extraction" | $SED "$delay_single_quote_subst"`'
+CC='`$ECHO "$CC" | $SED "$delay_single_quote_subst"`'
+CFLAGS='`$ECHO "$CFLAGS" | $SED "$delay_single_quote_subst"`'
+compiler='`$ECHO "$compiler" | $SED "$delay_single_quote_subst"`'
+GCC='`$ECHO "$GCC" | $SED "$delay_single_quote_subst"`'
+lt_cv_sys_global_symbol_pipe='`$ECHO "$lt_cv_sys_global_symbol_pipe" | $SED "$delay_single_quote_subst"`'
+lt_cv_sys_global_symbol_to_cdecl='`$ECHO "$lt_cv_sys_global_symbol_to_cdecl" | $SED "$delay_single_quote_subst"`'
+lt_cv_sys_global_symbol_to_c_name_address='`$ECHO "$lt_cv_sys_global_symbol_to_c_name_address" | $SED "$delay_single_quote_subst"`'
+lt_cv_sys_global_symbol_to_c_name_address_lib_prefix='`$ECHO "$lt_cv_sys_global_symbol_to_c_name_address_lib_prefix" | $SED "$delay_single_quote_subst"`'
+nm_file_list_spec='`$ECHO "$nm_file_list_spec" | $SED "$delay_single_quote_subst"`'
+lt_sysroot='`$ECHO "$lt_sysroot" | $SED "$delay_single_quote_subst"`'
+objdir='`$ECHO "$objdir" | $SED "$delay_single_quote_subst"`'
+MAGIC_CMD='`$ECHO "$MAGIC_CMD" | $SED "$delay_single_quote_subst"`'
+lt_prog_compiler_no_builtin_flag='`$ECHO "$lt_prog_compiler_no_builtin_flag" | $SED "$delay_single_quote_subst"`'
+lt_prog_compiler_pic='`$ECHO "$lt_prog_compiler_pic" | $SED "$delay_single_quote_subst"`'
+lt_prog_compiler_wl='`$ECHO "$lt_prog_compiler_wl" | $SED "$delay_single_quote_subst"`'
+lt_prog_compiler_static='`$ECHO "$lt_prog_compiler_static" | $SED "$delay_single_quote_subst"`'
+lt_cv_prog_compiler_c_o='`$ECHO "$lt_cv_prog_compiler_c_o" | $SED "$delay_single_quote_subst"`'
+need_locks='`$ECHO "$need_locks" | $SED "$delay_single_quote_subst"`'
+MANIFEST_TOOL='`$ECHO "$MANIFEST_TOOL" | $SED "$delay_single_quote_subst"`'
+DSYMUTIL='`$ECHO "$DSYMUTIL" | $SED "$delay_single_quote_subst"`'
+NMEDIT='`$ECHO "$NMEDIT" | $SED "$delay_single_quote_subst"`'
+LIPO='`$ECHO "$LIPO" | $SED "$delay_single_quote_subst"`'
+OTOOL='`$ECHO "$OTOOL" | $SED "$delay_single_quote_subst"`'
+OTOOL64='`$ECHO "$OTOOL64" | $SED "$delay_single_quote_subst"`'
+libext='`$ECHO "$libext" | $SED "$delay_single_quote_subst"`'
+shrext_cmds='`$ECHO "$shrext_cmds" | $SED "$delay_single_quote_subst"`'
+extract_expsyms_cmds='`$ECHO "$extract_expsyms_cmds" | $SED "$delay_single_quote_subst"`'
+archive_cmds_need_lc='`$ECHO "$archive_cmds_need_lc" | $SED "$delay_single_quote_subst"`'
+enable_shared_with_static_runtimes='`$ECHO "$enable_shared_with_static_runtimes" | $SED "$delay_single_quote_subst"`'
+export_dynamic_flag_spec='`$ECHO "$export_dynamic_flag_spec" | $SED "$delay_single_quote_subst"`'
+whole_archive_flag_spec='`$ECHO "$whole_archive_flag_spec" | $SED "$delay_single_quote_subst"`'
+compiler_needs_object='`$ECHO "$compiler_needs_object" | $SED "$delay_single_quote_subst"`'
+old_archive_from_new_cmds='`$ECHO "$old_archive_from_new_cmds" | $SED "$delay_single_quote_subst"`'
+old_archive_from_expsyms_cmds='`$ECHO "$old_archive_from_expsyms_cmds" | $SED "$delay_single_quote_subst"`'
+archive_cmds='`$ECHO "$archive_cmds" | $SED "$delay_single_quote_subst"`'
+archive_expsym_cmds='`$ECHO "$archive_expsym_cmds" | $SED "$delay_single_quote_subst"`'
+module_cmds='`$ECHO "$module_cmds" | $SED "$delay_single_quote_subst"`'
+module_expsym_cmds='`$ECHO "$module_expsym_cmds" | $SED "$delay_single_quote_subst"`'
+with_gnu_ld='`$ECHO "$with_gnu_ld" | $SED "$delay_single_quote_subst"`'
+allow_undefined_flag='`$ECHO "$allow_undefined_flag" | $SED "$delay_single_quote_subst"`'
+no_undefined_flag='`$ECHO "$no_undefined_flag" | $SED "$delay_single_quote_subst"`'
+hardcode_libdir_flag_spec='`$ECHO "$hardcode_libdir_flag_spec" | $SED "$delay_single_quote_subst"`'
+hardcode_libdir_separator='`$ECHO "$hardcode_libdir_separator" | $SED "$delay_single_quote_subst"`'
+hardcode_direct='`$ECHO "$hardcode_direct" | $SED "$delay_single_quote_subst"`'
+hardcode_direct_absolute='`$ECHO "$hardcode_direct_absolute" | $SED "$delay_single_quote_subst"`'
+hardcode_minus_L='`$ECHO "$hardcode_minus_L" | $SED "$delay_single_quote_subst"`'
+hardcode_shlibpath_var='`$ECHO "$hardcode_shlibpath_var" | $SED "$delay_single_quote_subst"`'
+hardcode_automatic='`$ECHO "$hardcode_automatic" | $SED "$delay_single_quote_subst"`'
+inherit_rpath='`$ECHO "$inherit_rpath" | $SED "$delay_single_quote_subst"`'
+link_all_deplibs='`$ECHO "$link_all_deplibs" | $SED "$delay_single_quote_subst"`'
+always_export_symbols='`$ECHO "$always_export_symbols" | $SED "$delay_single_quote_subst"`'
+export_symbols_cmds='`$ECHO "$export_symbols_cmds" | $SED "$delay_single_quote_subst"`'
+exclude_expsyms='`$ECHO "$exclude_expsyms" | $SED "$delay_single_quote_subst"`'
+include_expsyms='`$ECHO "$include_expsyms" | $SED "$delay_single_quote_subst"`'
+prelink_cmds='`$ECHO "$prelink_cmds" | $SED "$delay_single_quote_subst"`'
+postlink_cmds='`$ECHO "$postlink_cmds" | $SED "$delay_single_quote_subst"`'
+file_list_spec='`$ECHO "$file_list_spec" | $SED "$delay_single_quote_subst"`'
+variables_saved_for_relink='`$ECHO "$variables_saved_for_relink" | $SED "$delay_single_quote_subst"`'
+need_lib_prefix='`$ECHO "$need_lib_prefix" | $SED "$delay_single_quote_subst"`'
+need_version='`$ECHO "$need_version" | $SED "$delay_single_quote_subst"`'
+version_type='`$ECHO "$version_type" | $SED "$delay_single_quote_subst"`'
+runpath_var='`$ECHO "$runpath_var" | $SED "$delay_single_quote_subst"`'
+shlibpath_var='`$ECHO "$shlibpath_var" | $SED "$delay_single_quote_subst"`'
+shlibpath_overrides_runpath='`$ECHO "$shlibpath_overrides_runpath" | $SED "$delay_single_quote_subst"`'
+libname_spec='`$ECHO "$libname_spec" | $SED "$delay_single_quote_subst"`'
+library_names_spec='`$ECHO "$library_names_spec" | $SED "$delay_single_quote_subst"`'
+soname_spec='`$ECHO "$soname_spec" | $SED "$delay_single_quote_subst"`'
+install_override_mode='`$ECHO "$install_override_mode" | $SED "$delay_single_quote_subst"`'
+postinstall_cmds='`$ECHO "$postinstall_cmds" | $SED "$delay_single_quote_subst"`'
+postuninstall_cmds='`$ECHO "$postuninstall_cmds" | $SED "$delay_single_quote_subst"`'
+finish_cmds='`$ECHO "$finish_cmds" | $SED "$delay_single_quote_subst"`'
+finish_eval='`$ECHO "$finish_eval" | $SED "$delay_single_quote_subst"`'
+hardcode_into_libs='`$ECHO "$hardcode_into_libs" | $SED "$delay_single_quote_subst"`'
+sys_lib_search_path_spec='`$ECHO "$sys_lib_search_path_spec" | $SED "$delay_single_quote_subst"`'
+sys_lib_dlsearch_path_spec='`$ECHO "$sys_lib_dlsearch_path_spec" | $SED "$delay_single_quote_subst"`'
+hardcode_action='`$ECHO "$hardcode_action" | $SED "$delay_single_quote_subst"`'
+enable_dlopen='`$ECHO "$enable_dlopen" | $SED "$delay_single_quote_subst"`'
+enable_dlopen_self='`$ECHO "$enable_dlopen_self" | $SED "$delay_single_quote_subst"`'
+enable_dlopen_self_static='`$ECHO "$enable_dlopen_self_static" | $SED "$delay_single_quote_subst"`'
+old_striplib='`$ECHO "$old_striplib" | $SED "$delay_single_quote_subst"`'
+striplib='`$ECHO "$striplib" | $SED "$delay_single_quote_subst"`'
+
+LTCC='$LTCC'
+LTCFLAGS='$LTCFLAGS'
+compiler='$compiler_DEFAULT'
+
+# A function that is used when there is no print builtin or printf.
+func_fallback_echo ()
+{
+  eval 'cat <<_LTECHO_EOF
+\$1
+_LTECHO_EOF'
+}
+
+# Quote evaled strings.
+for var in SHELL \
+ECHO \
+PATH_SEPARATOR \
+SED \
+GREP \
+EGREP \
+FGREP \
+LD \
+NM \
+LN_S \
+lt_SP2NL \
+lt_NL2SP \
+reload_flag \
+OBJDUMP \
+deplibs_check_method \
+file_magic_cmd \
+file_magic_glob \
+want_nocaseglob \
+DLLTOOL \
+sharedlib_from_linklib_cmd \
+AR \
+AR_FLAGS \
+archiver_list_spec \
+STRIP \
+RANLIB \
+CC \
+CFLAGS \
+compiler \
+lt_cv_sys_global_symbol_pipe \
+lt_cv_sys_global_symbol_to_cdecl \
+lt_cv_sys_global_symbol_to_c_name_address \
+lt_cv_sys_global_symbol_to_c_name_address_lib_prefix \
+nm_file_list_spec \
+lt_prog_compiler_no_builtin_flag \
+lt_prog_compiler_pic \
+lt_prog_compiler_wl \
+lt_prog_compiler_static \
+lt_cv_prog_compiler_c_o \
+need_locks \
+MANIFEST_TOOL \
+DSYMUTIL \
+NMEDIT \
+LIPO \
+OTOOL \
+OTOOL64 \
+shrext_cmds \
+export_dynamic_flag_spec \
+whole_archive_flag_spec \
+compiler_needs_object \
+with_gnu_ld \
+allow_undefined_flag \
+no_undefined_flag \
+hardcode_libdir_flag_spec \
+hardcode_libdir_separator \
+exclude_expsyms \
+include_expsyms \
+file_list_spec \
+variables_saved_for_relink \
+libname_spec \
+library_names_spec \
+soname_spec \
+install_override_mode \
+finish_eval \
+old_striplib \
+striplib; do
+    case \`eval \\\\\$ECHO \\\\""\\\\\$\$var"\\\\"\` in
+    *[\\\\\\\`\\"\\\$]*)
+      eval "lt_\$var=\\\\\\"\\\`\\\$ECHO \\"\\\$\$var\\" | \\\$SED \\"\\\$sed_quote_subst\\"\\\`\\\\\\""
+      ;;
+    *)
+      eval "lt_\$var=\\\\\\"\\\$\$var\\\\\\""
+      ;;
+    esac
+done
+
+# Double-quote double-evaled strings.
+for var in reload_cmds \
+old_postinstall_cmds \
+old_postuninstall_cmds \
+old_archive_cmds \
+extract_expsyms_cmds \
+old_archive_from_new_cmds \
+old_archive_from_expsyms_cmds \
+archive_cmds \
+archive_expsym_cmds \
+module_cmds \
+module_expsym_cmds \
+export_symbols_cmds \
+prelink_cmds \
+postlink_cmds \
+postinstall_cmds \
+postuninstall_cmds \
+finish_cmds \
+sys_lib_search_path_spec \
+sys_lib_dlsearch_path_spec; do
+    case \`eval \\\\\$ECHO \\\\""\\\\\$\$var"\\\\"\` in
+    *[\\\\\\\`\\"\\\$]*)
+      eval "lt_\$var=\\\\\\"\\\`\\\$ECHO \\"\\\$\$var\\" | \\\$SED -e \\"\\\$double_quote_subst\\" -e \\"\\\$sed_quote_subst\\" -e \\"\\\$delay_variable_subst\\"\\\`\\\\\\""
+      ;;
+    *)
+      eval "lt_\$var=\\\\\\"\\\$\$var\\\\\\""
+      ;;
+    esac
+done
+
+ac_aux_dir='$ac_aux_dir'
+xsi_shell='$xsi_shell'
+lt_shell_append='$lt_shell_append'
+
+# See if we are running on zsh, and set the options which allow our
+# commands through without removal of \ escapes INIT.
+if test -n "\${ZSH_VERSION+set}" ; then
+   setopt NO_GLOB_SUBST
+fi
+
+
+    PACKAGE='$PACKAGE'
+    VERSION='$VERSION'
+    TIMESTAMP='$TIMESTAMP'
+    RM='$RM'
+    ofile='$ofile'
+
+
+
+
+_ACEOF
+
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+
+# Handling of arguments.
+for ac_config_target in $ac_config_targets
+do
+  case $ac_config_target in
+    "config.h") CONFIG_HEADERS="$CONFIG_HEADERS config.h" ;;
+    "depfiles") CONFIG_COMMANDS="$CONFIG_COMMANDS depfiles" ;;
+    "libtool") CONFIG_COMMANDS="$CONFIG_COMMANDS libtool" ;;
+    "Makefile") CONFIG_FILES="$CONFIG_FILES Makefile" ;;
+    "src/bluetoothd.8") CONFIG_FILES="$CONFIG_FILES src/bluetoothd.8" ;;
+    "lib/bluez.pc") CONFIG_FILES="$CONFIG_FILES lib/bluez.pc" ;;
+
+  *) as_fn_error $? "invalid argument: \`$ac_config_target'" "$LINENO" 5;;
+  esac
+done
+
+
+# If the user did not use the arguments to specify the items to instantiate,
+# then the envvar interface is used.  Set only those that are not.
+# We use the long form for the default assignment because of an extremely
+# bizarre bug on SunOS 4.1.3.
+if $ac_need_defaults; then
+  test "${CONFIG_FILES+set}" = set || CONFIG_FILES=$config_files
+  test "${CONFIG_HEADERS+set}" = set || CONFIG_HEADERS=$config_headers
+  test "${CONFIG_COMMANDS+set}" = set || CONFIG_COMMANDS=$config_commands
+fi
+
+# Have a temporary directory for convenience.  Make it in the build tree
+# simply because there is no reason against having it here, and in addition,
+# creating and moving files from /tmp can sometimes cause problems.
+# Hook for its removal unless debugging.
+# Note that there is a small window in which the directory will not be cleaned:
+# after its creation but before its name has been assigned to `$tmp'.
+$debug ||
+{
+  tmp= ac_tmp=
+  trap 'exit_status=$?
+  : "${ac_tmp:=$tmp}"
+  { test ! -d "$ac_tmp" || rm -fr "$ac_tmp"; } && exit $exit_status
+' 0
+  trap 'as_fn_exit 1' 1 2 13 15
+}
+# Create a (secure) tmp directory for tmp files.
+
+{
+  tmp=`(umask 077 && mktemp -d "./confXXXXXX") 2>/dev/null` &&
+  test -d "$tmp"
+}  ||
+{
+  tmp=./conf$$-$RANDOM
+  (umask 077 && mkdir "$tmp")
+} || as_fn_error $? "cannot create a temporary directory in ." "$LINENO" 5
+ac_tmp=$tmp
+
+# Set up the scripts for CONFIG_FILES section.
+# No need to generate them if there are no CONFIG_FILES.
+# This happens for instance with `./config.status config.h'.
+if test -n "$CONFIG_FILES"; then
+
+
+ac_cr=`echo X | tr X '\015'`
+# On cygwin, bash can eat \r inside `` if the user requested igncr.
+# But we know of no other shell where ac_cr would be empty at this
+# point, so we can use a bashism as a fallback.
+if test "x$ac_cr" = x; then
+  eval ac_cr=\$\'\\r\'
+fi
+ac_cs_awk_cr=`$AWK 'BEGIN { print "a\rb" }' </dev/null 2>/dev/null`
+if test "$ac_cs_awk_cr" = "a${ac_cr}b"; then
+  ac_cs_awk_cr='\\r'
+else
+  ac_cs_awk_cr=$ac_cr
+fi
+
+echo 'BEGIN {' >"$ac_tmp/subs1.awk" &&
+_ACEOF
+
+
+{
+  echo "cat >conf$$subs.awk <<_ACEOF" &&
+  echo "$ac_subst_vars" | sed 's/.*/&!$&$ac_delim/' &&
+  echo "_ACEOF"
+} >conf$$subs.sh ||
+  as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5
+ac_delim_num=`echo "$ac_subst_vars" | grep -c '^'`
+ac_delim='%!_!# '
+for ac_last_try in false false false false false :; do
+  . ./conf$$subs.sh ||
+    as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5
+
+  ac_delim_n=`sed -n "s/.*$ac_delim\$/X/p" conf$$subs.awk | grep -c X`
+  if test $ac_delim_n = $ac_delim_num; then
+    break
+  elif $ac_last_try; then
+    as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5
+  else
+    ac_delim="$ac_delim!$ac_delim _$ac_delim!! "
+  fi
+done
+rm -f conf$$subs.sh
+
+cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+cat >>"\$ac_tmp/subs1.awk" <<\\_ACAWK &&
+_ACEOF
+sed -n '
+h
+s/^/S["/; s/!.*/"]=/
+p
+g
+s/^[^!]*!//
+:repl
+t repl
+s/'"$ac_delim"'$//
+t delim
+:nl
+h
+s/\(.\{148\}\)..*/\1/
+t more1
+s/["\\]/\\&/g; s/^/"/; s/$/\\n"\\/
+p
+n
+b repl
+:more1
+s/["\\]/\\&/g; s/^/"/; s/$/"\\/
+p
+g
+s/.\{148\}//
+t nl
+:delim
+h
+s/\(.\{148\}\)..*/\1/
+t more2
+s/["\\]/\\&/g; s/^/"/; s/$/"/
+p
+b
+:more2
+s/["\\]/\\&/g; s/^/"/; s/$/"\\/
+p
+g
+s/.\{148\}//
+t delim
+' <conf$$subs.awk | sed '
+/^[^""]/{
+  N
+  s/\n//
+}
+' >>$CONFIG_STATUS || ac_write_fail=1
+rm -f conf$$subs.awk
+cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+_ACAWK
+cat >>"\$ac_tmp/subs1.awk" <<_ACAWK &&
+  for (key in S) S_is_set[key] = 1
+  FS = ""
+
+}
+{
+  line = $ 0
+  nfields = split(line, field, "@")
+  substed = 0
+  len = length(field[1])
+  for (i = 2; i < nfields; i++) {
+    key = field[i]
+    keylen = length(key)
+    if (S_is_set[key]) {
+      value = S[key]
+      line = substr(line, 1, len) "" value "" substr(line, len + keylen + 3)
+      len += length(value) + length(field[++i])
+      substed = 1
+    } else
+      len += 1 + keylen
+  }
+
+  print line
+}
+
+_ACAWK
+_ACEOF
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+if sed "s/$ac_cr//" < /dev/null > /dev/null 2>&1; then
+  sed "s/$ac_cr\$//; s/$ac_cr/$ac_cs_awk_cr/g"
+else
+  cat
+fi < "$ac_tmp/subs1.awk" > "$ac_tmp/subs.awk" \
+  || as_fn_error $? "could not setup config files machinery" "$LINENO" 5
+_ACEOF
+
+# VPATH may cause trouble with some makes, so we remove sole $(srcdir),
+# ${srcdir} and @srcdir@ entries from VPATH if srcdir is ".", strip leading and
+# trailing colons and then remove the whole line if VPATH becomes empty
+# (actually we leave an empty line to preserve line numbers).
+if test "x$srcdir" = x.; then
+  ac_vpsub='/^[	 ]*VPATH[	 ]*=[	 ]*/{
+h
+s///
+s/^/:/
+s/[	 ]*$/:/
+s/:\$(srcdir):/:/g
+s/:\${srcdir}:/:/g
+s/:@srcdir@:/:/g
+s/^:*//
+s/:*$//
+x
+s/\(=[	 ]*\).*/\1/
+G
+s/\n//
+s/^[^=]*=[	 ]*$//
+}'
+fi
+
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+fi # test -n "$CONFIG_FILES"
+
+# Set up the scripts for CONFIG_HEADERS section.
+# No need to generate them if there are no CONFIG_HEADERS.
+# This happens for instance with `./config.status Makefile'.
+if test -n "$CONFIG_HEADERS"; then
+cat >"$ac_tmp/defines.awk" <<\_ACAWK ||
+BEGIN {
+_ACEOF
+
+# Transform confdefs.h into an awk script `defines.awk', embedded as
+# here-document in config.status, that substitutes the proper values into
+# config.h.in to produce config.h.
+
+# Create a delimiter string that does not exist in confdefs.h, to ease
+# handling of long lines.
+ac_delim='%!_!# '
+for ac_last_try in false false :; do
+  ac_tt=`sed -n "/$ac_delim/p" confdefs.h`
+  if test -z "$ac_tt"; then
+    break
+  elif $ac_last_try; then
+    as_fn_error $? "could not make $CONFIG_HEADERS" "$LINENO" 5
+  else
+    ac_delim="$ac_delim!$ac_delim _$ac_delim!! "
+  fi
+done
+
+# For the awk script, D is an array of macro values keyed by name,
+# likewise P contains macro parameters if any.  Preserve backslash
+# newline sequences.
+
+ac_word_re=[_$as_cr_Letters][_$as_cr_alnum]*
+sed -n '
+s/.\{148\}/&'"$ac_delim"'/g
+t rset
+:rset
+s/^[	 ]*#[	 ]*define[	 ][	 ]*/ /
+t def
+d
+:def
+s/\\$//
+t bsnl
+s/["\\]/\\&/g
+s/^ \('"$ac_word_re"'\)\(([^()]*)\)[	 ]*\(.*\)/P["\1"]="\2"\
+D["\1"]=" \3"/p
+s/^ \('"$ac_word_re"'\)[	 ]*\(.*\)/D["\1"]=" \2"/p
+d
+:bsnl
+s/["\\]/\\&/g
+s/^ \('"$ac_word_re"'\)\(([^()]*)\)[	 ]*\(.*\)/P["\1"]="\2"\
+D["\1"]=" \3\\\\\\n"\\/p
+t cont
+s/^ \('"$ac_word_re"'\)[	 ]*\(.*\)/D["\1"]=" \2\\\\\\n"\\/p
+t cont
+d
+:cont
+n
+s/.\{148\}/&'"$ac_delim"'/g
+t clear
+:clear
+s/\\$//
+t bsnlc
+s/["\\]/\\&/g; s/^/"/; s/$/"/p
+d
+:bsnlc
+s/["\\]/\\&/g; s/^/"/; s/$/\\\\\\n"\\/p
+b cont
+' <confdefs.h | sed '
+s/'"$ac_delim"'/"\\\
+"/g' >>$CONFIG_STATUS || ac_write_fail=1
+
+cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+  for (key in D) D_is_set[key] = 1
+  FS = ""
+}
+/^[\t ]*#[\t ]*(define|undef)[\t ]+$ac_word_re([\t (]|\$)/ {
+  line = \$ 0
+  split(line, arg, " ")
+  if (arg[1] == "#") {
+    defundef = arg[2]
+    mac1 = arg[3]
+  } else {
+    defundef = substr(arg[1], 2)
+    mac1 = arg[2]
+  }
+  split(mac1, mac2, "(") #)
+  macro = mac2[1]
+  prefix = substr(line, 1, index(line, defundef) - 1)
+  if (D_is_set[macro]) {
+    # Preserve the white space surrounding the "#".
+    print prefix "define", macro P[macro] D[macro]
+    next
+  } else {
+    # Replace #undef with comments.  This is necessary, for example,
+    # in the case of _POSIX_SOURCE, which is predefined and required
+    # on some systems where configure will not decide to define it.
+    if (defundef == "undef") {
+      print "/*", prefix defundef, macro, "*/"
+      next
+    }
+  }
+}
+{ print }
+_ACAWK
+_ACEOF
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+  as_fn_error $? "could not setup config headers machinery" "$LINENO" 5
+fi # test -n "$CONFIG_HEADERS"
+
+
+eval set X "  :F $CONFIG_FILES  :H $CONFIG_HEADERS    :C $CONFIG_COMMANDS"
+shift
+for ac_tag
+do
+  case $ac_tag in
+  :[FHLC]) ac_mode=$ac_tag; continue;;
+  esac
+  case $ac_mode$ac_tag in
+  :[FHL]*:*);;
+  :L* | :C*:*) as_fn_error $? "invalid tag \`$ac_tag'" "$LINENO" 5;;
+  :[FH]-) ac_tag=-:-;;
+  :[FH]*) ac_tag=$ac_tag:$ac_tag.in;;
+  esac
+  ac_save_IFS=$IFS
+  IFS=:
+  set x $ac_tag
+  IFS=$ac_save_IFS
+  shift
+  ac_file=$1
+  shift
+
+  case $ac_mode in
+  :L) ac_source=$1;;
+  :[FH])
+    ac_file_inputs=
+    for ac_f
+    do
+      case $ac_f in
+      -) ac_f="$ac_tmp/stdin";;
+      *) # Look for the file first in the build tree, then in the source tree
+	 # (if the path is not absolute).  The absolute path cannot be DOS-style,
+	 # because $ac_f cannot contain `:'.
+	 test -f "$ac_f" ||
+	   case $ac_f in
+	   [\\/$]*) false;;
+	   *) test -f "$srcdir/$ac_f" && ac_f="$srcdir/$ac_f";;
+	   esac ||
+	   as_fn_error 1 "cannot find input file: \`$ac_f'" "$LINENO" 5;;
+      esac
+      case $ac_f in *\'*) ac_f=`$as_echo "$ac_f" | sed "s/'/'\\\\\\\\''/g"`;; esac
+      as_fn_append ac_file_inputs " '$ac_f'"
+    done
+
+    # Let's still pretend it is `configure' which instantiates (i.e., don't
+    # use $as_me), people would be surprised to read:
+    #    /* config.h.  Generated by config.status.  */
+    configure_input='Generated from '`
+	  $as_echo "$*" | sed 's|^[^:]*/||;s|:[^:]*/|, |g'
+	`' by configure.'
+    if test x"$ac_file" != x-; then
+      configure_input="$ac_file.  $configure_input"
+      { $as_echo "$as_me:${as_lineno-$LINENO}: creating $ac_file" >&5
+$as_echo "$as_me: creating $ac_file" >&6;}
+    fi
+    # Neutralize special characters interpreted by sed in replacement strings.
+    case $configure_input in #(
+    *\&* | *\|* | *\\* )
+       ac_sed_conf_input=`$as_echo "$configure_input" |
+       sed 's/[\\\\&|]/\\\\&/g'`;; #(
+    *) ac_sed_conf_input=$configure_input;;
+    esac
+
+    case $ac_tag in
+    *:-:* | *:-) cat >"$ac_tmp/stdin" \
+      || as_fn_error $? "could not create $ac_file" "$LINENO" 5 ;;
+    esac
+    ;;
+  esac
+
+  ac_dir=`$as_dirname -- "$ac_file" ||
+$as_expr X"$ac_file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+	 X"$ac_file" : 'X\(//\)[^/]' \| \
+	 X"$ac_file" : 'X\(//\)$' \| \
+	 X"$ac_file" : 'X\(/\)' \| . 2>/dev/null ||
+$as_echo X"$ac_file" |
+    sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{
+	    s//\1/
+	    q
+	  }
+	  /^X\(\/\/\)[^/].*/{
+	    s//\1/
+	    q
+	  }
+	  /^X\(\/\/\)$/{
+	    s//\1/
+	    q
+	  }
+	  /^X\(\/\).*/{
+	    s//\1/
+	    q
+	  }
+	  s/.*/./; q'`
+  as_dir="$ac_dir"; as_fn_mkdir_p
+  ac_builddir=.
+
+case "$ac_dir" in
+.) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;;
+*)
+  ac_dir_suffix=/`$as_echo "$ac_dir" | sed 's|^\.[\\/]||'`
+  # A ".." for each directory in $ac_dir_suffix.
+  ac_top_builddir_sub=`$as_echo "$ac_dir_suffix" | sed 's|/[^\\/]*|/..|g;s|/||'`
+  case $ac_top_builddir_sub in
+  "") ac_top_builddir_sub=. ac_top_build_prefix= ;;
+  *)  ac_top_build_prefix=$ac_top_builddir_sub/ ;;
+  esac ;;
+esac
+ac_abs_top_builddir=$ac_pwd
+ac_abs_builddir=$ac_pwd$ac_dir_suffix
+# for backward compatibility:
+ac_top_builddir=$ac_top_build_prefix
+
+case $srcdir in
+  .)  # We are building in place.
+    ac_srcdir=.
+    ac_top_srcdir=$ac_top_builddir_sub
+    ac_abs_top_srcdir=$ac_pwd ;;
+  [\\/]* | ?:[\\/]* )  # Absolute name.
+    ac_srcdir=$srcdir$ac_dir_suffix;
+    ac_top_srcdir=$srcdir
+    ac_abs_top_srcdir=$srcdir ;;
+  *) # Relative name.
+    ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix
+    ac_top_srcdir=$ac_top_build_prefix$srcdir
+    ac_abs_top_srcdir=$ac_pwd/$srcdir ;;
+esac
+ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix
+
+
+  case $ac_mode in
+  :F)
+  #
+  # CONFIG_FILE
+  #
+
+  case $INSTALL in
+  [\\/$]* | ?:[\\/]* ) ac_INSTALL=$INSTALL ;;
+  *) ac_INSTALL=$ac_top_build_prefix$INSTALL ;;
+  esac
+  ac_MKDIR_P=$MKDIR_P
+  case $MKDIR_P in
+  [\\/$]* | ?:[\\/]* ) ;;
+  */*) ac_MKDIR_P=$ac_top_build_prefix$MKDIR_P ;;
+  esac
+_ACEOF
+
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+# If the template does not know about datarootdir, expand it.
+# FIXME: This hack should be removed a few years after 2.60.
+ac_datarootdir_hack=; ac_datarootdir_seen=
+ac_sed_dataroot='
+/datarootdir/ {
+  p
+  q
+}
+/@datadir@/p
+/@docdir@/p
+/@infodir@/p
+/@localedir@/p
+/@mandir@/p'
+case `eval "sed -n \"\$ac_sed_dataroot\" $ac_file_inputs"` in
+*datarootdir*) ac_datarootdir_seen=yes;;
+*@datadir@*|*@docdir@*|*@infodir@*|*@localedir@*|*@mandir@*)
+  { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&5
+$as_echo "$as_me: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&2;}
+_ACEOF
+cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+  ac_datarootdir_hack='
+  s&@datadir@&$datadir&g
+  s&@docdir@&$docdir&g
+  s&@infodir@&$infodir&g
+  s&@localedir@&$localedir&g
+  s&@mandir@&$mandir&g
+  s&\\\${datarootdir}&$datarootdir&g' ;;
+esac
+_ACEOF
+
+# Neutralize VPATH when `$srcdir' = `.'.
+# Shell code in configure.ac might set extrasub.
+# FIXME: do we really want to maintain this feature?
+cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+ac_sed_extra="$ac_vpsub
+$extrasub
+_ACEOF
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+:t
+/@[a-zA-Z_][a-zA-Z_0-9]*@/!b
+s|@configure_input@|$ac_sed_conf_input|;t t
+s&@top_builddir@&$ac_top_builddir_sub&;t t
+s&@top_build_prefix@&$ac_top_build_prefix&;t t
+s&@srcdir@&$ac_srcdir&;t t
+s&@abs_srcdir@&$ac_abs_srcdir&;t t
+s&@top_srcdir@&$ac_top_srcdir&;t t
+s&@abs_top_srcdir@&$ac_abs_top_srcdir&;t t
+s&@builddir@&$ac_builddir&;t t
+s&@abs_builddir@&$ac_abs_builddir&;t t
+s&@abs_top_builddir@&$ac_abs_top_builddir&;t t
+s&@INSTALL@&$ac_INSTALL&;t t
+s&@MKDIR_P@&$ac_MKDIR_P&;t t
+$ac_datarootdir_hack
+"
+eval sed \"\$ac_sed_extra\" "$ac_file_inputs" | $AWK -f "$ac_tmp/subs.awk" \
+  >$ac_tmp/out || as_fn_error $? "could not create $ac_file" "$LINENO" 5
+
+test -z "$ac_datarootdir_hack$ac_datarootdir_seen" &&
+  { ac_out=`sed -n '/\${datarootdir}/p' "$ac_tmp/out"`; test -n "$ac_out"; } &&
+  { ac_out=`sed -n '/^[	 ]*datarootdir[	 ]*:*=/p' \
+      "$ac_tmp/out"`; test -z "$ac_out"; } &&
+  { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $ac_file contains a reference to the variable \`datarootdir'
+which seems to be undefined.  Please make sure it is defined" >&5
+$as_echo "$as_me: WARNING: $ac_file contains a reference to the variable \`datarootdir'
+which seems to be undefined.  Please make sure it is defined" >&2;}
+
+  rm -f "$ac_tmp/stdin"
+  case $ac_file in
+  -) cat "$ac_tmp/out" && rm -f "$ac_tmp/out";;
+  *) rm -f "$ac_file" && mv "$ac_tmp/out" "$ac_file";;
+  esac \
+  || as_fn_error $? "could not create $ac_file" "$LINENO" 5
+ ;;
+  :H)
+  #
+  # CONFIG_HEADER
+  #
+  if test x"$ac_file" != x-; then
+    {
+      $as_echo "/* $configure_input  */" \
+      && eval '$AWK -f "$ac_tmp/defines.awk"' "$ac_file_inputs"
+    } >"$ac_tmp/config.h" \
+      || as_fn_error $? "could not create $ac_file" "$LINENO" 5
+    if diff "$ac_file" "$ac_tmp/config.h" >/dev/null 2>&1; then
+      { $as_echo "$as_me:${as_lineno-$LINENO}: $ac_file is unchanged" >&5
+$as_echo "$as_me: $ac_file is unchanged" >&6;}
+    else
+      rm -f "$ac_file"
+      mv "$ac_tmp/config.h" "$ac_file" \
+	|| as_fn_error $? "could not create $ac_file" "$LINENO" 5
+    fi
+  else
+    $as_echo "/* $configure_input  */" \
+      && eval '$AWK -f "$ac_tmp/defines.awk"' "$ac_file_inputs" \
+      || as_fn_error $? "could not create -" "$LINENO" 5
+  fi
+# Compute "$ac_file"'s index in $config_headers.
+_am_arg="$ac_file"
+_am_stamp_count=1
+for _am_header in $config_headers :; do
+  case $_am_header in
+    $_am_arg | $_am_arg:* )
+      break ;;
+    * )
+      _am_stamp_count=`expr $_am_stamp_count + 1` ;;
+  esac
+done
+echo "timestamp for $_am_arg" >`$as_dirname -- "$_am_arg" ||
+$as_expr X"$_am_arg" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+	 X"$_am_arg" : 'X\(//\)[^/]' \| \
+	 X"$_am_arg" : 'X\(//\)$' \| \
+	 X"$_am_arg" : 'X\(/\)' \| . 2>/dev/null ||
+$as_echo X"$_am_arg" |
+    sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{
+	    s//\1/
+	    q
+	  }
+	  /^X\(\/\/\)[^/].*/{
+	    s//\1/
+	    q
+	  }
+	  /^X\(\/\/\)$/{
+	    s//\1/
+	    q
+	  }
+	  /^X\(\/\).*/{
+	    s//\1/
+	    q
+	  }
+	  s/.*/./; q'`/stamp-h$_am_stamp_count
+ ;;
+
+  :C)  { $as_echo "$as_me:${as_lineno-$LINENO}: executing $ac_file commands" >&5
+$as_echo "$as_me: executing $ac_file commands" >&6;}
+ ;;
+  esac
+
+
+  case $ac_file$ac_mode in
+    "depfiles":C) test x"$AMDEP_TRUE" != x"" || {
+  # Older Autoconf quotes --file arguments for eval, but not when files
+  # are listed without --file.  Let's play safe and only enable the eval
+  # if we detect the quoting.
+  case $CONFIG_FILES in
+  *\'*) eval set x "$CONFIG_FILES" ;;
+  *)   set x $CONFIG_FILES ;;
+  esac
+  shift
+  for mf
+  do
+    # Strip MF so we end up with the name of the file.
+    mf=`echo "$mf" | sed -e 's/:.*$//'`
+    # Check whether this is an Automake generated Makefile or not.
+    # We used to match only the files named 'Makefile.in', but
+    # some people rename them; so instead we look at the file content.
+    # Grep'ing the first line is not enough: some people post-process
+    # each Makefile.in and add a new line on top of each file to say so.
+    # Grep'ing the whole file is not good either: AIX grep has a line
+    # limit of 2048, but all sed's we know have understand at least 4000.
+    if sed -n 's,^#.*generated by automake.*,X,p' "$mf" | grep X >/dev/null 2>&1; then
+      dirpart=`$as_dirname -- "$mf" ||
+$as_expr X"$mf" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+	 X"$mf" : 'X\(//\)[^/]' \| \
+	 X"$mf" : 'X\(//\)$' \| \
+	 X"$mf" : 'X\(/\)' \| . 2>/dev/null ||
+$as_echo X"$mf" |
+    sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{
+	    s//\1/
+	    q
+	  }
+	  /^X\(\/\/\)[^/].*/{
+	    s//\1/
+	    q
+	  }
+	  /^X\(\/\/\)$/{
+	    s//\1/
+	    q
+	  }
+	  /^X\(\/\).*/{
+	    s//\1/
+	    q
+	  }
+	  s/.*/./; q'`
+    else
+      continue
+    fi
+    # Extract the definition of DEPDIR, am__include, and am__quote
+    # from the Makefile without running 'make'.
+    DEPDIR=`sed -n 's/^DEPDIR = //p' < "$mf"`
+    test -z "$DEPDIR" && continue
+    am__include=`sed -n 's/^am__include = //p' < "$mf"`
+    test -z "$am__include" && continue
+    am__quote=`sed -n 's/^am__quote = //p' < "$mf"`
+    # Find all dependency output files, they are included files with
+    # $(DEPDIR) in their names.  We invoke sed twice because it is the
+    # simplest approach to changing $(DEPDIR) to its actual value in the
+    # expansion.
+    for file in `sed -n "
+      s/^$am__include $am__quote\(.*(DEPDIR).*\)$am__quote"'$/\1/p' <"$mf" | \
+	 sed -e 's/\$(DEPDIR)/'"$DEPDIR"'/g'`; do
+      # Make sure the directory exists.
+      test -f "$dirpart/$file" && continue
+      fdir=`$as_dirname -- "$file" ||
+$as_expr X"$file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+	 X"$file" : 'X\(//\)[^/]' \| \
+	 X"$file" : 'X\(//\)$' \| \
+	 X"$file" : 'X\(/\)' \| . 2>/dev/null ||
+$as_echo X"$file" |
+    sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{
+	    s//\1/
+	    q
+	  }
+	  /^X\(\/\/\)[^/].*/{
+	    s//\1/
+	    q
+	  }
+	  /^X\(\/\/\)$/{
+	    s//\1/
+	    q
+	  }
+	  /^X\(\/\).*/{
+	    s//\1/
+	    q
+	  }
+	  s/.*/./; q'`
+      as_dir=$dirpart/$fdir; as_fn_mkdir_p
+      # echo "creating $dirpart/$file"
+      echo '# dummy' > "$dirpart/$file"
+    done
+  done
+}
+ ;;
+    "libtool":C)
+
+    # See if we are running on zsh, and set the options which allow our
+    # commands through without removal of \ escapes.
+    if test -n "${ZSH_VERSION+set}" ; then
+      setopt NO_GLOB_SUBST
+    fi
+
+    cfgfile="${ofile}T"
+    trap "$RM \"$cfgfile\"; exit 1" 1 2 15
+    $RM "$cfgfile"
+
+    cat <<_LT_EOF >> "$cfgfile"
+#! $SHELL
+
+# `$ECHO "$ofile" | sed 's%^.*/%%'` - Provide generalized library-building support services.
+# Generated automatically by $as_me ($PACKAGE$TIMESTAMP) $VERSION
+# Libtool was configured on host `(hostname || uname -n) 2>/dev/null | sed 1q`:
+# NOTE: Changes made to this file will be lost: look at ltmain.sh.
+#
+#   Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2003, 2004, 2005,
+#                 2006, 2007, 2008, 2009, 2010, 2011 Free Software
+#                 Foundation, Inc.
+#   Written by Gordon Matzigkeit, 1996
+#
+#   This file is part of GNU Libtool.
+#
+# GNU Libtool is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License as
+# published by the Free Software Foundation; either version 2 of
+# the License, or (at your option) any later version.
+#
+# As a special exception to the GNU General Public License,
+# if you distribute this file as part of a program or library that
+# is built using GNU Libtool, you may include this file under the
+# same distribution terms that you use for the rest of that program.
+#
+# GNU Libtool is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with GNU Libtool; see the file COPYING.  If not, a copy
+# can be downloaded from http://www.gnu.org/licenses/gpl.html, or
+# obtained by writing to the Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+
+
+# The names of the tagged configurations supported by this script.
+available_tags=""
+
+# ### BEGIN LIBTOOL CONFIG
+
+# Whether or not to build static libraries.
+build_old_libs=$enable_static
+
+# Which release of libtool.m4 was used?
+macro_version=$macro_version
+macro_revision=$macro_revision
+
+# Whether or not to build shared libraries.
+build_libtool_libs=$enable_shared
+
+# What type of objects to build.
+pic_mode=$pic_mode
+
+# Whether or not to optimize for fast installation.
+fast_install=$enable_fast_install
+
+# Shell to use when invoking shell scripts.
+SHELL=$lt_SHELL
+
+# An echo program that protects backslashes.
+ECHO=$lt_ECHO
+
+# The PATH separator for the build system.
+PATH_SEPARATOR=$lt_PATH_SEPARATOR
+
+# The host system.
+host_alias=$host_alias
+host=$host
+host_os=$host_os
+
+# The build system.
+build_alias=$build_alias
+build=$build
+build_os=$build_os
+
+# A sed program that does not truncate output.
+SED=$lt_SED
+
+# Sed that helps us avoid accidentally triggering echo(1) options like -n.
+Xsed="\$SED -e 1s/^X//"
+
+# A grep program that handles long lines.
+GREP=$lt_GREP
+
+# An ERE matcher.
+EGREP=$lt_EGREP
+
+# A literal string matcher.
+FGREP=$lt_FGREP
+
+# A BSD- or MS-compatible name lister.
+NM=$lt_NM
+
+# Whether we need soft or hard links.
+LN_S=$lt_LN_S
+
+# What is the maximum length of a command?
+max_cmd_len=$max_cmd_len
+
+# Object file suffix (normally "o").
+objext=$ac_objext
+
+# Executable file suffix (normally "").
+exeext=$exeext
+
+# whether the shell understands "unset".
+lt_unset=$lt_unset
+
+# turn spaces into newlines.
+SP2NL=$lt_lt_SP2NL
+
+# turn newlines into spaces.
+NL2SP=$lt_lt_NL2SP
+
+# convert \$build file names to \$host format.
+to_host_file_cmd=$lt_cv_to_host_file_cmd
+
+# convert \$build files to toolchain format.
+to_tool_file_cmd=$lt_cv_to_tool_file_cmd
+
+# An object symbol dumper.
+OBJDUMP=$lt_OBJDUMP
+
+# Method to check whether dependent libraries are shared objects.
+deplibs_check_method=$lt_deplibs_check_method
+
+# Command to use when deplibs_check_method = "file_magic".
+file_magic_cmd=$lt_file_magic_cmd
+
+# How to find potential files when deplibs_check_method = "file_magic".
+file_magic_glob=$lt_file_magic_glob
+
+# Find potential files using nocaseglob when deplibs_check_method = "file_magic".
+want_nocaseglob=$lt_want_nocaseglob
+
+# DLL creation program.
+DLLTOOL=$lt_DLLTOOL
+
+# Command to associate shared and link libraries.
+sharedlib_from_linklib_cmd=$lt_sharedlib_from_linklib_cmd
+
+# The archiver.
+AR=$lt_AR
+
+# Flags to create an archive.
+AR_FLAGS=$lt_AR_FLAGS
+
+# How to feed a file listing to the archiver.
+archiver_list_spec=$lt_archiver_list_spec
+
+# A symbol stripping program.
+STRIP=$lt_STRIP
+
+# Commands used to install an old-style archive.
+RANLIB=$lt_RANLIB
+old_postinstall_cmds=$lt_old_postinstall_cmds
+old_postuninstall_cmds=$lt_old_postuninstall_cmds
+
+# Whether to use a lock for old archive extraction.
+lock_old_archive_extraction=$lock_old_archive_extraction
+
+# A C compiler.
+LTCC=$lt_CC
+
+# LTCC compiler flags.
+LTCFLAGS=$lt_CFLAGS
+
+# Take the output of nm and produce a listing of raw symbols and C names.
+global_symbol_pipe=$lt_lt_cv_sys_global_symbol_pipe
+
+# Transform the output of nm in a proper C declaration.
+global_symbol_to_cdecl=$lt_lt_cv_sys_global_symbol_to_cdecl
+
+# Transform the output of nm in a C name address pair.
+global_symbol_to_c_name_address=$lt_lt_cv_sys_global_symbol_to_c_name_address
+
+# Transform the output of nm in a C name address pair when lib prefix is needed.
+global_symbol_to_c_name_address_lib_prefix=$lt_lt_cv_sys_global_symbol_to_c_name_address_lib_prefix
+
+# Specify filename containing input files for \$NM.
+nm_file_list_spec=$lt_nm_file_list_spec
+
+# The root where to search for dependent libraries,and in which our libraries should be installed.
+lt_sysroot=$lt_sysroot
+
+# The name of the directory that contains temporary libtool files.
+objdir=$objdir
+
+# Used to examine libraries when file_magic_cmd begins with "file".
+MAGIC_CMD=$MAGIC_CMD
+
+# Must we lock files when doing compilation?
+need_locks=$lt_need_locks
+
+# Manifest tool.
+MANIFEST_TOOL=$lt_MANIFEST_TOOL
+
+# Tool to manipulate archived DWARF debug symbol files on Mac OS X.
+DSYMUTIL=$lt_DSYMUTIL
+
+# Tool to change global to local symbols on Mac OS X.
+NMEDIT=$lt_NMEDIT
+
+# Tool to manipulate fat objects and archives on Mac OS X.
+LIPO=$lt_LIPO
+
+# ldd/readelf like tool for Mach-O binaries on Mac OS X.
+OTOOL=$lt_OTOOL
+
+# ldd/readelf like tool for 64 bit Mach-O binaries on Mac OS X 10.4.
+OTOOL64=$lt_OTOOL64
+
+# Old archive suffix (normally "a").
+libext=$libext
+
+# Shared library suffix (normally ".so").
+shrext_cmds=$lt_shrext_cmds
+
+# The commands to extract the exported symbol list from a shared archive.
+extract_expsyms_cmds=$lt_extract_expsyms_cmds
+
+# Variables whose values should be saved in libtool wrapper scripts and
+# restored at link time.
+variables_saved_for_relink=$lt_variables_saved_for_relink
+
+# Do we need the "lib" prefix for modules?
+need_lib_prefix=$need_lib_prefix
+
+# Do we need a version for libraries?
+need_version=$need_version
+
+# Library versioning type.
+version_type=$version_type
+
+# Shared library runtime path variable.
+runpath_var=$runpath_var
+
+# Shared library path variable.
+shlibpath_var=$shlibpath_var
+
+# Is shlibpath searched before the hard-coded library search path?
+shlibpath_overrides_runpath=$shlibpath_overrides_runpath
+
+# Format of library name prefix.
+libname_spec=$lt_libname_spec
+
+# List of archive names.  First name is the real one, the rest are links.
+# The last name is the one that the linker finds with -lNAME
+library_names_spec=$lt_library_names_spec
+
+# The coded name of the library, if different from the real name.
+soname_spec=$lt_soname_spec
+
+# Permission mode override for installation of shared libraries.
+install_override_mode=$lt_install_override_mode
+
+# Command to use after installation of a shared archive.
+postinstall_cmds=$lt_postinstall_cmds
+
+# Command to use after uninstallation of a shared archive.
+postuninstall_cmds=$lt_postuninstall_cmds
+
+# Commands used to finish a libtool library installation in a directory.
+finish_cmds=$lt_finish_cmds
+
+# As "finish_cmds", except a single script fragment to be evaled but
+# not shown.
+finish_eval=$lt_finish_eval
+
+# Whether we should hardcode library paths into libraries.
+hardcode_into_libs=$hardcode_into_libs
+
+# Compile-time system search path for libraries.
+sys_lib_search_path_spec=$lt_sys_lib_search_path_spec
+
+# Run-time system search path for libraries.
+sys_lib_dlsearch_path_spec=$lt_sys_lib_dlsearch_path_spec
+
+# Whether dlopen is supported.
+dlopen_support=$enable_dlopen
+
+# Whether dlopen of programs is supported.
+dlopen_self=$enable_dlopen_self
+
+# Whether dlopen of statically linked programs is supported.
+dlopen_self_static=$enable_dlopen_self_static
+
+# Commands to strip libraries.
+old_striplib=$lt_old_striplib
+striplib=$lt_striplib
+
+
+# The linker used to build libraries.
+LD=$lt_LD
+
+# How to create reloadable object files.
+reload_flag=$lt_reload_flag
+reload_cmds=$lt_reload_cmds
+
+# Commands used to build an old-style archive.
+old_archive_cmds=$lt_old_archive_cmds
+
+# A language specific compiler.
+CC=$lt_compiler
+
+# Is the compiler the GNU compiler?
+with_gcc=$GCC
+
+# Compiler flag to turn off builtin functions.
+no_builtin_flag=$lt_lt_prog_compiler_no_builtin_flag
+
+# Additional compiler flags for building library objects.
+pic_flag=$lt_lt_prog_compiler_pic
+
+# How to pass a linker flag through the compiler.
+wl=$lt_lt_prog_compiler_wl
+
+# Compiler flag to prevent dynamic linking.
+link_static_flag=$lt_lt_prog_compiler_static
+
+# Does compiler simultaneously support -c and -o options?
+compiler_c_o=$lt_lt_cv_prog_compiler_c_o
+
+# Whether or not to add -lc for building shared libraries.
+build_libtool_need_lc=$archive_cmds_need_lc
+
+# Whether or not to disallow shared libs when runtime libs are static.
+allow_libtool_libs_with_static_runtimes=$enable_shared_with_static_runtimes
+
+# Compiler flag to allow reflexive dlopens.
+export_dynamic_flag_spec=$lt_export_dynamic_flag_spec
+
+# Compiler flag to generate shared objects directly from archives.
+whole_archive_flag_spec=$lt_whole_archive_flag_spec
+
+# Whether the compiler copes with passing no objects directly.
+compiler_needs_object=$lt_compiler_needs_object
+
+# Create an old-style archive from a shared archive.
+old_archive_from_new_cmds=$lt_old_archive_from_new_cmds
+
+# Create a temporary old-style archive to link instead of a shared archive.
+old_archive_from_expsyms_cmds=$lt_old_archive_from_expsyms_cmds
+
+# Commands used to build a shared archive.
+archive_cmds=$lt_archive_cmds
+archive_expsym_cmds=$lt_archive_expsym_cmds
+
+# Commands used to build a loadable module if different from building
+# a shared archive.
+module_cmds=$lt_module_cmds
+module_expsym_cmds=$lt_module_expsym_cmds
+
+# Whether we are building with GNU ld or not.
+with_gnu_ld=$lt_with_gnu_ld
+
+# Flag that allows shared libraries with undefined symbols to be built.
+allow_undefined_flag=$lt_allow_undefined_flag
+
+# Flag that enforces no undefined symbols.
+no_undefined_flag=$lt_no_undefined_flag
+
+# Flag to hardcode \$libdir into a binary during linking.
+# This must work even if \$libdir does not exist
+hardcode_libdir_flag_spec=$lt_hardcode_libdir_flag_spec
+
+# Whether we need a single "-rpath" flag with a separated argument.
+hardcode_libdir_separator=$lt_hardcode_libdir_separator
+
+# Set to "yes" if using DIR/libNAME\${shared_ext} during linking hardcodes
+# DIR into the resulting binary.
+hardcode_direct=$hardcode_direct
+
+# Set to "yes" if using DIR/libNAME\${shared_ext} during linking hardcodes
+# DIR into the resulting binary and the resulting library dependency is
+# "absolute",i.e impossible to change by setting \${shlibpath_var} if the
+# library is relocated.
+hardcode_direct_absolute=$hardcode_direct_absolute
+
+# Set to "yes" if using the -LDIR flag during linking hardcodes DIR
+# into the resulting binary.
+hardcode_minus_L=$hardcode_minus_L
+
+# Set to "yes" if using SHLIBPATH_VAR=DIR during linking hardcodes DIR
+# into the resulting binary.
+hardcode_shlibpath_var=$hardcode_shlibpath_var
+
+# Set to "yes" if building a shared library automatically hardcodes DIR
+# into the library and all subsequent libraries and executables linked
+# against it.
+hardcode_automatic=$hardcode_automatic
+
+# Set to yes if linker adds runtime paths of dependent libraries
+# to runtime path list.
+inherit_rpath=$inherit_rpath
+
+# Whether libtool must link a program against all its dependency libraries.
+link_all_deplibs=$link_all_deplibs
+
+# Set to "yes" if exported symbols are required.
+always_export_symbols=$always_export_symbols
+
+# The commands to list exported symbols.
+export_symbols_cmds=$lt_export_symbols_cmds
+
+# Symbols that should not be listed in the preloaded symbols.
+exclude_expsyms=$lt_exclude_expsyms
+
+# Symbols that must always be exported.
+include_expsyms=$lt_include_expsyms
+
+# Commands necessary for linking programs (against libraries) with templates.
+prelink_cmds=$lt_prelink_cmds
+
+# Commands necessary for finishing linking programs.
+postlink_cmds=$lt_postlink_cmds
+
+# Specify filename containing input files.
+file_list_spec=$lt_file_list_spec
+
+# How to hardcode a shared library path into an executable.
+hardcode_action=$hardcode_action
+
+# ### END LIBTOOL CONFIG
+
+_LT_EOF
+
+  case $host_os in
+  aix3*)
+    cat <<\_LT_EOF >> "$cfgfile"
+# AIX sometimes has problems with the GCC collect2 program.  For some
+# reason, if we set the COLLECT_NAMES environment variable, the problems
+# vanish in a puff of smoke.
+if test "X${COLLECT_NAMES+set}" != Xset; then
+  COLLECT_NAMES=
+  export COLLECT_NAMES
+fi
+_LT_EOF
+    ;;
+  esac
+
+
+ltmain="$ac_aux_dir/ltmain.sh"
+
+
+  # We use sed instead of cat because bash on DJGPP gets confused if
+  # if finds mixed CR/LF and LF-only lines.  Since sed operates in
+  # text mode, it properly converts lines to CR/LF.  This bash problem
+  # is reportedly fixed, but why not run on old versions too?
+  sed '$q' "$ltmain" >> "$cfgfile" \
+     || (rm -f "$cfgfile"; exit 1)
+
+  if test x"$xsi_shell" = xyes; then
+  sed -e '/^func_dirname ()$/,/^} # func_dirname /c\
+func_dirname ()\
+{\
+\    case ${1} in\
+\      */*) func_dirname_result="${1%/*}${2}" ;;\
+\      *  ) func_dirname_result="${3}" ;;\
+\    esac\
+} # Extended-shell func_dirname implementation' "$cfgfile" > $cfgfile.tmp \
+  && mv -f "$cfgfile.tmp" "$cfgfile" \
+    || (rm -f "$cfgfile" && cp "$cfgfile.tmp" "$cfgfile" && rm -f "$cfgfile.tmp")
+test 0 -eq $? || _lt_function_replace_fail=:
+
+
+  sed -e '/^func_basename ()$/,/^} # func_basename /c\
+func_basename ()\
+{\
+\    func_basename_result="${1##*/}"\
+} # Extended-shell func_basename implementation' "$cfgfile" > $cfgfile.tmp \
+  && mv -f "$cfgfile.tmp" "$cfgfile" \
+    || (rm -f "$cfgfile" && cp "$cfgfile.tmp" "$cfgfile" && rm -f "$cfgfile.tmp")
+test 0 -eq $? || _lt_function_replace_fail=:
+
+
+  sed -e '/^func_dirname_and_basename ()$/,/^} # func_dirname_and_basename /c\
+func_dirname_and_basename ()\
+{\
+\    case ${1} in\
+\      */*) func_dirname_result="${1%/*}${2}" ;;\
+\      *  ) func_dirname_result="${3}" ;;\
+\    esac\
+\    func_basename_result="${1##*/}"\
+} # Extended-shell func_dirname_and_basename implementation' "$cfgfile" > $cfgfile.tmp \
+  && mv -f "$cfgfile.tmp" "$cfgfile" \
+    || (rm -f "$cfgfile" && cp "$cfgfile.tmp" "$cfgfile" && rm -f "$cfgfile.tmp")
+test 0 -eq $? || _lt_function_replace_fail=:
+
+
+  sed -e '/^func_stripname ()$/,/^} # func_stripname /c\
+func_stripname ()\
+{\
+\    # pdksh 5.2.14 does not do ${X%$Y} correctly if both X and Y are\
+\    # positional parameters, so assign one to ordinary parameter first.\
+\    func_stripname_result=${3}\
+\    func_stripname_result=${func_stripname_result#"${1}"}\
+\    func_stripname_result=${func_stripname_result%"${2}"}\
+} # Extended-shell func_stripname implementation' "$cfgfile" > $cfgfile.tmp \
+  && mv -f "$cfgfile.tmp" "$cfgfile" \
+    || (rm -f "$cfgfile" && cp "$cfgfile.tmp" "$cfgfile" && rm -f "$cfgfile.tmp")
+test 0 -eq $? || _lt_function_replace_fail=:
+
+
+  sed -e '/^func_split_long_opt ()$/,/^} # func_split_long_opt /c\
+func_split_long_opt ()\
+{\
+\    func_split_long_opt_name=${1%%=*}\
+\    func_split_long_opt_arg=${1#*=}\
+} # Extended-shell func_split_long_opt implementation' "$cfgfile" > $cfgfile.tmp \
+  && mv -f "$cfgfile.tmp" "$cfgfile" \
+    || (rm -f "$cfgfile" && cp "$cfgfile.tmp" "$cfgfile" && rm -f "$cfgfile.tmp")
+test 0 -eq $? || _lt_function_replace_fail=:
+
+
+  sed -e '/^func_split_short_opt ()$/,/^} # func_split_short_opt /c\
+func_split_short_opt ()\
+{\
+\    func_split_short_opt_arg=${1#??}\
+\    func_split_short_opt_name=${1%"$func_split_short_opt_arg"}\
+} # Extended-shell func_split_short_opt implementation' "$cfgfile" > $cfgfile.tmp \
+  && mv -f "$cfgfile.tmp" "$cfgfile" \
+    || (rm -f "$cfgfile" && cp "$cfgfile.tmp" "$cfgfile" && rm -f "$cfgfile.tmp")
+test 0 -eq $? || _lt_function_replace_fail=:
+
+
+  sed -e '/^func_lo2o ()$/,/^} # func_lo2o /c\
+func_lo2o ()\
+{\
+\    case ${1} in\
+\      *.lo) func_lo2o_result=${1%.lo}.${objext} ;;\
+\      *)    func_lo2o_result=${1} ;;\
+\    esac\
+} # Extended-shell func_lo2o implementation' "$cfgfile" > $cfgfile.tmp \
+  && mv -f "$cfgfile.tmp" "$cfgfile" \
+    || (rm -f "$cfgfile" && cp "$cfgfile.tmp" "$cfgfile" && rm -f "$cfgfile.tmp")
+test 0 -eq $? || _lt_function_replace_fail=:
+
+
+  sed -e '/^func_xform ()$/,/^} # func_xform /c\
+func_xform ()\
+{\
+    func_xform_result=${1%.*}.lo\
+} # Extended-shell func_xform implementation' "$cfgfile" > $cfgfile.tmp \
+  && mv -f "$cfgfile.tmp" "$cfgfile" \
+    || (rm -f "$cfgfile" && cp "$cfgfile.tmp" "$cfgfile" && rm -f "$cfgfile.tmp")
+test 0 -eq $? || _lt_function_replace_fail=:
+
+
+  sed -e '/^func_arith ()$/,/^} # func_arith /c\
+func_arith ()\
+{\
+    func_arith_result=$(( $* ))\
+} # Extended-shell func_arith implementation' "$cfgfile" > $cfgfile.tmp \
+  && mv -f "$cfgfile.tmp" "$cfgfile" \
+    || (rm -f "$cfgfile" && cp "$cfgfile.tmp" "$cfgfile" && rm -f "$cfgfile.tmp")
+test 0 -eq $? || _lt_function_replace_fail=:
+
+
+  sed -e '/^func_len ()$/,/^} # func_len /c\
+func_len ()\
+{\
+    func_len_result=${#1}\
+} # Extended-shell func_len implementation' "$cfgfile" > $cfgfile.tmp \
+  && mv -f "$cfgfile.tmp" "$cfgfile" \
+    || (rm -f "$cfgfile" && cp "$cfgfile.tmp" "$cfgfile" && rm -f "$cfgfile.tmp")
+test 0 -eq $? || _lt_function_replace_fail=:
+
+fi
+
+if test x"$lt_shell_append" = xyes; then
+  sed -e '/^func_append ()$/,/^} # func_append /c\
+func_append ()\
+{\
+    eval "${1}+=\\${2}"\
+} # Extended-shell func_append implementation' "$cfgfile" > $cfgfile.tmp \
+  && mv -f "$cfgfile.tmp" "$cfgfile" \
+    || (rm -f "$cfgfile" && cp "$cfgfile.tmp" "$cfgfile" && rm -f "$cfgfile.tmp")
+test 0 -eq $? || _lt_function_replace_fail=:
+
+
+  sed -e '/^func_append_quoted ()$/,/^} # func_append_quoted /c\
+func_append_quoted ()\
+{\
+\    func_quote_for_eval "${2}"\
+\    eval "${1}+=\\\\ \\$func_quote_for_eval_result"\
+} # Extended-shell func_append_quoted implementation' "$cfgfile" > $cfgfile.tmp \
+  && mv -f "$cfgfile.tmp" "$cfgfile" \
+    || (rm -f "$cfgfile" && cp "$cfgfile.tmp" "$cfgfile" && rm -f "$cfgfile.tmp")
+test 0 -eq $? || _lt_function_replace_fail=:
+
+
+  # Save a `func_append' function call where possible by direct use of '+='
+  sed -e 's%func_append \([a-zA-Z_]\{1,\}\) "%\1+="%g' $cfgfile > $cfgfile.tmp \
+    && mv -f "$cfgfile.tmp" "$cfgfile" \
+      || (rm -f "$cfgfile" && cp "$cfgfile.tmp" "$cfgfile" && rm -f "$cfgfile.tmp")
+  test 0 -eq $? || _lt_function_replace_fail=:
+else
+  # Save a `func_append' function call even when '+=' is not available
+  sed -e 's%func_append \([a-zA-Z_]\{1,\}\) "%\1="$\1%g' $cfgfile > $cfgfile.tmp \
+    && mv -f "$cfgfile.tmp" "$cfgfile" \
+      || (rm -f "$cfgfile" && cp "$cfgfile.tmp" "$cfgfile" && rm -f "$cfgfile.tmp")
+  test 0 -eq $? || _lt_function_replace_fail=:
+fi
+
+if test x"$_lt_function_replace_fail" = x":"; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: Unable to substitute extended shell functions in $ofile" >&5
+$as_echo "$as_me: WARNING: Unable to substitute extended shell functions in $ofile" >&2;}
+fi
+
+
+   mv -f "$cfgfile" "$ofile" ||
+    (rm -f "$ofile" && cp "$cfgfile" "$ofile" && rm -f "$cfgfile")
+  chmod +x "$ofile"
+
+ ;;
+
+  esac
+done # for ac_tag
+
+
+as_fn_exit 0
+_ACEOF
+ac_clean_files=$ac_clean_files_save
+
+test $ac_write_fail = 0 ||
+  as_fn_error $? "write failure creating $CONFIG_STATUS" "$LINENO" 5
+
+
+# configure is writing to config.log, and then calls config.status.
+# config.status does its own redirection, appending to config.log.
+# Unfortunately, on DOS this fails, as config.log is still kept open
+# by configure, so config.status won't be able to write to it; its
+# output is simply discarded.  So we exec the FD to /dev/null,
+# effectively closing config.log, so it can be properly (re)opened and
+# appended to by config.status.  When coming back to configure, we
+# need to make the FD available again.
+if test "$no_create" != yes; then
+  ac_cs_success=:
+  ac_config_status_args=
+  test "$silent" = yes &&
+    ac_config_status_args="$ac_config_status_args --quiet"
+  exec 5>/dev/null
+  $SHELL $CONFIG_STATUS $ac_config_status_args || ac_cs_success=false
+  exec 5>>config.log
+  # Use ||, not &&, to avoid exiting from the if with $? = 1, which
+  # would make configure fail if this is the last instruction.
+  $ac_cs_success || as_fn_exit 1
+fi
+if test -n "$ac_unrecognized_opts" && test "$enable_option_checking" != no; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: unrecognized options: $ac_unrecognized_opts" >&5
+$as_echo "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2;}
+fi
+
diff --git a/bluez/configure.ac b/bluez/configure.ac
new file mode 100644
index 0000000..0ab2df1
--- /dev/null
+++ b/bluez/configure.ac
@@ -0,0 +1,265 @@
+AC_PREREQ(2.60)
+AC_INIT(bluez, 5.17)
+
+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
+
+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))
+
+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(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 >= 143, dummy=yes,
+				AC_MSG_ERROR(libudev >= 143 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(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
+
+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/depcomp b/bluez/depcomp
new file mode 100755
index 0000000..25a39e6
--- /dev/null
+++ b/bluez/depcomp
@@ -0,0 +1,708 @@
+#! /bin/sh
+# depcomp - compile a program generating dependencies as side-effects
+
+scriptversion=2012-03-27.16; # UTC
+
+# Copyright (C) 1999, 2000, 2003, 2004, 2005, 2006, 2007, 2009, 2010,
+# 2011, 2012 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, or (at your option)
+# any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+# As a special exception to the GNU General Public License, if you
+# distribute this file as part of a program that contains a
+# configuration script generated by Autoconf, you may include it under
+# the same distribution terms that you use for the rest of that program.
+
+# Originally written by Alexandre Oliva <oliva@dcc.unicamp.br>.
+
+case $1 in
+  '')
+     echo "$0: No command.  Try '$0 --help' for more information." 1>&2
+     exit 1;
+     ;;
+  -h | --h*)
+    cat <<\EOF
+Usage: depcomp [--help] [--version] PROGRAM [ARGS]
+
+Run PROGRAMS ARGS to compile a file, generating dependencies
+as side-effects.
+
+Environment variables:
+  depmode     Dependency tracking mode.
+  source      Source file read by 'PROGRAMS ARGS'.
+  object      Object file output by 'PROGRAMS ARGS'.
+  DEPDIR      directory where to store dependencies.
+  depfile     Dependency file to output.
+  tmpdepfile  Temporary file to use when outputting dependencies.
+  libtool     Whether libtool is used (yes/no).
+
+Report bugs to <bug-automake@gnu.org>.
+EOF
+    exit $?
+    ;;
+  -v | --v*)
+    echo "depcomp $scriptversion"
+    exit $?
+    ;;
+esac
+
+# A tabulation character.
+tab='	'
+# A newline character.
+nl='
+'
+
+if test -z "$depmode" || test -z "$source" || test -z "$object"; then
+  echo "depcomp: Variables source, object and depmode must be set" 1>&2
+  exit 1
+fi
+
+# Dependencies for sub/bar.o or sub/bar.obj go into sub/.deps/bar.Po.
+depfile=${depfile-`echo "$object" |
+  sed 's|[^\\/]*$|'${DEPDIR-.deps}'/&|;s|\.\([^.]*\)$|.P\1|;s|Pobj$|Po|'`}
+tmpdepfile=${tmpdepfile-`echo "$depfile" | sed 's/\.\([^.]*\)$/.T\1/'`}
+
+rm -f "$tmpdepfile"
+
+# Some modes work just like other modes, but use different flags.  We
+# parameterize here, but still list the modes in the big case below,
+# to make depend.m4 easier to write.  Note that we *cannot* use a case
+# here, because this file can only contain one case statement.
+if test "$depmode" = hp; then
+  # HP compiler uses -M and no extra arg.
+  gccflag=-M
+  depmode=gcc
+fi
+
+if test "$depmode" = dashXmstdout; then
+   # This is just like dashmstdout with a different argument.
+   dashmflag=-xM
+   depmode=dashmstdout
+fi
+
+cygpath_u="cygpath -u -f -"
+if test "$depmode" = msvcmsys; then
+   # This is just like msvisualcpp but w/o cygpath translation.
+   # Just convert the backslash-escaped backslashes to single forward
+   # slashes to satisfy depend.m4
+   cygpath_u='sed s,\\\\,/,g'
+   depmode=msvisualcpp
+fi
+
+if test "$depmode" = msvc7msys; then
+   # This is just like msvc7 but w/o cygpath translation.
+   # Just convert the backslash-escaped backslashes to single forward
+   # slashes to satisfy depend.m4
+   cygpath_u='sed s,\\\\,/,g'
+   depmode=msvc7
+fi
+
+if test "$depmode" = xlc; then
+   # IBM C/C++ Compilers xlc/xlC can output gcc-like dependency informations.
+   gccflag=-qmakedep=gcc,-MF
+   depmode=gcc
+fi
+
+case "$depmode" in
+gcc3)
+## gcc 3 implements dependency tracking that does exactly what
+## we want.  Yay!  Note: for some reason libtool 1.4 doesn't like
+## it if -MD -MP comes after the -MF stuff.  Hmm.
+## Unfortunately, FreeBSD c89 acceptance of flags depends upon
+## the command line argument order; so add the flags where they
+## appear in depend2.am.  Note that the slowdown incurred here
+## affects only configure: in makefiles, %FASTDEP% shortcuts this.
+  for arg
+  do
+    case $arg in
+    -c) set fnord "$@" -MT "$object" -MD -MP -MF "$tmpdepfile" "$arg" ;;
+    *)  set fnord "$@" "$arg" ;;
+    esac
+    shift # fnord
+    shift # $arg
+  done
+  "$@"
+  stat=$?
+  if test $stat -eq 0; then :
+  else
+    rm -f "$tmpdepfile"
+    exit $stat
+  fi
+  mv "$tmpdepfile" "$depfile"
+  ;;
+
+gcc)
+## There are various ways to get dependency output from gcc.  Here's
+## why we pick this rather obscure method:
+## - Don't want to use -MD because we'd like the dependencies to end
+##   up in a subdir.  Having to rename by hand is ugly.
+##   (We might end up doing this anyway to support other compilers.)
+## - The DEPENDENCIES_OUTPUT environment variable makes gcc act like
+##   -MM, not -M (despite what the docs say).
+## - Using -M directly means running the compiler twice (even worse
+##   than renaming).
+  if test -z "$gccflag"; then
+    gccflag=-MD,
+  fi
+  "$@" -Wp,"$gccflag$tmpdepfile"
+  stat=$?
+  if test $stat -eq 0; then :
+  else
+    rm -f "$tmpdepfile"
+    exit $stat
+  fi
+  rm -f "$depfile"
+  echo "$object : \\" > "$depfile"
+  alpha=ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz
+## The second -e expression handles DOS-style file names with drive letters.
+  sed -e 's/^[^:]*: / /' \
+      -e 's/^['$alpha']:\/[^:]*: / /' < "$tmpdepfile" >> "$depfile"
+## This next piece of magic avoids the "deleted header file" problem.
+## The problem is that when a header file which appears in a .P file
+## is deleted, the dependency causes make to die (because there is
+## typically no way to rebuild the header).  We avoid this by adding
+## dummy dependencies for each header file.  Too bad gcc doesn't do
+## this for us directly.
+  tr ' ' "$nl" < "$tmpdepfile" |
+## Some versions of gcc put a space before the ':'.  On the theory
+## that the space means something, we add a space to the output as
+## well.  hp depmode also adds that space, but also prefixes the VPATH
+## to the object.  Take care to not repeat it in the output.
+## Some versions of the HPUX 10.20 sed can't process this invocation
+## correctly.  Breaking it into two sed invocations is a workaround.
+    sed -e 's/^\\$//' -e '/^$/d' -e "s|.*$object$||" -e '/:$/d' \
+      | sed -e 's/$/ :/' >> "$depfile"
+  rm -f "$tmpdepfile"
+  ;;
+
+hp)
+  # This case exists only to let depend.m4 do its work.  It works by
+  # looking at the text of this script.  This case will never be run,
+  # since it is checked for above.
+  exit 1
+  ;;
+
+sgi)
+  if test "$libtool" = yes; then
+    "$@" "-Wp,-MDupdate,$tmpdepfile"
+  else
+    "$@" -MDupdate "$tmpdepfile"
+  fi
+  stat=$?
+  if test $stat -eq 0; then :
+  else
+    rm -f "$tmpdepfile"
+    exit $stat
+  fi
+  rm -f "$depfile"
+
+  if test -f "$tmpdepfile"; then  # yes, the sourcefile depend on other files
+    echo "$object : \\" > "$depfile"
+
+    # Clip off the initial element (the dependent).  Don't try to be
+    # clever and replace this with sed code, as IRIX sed won't handle
+    # lines with more than a fixed number of characters (4096 in
+    # IRIX 6.2 sed, 8192 in IRIX 6.5).  We also remove comment lines;
+    # the IRIX cc adds comments like '#:fec' to the end of the
+    # dependency line.
+    tr ' ' "$nl" < "$tmpdepfile" \
+    | sed -e 's/^.*\.o://' -e 's/#.*$//' -e '/^$/ d' | \
+    tr "$nl" ' ' >> "$depfile"
+    echo >> "$depfile"
+
+    # The second pass generates a dummy entry for each header file.
+    tr ' ' "$nl" < "$tmpdepfile" \
+   | sed -e 's/^.*\.o://' -e 's/#.*$//' -e '/^$/ d' -e 's/$/:/' \
+   >> "$depfile"
+  else
+    # The sourcefile does not contain any dependencies, so just
+    # store a dummy comment line, to avoid errors with the Makefile
+    # "include basename.Plo" scheme.
+    echo "#dummy" > "$depfile"
+  fi
+  rm -f "$tmpdepfile"
+  ;;
+
+xlc)
+  # This case exists only to let depend.m4 do its work.  It works by
+  # looking at the text of this script.  This case will never be run,
+  # since it is checked for above.
+  exit 1
+  ;;
+
+aix)
+  # The C for AIX Compiler uses -M and outputs the dependencies
+  # in a .u file.  In older versions, this file always lives in the
+  # current directory.  Also, the AIX compiler puts '$object:' at the
+  # start of each line; $object doesn't have directory information.
+  # Version 6 uses the directory in both cases.
+  dir=`echo "$object" | sed -e 's|/[^/]*$|/|'`
+  test "x$dir" = "x$object" && dir=
+  base=`echo "$object" | sed -e 's|^.*/||' -e 's/\.o$//' -e 's/\.lo$//'`
+  if test "$libtool" = yes; then
+    tmpdepfile1=$dir$base.u
+    tmpdepfile2=$base.u
+    tmpdepfile3=$dir.libs/$base.u
+    "$@" -Wc,-M
+  else
+    tmpdepfile1=$dir$base.u
+    tmpdepfile2=$dir$base.u
+    tmpdepfile3=$dir$base.u
+    "$@" -M
+  fi
+  stat=$?
+
+  if test $stat -eq 0; then :
+  else
+    rm -f "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3"
+    exit $stat
+  fi
+
+  for tmpdepfile in "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3"
+  do
+    test -f "$tmpdepfile" && break
+  done
+  if test -f "$tmpdepfile"; then
+    # Each line is of the form 'foo.o: dependent.h'.
+    # Do two passes, one to just change these to
+    # '$object: dependent.h' and one to simply 'dependent.h:'.
+    sed -e "s,^.*\.[a-z]*:,$object:," < "$tmpdepfile" > "$depfile"
+    sed -e 's,^.*\.[a-z]*:['"$tab"' ]*,,' -e 's,$,:,' < "$tmpdepfile" >> "$depfile"
+  else
+    # The sourcefile does not contain any dependencies, so just
+    # store a dummy comment line, to avoid errors with the Makefile
+    # "include basename.Plo" scheme.
+    echo "#dummy" > "$depfile"
+  fi
+  rm -f "$tmpdepfile"
+  ;;
+
+icc)
+  # Intel's C compiler anf tcc (Tiny C Compiler) understand '-MD -MF file'.
+  # However on
+  #    $CC -MD -MF foo.d -c -o sub/foo.o sub/foo.c
+  # ICC 7.0 will fill foo.d with something like
+  #    foo.o: sub/foo.c
+  #    foo.o: sub/foo.h
+  # which is wrong.  We want
+  #    sub/foo.o: sub/foo.c
+  #    sub/foo.o: sub/foo.h
+  #    sub/foo.c:
+  #    sub/foo.h:
+  # ICC 7.1 will output
+  #    foo.o: sub/foo.c sub/foo.h
+  # and will wrap long lines using '\':
+  #    foo.o: sub/foo.c ... \
+  #     sub/foo.h ... \
+  #     ...
+  # tcc 0.9.26 (FIXME still under development at the moment of writing)
+  # will emit a similar output, but also prepend the continuation lines
+  # with horizontal tabulation characters.
+  "$@" -MD -MF "$tmpdepfile"
+  stat=$?
+  if test $stat -eq 0; then :
+  else
+    rm -f "$tmpdepfile"
+    exit $stat
+  fi
+  rm -f "$depfile"
+  # Each line is of the form 'foo.o: dependent.h',
+  # or 'foo.o: dep1.h dep2.h \', or ' dep3.h dep4.h \'.
+  # Do two passes, one to just change these to
+  # '$object: dependent.h' and one to simply 'dependent.h:'.
+  sed -e "s/^[ $tab][ $tab]*/  /" -e "s,^[^:]*:,$object :," \
+    < "$tmpdepfile" > "$depfile"
+  sed '
+    s/[ '"$tab"'][ '"$tab"']*/ /g
+    s/^ *//
+    s/ *\\*$//
+    s/^[^:]*: *//
+    /^$/d
+    /:$/d
+    s/$/ :/
+  ' < "$tmpdepfile" >> "$depfile"
+  rm -f "$tmpdepfile"
+  ;;
+
+hp2)
+  # The "hp" stanza above does not work with aCC (C++) and HP's ia64
+  # compilers, which have integrated preprocessors.  The correct option
+  # to use with these is +Maked; it writes dependencies to a file named
+  # 'foo.d', which lands next to the object file, wherever that
+  # happens to be.
+  # Much of this is similar to the tru64 case; see comments there.
+  dir=`echo "$object" | sed -e 's|/[^/]*$|/|'`
+  test "x$dir" = "x$object" && dir=
+  base=`echo "$object" | sed -e 's|^.*/||' -e 's/\.o$//' -e 's/\.lo$//'`
+  if test "$libtool" = yes; then
+    tmpdepfile1=$dir$base.d
+    tmpdepfile2=$dir.libs/$base.d
+    "$@" -Wc,+Maked
+  else
+    tmpdepfile1=$dir$base.d
+    tmpdepfile2=$dir$base.d
+    "$@" +Maked
+  fi
+  stat=$?
+  if test $stat -eq 0; then :
+  else
+     rm -f "$tmpdepfile1" "$tmpdepfile2"
+     exit $stat
+  fi
+
+  for tmpdepfile in "$tmpdepfile1" "$tmpdepfile2"
+  do
+    test -f "$tmpdepfile" && break
+  done
+  if test -f "$tmpdepfile"; then
+    sed -e "s,^.*\.[a-z]*:,$object:," "$tmpdepfile" > "$depfile"
+    # Add 'dependent.h:' lines.
+    sed -ne '2,${
+	       s/^ *//
+	       s/ \\*$//
+	       s/$/:/
+	       p
+	     }' "$tmpdepfile" >> "$depfile"
+  else
+    echo "#dummy" > "$depfile"
+  fi
+  rm -f "$tmpdepfile" "$tmpdepfile2"
+  ;;
+
+tru64)
+   # The Tru64 compiler uses -MD to generate dependencies as a side
+   # effect.  'cc -MD -o foo.o ...' puts the dependencies into 'foo.o.d'.
+   # At least on Alpha/Redhat 6.1, Compaq CCC V6.2-504 seems to put
+   # dependencies in 'foo.d' instead, so we check for that too.
+   # Subdirectories are respected.
+   dir=`echo "$object" | sed -e 's|/[^/]*$|/|'`
+   test "x$dir" = "x$object" && dir=
+   base=`echo "$object" | sed -e 's|^.*/||' -e 's/\.o$//' -e 's/\.lo$//'`
+
+   if test "$libtool" = yes; then
+      # With Tru64 cc, shared objects can also be used to make a
+      # static library.  This mechanism is used in libtool 1.4 series to
+      # handle both shared and static libraries in a single compilation.
+      # With libtool 1.4, dependencies were output in $dir.libs/$base.lo.d.
+      #
+      # With libtool 1.5 this exception was removed, and libtool now
+      # generates 2 separate objects for the 2 libraries.  These two
+      # compilations output dependencies in $dir.libs/$base.o.d and
+      # in $dir$base.o.d.  We have to check for both files, because
+      # one of the two compilations can be disabled.  We should prefer
+      # $dir$base.o.d over $dir.libs/$base.o.d because the latter is
+      # automatically cleaned when .libs/ is deleted, while ignoring
+      # the former would cause a distcleancheck panic.
+      tmpdepfile1=$dir.libs/$base.lo.d   # libtool 1.4
+      tmpdepfile2=$dir$base.o.d          # libtool 1.5
+      tmpdepfile3=$dir.libs/$base.o.d    # libtool 1.5
+      tmpdepfile4=$dir.libs/$base.d      # Compaq CCC V6.2-504
+      "$@" -Wc,-MD
+   else
+      tmpdepfile1=$dir$base.o.d
+      tmpdepfile2=$dir$base.d
+      tmpdepfile3=$dir$base.d
+      tmpdepfile4=$dir$base.d
+      "$@" -MD
+   fi
+
+   stat=$?
+   if test $stat -eq 0; then :
+   else
+      rm -f "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3" "$tmpdepfile4"
+      exit $stat
+   fi
+
+   for tmpdepfile in "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3" "$tmpdepfile4"
+   do
+     test -f "$tmpdepfile" && break
+   done
+   if test -f "$tmpdepfile"; then
+      sed -e "s,^.*\.[a-z]*:,$object:," < "$tmpdepfile" > "$depfile"
+      sed -e 's,^.*\.[a-z]*:['"$tab"' ]*,,' -e 's,$,:,' < "$tmpdepfile" >> "$depfile"
+   else
+      echo "#dummy" > "$depfile"
+   fi
+   rm -f "$tmpdepfile"
+   ;;
+
+msvc7)
+  if test "$libtool" = yes; then
+    showIncludes=-Wc,-showIncludes
+  else
+    showIncludes=-showIncludes
+  fi
+  "$@" $showIncludes > "$tmpdepfile"
+  stat=$?
+  grep -v '^Note: including file: ' "$tmpdepfile"
+  if test "$stat" = 0; then :
+  else
+    rm -f "$tmpdepfile"
+    exit $stat
+  fi
+  rm -f "$depfile"
+  echo "$object : \\" > "$depfile"
+  # The first sed program below extracts the file names and escapes
+  # backslashes for cygpath.  The second sed program outputs the file
+  # name when reading, but also accumulates all include files in the
+  # hold buffer in order to output them again at the end.  This only
+  # works with sed implementations that can handle large buffers.
+  sed < "$tmpdepfile" -n '
+/^Note: including file:  *\(.*\)/ {
+  s//\1/
+  s/\\/\\\\/g
+  p
+}' | $cygpath_u | sort -u | sed -n '
+s/ /\\ /g
+s/\(.*\)/'"$tab"'\1 \\/p
+s/.\(.*\) \\/\1:/
+H
+$ {
+  s/.*/'"$tab"'/
+  G
+  p
+}' >> "$depfile"
+  rm -f "$tmpdepfile"
+  ;;
+
+msvc7msys)
+  # This case exists only to let depend.m4 do its work.  It works by
+  # looking at the text of this script.  This case will never be run,
+  # since it is checked for above.
+  exit 1
+  ;;
+
+#nosideeffect)
+  # This comment above is used by automake to tell side-effect
+  # dependency tracking mechanisms from slower ones.
+
+dashmstdout)
+  # Important note: in order to support this mode, a compiler *must*
+  # always write the preprocessed file to stdout, regardless of -o.
+  "$@" || exit $?
+
+  # Remove the call to Libtool.
+  if test "$libtool" = yes; then
+    while test "X$1" != 'X--mode=compile'; do
+      shift
+    done
+    shift
+  fi
+
+  # Remove '-o $object'.
+  IFS=" "
+  for arg
+  do
+    case $arg in
+    -o)
+      shift
+      ;;
+    $object)
+      shift
+      ;;
+    *)
+      set fnord "$@" "$arg"
+      shift # fnord
+      shift # $arg
+      ;;
+    esac
+  done
+
+  test -z "$dashmflag" && dashmflag=-M
+  # Require at least two characters before searching for ':'
+  # in the target name.  This is to cope with DOS-style filenames:
+  # a dependency such as 'c:/foo/bar' could be seen as target 'c' otherwise.
+  "$@" $dashmflag |
+    sed 's:^['"$tab"' ]*[^:'"$tab"' ][^:][^:]*\:['"$tab"' ]*:'"$object"'\: :' > "$tmpdepfile"
+  rm -f "$depfile"
+  cat < "$tmpdepfile" > "$depfile"
+  tr ' ' "$nl" < "$tmpdepfile" | \
+## Some versions of the HPUX 10.20 sed can't process this invocation
+## correctly.  Breaking it into two sed invocations is a workaround.
+    sed -e 's/^\\$//' -e '/^$/d' -e '/:$/d' | sed -e 's/$/ :/' >> "$depfile"
+  rm -f "$tmpdepfile"
+  ;;
+
+dashXmstdout)
+  # This case only exists to satisfy depend.m4.  It is never actually
+  # run, as this mode is specially recognized in the preamble.
+  exit 1
+  ;;
+
+makedepend)
+  "$@" || exit $?
+  # Remove any Libtool call
+  if test "$libtool" = yes; then
+    while test "X$1" != 'X--mode=compile'; do
+      shift
+    done
+    shift
+  fi
+  # X makedepend
+  shift
+  cleared=no eat=no
+  for arg
+  do
+    case $cleared in
+    no)
+      set ""; shift
+      cleared=yes ;;
+    esac
+    if test $eat = yes; then
+      eat=no
+      continue
+    fi
+    case "$arg" in
+    -D*|-I*)
+      set fnord "$@" "$arg"; shift ;;
+    # Strip any option that makedepend may not understand.  Remove
+    # the object too, otherwise makedepend will parse it as a source file.
+    -arch)
+      eat=yes ;;
+    -*|$object)
+      ;;
+    *)
+      set fnord "$@" "$arg"; shift ;;
+    esac
+  done
+  obj_suffix=`echo "$object" | sed 's/^.*\././'`
+  touch "$tmpdepfile"
+  ${MAKEDEPEND-makedepend} -o"$obj_suffix" -f"$tmpdepfile" "$@"
+  rm -f "$depfile"
+  # makedepend may prepend the VPATH from the source file name to the object.
+  # No need to regex-escape $object, excess matching of '.' is harmless.
+  sed "s|^.*\($object *:\)|\1|" "$tmpdepfile" > "$depfile"
+  sed '1,2d' "$tmpdepfile" | tr ' ' "$nl" | \
+## Some versions of the HPUX 10.20 sed can't process this invocation
+## correctly.  Breaking it into two sed invocations is a workaround.
+    sed -e 's/^\\$//' -e '/^$/d' -e '/:$/d' | sed -e 's/$/ :/' >> "$depfile"
+  rm -f "$tmpdepfile" "$tmpdepfile".bak
+  ;;
+
+cpp)
+  # Important note: in order to support this mode, a compiler *must*
+  # always write the preprocessed file to stdout.
+  "$@" || exit $?
+
+  # Remove the call to Libtool.
+  if test "$libtool" = yes; then
+    while test "X$1" != 'X--mode=compile'; do
+      shift
+    done
+    shift
+  fi
+
+  # Remove '-o $object'.
+  IFS=" "
+  for arg
+  do
+    case $arg in
+    -o)
+      shift
+      ;;
+    $object)
+      shift
+      ;;
+    *)
+      set fnord "$@" "$arg"
+      shift # fnord
+      shift # $arg
+      ;;
+    esac
+  done
+
+  "$@" -E |
+    sed -n -e '/^# [0-9][0-9]* "\([^"]*\)".*/ s:: \1 \\:p' \
+       -e '/^#line [0-9][0-9]* "\([^"]*\)".*/ s:: \1 \\:p' |
+    sed '$ s: \\$::' > "$tmpdepfile"
+  rm -f "$depfile"
+  echo "$object : \\" > "$depfile"
+  cat < "$tmpdepfile" >> "$depfile"
+  sed < "$tmpdepfile" '/^$/d;s/^ //;s/ \\$//;s/$/ :/' >> "$depfile"
+  rm -f "$tmpdepfile"
+  ;;
+
+msvisualcpp)
+  # Important note: in order to support this mode, a compiler *must*
+  # always write the preprocessed file to stdout.
+  "$@" || exit $?
+
+  # Remove the call to Libtool.
+  if test "$libtool" = yes; then
+    while test "X$1" != 'X--mode=compile'; do
+      shift
+    done
+    shift
+  fi
+
+  IFS=" "
+  for arg
+  do
+    case "$arg" in
+    -o)
+      shift
+      ;;
+    $object)
+      shift
+      ;;
+    "-Gm"|"/Gm"|"-Gi"|"/Gi"|"-ZI"|"/ZI")
+	set fnord "$@"
+	shift
+	shift
+	;;
+    *)
+	set fnord "$@" "$arg"
+	shift
+	shift
+	;;
+    esac
+  done
+  "$@" -E 2>/dev/null |
+  sed -n '/^#line [0-9][0-9]* "\([^"]*\)"/ s::\1:p' | $cygpath_u | sort -u > "$tmpdepfile"
+  rm -f "$depfile"
+  echo "$object : \\" > "$depfile"
+  sed < "$tmpdepfile" -n -e 's% %\\ %g' -e '/^\(.*\)$/ s::'"$tab"'\1 \\:p' >> "$depfile"
+  echo "$tab" >> "$depfile"
+  sed < "$tmpdepfile" -n -e 's% %\\ %g' -e '/^\(.*\)$/ s::\1\::p' >> "$depfile"
+  rm -f "$tmpdepfile"
+  ;;
+
+msvcmsys)
+  # This case exists only to let depend.m4 do its work.  It works by
+  # looking at the text of this script.  This case will never be run,
+  # since it is checked for above.
+  exit 1
+  ;;
+
+none)
+  exec "$@"
+  ;;
+
+*)
+  echo "Unknown depmode $depmode" 1>&2
+  exit 1
+  ;;
+esac
+
+exit 0
+
+# Local Variables:
+# mode: shell-script
+# sh-indentation: 2
+# eval: (add-hook 'write-file-hooks 'time-stamp)
+# time-stamp-start: "scriptversion="
+# time-stamp-format: "%:y-%02m-%02d.%02H"
+# time-stamp-time-zone: "UTC"
+# time-stamp-end: "; # UTC"
+# End:
diff --git a/bluez/doc/adapter-api.txt b/bluez/doc/adapter-api.txt
new file mode 100644
index 0000000..74d235a
--- /dev/null
+++ b/bluez/doc/adapter-api.txt
@@ -0,0 +1,155 @@
+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.
+
+			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
+
+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 PropertyChanged
+			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/doc/agent-api.txt b/bluez/doc/agent-api.txt
new file mode 100644
index 0000000..2e70931
--- /dev/null
+++ b/bluez/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
+			"DisplayYesNo".
+
+			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/doc/alert-api.txt b/bluez/doc/alert-api.txt
new file mode 100644
index 0000000..fc427c8
--- /dev/null
+++ b/bluez/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/doc/assigned-numbers.txt b/bluez/doc/assigned-numbers.txt
new file mode 100644
index 0000000..ca171c4
--- /dev/null
+++ b/bluez/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/doc/cyclingspeed-api.txt b/bluez/doc/cyclingspeed-api.txt
new file mode 100644
index 0000000..a1f1a93
--- /dev/null
+++ b/bluez/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/doc/device-api.txt b/bluez/doc/device-api.txt
new file mode 100644
index 0000000..2d9fa3c
--- /dev/null
+++ b/bluez/doc/device-api.txt
@@ -0,0 +1,195 @@
+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.DoesNotExist
+					 org.bluez.Error.AlreadyConnected
+					 org.bluez.Error.ConnectFailed
+
+		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.DoesNotExist
+					 org.bluez.Error.Failed
+					 org.bluez.Error.NotConnected
+					 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 in the
+			Adapter.DeviceFound signal to anticipate whether
+			legacy or simple pairing will occur.
+
+			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.
diff --git a/bluez/doc/health-api.txt b/bluez/doc/health-api.txt
new file mode 100644
index 0000000..2c48ff2
--- /dev/null
+++ b/bluez/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/doc/heartrate-api.txt b/bluez/doc/heartrate-api.txt
new file mode 100644
index 0000000..665db12
--- /dev/null
+++ b/bluez/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/doc/media-api.txt b/bluez/doc/media-api.txt
new file mode 100644
index 0000000..07ea991
--- /dev/null
+++ b/bluez/doc/media-api.txt
@@ -0,0 +1,646 @@
+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 [Deprecated]
+Object path	[variable prefix]/{hci0,hci1,...}/dev_XX_XX_XX_XX_XX_XX
+
+Methods		void Play()
+
+			Resume playback.
+
+		void Pause()
+
+			Pause playback.
+
+		void Stop()
+
+			Stop playback.
+
+		void Next()
+
+			Next item.
+
+		void Previous()
+
+			Previous item.
+
+		void VolumeUp()
+
+			Adjust remote volume one step up
+
+		void VolumeDown()
+
+			Adjust remote volume one step down
+
+		void FastForward()
+
+			Fast forward playback, this action is only stopped
+			when another method in this interface is called.
+
+		void Rewind()
+
+			Rewind playback, this action is only stopped
+			when another method in this interface is called.
+
+Properties
+
+		boolean Connected [readonly]
+
+
+MediaPlayer1 hierarchy
+======================
+
+Service		org.bluez (Controller role)
+Interface	org.bluez.MediaPlayer1 [Experimental]
+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 [Experimental]
+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 [Experimental]
+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.
+
+		boolean NREC [readwrite]
+
+			Optional and HFP specific (external to BlueZ).
+			Indicates if echo cancelling and noise reduction
+			functions are active in the transport, this
+			property is only writeable when the transport
+			was acquired by the sender.
+
+		boolean InbandRingtone [readwrite]
+
+			Optional and HFP specific (external to BlueZ).
+			Indicates if the transport support sending
+			ringtones, this property is only writeable when
+			the transport was acquired by the sender.
+
+		string Routing [readonly]
+
+			Optional and HFP specific (external to BlueZ).
+			Indicates where is the transport being routed.
+
+			Possible Values: "HCI" or "PCM"
+
+		uint16 Volume [readwrite]
+
+			Optional. Indicates volume level of the transport,
+			this property is only writeable when the transport was
+			acquired by the sender.
+
+			Note: the property will not be present for HSP/HFP
+			transports and MicrophoneGain/SpeakerGain should be
+			used instead.
+
+			Possible Values: 0-127
+
+		byte MicrophoneGain [readwrite]
+
+			Optional. Indicates volume level of the transport's
+			incoming audio stream for HSP/HFP transports. This
+			property is only writeable when the transport was
+			acquired by the sender.
+
+			Possible Values: 0-15
+
+		byte SpeakerGain [readwrite]
+
+			Optional. Indicates volume level of the transport's
+			outgoing audio stream for HSP/HFP transports. This
+			property is only writeable when the transport was
+			acquired by the sender.
+
+			Possible Values: 0-15
diff --git a/bluez/doc/mgmt-api.txt b/bluez/doc/mgmt-api.txt
new file mode 100644
index 0000000..78f0bd2
--- /dev/null
+++ b/bluez/doc/mgmt-api.txt
@@ -0,0 +1,2259 @@
+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
+
+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.
+
+
+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
+
+
+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.
+
+	If not short name is set the Short_Name parameter will be empty
+	(begin with a nul byte).
+
+	Current_Settings & Supported_Settings is a bitmask with
+	currently the following available bits:
+
+		1	Powered
+		2	Connectable
+		3	Fast Connectable
+		4	Discoverable
+		5	Pairable
+		6	Link Level Security (Sec. mode 3)
+		7	Secure Simple Pairing
+		8	Basic Rate/Enhanced Data Rate
+		9	High Speed
+		10	Low Energy
+		11	Advertising
+		12	Secure Connections
+		13	Debug Keys
+		14	Privacy
+
+	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.
+
+	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 only takes
+	affect when advertising is enabled.
+
+	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 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 only be used when the controller is powered on
+	and will return Not Powerd otherwise.
+
+	If connectable is not set, then this command will fail with
+	Rejected error.
+
+	This command generates a Command Complete event on success or
+	a Command Status event on failure.
+
+	Possible errors:	Failed
+				Busy
+				Rejected
+				Not Supported
+				Invalid Parameters
+				Not Powered
+				Invalid Index
+
+
+Set Pairable Command
+====================
+
+	Command Code:		0x0009
+	Controller Index:	<controller id>
+	Command Parameters:	Pairable (1 Octet)
+	Return Parameters:	Current_Settings (4 Octets)
+
+	This command is used to set the pairable property of an
+	controller. The allowed values for the Pairable 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 pairable 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.
+
+	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
+	and/or AD 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.
+
+	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 retreive 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. The KeyboardDisplay
+	SMP value (which doesn't exist for SSP will) automatically be
+	downgraded to DisplayYesNo by the kernel for SSP pairing events.
+
+	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
+
+	The Address and Address_Type of the return parameters will
+	return the identity address if know. 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 send 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.
+
+	Possible errors:	Connect Failed
+				Busy
+				Invalid Parameters
+				Not Powered
+				Invalid Index
+
+
+Cancel Pair Device
+==================
+
+	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.
+
+	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.
+
+	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
+
+	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:
+
+		1	BR/EDR
+		2	LE Public
+		3	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 connect to the local
+	controller.
+
+	Possible values for the Address_Type parameter:
+		0	BR/EDR
+		1	LE Public
+		2	LE Random
+
+	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
+
+	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 are put into the EIR data. If the controller does
+	not support EIR or if SSP is disabled, this command will still
+	succeed. The information are 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 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.
+
+	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:
+
+	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. 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.
+
+	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 BR/EDR capable controllers
+	supporting the core specification version 4.1 or greater
+	(e.g. not for single-mode LE controllers or pre-4.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 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 and 0x01. All other values will return an Invalid Parameters
+	response.
+
+	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 value 0x01 enables
+	privacy mode.
+
+	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
+
+
+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.
+
+
+Index Removed Event
+===================
+
+	Event Code:		0x0005
+	Controller Index:	<controller id>
+	Event Parameters:
+
+	This event indicates that a controller has been removed from the
+	system.
+
+
+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 key
+		0x01	Authenticated key
+
+	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
+
+
+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
+
+	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.
+
+
+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 StartDiscover and a StopDiscovery command.
+
+	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.
+
+	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)
+					Master (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.
+
+	When the Master parameter is set to 0x01, then the signature
+	resolving key from the remote peer device is provided. It is
+	the key that is used for signature verification.
+
+	When the Master parameter is set to 0x00, then it is the local
+	signature resolving key that is used to sign data. The remote
+	peer device will be using it for signature verification.
+
+	The local signature resolving key will be generated with each
+	pairing request. Only after receiving this event with Master
+	parameter set to 0x00 it is 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.
diff --git a/bluez/doc/network-api.txt b/bluez/doc/network-api.txt
new file mode 100644
index 0000000..109da28
--- /dev/null
+++ b/bluez/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/doc/obex-agent-api.txt b/bluez/doc/obex-agent-api.txt
new file mode 100644
index 0000000..3923da6
--- /dev/null
+++ b/bluez/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/doc/obex-api.txt b/bluez/doc/obex-api.txt
new file mode 100644
index 0000000..9542a30
--- /dev/null
+++ b/bluez/doc/obex-api.txt
@@ -0,0 +1,834 @@
+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
+
+			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
+
+		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.
+
+
+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/doc/profile-api.txt b/bluez/doc/profile-api.txt
new file mode 100644
index 0000000..ec18034
--- /dev/null
+++ b/bluez/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/doc/proximity-api.txt b/bluez/doc/proximity-api.txt
new file mode 100644
index 0000000..5322544
--- /dev/null
+++ b/bluez/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/doc/sap-api.txt b/bluez/doc/sap-api.txt
new file mode 100644
index 0000000..b28c4e3
--- /dev/null
+++ b/bluez/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/doc/settings-storage.txt b/bluez/doc/settings-storage.txt
new file mode 100644
index 0000000..7cdecd5
--- /dev/null
+++ b/bluez/doc/settings-storage.txt
@@ -0,0 +1,228 @@
+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 and ServiceRecords).
+
+In ServiceRecords, SDP records are stored using their handle as key
+(hexadecimal format).
+
+[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
+
+
+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.
diff --git a/bluez/doc/supported-features.txt b/bluez/doc/supported-features.txt
new file mode 100644
index 0000000..b337f78
--- /dev/null
+++ b/bluez/doc/supported-features.txt
@@ -0,0 +1,52 @@
+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.0		(LE) Central, Observer, Broadcaster
+L2CAP			4.0		Server, Client
+SDP			4.0		Server, Client
+GATT			4.0		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.5		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/doc/test-coverage.txt b/bluez/doc/test-coverage.txt
new file mode 100644
index 0000000..f6f2de0
--- /dev/null
+++ b/bluez/doc/test-coverage.txt
@@ -0,0 +1,67 @@
+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		   2	Management interface handling
+test-textfile		   4	Old textfile storage format
+test-ringbuf		   3	Ring buffer functionality
+test-queue		   1	Queue handling functionality
+test-hfp		  13	HFP Audio Gateway functionality
+test-avdtp		  60	AVDTP qualification test cases
+test-avctp		   9	AVCTP qualification test cases
+test-avrcp		  37	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	  12	D-Bus client handling
+			-----
+			 446
+
+
+Automated end-to-end testing
+============================
+
+Application		Count	Description
+-------------------------------------------
+mgmt-tester		 184	Kernel management interface testing
+l2cap-tester		  26	Kernel L2CAP implementation testing
+rfcomm-tester		   9	Kernel RFCOMM implementation testing
+smp-tester		   5	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
+			-----
+			 247
+
+
+Android end-to-end testing
+==========================
+
+Application		Count	Description
+-------------------------------------------
+android-tester		  86	Android HAL interface testing
+ipc-tester		  94	Android IPC resistance testing
+			-----
+			 180
+
+
+Android automated unit testing
+==============================
+
+Application		Count	Description
+-------------------------------------------
+test-ipc		  14	Android IPC library functions
+			-----
+			  14
diff --git a/bluez/doc/thermometer-api.txt b/bluez/doc/thermometer-api.txt
new file mode 100644
index 0000000..c7c8a5d
--- /dev/null
+++ b/bluez/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/emulator/amp.c b/bluez/emulator/amp.c
new file mode 100644
index 0000000..ab7f9de
--- /dev/null
+++ b/bluez/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 <bluetooth/bluetooth.h>
+#include <bluetooth/hci.h>
+
+#include "src/shared/util.h"
+#include "monitor/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/emulator/amp.h b/bluez/emulator/amp.h
new file mode 100644
index 0000000..189dfb7
--- /dev/null
+++ b/bluez/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/emulator/b1ee.c b/bluez/emulator/b1ee.c
new file mode 100644
index 0000000..17a60fc
--- /dev/null
+++ b/bluez/emulator/b1ee.c
@@ -0,0 +1,256 @@
+/*
+ *
+ *  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 <bluetooth/bluetooth.h>
+#include <bluetooth/hci.h>
+
+#include "monitor/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");
+			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/emulator/btdev.c b/bluez/emulator/btdev.c
new file mode 100644
index 0000000..e590efa
--- /dev/null
+++ b/bluez/emulator/btdev.c
@@ -0,0 +1,3074 @@
+/*
+ *
+ *  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 "src/shared/util.h"
+#include "src/shared/timeout.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];
+
+        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  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];
+
+	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 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] |= 0x40;	/* Host Buffer Size */
+	btdev->commands[15] |= 0x02;	/* Read BD ADDR */
+}
+
+static void set_bredr_commands(struct btdev *btdev)
+{
+	set_common_commands_all(btdev);
+	set_common_commands_bredrle(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[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 */
+	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[23] |= 0x04;	/* Read Data Block Size */
+}
+
+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] |= 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] |= 0x02;	/* LE Set Adv Enable */
+	btdev->commands[26] |= 0x04;	/* LE Set Scan Parameters */
+	btdev->commands[26] |= 0x08;	/* LE Set Scan Enable */
+	btdev->commands[26] |= 0x40;	/* LE Read White List Size */
+	btdev->commands[27] |= 0x80;	/* LE Rand */
+	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 */
+}
+
+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_le_features(struct btdev *btdev)
+{
+	btdev->features[4] |= 0x20;	/* BR/EDR Not Supported */
+	btdev->features[4] |= 0x40;	/* LE Supported */
+
+	btdev->max_page = 1;
+}
+
+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));
+	btdev->type = type;
+
+	btdev->manufacturer = 63;
+
+	if (type == BTDEV_TYPE_BREDR)
+		btdev->version = 0x05;
+	else
+		btdev->version = 0x06;
+
+	btdev->revision = 0x0000;
+
+	switch (btdev->type) {
+	case BTDEV_TYPE_BREDRLE:
+		set_bredrle_features(btdev);
+		set_bredrle_commands(btdev);
+		break;
+	case BTDEV_TYPE_BREDR:
+		set_bredr_features(btdev);
+		set_bredr_commands(btdev);
+		break;
+	case BTDEV_TYPE_LE:
+		set_le_features(btdev);
+		set_le_commands(btdev);
+		break;
+	case BTDEV_TYPE_AMP:
+		set_amp_features(btdev);
+		set_amp_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) {
+		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);
+
+	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;
+}
+
+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 void *data, uint16_t len)
+{
+	if (!btdev->send_handler)
+		return;
+
+	btdev->send_handler(data, len, 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;
+	uint16_t pkt_len;
+	void *pkt_data;
+
+	pkt_len = 1 + sizeof(*hdr) + len;
+
+	pkt_data = malloc(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);
+
+	if (run_hooks(btdev, BTDEV_HOOK_POST_EVT, event, pkt_data, pkt_len))
+		send_packet(btdev, pkt_data, pkt_len);
+
+	free(pkt_data);
+}
+
+static void cmd_complete(struct btdev *btdev, 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 = malloc(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);
+
+	if (run_hooks(btdev, BTDEV_HOOK_POST_CMD, opcode, pkt_data, pkt_len))
+		send_packet(btdev, pkt_data, pkt_len);
+
+	free(pkt_data);
+}
+
+static void cmd_status(struct btdev *btdev, 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 = malloc(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);
+
+	if (run_hooks(btdev, BTDEV_HOOK_POST_CMD, opcode, pkt_data, pkt_len))
+		send_packet(btdev, pkt_data, pkt_len);
+
+	free(pkt_data);
+}
+
+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 uint8_t *bdaddr, 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 = find_btdev_by_bdaddr(bdaddr);
+
+		btdev->conn = remote;
+		remote->conn = btdev;
+
+		cc->status = status;
+		memcpy(cc->peer_addr, btdev->bdaddr, 6);
+
+		cc->role = 0x01;
+		cc->handle = cpu_to_le16(42);
+
+		send_event(remote, BT_HCI_EVT_LE_META_EVENT, buf, sizeof(buf));
+
+		cc->handle = cpu_to_le16(42);
+	}
+
+	cc->status = status;
+	memcpy(cc->peer_addr, bdaddr, 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 uint8_t *bdaddr)
+{
+	struct btdev *remote = find_btdev_by_bdaddr(bdaddr);
+
+	if (remote && adv_connectable(remote) && adv_match(btdev, remote))
+		le_conn_complete(btdev, bdaddr, 0);
+	else
+		le_conn_complete(btdev, bdaddr,
+					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 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 = 0x04;
+		auth = 0x05;
+	} else {
+		unauth = 0x07;
+		auth = 0x08;
+	}
+
+	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, 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 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_start_encrypt_complete(struct btdev *btdev)
+{
+	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);
+
+	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));
+	ev.handle = cpu_to_le16(42);
+	ev.encr_mode = 0x01;
+
+	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 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_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_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_read_local_amp_assoc *rlaa_cmd;
+	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_amp_info rlai;
+	struct bt_hci_rsp_read_local_amp_assoc rlaa_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_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;
+	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_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:
+		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_HOST_BUFFER_SIZE:
+		status = BT_HCI_ERR_SUCCESS;
+		cmd_complete(btdev, opcode, &status, sizeof(status));
+		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_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_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));
+		if (status == BT_HCI_ERR_SUCCESS && btdev->le_scan_enable)
+			le_set_scan_enable_complete(btdev);
+		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_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_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_RAND:
+		if (btdev->type == BTDEV_TYPE_BREDR)
+			goto unsupported;
+		lr.status = BT_HCI_ERR_SUCCESS;
+		lr.number = rand();
+		cmd_complete(btdev, opcode, &lr, sizeof(lr));
+		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);
+		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;
+
+	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_le_create_conn *lecc;
+
+	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) {
+			encrypt_change(btdev, sce->encr_mode,
+							BT_HCI_ERR_SUCCESS);
+			encrypt_change(btdev->conn, sce->encr_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_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->peer_addr);
+		break;
+	}
+}
+
+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);
+	}
+}
+
+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_packet(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/emulator/btdev.h b/bluez/emulator/btdev.h
new file mode 100644
index 0000000..1e623f4
--- /dev/null
+++ b/bluez/emulator/btdev.h
@@ -0,0 +1,95 @@
+/*
+ *
+ *  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 void *data, uint16_t len,
+							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,
+};
+
+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);
+
+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/emulator/bthost.c b/bluez/emulator/bthost.c
new file mode 100644
index 0000000..1e3123a
--- /dev/null
+++ b/bluez/emulator/bthost.c
@@ -0,0 +1,2294 @@
+/*
+ *
+ *  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 "bluetooth/bluetooth.h"
+
+#include "src/shared/util.h"
+#include "monitor/bt.h"
+#include "monitor/rfcomm.h"
+#include "bthost.h"
+
+/* 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)
+
+/* 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;
+	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 {
+	uint8_t bdaddr[6];
+	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;
+	bool reject_user_confirm;
+	void *smp_data;
+	bool conn_init;
+};
+
+struct bthost *bthost_create(void)
+{
+	struct bthost *bthost;
+
+	bthost = malloc(sizeof(*bthost));
+	if (!bthost)
+		return NULL;
+
+	memset(bthost, 0, sizeof(*bthost));
+
+	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);
+
+	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 void *data,
+								uint16_t len)
+{
+	struct cmd_queue *cmd_q = &bthost->cmd_q;
+	struct cmd *cmd;
+
+	cmd = malloc(sizeof(*cmd));
+	if (!cmd)
+		return;
+
+	memset(cmd, 0, sizeof(*cmd));
+
+	memcpy(cmd->data, data, len);
+	cmd->len = 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 void *data, uint16_t len)
+{
+	if (!bthost->send_handler)
+		return;
+
+	bthost->send_handler(data, len, bthost->send_data);
+}
+
+static void send_acl(struct bthost *bthost, uint16_t handle, uint16_t cid,
+						const void *data, uint16_t len)
+{
+	struct bt_hci_acl_hdr *acl_hdr;
+	struct bt_l2cap_hdr *l2_hdr;
+	uint16_t pkt_len;
+	void *pkt_data;
+
+	pkt_len = 1 + sizeof(*acl_hdr) + sizeof(*l2_hdr) + len;
+
+	pkt_data = malloc(pkt_len);
+	if (!pkt_data)
+		return;
+
+	((uint8_t *) pkt_data)[0] = BT_H4_ACL_PKT;
+
+	acl_hdr = pkt_data + 1;
+	acl_hdr->handle = acl_handle_pack(handle, 0);
+	acl_hdr->dlen = cpu_to_le16(len + sizeof(*l2_hdr));
+
+	l2_hdr = pkt_data + 1 + sizeof(*acl_hdr);
+	l2_hdr->cid = cpu_to_le16(cid);
+	l2_hdr->len = cpu_to_le16(len);
+
+	if (len > 0)
+		memcpy(pkt_data + 1 + sizeof(*acl_hdr) + sizeof(*l2_hdr),
+								data, len);
+
+	send_packet(bthost, pkt_data, pkt_len);
+
+	free(pkt_data);
+}
+
+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 pkt_len, cid;
+	void *pkt_data;
+
+	pkt_len = sizeof(*hdr) + len;
+
+	pkt_data = malloc(pkt_len);
+	if (!pkt_data)
+		return 0;
+
+	if (!ident) {
+		ident = next_ident++;
+		if (!ident)
+			ident = next_ident++;
+	}
+
+	hdr = pkt_data;
+	hdr->code  = code;
+	hdr->ident = ident;
+	hdr->len   = cpu_to_le16(len);
+
+	if (len > 0)
+		memcpy(pkt_data + sizeof(*hdr), data, len);
+
+	if (conn->addr_type == BDADDR_BREDR)
+		cid = 0x0001;
+	else
+		cid = 0x0005;
+
+	send_acl(bthost, conn->handle, cid, pkt_data, pkt_len);
+
+	free(pkt_data);
+
+	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);
+}
+
+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;
+	uint16_t pkt_len;
+	void *pkt_data;
+
+	pkt_len = 1 + sizeof(*hdr) + len;
+
+	pkt_data = malloc(pkt_len);
+	if (!pkt_data)
+		return;
+
+	((uint8_t *) pkt_data)[0] = BT_H4_CMD_PKT;
+
+	hdr = pkt_data + 1;
+	hdr->opcode = cpu_to_le16(opcode);
+	hdr->plen = len;
+
+	if (len > 0)
+		memcpy(pkt_data + 1 + sizeof(*hdr), data, len);
+
+	if (bthost->ncmd) {
+		send_packet(bthost, pkt_data, pkt_len);
+		bthost->ncmd--;
+	} else {
+		queue_command(bthost, pkt_data, pkt_len);
+	}
+
+	free(pkt_data);
+}
+
+static void next_cmd(struct bthost *bthost)
+{
+	struct cmd_queue *cmd_q = &bthost->cmd_q;
+	struct cmd *cmd = cmd_q->head;
+	struct cmd *next;
+
+	if (!cmd)
+		return;
+
+	next = cmd->next;
+
+	if (!bthost->ncmd)
+		return;
+
+	send_packet(bthost, cmd->data, cmd->len);
+	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);
+}
+
+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_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_IO_CAPABILITY_REQUEST_REPLY:
+		break;
+	case BT_HCI_CMD_USER_CONFIRM_REQUEST_REPLY:
+		break;
+	case BT_HCI_CMD_LE_LTK_REQ_REPLY:
+		break;
+	case BT_HCI_CMD_LE_LTK_REQ_NEG_REPLY:
+		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,
+							bthost->conn_init);
+
+	if (bthost->new_conn_cb)
+		bthost->new_conn_cb(conn->handle, bthost->new_conn_data);
+}
+
+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;
+
+	conn->encr_mode = ev->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 = 0x00;
+
+	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_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_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 = cpu_to_le16(conn->next_cid++);
+	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 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 (le16_to_cpu(rsp->result) == 0x0001) {
+		struct bt_l2cap_pdu_config_req req;
+
+		memset(&req, 0, sizeof(req));
+		req.dcid = rsp->dcid;
+
+		l2cap_sig_send(bthost, conn, BT_L2CAP_PDU_CONFIG_REQ, 0,
+							&req, sizeof(req));
+	} else if (l2conn->psm == 0x0003 && !rsp->result && !rsp->status &&
+						bthost->rfcomm_conn_data) {
+		rfcomm_sabm_send(bthost, conn, l2conn, 1, 0);
+	}
+
+	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;
+
+	if (len < sizeof(*rsp))
+		return false;
+
+	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;
+	struct bt_l2cap_pdu_info_rsp rsp;
+
+	if (len < sizeof(*req))
+		return false;
+
+	rsp.type = req->type;
+	rsp.result = cpu_to_le16(0x0001); /* Not Supported */
+
+	l2cap_sig_send(bthost, conn, BT_L2CAP_PDU_INFO_RSP, ident, &rsp,
+								sizeof(rsp));
+
+	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;
+
+	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_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;
+	uint8_t buf[14];
+	struct rfcomm_hdr *hdr;
+	struct rfcomm_mcc *mcc;
+	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);
+
+	memset(buf, 0, sizeof(buf));
+
+	hdr = (struct rfcomm_hdr *) buf;
+	mcc = (struct rfcomm_mcc *) (buf + sizeof(*hdr));
+	pn_cmd = (struct rfcomm_pn *) (buf + sizeof(*hdr) + sizeof(*mcc));
+
+	hdr->address = RFCOMM_ADDR(1, 0);
+	hdr->control = RFCOMM_CTRL(RFCOMM_UIH, 0);
+	hdr->length  = RFCOMM_LEN8(sizeof(*mcc) + sizeof(*pn_cmd));
+
+	mcc->type = RFCOMM_MCC_TYPE(1, RFCOMM_PN);
+	mcc->length = RFCOMM_LEN8(sizeof(*pn_cmd));
+
+	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;
+
+	buf[sizeof(*hdr) + sizeof(*mcc) + sizeof(*pn_cmd)] = rfcomm_fcs(buf);
+
+	send_acl(bthost, conn->handle, l2conn->dcid, buf, sizeof(buf));
+}
+
+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)
+{
+	uint8_t buf[8];
+	struct rfcomm_hdr *hdr = (struct rfcomm_hdr *) buf;
+	struct rfcomm_mcc *mcc = (struct rfcomm_mcc *) (buf + sizeof(*hdr));
+	struct rfcomm_msc *msc_cmd = (struct rfcomm_msc *) (buf +
+								sizeof(*hdr) +
+								sizeof(*mcc));
+
+	hdr->address = RFCOMM_ADDR(0, 0);
+	hdr->control = RFCOMM_CTRL(RFCOMM_UIH, 0);
+	hdr->length  = RFCOMM_LEN8(sizeof(*mcc) + sizeof(*msc));
+	mcc->type = RFCOMM_MCC_TYPE(cr, RFCOMM_MSC);
+	mcc->length = RFCOMM_LEN8(sizeof(*msc));
+
+	msc_cmd->dlci = msc->dlci;
+	msc_cmd->v24_sig = msc->v24_sig;
+	buf[sizeof(*hdr) + sizeof(*mcc) + sizeof(*msc_cmd)] = rfcomm_fcs(buf);
+
+	send_acl(bthost, conn->handle, l2conn->dcid, buf, sizeof(buf));
+}
+
+static void rfcomm_pn_recv(struct bthost *bthost, struct btconn *conn,
+					struct l2conn *l2conn, uint8_t cr,
+					const struct rfcomm_pn *pn)
+{
+	uint8_t buf[14];
+	struct rfcomm_hdr *hdr;
+	struct rfcomm_mcc *mcc;
+	struct rfcomm_pn *pn_cmd;
+
+	if (!cr) {
+		rfcomm_sabm_send(bthost, conn, l2conn, 1,
+					bthost->rfcomm_conn_data->channel * 2);
+		return;
+	}
+
+	hdr = (struct rfcomm_hdr *) buf;
+	mcc = (struct rfcomm_mcc *) (buf + sizeof(*hdr));
+	pn_cmd = (struct rfcomm_pn *) (buf + sizeof(*hdr) + sizeof(*mcc));
+
+	memset(buf, 0, sizeof(buf));
+
+	hdr->address = RFCOMM_ADDR(1, 0);
+	hdr->control = RFCOMM_CTRL(RFCOMM_UIH, 0);
+	hdr->length  = RFCOMM_LEN8(sizeof(*mcc) + sizeof(*pn_cmd));
+
+	mcc->type = RFCOMM_MCC_TYPE(0, RFCOMM_PN);
+	mcc->length = RFCOMM_LEN8(sizeof(*pn_cmd));
+
+	pn_cmd->dlci = pn->dlci;
+	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;
+
+	buf[sizeof(*hdr) + sizeof(*mcc) + sizeof(*pn_cmd)] = rfcomm_fcs(buf);
+
+	send_acl(bthost, conn->handle, l2conn->dcid, buf, sizeof(buf));
+}
+
+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;
+	}
+}
+
+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;
+	const void *p;
+
+	if (len < sizeof(*hdr))
+		return;
+
+	if (RFCOMM_TEST_EA(hdr->length))
+		hdr_len = sizeof(*hdr);
+	else
+		hdr_len = sizeof(*hdr) + sizeof(uint8_t);
+
+	if (len < hdr_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)
+			return;
+
+		hook->func(p, len - hdr_len - sizeof(uint8_t),
+							hook->user_data);
+	} else {
+		rfcomm_mcc_recv(bthost, conn, l2conn, p, len - hdr_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;
+	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;
+
+		send_command(bthost, BT_HCI_CMD_LE_CREATE_CONN,
+							&cc, sizeof(cc));
+	}
+}
+
+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_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_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 {
+		smp_pair(conn->smp_data);
+	}
+}
+
+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));
+}
+
+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_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;
+}
+
+void bthost_set_reject_user_confirm(struct bthost *bthost, bool reject)
+{
+	bthost->reject_user_confirm = reject;
+}
+
+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->smp_data = smp_start(bthost);
+
+	bthost->ncmd = 1;
+
+	send_command(bthost, BT_HCI_CMD_RESET, 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);
+}
+
+void bthost_stop(struct bthost *bthost)
+{
+	if (bthost->smp_data) {
+		smp_stop(bthost->smp_data);
+		bthost->smp_data = NULL;
+	}
+}
diff --git a/bluez/emulator/bthost.h b/bluez/emulator/bthost.h
new file mode 100644
index 0000000..4933b33
--- /dev/null
+++ b/bluez/emulator/bthost.h
@@ -0,0 +1,126 @@
+/*
+ *
+ *  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>
+
+typedef void (*bthost_send_func) (const void *data, uint16_t len,
+							void *user_data);
+
+struct bthost;
+
+struct bthost *bthost_create(void);
+void bthost_destroy(struct bthost *bthost);
+
+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);
+
+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);
+
+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_enable(struct bthost *bthost, uint8_t enable);
+
+void bthost_write_ssp_mode(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_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);
+void bthost_set_reject_user_confirm(struct bthost *bthost, bool reject);
+
+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);
+void bthost_stop(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, bool conn_init);
+void smp_conn_del(void *conn_data);
+void smp_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);
diff --git a/bluez/emulator/hfp.c b/bluez/emulator/hfp.c
new file mode 100644
index 0000000..928a729
--- /dev/null
+++ b/bluez/emulator/hfp.c
@@ -0,0 +1,119 @@
+/*
+ *
+ *  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 <sys/socket.h>
+#include <sys/un.h>
+
+#include "monitor/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/emulator/le.c b/bluez/emulator/le.c
new file mode 100644
index 0000000..0a1b00d
--- /dev/null
+++ b/bluez/emulator/le.c
@@ -0,0 +1,809 @@
+/*
+ *
+ *  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 <bluetooth/bluetooth.h>
+#include <bluetooth/hci.h>
+
+#include "src/shared/util.h"
+#include "src/shared/crypto.h"
+#include "monitor/mainloop.h"
+#include "monitor/bt.h"
+
+#include "le.h"
+
+#define WHITE_LIST_SIZE  16
+
+struct bt_le {
+	volatile int ref_count;
+	int vhci_fd;
+	struct bt_crypto *crypto;
+
+	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_white_list_size;
+	uint8_t  le_states[8];
+};
+
+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->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[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 */
+
+	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_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 */
+
+	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_white_list_size = WHITE_LIST_SIZE;
+
+	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;	/* Directed Advertising */
+	hci->le_states[0] |= 0x10;	/* Passive Scanning */
+	hci->le_states[0] |= 0x20;	/* Active Scanning */
+	hci->le_states[0] |= 0x40;	/* Initiating */
+	hci->le_states[0] |= 0x80;	/* Connection */
+}
+
+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 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 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;
+
+	reset_defaults(hci);
+
+	status = BT_HCI_ERR_SUCCESS;
+	cmd_complete(hci, BT_HCI_CMD_RESET, &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 = 0x06;
+	rsp.hci_rev = cpu_to_le16(0x0000);
+	rsp.lmp_ver = 0x06;
+	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 0x01 */
+	if (cmd->own_addr_type > 0x01) {
+		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;
+
+	/* 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;
+	}
+
+	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_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;
+
+	status = BT_HCI_ERR_SUCCESS;
+	cmd_complete(hci, BT_HCI_CMD_LE_CLEAR_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 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_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_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_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_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 },
+
+	{ }
+};
+
+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;
+	}
+}
+
+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;
+
+	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->crypto = bt_crypto_new();
+
+	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;
+
+	bt_crypto_unref(hci->crypto);
+
+	mainloop_remove_fd(hci->vhci_fd);
+
+	close(hci->vhci_fd);
+
+	free(hci);
+}
diff --git a/bluez/emulator/le.h b/bluez/emulator/le.h
new file mode 100644
index 0000000..5e832e8
--- /dev/null
+++ b/bluez/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/emulator/main.c b/bluez/emulator/main.c
new file mode 100644
index 0000000..bd2a29a
--- /dev/null
+++ b/bluez/emulator/main.c
@@ -0,0 +1,216 @@
+/*
+ *
+ *  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 "monitor/mainloop.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 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[] = {
+	{ "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;
+	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, "sl::LBAUTvh",
+						main_options, NULL);
+		if (opt < 0)
+			break;
+
+		switch (opt) {
+		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) {
+		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 (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/emulator/server.c b/bluez/emulator/server.c
new file mode 100644
index 0000000..f3c82d3
--- /dev/null
+++ b/bluez/emulator/server.c
@@ -0,0 +1,382 @@
+/*
+ *
+ *  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 <bluetooth/bluetooth.h>
+#include <bluetooth/hci.h>
+
+#include "monitor/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 void *data, uint16_t len,
+							void *user_data)
+{
+	struct client *client = user_data;
+	ssize_t written;
+
+	written = send(client->fd, data, len, 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;
+
+		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;
+			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/emulator/server.h b/bluez/emulator/server.h
new file mode 100644
index 0000000..bf725e7
--- /dev/null
+++ b/bluez/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/emulator/smp.c b/bluez/emulator/smp.c
new file mode 100644
index 0000000..32c82e5
--- /dev/null
+++ b/bluez/emulator/smp.c
@@ -0,0 +1,276 @@
+/*
+ *
+ *  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 "bluetooth/bluetooth.h"
+#include "bluetooth/hci.h"
+
+#include "src/shared/crypto.h"
+#include "monitor/bt.h"
+#include "bthost.h"
+
+#define SMP_CID 0x0006
+
+struct smp {
+	struct bthost *bthost;
+	struct smp_conn *conn;
+	struct bt_crypto *crypto;
+};
+
+struct smp_conn {
+	struct smp *smp;
+	uint16_t handle;
+	bool out;
+	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];
+};
+
+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 pairing_req(struct smp_conn *conn, const void *data, uint16_t len)
+{
+	struct bthost *bthost = conn->smp->bthost;
+	static const uint8_t rsp[] = {	0x02,	/* Pairing Response */
+					0x03,	/* NoInputNoOutput */
+					0x00,	/* OOB Flag */
+					0x01,	/* Bonding - no MITM */
+					0x10,	/* Max key size */
+					0x00,	/* Init. key dist. */
+					0x01,	/* Rsp. key dist. */
+				};
+
+	memcpy(conn->preq, data, sizeof(conn->preq));
+	memcpy(conn->prsp, rsp, sizeof(rsp));
+
+	bthost_send_cid(bthost, conn->handle, SMP_CID, rsp, sizeof(rsp));
+}
+
+static void pairing_rsp(struct smp_conn *conn, const void *data, uint16_t len)
+{
+	memcpy(conn->prsp, data, sizeof(conn->prsp));
+
+	/*bthost_send_cid(bthost, handle, SMP_CID, pdu, req->send_len);*/
+}
+
+static void pairing_cfm(struct smp_conn *conn, const void *data, uint16_t len)
+{
+	struct bthost *bthost = conn->smp->bthost;
+	const uint8_t *cfm = data;
+	uint8_t rsp[17];
+
+	memcpy(conn->pcnf, data + 1, 16);
+
+	rsp[0] = cfm[0];
+	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[1]);
+
+	bthost_send_cid(bthost, conn->handle, SMP_CID, rsp, sizeof(rsp));
+}
+
+static void pairing_rnd(struct smp_conn *conn, const void *data, uint16_t len)
+{
+	struct bthost *bthost = conn->smp->bthost;
+	const uint8_t *rnd = data;
+	uint8_t rsp[17];
+
+	memcpy(conn->rrnd, data + 1, 16);
+
+	if (!verify_random(conn, data + 1))
+		return;
+
+	rsp[0] = rnd[0];
+	memcpy(&rsp[1], conn->prnd, 16);
+
+	bthost_send_cid(bthost, conn->handle, SMP_CID, rsp, sizeof(rsp));
+}
+
+void smp_pair(void *conn_data)
+{
+	struct smp_conn *conn = conn_data;
+	struct bthost *bthost = conn->smp->bthost;
+	const uint8_t smp_pair_req[] = {	0x01,	/* Pairing Request */
+						0x03,	/* NoInputNoOutput */
+						0x00,	/* OOB Flag */
+						0x01,	/* Bonding - no MITM */
+						0x10,	/* Max key size */
+						0x00,	/* Init. key dist. */
+						0x01,	/* Rsp. key dist. */
+					};
+
+	memcpy(conn->preq, smp_pair_req, sizeof(smp_pair_req));
+
+	bthost_send_cid(bthost, conn->handle, SMP_CID, smp_pair_req,
+							sizeof(smp_pair_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;
+	}
+
+	opcode = *((const uint8_t *) data);
+
+	switch (opcode) {
+	case 0x01: /* Pairing Request */
+		pairing_req(conn, data, len);
+		break;
+	case 0x02: /* Pairing Response */
+		pairing_rsp(conn, data, len);
+		break;
+	case 0x03: /* Pairing Confirm */
+		pairing_cfm(conn, data, len);
+		break;
+	case 0x04: /* Pairing Random */
+		pairing_rnd(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;
+}
+
+void *smp_conn_add(void *smp_data, uint16_t handle, const uint8_t *ia,
+					const uint8_t *ra, 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->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/emulator/vhci.c b/bluez/emulator/vhci.c
new file mode 100644
index 0000000..00c6118
--- /dev/null
+++ b/bluez/emulator/vhci.c
@@ -0,0 +1,170 @@
+/*
+ *
+ *  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 <bluetooth/bluetooth.h>
+#include <bluetooth/hci.h>
+
+#include "monitor/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 void *data, uint16_t len, void *user_data)
+{
+	struct vhci *vhci = user_data;
+	ssize_t written;
+
+	written = write(vhci->fd, data, len);
+	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/emulator/vhci.h b/bluez/emulator/vhci.h
new file mode 100644
index 0000000..1ec7191
--- /dev/null
+++ b/bluez/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/gdbus/client.c b/bluez/gdbus/client.c
new file mode 100644
index 0000000..3bf883a
--- /dev/null
+++ b/bluez/gdbus/client.c
@@ -0,0 +1,1367 @@
+/*
+ *
+ *  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;
+	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;
+	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->proxy_added && !client->proxy_removed) {
+		refresh_properties(client);
+		return;
+	}
+
+	if (client->get_objects_call != NULL)
+		return;
+
+	msg = dbus_message_new_method_call(client->service_name, "/",
+					DBUS_INTERFACE_DBUS ".ObjectManager",
+							"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);
+
+	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;
+
+	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)
+{
+	GDBusClient *client;
+	unsigned int i;
+
+	if (connection == NULL)
+		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->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);
+	client->added_watch = g_dbus_add_signal_watch(connection, service,
+						"/",
+						DBUS_INTERFACE_OBJECT_MANAGER,
+						"InterfacesAdded",
+						interfaces_added,
+						client, NULL);
+	client->removed_watch = g_dbus_add_signal_watch(connection, service,
+						"/",
+						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);
+
+	if (client->disconn_func)
+		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);
+}
+
+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;
+
+	get_managed_objects(client);
+
+	return TRUE;
+}
diff --git a/bluez/gdbus/gdbus.h b/bluez/gdbus/gdbus.h
new file mode 100644
index 0000000..551c306
--- /dev/null
+++ b/bluez/gdbus/gdbus.h
@@ -0,0 +1,380 @@
+/*
+ *
+ *  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 enum GDBusMethodFlags GDBusMethodFlags;
+typedef enum GDBusSignalFlags GDBusSignalFlags;
+typedef enum GDBusPropertyFlags GDBusPropertyFlags;
+typedef enum GDBusSecurityFlags GDBusSecurityFlags;
+
+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),
+};
+
+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);
+
+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);
+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_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/gdbus/mainloop.c b/bluez/gdbus/mainloop.c
new file mode 100644
index 0000000..435fb93
--- /dev/null
+++ b/bluez/gdbus/mainloop.c
@@ -0,0 +1,371 @@
+/*
+ *
+ *  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;
+
+	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;
+
+	dbus_watch_handle(info->watch, flags);
+
+	status = dbus_connection_get_dispatch_status(info->conn);
+	queue_dispatch(info->conn, status);
+
+	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_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/gdbus/object.c b/bluez/gdbus/object.c
new file mode 100644
index 0000000..13cf9a9
--- /dev/null
+++ b/bluez/gdbus/object.c
@@ -0,0 +1,1824 @@
+/*
+ *
+ *  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) {
+		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;
+	GSList *l;
+
+	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);
+
+	for (l = data->interfaces; l != NULL; l = l->next) {
+		if (g_slist_find(data->added, l->data))
+			continue;
+
+		append_interface(l->data, &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];
+
+	vsnprintf(str, sizeof(str), format, args);
+
+	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;
+	char str[1024];
+
+	vsnprintf(str, sizeof(str), format, args);
+
+	error = dbus_message_new_error(message, name, str);
+	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(DBusConnection *connection,
+				const char *path, const char *interface,
+				const char *name)
+{
+	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);
+
+	add_pending(data);
+}
+
+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;
+}
diff --git a/bluez/gdbus/polkit.c b/bluez/gdbus/polkit.c
new file mode 100644
index 0000000..9e95fa3
--- /dev/null
+++ b/bluez/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/gdbus/watch.c b/bluez/gdbus/watch.c
new file mode 100644
index 0000000..0f99f4f
--- /dev/null
+++ b/bluez/gdbus/watch.c
@@ -0,0 +1,812 @@
+/*
+ *
+ *  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 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)) {
+		g_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_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 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;
+}
+
+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 TRUE;
+
+	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)) {
+			filter_data_remove_callback(data, cb);
+			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)) {
+			filter_data_remove_callback(data, 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 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);
+
+	/* Sender is always the owner */
+	if (sender == NULL)
+		return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+
+	for (current = listeners; current != NULL; current = current->next) {
+		data = current->data;
+
+		if (connection != data->connection)
+			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;
+
+	update_name_cache(data->name, data->owner);
+	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, NULL, NULL,
+				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/gobex/gobex-apparam.c b/bluez/gobex/gobex-apparam.c
new file mode 100644
index 0000000..4328172
--- /dev/null
+++ b/bluez/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/gobex/gobex-apparam.h b/bluez/gobex/gobex-apparam.h
new file mode 100644
index 0000000..6c08609
--- /dev/null
+++ b/bluez/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/gobex/gobex-debug.h b/bluez/gobex/gobex-debug.h
new file mode 100644
index 0000000..a98653d
--- /dev/null
+++ b/bluez/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/gobex/gobex-defs.c b/bluez/gobex/gobex-defs.c
new file mode 100644
index 0000000..1c7c39a
--- /dev/null
+++ b/bluez/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/gobex/gobex-defs.h b/bluez/gobex/gobex-defs.h
new file mode 100644
index 0000000..326e3cb
--- /dev/null
+++ b/bluez/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/gobex/gobex-header.c b/bluez/gobex/gobex-header.c
new file mode 100644
index 0000000..281f8ea
--- /dev/null
+++ b/bluez/gobex/gobex-header.c
@@ -0,0 +1,543 @@
+/*
+ *
+ *  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) {
+		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;
+		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_apparam(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(G_OBEX_HDR_APPARAM, buf, len);
+}
+
+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/gobex/gobex-header.h b/bluez/gobex/gobex-header.h
new file mode 100644
index 0000000..42a2a0c
--- /dev/null
+++ b/bluez/gobex/gobex-header.h
@@ -0,0 +1,102 @@
+/*
+ *
+ *  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_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/gobex/gobex-packet.c b/bluez/gobex/gobex-packet.c
new file mode 100644
index 0000000..4c14cf7
--- /dev/null
+++ b/bluez/gobex/gobex-packet.c
@@ -0,0 +1,456 @@
+/*
+ *
+ *  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) {
+		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) {
+		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) {
+		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/gobex/gobex-packet.h b/bluez/gobex/gobex-packet.h
new file mode 100644
index 0000000..6121fa7
--- /dev/null
+++ b/bluez/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/gobex/gobex-transfer.c b/bluez/gobex/gobex-transfer.c
new file mode 100644
index 0000000..8498177
--- /dev/null
+++ b/bluez/gobex/gobex-transfer.c
@@ -0,0 +1,662 @@
+/*
+ *
+ *  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.h"
+#include "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;
+	}
+
+	req = g_obex_packet_new(G_OBEX_OP_ABORT, TRUE, G_OBEX_HDR_INVALID);
+
+	transfer->req_id = g_obex_send_req(transfer->obex, req, -1,
+						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);
+	}
+
+	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);
+	}
+
+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 void 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);
+	}
+}
+
+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;
+
+	transfer_get_req_first(transfer, rsp);
+
+	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 == 0)
+		goto done;
+
+	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/gobex/gobex.c b/bluez/gobex/gobex.c
new file mode 100644
index 0000000..ca15941
--- /dev/null
+++ b/bluez/gobex/gobex.c
@@ -0,0 +1,1579 @@
+/*
+ *
+ *  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
+
+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;
+
+	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;
+};
+
+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);
+
+	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->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) {
+		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 void prepare_connect_rsp(GObex *obex, GObexPacket *rsp)
+{
+	GObexHeader *connid;
+	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);
+
+	connid = g_obex_packet_get_header(rsp, G_OBEX_HDR_CONNECTION);
+	if (connid != NULL) {
+		g_obex_header_get_uint32(connid, &obex->conn_id);
+		return;
+	}
+
+	obex->conn_id = next_connid++;
+
+	connid = g_obex_header_new_uint32(G_OBEX_HDR_CONNECTION,
+							obex->conn_id);
+	g_obex_packet_prepend_header(rsp, connid);
+}
+
+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) {
+		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;
+
+	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;
+
+	g_source_remove(p->timeout_id);
+	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 parse_connect_data(GObex *obex, GObexPacket *pkt)
+{
+	const struct connect_data *data;
+	GObexHeader *connid;
+	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);
+
+	connid = g_obex_packet_get_header(pkt, G_OBEX_HDR_CONNECTION);
+	if (connid != NULL)
+		g_obex_header_get_uint32(connid, &obex->conn_id);
+}
+
+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);
+
+	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) {
+		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 = obex->pending_req;
+	gboolean disconn = err ? TRUE : FALSE, final_rsp = TRUE;
+
+	if (rsp != NULL)
+		final_rsp = parse_response(obex, rsp);
+
+	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 (p != obex->pending_req)
+			return;
+	}
+
+	if (p->cancelled)
+		g_error_free(err);
+
+	if (final_rsp) {
+		pending_pkt_free(p);
+		obex->pending_req = NULL;
+	}
+
+	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) {
+		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:
+	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);
+
+	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);
+}
+
+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/gobex/gobex.h b/bluez/gobex/gobex.h
new file mode 100644
index 0000000..7c47590
--- /dev/null
+++ b/bluez/gobex/gobex.h
@@ -0,0 +1,135 @@
+/*
+ *
+ *  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);
+
+/* 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/install-sh b/bluez/install-sh
new file mode 100755
index 0000000..a9244eb
--- /dev/null
+++ b/bluez/install-sh
@@ -0,0 +1,527 @@
+#!/bin/sh
+# install - install a program, script, or datafile
+
+scriptversion=2011-01-19.21; # UTC
+
+# This originates from X11R5 (mit/util/scripts/install.sh), which was
+# later released in X11R6 (xc/config/util/install.sh) with the
+# following copyright and license.
+#
+# Copyright (C) 1994 X Consortium
+#
+# 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
+# X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+# AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNEC-
+# TION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+# Except as contained in this notice, the name of the X Consortium shall not
+# be used in advertising or otherwise to promote the sale, use or other deal-
+# ings in this Software without prior written authorization from the X Consor-
+# tium.
+#
+#
+# FSF changes to this file are in the public domain.
+#
+# Calling this script install-sh is preferred over install.sh, to prevent
+# `make' implicit rules from creating a file called install from it
+# when there is no Makefile.
+#
+# This script is compatible with the BSD install script, but was written
+# from scratch.
+
+nl='
+'
+IFS=" ""	$nl"
+
+# set DOITPROG to echo to test this script
+
+# Don't use :- since 4.3BSD and earlier shells don't like it.
+doit=${DOITPROG-}
+if test -z "$doit"; then
+  doit_exec=exec
+else
+  doit_exec=$doit
+fi
+
+# Put in absolute file names if you don't have them in your path;
+# or use environment vars.
+
+chgrpprog=${CHGRPPROG-chgrp}
+chmodprog=${CHMODPROG-chmod}
+chownprog=${CHOWNPROG-chown}
+cmpprog=${CMPPROG-cmp}
+cpprog=${CPPROG-cp}
+mkdirprog=${MKDIRPROG-mkdir}
+mvprog=${MVPROG-mv}
+rmprog=${RMPROG-rm}
+stripprog=${STRIPPROG-strip}
+
+posix_glob='?'
+initialize_posix_glob='
+  test "$posix_glob" != "?" || {
+    if (set -f) 2>/dev/null; then
+      posix_glob=
+    else
+      posix_glob=:
+    fi
+  }
+'
+
+posix_mkdir=
+
+# Desired mode of installed file.
+mode=0755
+
+chgrpcmd=
+chmodcmd=$chmodprog
+chowncmd=
+mvcmd=$mvprog
+rmcmd="$rmprog -f"
+stripcmd=
+
+src=
+dst=
+dir_arg=
+dst_arg=
+
+copy_on_change=false
+no_target_directory=
+
+usage="\
+Usage: $0 [OPTION]... [-T] SRCFILE DSTFILE
+   or: $0 [OPTION]... SRCFILES... DIRECTORY
+   or: $0 [OPTION]... -t DIRECTORY SRCFILES...
+   or: $0 [OPTION]... -d DIRECTORIES...
+
+In the 1st form, copy SRCFILE to DSTFILE.
+In the 2nd and 3rd, copy all SRCFILES to DIRECTORY.
+In the 4th, create DIRECTORIES.
+
+Options:
+     --help     display this help and exit.
+     --version  display version info and exit.
+
+  -c            (ignored)
+  -C            install only if different (preserve the last data modification time)
+  -d            create directories instead of installing files.
+  -g GROUP      $chgrpprog installed files to GROUP.
+  -m MODE       $chmodprog installed files to MODE.
+  -o USER       $chownprog installed files to USER.
+  -s            $stripprog installed files.
+  -t DIRECTORY  install into DIRECTORY.
+  -T            report an error if DSTFILE is a directory.
+
+Environment variables override the default commands:
+  CHGRPPROG CHMODPROG CHOWNPROG CMPPROG CPPROG MKDIRPROG MVPROG
+  RMPROG STRIPPROG
+"
+
+while test $# -ne 0; do
+  case $1 in
+    -c) ;;
+
+    -C) copy_on_change=true;;
+
+    -d) dir_arg=true;;
+
+    -g) chgrpcmd="$chgrpprog $2"
+	shift;;
+
+    --help) echo "$usage"; exit $?;;
+
+    -m) mode=$2
+	case $mode in
+	  *' '* | *'	'* | *'
+'*	  | *'*'* | *'?'* | *'['*)
+	    echo "$0: invalid mode: $mode" >&2
+	    exit 1;;
+	esac
+	shift;;
+
+    -o) chowncmd="$chownprog $2"
+	shift;;
+
+    -s) stripcmd=$stripprog;;
+
+    -t) dst_arg=$2
+	# Protect names problematic for `test' and other utilities.
+	case $dst_arg in
+	  -* | [=\(\)!]) dst_arg=./$dst_arg;;
+	esac
+	shift;;
+
+    -T) no_target_directory=true;;
+
+    --version) echo "$0 $scriptversion"; exit $?;;
+
+    --)	shift
+	break;;
+
+    -*)	echo "$0: invalid option: $1" >&2
+	exit 1;;
+
+    *)  break;;
+  esac
+  shift
+done
+
+if test $# -ne 0 && test -z "$dir_arg$dst_arg"; then
+  # When -d is used, all remaining arguments are directories to create.
+  # When -t is used, the destination is already specified.
+  # Otherwise, the last argument is the destination.  Remove it from $@.
+  for arg
+  do
+    if test -n "$dst_arg"; then
+      # $@ is not empty: it contains at least $arg.
+      set fnord "$@" "$dst_arg"
+      shift # fnord
+    fi
+    shift # arg
+    dst_arg=$arg
+    # Protect names problematic for `test' and other utilities.
+    case $dst_arg in
+      -* | [=\(\)!]) dst_arg=./$dst_arg;;
+    esac
+  done
+fi
+
+if test $# -eq 0; then
+  if test -z "$dir_arg"; then
+    echo "$0: no input file specified." >&2
+    exit 1
+  fi
+  # It's OK to call `install-sh -d' without argument.
+  # This can happen when creating conditional directories.
+  exit 0
+fi
+
+if test -z "$dir_arg"; then
+  do_exit='(exit $ret); exit $ret'
+  trap "ret=129; $do_exit" 1
+  trap "ret=130; $do_exit" 2
+  trap "ret=141; $do_exit" 13
+  trap "ret=143; $do_exit" 15
+
+  # Set umask so as not to create temps with too-generous modes.
+  # However, 'strip' requires both read and write access to temps.
+  case $mode in
+    # Optimize common cases.
+    *644) cp_umask=133;;
+    *755) cp_umask=22;;
+
+    *[0-7])
+      if test -z "$stripcmd"; then
+	u_plus_rw=
+      else
+	u_plus_rw='% 200'
+      fi
+      cp_umask=`expr '(' 777 - $mode % 1000 ')' $u_plus_rw`;;
+    *)
+      if test -z "$stripcmd"; then
+	u_plus_rw=
+      else
+	u_plus_rw=,u+rw
+      fi
+      cp_umask=$mode$u_plus_rw;;
+  esac
+fi
+
+for src
+do
+  # Protect names problematic for `test' and other utilities.
+  case $src in
+    -* | [=\(\)!]) src=./$src;;
+  esac
+
+  if test -n "$dir_arg"; then
+    dst=$src
+    dstdir=$dst
+    test -d "$dstdir"
+    dstdir_status=$?
+  else
+
+    # Waiting for this to be detected by the "$cpprog $src $dsttmp" command
+    # might cause directories to be created, which would be especially bad
+    # if $src (and thus $dsttmp) contains '*'.
+    if test ! -f "$src" && test ! -d "$src"; then
+      echo "$0: $src does not exist." >&2
+      exit 1
+    fi
+
+    if test -z "$dst_arg"; then
+      echo "$0: no destination specified." >&2
+      exit 1
+    fi
+    dst=$dst_arg
+
+    # If destination is a directory, append the input filename; won't work
+    # if double slashes aren't ignored.
+    if test -d "$dst"; then
+      if test -n "$no_target_directory"; then
+	echo "$0: $dst_arg: Is a directory" >&2
+	exit 1
+      fi
+      dstdir=$dst
+      dst=$dstdir/`basename "$src"`
+      dstdir_status=0
+    else
+      # Prefer dirname, but fall back on a substitute if dirname fails.
+      dstdir=`
+	(dirname "$dst") 2>/dev/null ||
+	expr X"$dst" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+	     X"$dst" : 'X\(//\)[^/]' \| \
+	     X"$dst" : 'X\(//\)$' \| \
+	     X"$dst" : 'X\(/\)' \| . 2>/dev/null ||
+	echo X"$dst" |
+	    sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{
+		   s//\1/
+		   q
+		 }
+		 /^X\(\/\/\)[^/].*/{
+		   s//\1/
+		   q
+		 }
+		 /^X\(\/\/\)$/{
+		   s//\1/
+		   q
+		 }
+		 /^X\(\/\).*/{
+		   s//\1/
+		   q
+		 }
+		 s/.*/./; q'
+      `
+
+      test -d "$dstdir"
+      dstdir_status=$?
+    fi
+  fi
+
+  obsolete_mkdir_used=false
+
+  if test $dstdir_status != 0; then
+    case $posix_mkdir in
+      '')
+	# Create intermediate dirs using mode 755 as modified by the umask.
+	# This is like FreeBSD 'install' as of 1997-10-28.
+	umask=`umask`
+	case $stripcmd.$umask in
+	  # Optimize common cases.
+	  *[2367][2367]) mkdir_umask=$umask;;
+	  .*0[02][02] | .[02][02] | .[02]) mkdir_umask=22;;
+
+	  *[0-7])
+	    mkdir_umask=`expr $umask + 22 \
+	      - $umask % 100 % 40 + $umask % 20 \
+	      - $umask % 10 % 4 + $umask % 2
+	    `;;
+	  *) mkdir_umask=$umask,go-w;;
+	esac
+
+	# With -d, create the new directory with the user-specified mode.
+	# Otherwise, rely on $mkdir_umask.
+	if test -n "$dir_arg"; then
+	  mkdir_mode=-m$mode
+	else
+	  mkdir_mode=
+	fi
+
+	posix_mkdir=false
+	case $umask in
+	  *[123567][0-7][0-7])
+	    # POSIX mkdir -p sets u+wx bits regardless of umask, which
+	    # is incompatible with FreeBSD 'install' when (umask & 300) != 0.
+	    ;;
+	  *)
+	    tmpdir=${TMPDIR-/tmp}/ins$RANDOM-$$
+	    trap 'ret=$?; rmdir "$tmpdir/d" "$tmpdir" 2>/dev/null; exit $ret' 0
+
+	    if (umask $mkdir_umask &&
+		exec $mkdirprog $mkdir_mode -p -- "$tmpdir/d") >/dev/null 2>&1
+	    then
+	      if test -z "$dir_arg" || {
+		   # Check for POSIX incompatibilities with -m.
+		   # HP-UX 11.23 and IRIX 6.5 mkdir -m -p sets group- or
+		   # other-writeable bit of parent directory when it shouldn't.
+		   # FreeBSD 6.1 mkdir -m -p sets mode of existing directory.
+		   ls_ld_tmpdir=`ls -ld "$tmpdir"`
+		   case $ls_ld_tmpdir in
+		     d????-?r-*) different_mode=700;;
+		     d????-?--*) different_mode=755;;
+		     *) false;;
+		   esac &&
+		   $mkdirprog -m$different_mode -p -- "$tmpdir" && {
+		     ls_ld_tmpdir_1=`ls -ld "$tmpdir"`
+		     test "$ls_ld_tmpdir" = "$ls_ld_tmpdir_1"
+		   }
+		 }
+	      then posix_mkdir=:
+	      fi
+	      rmdir "$tmpdir/d" "$tmpdir"
+	    else
+	      # Remove any dirs left behind by ancient mkdir implementations.
+	      rmdir ./$mkdir_mode ./-p ./-- 2>/dev/null
+	    fi
+	    trap '' 0;;
+	esac;;
+    esac
+
+    if
+      $posix_mkdir && (
+	umask $mkdir_umask &&
+	$doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir"
+      )
+    then :
+    else
+
+      # The umask is ridiculous, or mkdir does not conform to POSIX,
+      # or it failed possibly due to a race condition.  Create the
+      # directory the slow way, step by step, checking for races as we go.
+
+      case $dstdir in
+	/*) prefix='/';;
+	[-=\(\)!]*) prefix='./';;
+	*)  prefix='';;
+      esac
+
+      eval "$initialize_posix_glob"
+
+      oIFS=$IFS
+      IFS=/
+      $posix_glob set -f
+      set fnord $dstdir
+      shift
+      $posix_glob set +f
+      IFS=$oIFS
+
+      prefixes=
+
+      for d
+      do
+	test X"$d" = X && continue
+
+	prefix=$prefix$d
+	if test -d "$prefix"; then
+	  prefixes=
+	else
+	  if $posix_mkdir; then
+	    (umask=$mkdir_umask &&
+	     $doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir") && break
+	    # Don't fail if two instances are running concurrently.
+	    test -d "$prefix" || exit 1
+	  else
+	    case $prefix in
+	      *\'*) qprefix=`echo "$prefix" | sed "s/'/'\\\\\\\\''/g"`;;
+	      *) qprefix=$prefix;;
+	    esac
+	    prefixes="$prefixes '$qprefix'"
+	  fi
+	fi
+	prefix=$prefix/
+      done
+
+      if test -n "$prefixes"; then
+	# Don't fail if two instances are running concurrently.
+	(umask $mkdir_umask &&
+	 eval "\$doit_exec \$mkdirprog $prefixes") ||
+	  test -d "$dstdir" || exit 1
+	obsolete_mkdir_used=true
+      fi
+    fi
+  fi
+
+  if test -n "$dir_arg"; then
+    { test -z "$chowncmd" || $doit $chowncmd "$dst"; } &&
+    { test -z "$chgrpcmd" || $doit $chgrpcmd "$dst"; } &&
+    { test "$obsolete_mkdir_used$chowncmd$chgrpcmd" = false ||
+      test -z "$chmodcmd" || $doit $chmodcmd $mode "$dst"; } || exit 1
+  else
+
+    # Make a couple of temp file names in the proper directory.
+    dsttmp=$dstdir/_inst.$$_
+    rmtmp=$dstdir/_rm.$$_
+
+    # Trap to clean up those temp files at exit.
+    trap 'ret=$?; rm -f "$dsttmp" "$rmtmp" && exit $ret' 0
+
+    # Copy the file name to the temp name.
+    (umask $cp_umask && $doit_exec $cpprog "$src" "$dsttmp") &&
+
+    # and set any options; do chmod last to preserve setuid bits.
+    #
+    # If any of these fail, we abort the whole thing.  If we want to
+    # ignore errors from any of these, just make sure not to ignore
+    # errors from the above "$doit $cpprog $src $dsttmp" command.
+    #
+    { test -z "$chowncmd" || $doit $chowncmd "$dsttmp"; } &&
+    { test -z "$chgrpcmd" || $doit $chgrpcmd "$dsttmp"; } &&
+    { test -z "$stripcmd" || $doit $stripcmd "$dsttmp"; } &&
+    { test -z "$chmodcmd" || $doit $chmodcmd $mode "$dsttmp"; } &&
+
+    # If -C, don't bother to copy if it wouldn't change the file.
+    if $copy_on_change &&
+       old=`LC_ALL=C ls -dlL "$dst"	2>/dev/null` &&
+       new=`LC_ALL=C ls -dlL "$dsttmp"	2>/dev/null` &&
+
+       eval "$initialize_posix_glob" &&
+       $posix_glob set -f &&
+       set X $old && old=:$2:$4:$5:$6 &&
+       set X $new && new=:$2:$4:$5:$6 &&
+       $posix_glob set +f &&
+
+       test "$old" = "$new" &&
+       $cmpprog "$dst" "$dsttmp" >/dev/null 2>&1
+    then
+      rm -f "$dsttmp"
+    else
+      # Rename the file to the real destination.
+      $doit $mvcmd -f "$dsttmp" "$dst" 2>/dev/null ||
+
+      # The rename failed, perhaps because mv can't rename something else
+      # to itself, or perhaps because mv is so ancient that it does not
+      # support -f.
+      {
+	# Now remove or move aside any old file at destination location.
+	# We try this two ways since rm can't unlink itself on some
+	# systems and the destination file might be busy for other
+	# reasons.  In this case, the final cleanup might fail but the new
+	# file should still install successfully.
+	{
+	  test ! -f "$dst" ||
+	  $doit $rmcmd -f "$dst" 2>/dev/null ||
+	  { $doit $mvcmd -f "$dst" "$rmtmp" 2>/dev/null &&
+	    { $doit $rmcmd -f "$rmtmp" 2>/dev/null; :; }
+	  } ||
+	  { echo "$0: cannot unlink or rename $dst" >&2
+	    (exit 1); exit 1
+	  }
+	} &&
+
+	# Now rename the file to the real destination.
+	$doit $mvcmd "$dsttmp" "$dst"
+      }
+    fi || exit 1
+
+    trap '' 0
+  fi
+done
+
+# Local variables:
+# eval: (add-hook 'write-file-hooks 'time-stamp)
+# time-stamp-start: "scriptversion="
+# time-stamp-format: "%:y-%02m-%02d.%02H"
+# time-stamp-time-zone: "UTC"
+# time-stamp-end: "; # UTC"
+# End:
diff --git a/bluez/lib/a2mp.h b/bluez/lib/a2mp.h
new file mode 100644
index 0000000..da937d1
--- /dev/null
+++ b/bluez/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/lib/amp.h b/bluez/lib/amp.h
new file mode 100644
index 0000000..27aab1d
--- /dev/null
+++ b/bluez/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/lib/bluetooth.c b/bluez/lib/bluetooth.c
new file mode 100644
index 0000000..83035e8
--- /dev/null
+++ b/bluez/lib/bluetooth.c
@@ -0,0 +1,893 @@
+/*
+ *
+ *  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 "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 Technology Corp.";
+	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 "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 "Qualcomm Retail Solutions, Inc. (formerly Qualcomm Labs, 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 "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 "vyzybl 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 "ETC sp. z.o.o.";
+	case 311:
+		return "Prestigio Plaza Ltd.";
+	case 65535:
+		return "internal use";
+	default:
+		return "not assigned";
+	}
+}
diff --git a/bluez/lib/bluetooth.h b/bluez/lib/bluetooth.h
new file mode 100644
index 0000000..61c1f9a
--- /dev/null
+++ b/bluez/lib/bluetooth.h
@@ -0,0 +1,399 @@
+/*
+ *
+ *  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)			\
+({						\
+	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;
+
+#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)
+{
+	int i;
+
+	for (i = 0; i < 16; i++)
+		dst->data[15 - i] = src->data[i];
+}
+
+#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)
+{
+	int i;
+
+	for (i = 0; i < 16; i++)
+		dst->data[15 - i] = src->data[i];
+}
+
+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/lib/bluez.pc.in b/bluez/lib/bluez.pc.in
new file mode 100644
index 0000000..3d6e596
--- /dev/null
+++ b/bluez/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/lib/bnep.h b/bluez/lib/bnep.h
new file mode 100644
index 0000000..2bbfb17
--- /dev/null
+++ b/bluez/lib/bnep.h
@@ -0,0 +1,153 @@
+/*
+ *
+ *  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_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)
+
+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/lib/cmtp.h b/bluez/lib/cmtp.h
new file mode 100644
index 0000000..ce937bd
--- /dev/null
+++ b/bluez/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/lib/hci.c b/bluez/lib/hci.c
new file mode 100644
index 0000000..005578a
--- /dev/null
+++ b/bluez/lib/hci.c
@@ -0,0 +1,2929 @@
+/*
+ *
+ *  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 <sys/param.h>
+#include <sys/uio.h>
+#include <sys/poll.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 },
+	{ 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)
+{
+	return hci_for_each_dev(HCI_UP, __other_bdaddr,
+				(long) (bdaddr ? bdaddr : BDADDR_ANY));
+}
+
+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;
+
+	/* 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_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;
+}
diff --git a/bluez/lib/hci.h b/bluez/lib/hci.h
new file mode 100644
index 0000000..0c94829
--- /dev/null
+++ b/bluez/lib/hci.h
@@ -0,0 +1,2418 @@
+/*
+ *
+ *  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
+
+/* 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
+
+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/lib/hci_lib.h b/bluez/lib/hci_lib.h
new file mode 100644
index 0000000..50744c3
--- /dev/null
+++ b/bluez/lib/hci_lib.h
@@ -0,0 +1,235 @@
+/*
+ *
+ *  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_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/lib/hidp.h b/bluez/lib/hidp.h
new file mode 100644
index 0000000..c5e6a78
--- /dev/null
+++ b/bluez/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/lib/l2cap.h b/bluez/lib/l2cap.h
new file mode 100644
index 0000000..5ce94c4
--- /dev/null
+++ b/bluez/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/lib/mgmt.h b/bluez/lib/mgmt.h
new file mode 100644
index 0000000..ab45735
--- /dev/null
+++ b/bluez/lib/mgmt.h
@@ -0,0 +1,649 @@
+/*
+ *  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
+
+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_PAIRABLE		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_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_PAIRABLE		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 hash[16];
+	uint8_t randomizer[16];
+} __packed;
+struct mgmt_rp_read_local_oob_ext_data {
+	uint8_t hash192[16];
+	uint8_t randomizer192[16];
+	uint8_t hash256[16];
+	uint8_t randomizer256[16];
+} __packed;
+
+#define MGMT_OP_ADD_REMOTE_OOB_DATA	0x0021
+struct mgmt_cp_add_remote_oob_data {
+	struct mgmt_addr_info addr;
+	uint8_t hash[16];
+	uint8_t randomizer[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_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 class_of_dev[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_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 master;
+	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;
+
+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 Pairable",
+	"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",
+	"Set Advertising",
+	"Set BR/EDR",
+	"Set Static Address",
+	"Set Scan Parameters",
+	"Set Secure Connections",
+	"Set Debug Keys",
+	"Set Privacy",
+	"Load Identity Resolving Keys",
+};
+
+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",
+	"New Signature Resolving Key",
+};
+
+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",
+};
+
+#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/lib/rfcomm.h b/bluez/lib/rfcomm.h
new file mode 100644
index 0000000..ad6c0e1
--- /dev/null
+++ b/bluez/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/lib/sco.h b/bluez/lib/sco.h
new file mode 100644
index 0000000..75336a5
--- /dev/null
+++ b/bluez/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/lib/sdp.c b/bluez/lib/sdp.c
new file mode 100644
index 0000000..e5e4622
--- /dev/null
+++ b/bluez/lib/sdp.c
@@ -0,0 +1,4940 @@
+/*
+ *
+ *  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 = 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->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 sendind 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 sendind 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 sendind 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 (n == 0 || reqhdr->tid != rsphdr->tid ||
+		(n != (int) (ntohs(rsphdr->plen) + sizeof(sdp_pdu_hdr_t)))) {
+		t->err = EPROTO;
+		SDPERR("Protocol error.");
+		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 sendind 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:
+				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/lib/sdp.h b/bluez/lib/sdp.h
new file mode 100644
index 0000000..34adf9a
--- /dev/null
+++ b/bluez/lib/sdp.h
@@ -0,0 +1,536 @@
+/*
+ *
+ *  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 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
+
+/*
+ * 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_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/lib/sdp_lib.h b/bluez/lib/sdp_lib.h
new file mode 100644
index 0000000..3ded393
--- /dev/null
+++ b/bluez/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/lib/uuid.c b/bluez/lib/uuid.c
new file mode 100644
index 0000000..5c3f986
--- /dev/null
+++ b/bluez/lib/uuid.c
@@ -0,0 +1,275 @@
+/*
+ *
+ *  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 "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;
+	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)
+{
+	if (!uuid) {
+		snprintf(str, n, "NULL");
+		return -EINVAL;
+	}
+
+	switch (uuid->type) {
+	case BT_UUID16:
+		snprintf(str, n, "%.4x", uuid->value.u16);
+		break;
+	case BT_UUID32:
+		snprintf(str, n, "%.8x", uuid->value.u32);
+		break;
+	case BT_UUID128: {
+		unsigned int   data0;
+		unsigned short data1;
+		unsigned short data2;
+		unsigned short data3;
+		unsigned int   data4;
+		unsigned short data5;
+
+		const uint8_t *data = (uint8_t *) &uuid->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));
+		}
+		break;
+	default:
+		snprintf(str, n, "Type of UUID (%x) unknown.", uuid->type);
+		return -EINVAL;	/* Enum type of UUID not set */
+	}
+
+	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_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') {
+		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_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)
+{
+	return strcasecmp(a, b);
+}
diff --git a/bluez/lib/uuid.h b/bluez/lib/uuid.h
new file mode 100644
index 0000000..237145b
--- /dev/null
+++ b/bluez/lib/uuid.h
@@ -0,0 +1,170 @@
+/*
+ *
+ *  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>
+#include <bluetooth/bluetooth.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 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
+
+/* 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);
+
+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/ltmain.sh b/bluez/ltmain.sh
new file mode 100644
index 0000000..33f642a
--- /dev/null
+++ b/bluez/ltmain.sh
@@ -0,0 +1,9661 @@
+
+# libtool (GNU libtool) 2.4.2
+# Written by Gordon Matzigkeit <gord@gnu.ai.mit.edu>, 1996
+
+# Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2003, 2004, 2005, 2006,
+# 2007, 2008, 2009, 2010, 2011 Free Software Foundation, Inc.
+# This is free software; see the source for copying conditions.  There is NO
+# warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+
+# GNU Libtool is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# As a special exception to the GNU General Public License,
+# if you distribute this file as part of a program or library that
+# is built using GNU Libtool, you may include this file under the
+# same distribution terms that you use for the rest of that program.
+#
+# GNU Libtool is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with GNU Libtool; see the file COPYING.  If not, a copy
+# can be downloaded from http://www.gnu.org/licenses/gpl.html,
+# or obtained by writing to the Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+
+# Usage: $progname [OPTION]... [MODE-ARG]...
+#
+# Provide generalized library-building support services.
+#
+#       --config             show all configuration variables
+#       --debug              enable verbose shell tracing
+#   -n, --dry-run            display commands without modifying any files
+#       --features           display basic configuration information and exit
+#       --mode=MODE          use operation mode MODE
+#       --preserve-dup-deps  don't remove duplicate dependency libraries
+#       --quiet, --silent    don't print informational messages
+#       --no-quiet, --no-silent
+#                            print informational messages (default)
+#       --no-warn            don't display warning messages
+#       --tag=TAG            use configuration variables from tag TAG
+#   -v, --verbose            print more informational messages than default
+#       --no-verbose         don't print the extra informational messages
+#       --version            print version information
+#   -h, --help, --help-all   print short, long, or detailed help message
+#
+# MODE must be one of the following:
+#
+#         clean              remove files from the build directory
+#         compile            compile a source file into a libtool object
+#         execute            automatically set library path, then run a program
+#         finish             complete the installation of libtool libraries
+#         install            install libraries or executables
+#         link               create a library or an executable
+#         uninstall          remove libraries from an installed directory
+#
+# MODE-ARGS vary depending on the MODE.  When passed as first option,
+# `--mode=MODE' may be abbreviated as `MODE' or a unique abbreviation of that.
+# Try `$progname --help --mode=MODE' for a more detailed description of MODE.
+#
+# When reporting a bug, please describe a test case to reproduce it and
+# include the following information:
+#
+#         host-triplet:	$host
+#         shell:		$SHELL
+#         compiler:		$LTCC
+#         compiler flags:		$LTCFLAGS
+#         linker:		$LD (gnu? $with_gnu_ld)
+#         $progname:	(GNU libtool) 2.4.2 Debian-2.4.2-1.1
+#         automake:	$automake_version
+#         autoconf:	$autoconf_version
+#
+# Report bugs to <bug-libtool@gnu.org>.
+# GNU libtool home page: <http://www.gnu.org/software/libtool/>.
+# General help using GNU software: <http://www.gnu.org/gethelp/>.
+
+PROGRAM=libtool
+PACKAGE=libtool
+VERSION="2.4.2 Debian-2.4.2-1.1"
+TIMESTAMP=""
+package_revision=1.3337
+
+# Be Bourne compatible
+if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then
+  emulate sh
+  NULLCMD=:
+  # Zsh 3.x and 4.x performs word splitting on ${1+"$@"}, which
+  # is contrary to our usage.  Disable this feature.
+  alias -g '${1+"$@"}'='"$@"'
+  setopt NO_GLOB_SUBST
+else
+  case `(set -o) 2>/dev/null` in *posix*) set -o posix;; esac
+fi
+BIN_SH=xpg4; export BIN_SH # for Tru64
+DUALCASE=1; export DUALCASE # for MKS sh
+
+# A function that is used when there is no print builtin or printf.
+func_fallback_echo ()
+{
+  eval 'cat <<_LTECHO_EOF
+$1
+_LTECHO_EOF'
+}
+
+# NLS nuisances: We save the old values to restore during execute mode.
+lt_user_locale=
+lt_safe_locale=
+for lt_var in LANG LANGUAGE LC_ALL LC_CTYPE LC_COLLATE LC_MESSAGES
+do
+  eval "if test \"\${$lt_var+set}\" = set; then
+          save_$lt_var=\$$lt_var
+          $lt_var=C
+	  export $lt_var
+	  lt_user_locale=\"$lt_var=\\\$save_\$lt_var; \$lt_user_locale\"
+	  lt_safe_locale=\"$lt_var=C; \$lt_safe_locale\"
+	fi"
+done
+LC_ALL=C
+LANGUAGE=C
+export LANGUAGE LC_ALL
+
+$lt_unset CDPATH
+
+
+# Work around backward compatibility issue on IRIX 6.5. On IRIX 6.4+, sh
+# is ksh but when the shell is invoked as "sh" and the current value of
+# the _XPG environment variable is not equal to 1 (one), the special
+# positional parameter $0, within a function call, is the name of the
+# function.
+progpath="$0"
+
+
+
+: ${CP="cp -f"}
+test "${ECHO+set}" = set || ECHO=${as_echo-'printf %s\n'}
+: ${MAKE="make"}
+: ${MKDIR="mkdir"}
+: ${MV="mv -f"}
+: ${RM="rm -f"}
+: ${SHELL="${CONFIG_SHELL-/bin/sh}"}
+: ${Xsed="$SED -e 1s/^X//"}
+
+# Global variables:
+EXIT_SUCCESS=0
+EXIT_FAILURE=1
+EXIT_MISMATCH=63  # $? = 63 is used to indicate version mismatch to missing.
+EXIT_SKIP=77	  # $? = 77 is used to indicate a skipped test to automake.
+
+exit_status=$EXIT_SUCCESS
+
+# Make sure IFS has a sensible default
+lt_nl='
+'
+IFS=" 	$lt_nl"
+
+dirname="s,/[^/]*$,,"
+basename="s,^.*/,,"
+
+# func_dirname file append nondir_replacement
+# Compute the dirname of FILE.  If nonempty, add APPEND to the result,
+# otherwise set result to NONDIR_REPLACEMENT.
+func_dirname ()
+{
+    func_dirname_result=`$ECHO "${1}" | $SED "$dirname"`
+    if test "X$func_dirname_result" = "X${1}"; then
+      func_dirname_result="${3}"
+    else
+      func_dirname_result="$func_dirname_result${2}"
+    fi
+} # func_dirname may be replaced by extended shell implementation
+
+
+# func_basename file
+func_basename ()
+{
+    func_basename_result=`$ECHO "${1}" | $SED "$basename"`
+} # func_basename may be replaced by extended shell implementation
+
+
+# func_dirname_and_basename file append nondir_replacement
+# perform func_basename and func_dirname in a single function
+# call:
+#   dirname:  Compute the dirname of FILE.  If nonempty,
+#             add APPEND to the result, otherwise set result
+#             to NONDIR_REPLACEMENT.
+#             value returned in "$func_dirname_result"
+#   basename: Compute filename of FILE.
+#             value retuned in "$func_basename_result"
+# Implementation must be kept synchronized with func_dirname
+# and func_basename. For efficiency, we do not delegate to
+# those functions but instead duplicate the functionality here.
+func_dirname_and_basename ()
+{
+    # Extract subdirectory from the argument.
+    func_dirname_result=`$ECHO "${1}" | $SED -e "$dirname"`
+    if test "X$func_dirname_result" = "X${1}"; then
+      func_dirname_result="${3}"
+    else
+      func_dirname_result="$func_dirname_result${2}"
+    fi
+    func_basename_result=`$ECHO "${1}" | $SED -e "$basename"`
+} # func_dirname_and_basename may be replaced by extended shell implementation
+
+
+# func_stripname prefix suffix name
+# strip PREFIX and SUFFIX off of NAME.
+# PREFIX and SUFFIX must not contain globbing or regex special
+# characters, hashes, percent signs, but SUFFIX may contain a leading
+# dot (in which case that matches only a dot).
+# func_strip_suffix prefix name
+func_stripname ()
+{
+    case ${2} in
+      .*) func_stripname_result=`$ECHO "${3}" | $SED "s%^${1}%%; s%\\\\${2}\$%%"`;;
+      *)  func_stripname_result=`$ECHO "${3}" | $SED "s%^${1}%%; s%${2}\$%%"`;;
+    esac
+} # func_stripname may be replaced by extended shell implementation
+
+
+# These SED scripts presuppose an absolute path with a trailing slash.
+pathcar='s,^/\([^/]*\).*$,\1,'
+pathcdr='s,^/[^/]*,,'
+removedotparts=':dotsl
+		s@/\./@/@g
+		t dotsl
+		s,/\.$,/,'
+collapseslashes='s@/\{1,\}@/@g'
+finalslash='s,/*$,/,'
+
+# func_normal_abspath PATH
+# Remove doubled-up and trailing slashes, "." path components,
+# and cancel out any ".." path components in PATH after making
+# it an absolute path.
+#             value returned in "$func_normal_abspath_result"
+func_normal_abspath ()
+{
+  # Start from root dir and reassemble the path.
+  func_normal_abspath_result=
+  func_normal_abspath_tpath=$1
+  func_normal_abspath_altnamespace=
+  case $func_normal_abspath_tpath in
+    "")
+      # Empty path, that just means $cwd.
+      func_stripname '' '/' "`pwd`"
+      func_normal_abspath_result=$func_stripname_result
+      return
+    ;;
+    # The next three entries are used to spot a run of precisely
+    # two leading slashes without using negated character classes;
+    # we take advantage of case's first-match behaviour.
+    ///*)
+      # Unusual form of absolute path, do nothing.
+    ;;
+    //*)
+      # Not necessarily an ordinary path; POSIX reserves leading '//'
+      # and for example Cygwin uses it to access remote file shares
+      # over CIFS/SMB, so we conserve a leading double slash if found.
+      func_normal_abspath_altnamespace=/
+    ;;
+    /*)
+      # Absolute path, do nothing.
+    ;;
+    *)
+      # Relative path, prepend $cwd.
+      func_normal_abspath_tpath=`pwd`/$func_normal_abspath_tpath
+    ;;
+  esac
+  # Cancel out all the simple stuff to save iterations.  We also want
+  # the path to end with a slash for ease of parsing, so make sure
+  # there is one (and only one) here.
+  func_normal_abspath_tpath=`$ECHO "$func_normal_abspath_tpath" | $SED \
+        -e "$removedotparts" -e "$collapseslashes" -e "$finalslash"`
+  while :; do
+    # Processed it all yet?
+    if test "$func_normal_abspath_tpath" = / ; then
+      # If we ascended to the root using ".." the result may be empty now.
+      if test -z "$func_normal_abspath_result" ; then
+        func_normal_abspath_result=/
+      fi
+      break
+    fi
+    func_normal_abspath_tcomponent=`$ECHO "$func_normal_abspath_tpath" | $SED \
+        -e "$pathcar"`
+    func_normal_abspath_tpath=`$ECHO "$func_normal_abspath_tpath" | $SED \
+        -e "$pathcdr"`
+    # Figure out what to do with it
+    case $func_normal_abspath_tcomponent in
+      "")
+        # Trailing empty path component, ignore it.
+      ;;
+      ..)
+        # Parent dir; strip last assembled component from result.
+        func_dirname "$func_normal_abspath_result"
+        func_normal_abspath_result=$func_dirname_result
+      ;;
+      *)
+        # Actual path component, append it.
+        func_normal_abspath_result=$func_normal_abspath_result/$func_normal_abspath_tcomponent
+      ;;
+    esac
+  done
+  # Restore leading double-slash if one was found on entry.
+  func_normal_abspath_result=$func_normal_abspath_altnamespace$func_normal_abspath_result
+}
+
+# func_relative_path SRCDIR DSTDIR
+# generates a relative path from SRCDIR to DSTDIR, with a trailing
+# slash if non-empty, suitable for immediately appending a filename
+# without needing to append a separator.
+#             value returned in "$func_relative_path_result"
+func_relative_path ()
+{
+  func_relative_path_result=
+  func_normal_abspath "$1"
+  func_relative_path_tlibdir=$func_normal_abspath_result
+  func_normal_abspath "$2"
+  func_relative_path_tbindir=$func_normal_abspath_result
+
+  # Ascend the tree starting from libdir
+  while :; do
+    # check if we have found a prefix of bindir
+    case $func_relative_path_tbindir in
+      $func_relative_path_tlibdir)
+        # found an exact match
+        func_relative_path_tcancelled=
+        break
+        ;;
+      $func_relative_path_tlibdir*)
+        # found a matching prefix
+        func_stripname "$func_relative_path_tlibdir" '' "$func_relative_path_tbindir"
+        func_relative_path_tcancelled=$func_stripname_result
+        if test -z "$func_relative_path_result"; then
+          func_relative_path_result=.
+        fi
+        break
+        ;;
+      *)
+        func_dirname $func_relative_path_tlibdir
+        func_relative_path_tlibdir=${func_dirname_result}
+        if test "x$func_relative_path_tlibdir" = x ; then
+          # Have to descend all the way to the root!
+          func_relative_path_result=../$func_relative_path_result
+          func_relative_path_tcancelled=$func_relative_path_tbindir
+          break
+        fi
+        func_relative_path_result=../$func_relative_path_result
+        ;;
+    esac
+  done
+
+  # Now calculate path; take care to avoid doubling-up slashes.
+  func_stripname '' '/' "$func_relative_path_result"
+  func_relative_path_result=$func_stripname_result
+  func_stripname '/' '/' "$func_relative_path_tcancelled"
+  if test "x$func_stripname_result" != x ; then
+    func_relative_path_result=${func_relative_path_result}/${func_stripname_result}
+  fi
+
+  # Normalisation. If bindir is libdir, return empty string,
+  # else relative path ending with a slash; either way, target
+  # file name can be directly appended.
+  if test ! -z "$func_relative_path_result"; then
+    func_stripname './' '' "$func_relative_path_result/"
+    func_relative_path_result=$func_stripname_result
+  fi
+}
+
+# The name of this program:
+func_dirname_and_basename "$progpath"
+progname=$func_basename_result
+
+# Make sure we have an absolute path for reexecution:
+case $progpath in
+  [\\/]*|[A-Za-z]:\\*) ;;
+  *[\\/]*)
+     progdir=$func_dirname_result
+     progdir=`cd "$progdir" && pwd`
+     progpath="$progdir/$progname"
+     ;;
+  *)
+     save_IFS="$IFS"
+     IFS=${PATH_SEPARATOR-:}
+     for progdir in $PATH; do
+       IFS="$save_IFS"
+       test -x "$progdir/$progname" && break
+     done
+     IFS="$save_IFS"
+     test -n "$progdir" || progdir=`pwd`
+     progpath="$progdir/$progname"
+     ;;
+esac
+
+# Sed substitution that helps us do robust quoting.  It backslashifies
+# metacharacters that are still active within double-quoted strings.
+Xsed="${SED}"' -e 1s/^X//'
+sed_quote_subst='s/\([`"$\\]\)/\\\1/g'
+
+# Same as above, but do not quote variable references.
+double_quote_subst='s/\(["`\\]\)/\\\1/g'
+
+# Sed substitution that turns a string into a regex matching for the
+# string literally.
+sed_make_literal_regex='s,[].[^$\\*\/],\\&,g'
+
+# Sed substitution that converts a w32 file name or path
+# which contains forward slashes, into one that contains
+# (escaped) backslashes.  A very naive implementation.
+lt_sed_naive_backslashify='s|\\\\*|\\|g;s|/|\\|g;s|\\|\\\\|g'
+
+# Re-`\' parameter expansions in output of double_quote_subst that were
+# `\'-ed in input to the same.  If an odd number of `\' preceded a '$'
+# in input to double_quote_subst, that '$' was protected from expansion.
+# Since each input `\' is now two `\'s, look for any number of runs of
+# four `\'s followed by two `\'s and then a '$'.  `\' that '$'.
+bs='\\'
+bs2='\\\\'
+bs4='\\\\\\\\'
+dollar='\$'
+sed_double_backslash="\
+  s/$bs4/&\\
+/g
+  s/^$bs2$dollar/$bs&/
+  s/\\([^$bs]\\)$bs2$dollar/\\1$bs2$bs$dollar/g
+  s/\n//g"
+
+# Standard options:
+opt_dry_run=false
+opt_help=false
+opt_quiet=false
+opt_verbose=false
+opt_warning=:
+
+# func_echo arg...
+# Echo program name prefixed message, along with the current mode
+# name if it has been set yet.
+func_echo ()
+{
+    $ECHO "$progname: ${opt_mode+$opt_mode: }$*"
+}
+
+# func_verbose arg...
+# Echo program name prefixed message in verbose mode only.
+func_verbose ()
+{
+    $opt_verbose && func_echo ${1+"$@"}
+
+    # A bug in bash halts the script if the last line of a function
+    # fails when set -e is in force, so we need another command to
+    # work around that:
+    :
+}
+
+# func_echo_all arg...
+# Invoke $ECHO with all args, space-separated.
+func_echo_all ()
+{
+    $ECHO "$*"
+}
+
+# func_error arg...
+# Echo program name prefixed message to standard error.
+func_error ()
+{
+    $ECHO "$progname: ${opt_mode+$opt_mode: }"${1+"$@"} 1>&2
+}
+
+# func_warning arg...
+# Echo program name prefixed warning message to standard error.
+func_warning ()
+{
+    $opt_warning && $ECHO "$progname: ${opt_mode+$opt_mode: }warning: "${1+"$@"} 1>&2
+
+    # bash bug again:
+    :
+}
+
+# func_fatal_error arg...
+# Echo program name prefixed message to standard error, and exit.
+func_fatal_error ()
+{
+    func_error ${1+"$@"}
+    exit $EXIT_FAILURE
+}
+
+# func_fatal_help arg...
+# Echo program name prefixed message to standard error, followed by
+# a help hint, and exit.
+func_fatal_help ()
+{
+    func_error ${1+"$@"}
+    func_fatal_error "$help"
+}
+help="Try \`$progname --help' for more information."  ## default
+
+
+# func_grep expression filename
+# Check whether EXPRESSION matches any line of FILENAME, without output.
+func_grep ()
+{
+    $GREP "$1" "$2" >/dev/null 2>&1
+}
+
+
+# func_mkdir_p directory-path
+# Make sure the entire path to DIRECTORY-PATH is available.
+func_mkdir_p ()
+{
+    my_directory_path="$1"
+    my_dir_list=
+
+    if test -n "$my_directory_path" && test "$opt_dry_run" != ":"; then
+
+      # Protect directory names starting with `-'
+      case $my_directory_path in
+        -*) my_directory_path="./$my_directory_path" ;;
+      esac
+
+      # While some portion of DIR does not yet exist...
+      while test ! -d "$my_directory_path"; do
+        # ...make a list in topmost first order.  Use a colon delimited
+	# list incase some portion of path contains whitespace.
+        my_dir_list="$my_directory_path:$my_dir_list"
+
+        # If the last portion added has no slash in it, the list is done
+        case $my_directory_path in */*) ;; *) break ;; esac
+
+        # ...otherwise throw away the child directory and loop
+        my_directory_path=`$ECHO "$my_directory_path" | $SED -e "$dirname"`
+      done
+      my_dir_list=`$ECHO "$my_dir_list" | $SED 's,:*$,,'`
+
+      save_mkdir_p_IFS="$IFS"; IFS=':'
+      for my_dir in $my_dir_list; do
+	IFS="$save_mkdir_p_IFS"
+        # mkdir can fail with a `File exist' error if two processes
+        # try to create one of the directories concurrently.  Don't
+        # stop in that case!
+        $MKDIR "$my_dir" 2>/dev/null || :
+      done
+      IFS="$save_mkdir_p_IFS"
+
+      # Bail out if we (or some other process) failed to create a directory.
+      test -d "$my_directory_path" || \
+        func_fatal_error "Failed to create \`$1'"
+    fi
+}
+
+
+# func_mktempdir [string]
+# Make a temporary directory that won't clash with other running
+# libtool processes, and avoids race conditions if possible.  If
+# given, STRING is the basename for that directory.
+func_mktempdir ()
+{
+    my_template="${TMPDIR-/tmp}/${1-$progname}"
+
+    if test "$opt_dry_run" = ":"; then
+      # Return a directory name, but don't create it in dry-run mode
+      my_tmpdir="${my_template}-$$"
+    else
+
+      # If mktemp works, use that first and foremost
+      my_tmpdir=`mktemp -d "${my_template}-XXXXXXXX" 2>/dev/null`
+
+      if test ! -d "$my_tmpdir"; then
+        # Failing that, at least try and use $RANDOM to avoid a race
+        my_tmpdir="${my_template}-${RANDOM-0}$$"
+
+        save_mktempdir_umask=`umask`
+        umask 0077
+        $MKDIR "$my_tmpdir"
+        umask $save_mktempdir_umask
+      fi
+
+      # If we're not in dry-run mode, bomb out on failure
+      test -d "$my_tmpdir" || \
+        func_fatal_error "cannot create temporary directory \`$my_tmpdir'"
+    fi
+
+    $ECHO "$my_tmpdir"
+}
+
+
+# func_quote_for_eval arg
+# Aesthetically quote ARG to be evaled later.
+# This function returns two values: FUNC_QUOTE_FOR_EVAL_RESULT
+# is double-quoted, suitable for a subsequent eval, whereas
+# FUNC_QUOTE_FOR_EVAL_UNQUOTED_RESULT has merely all characters
+# which are still active within double quotes backslashified.
+func_quote_for_eval ()
+{
+    case $1 in
+      *[\\\`\"\$]*)
+	func_quote_for_eval_unquoted_result=`$ECHO "$1" | $SED "$sed_quote_subst"` ;;
+      *)
+        func_quote_for_eval_unquoted_result="$1" ;;
+    esac
+
+    case $func_quote_for_eval_unquoted_result in
+      # Double-quote args containing shell metacharacters to delay
+      # word splitting, command substitution and and variable
+      # expansion for a subsequent eval.
+      # Many Bourne shells cannot handle close brackets correctly
+      # in scan sets, so we specify it separately.
+      *[\[\~\#\^\&\*\(\)\{\}\|\;\<\>\?\'\ \	]*|*]*|"")
+        func_quote_for_eval_result="\"$func_quote_for_eval_unquoted_result\""
+        ;;
+      *)
+        func_quote_for_eval_result="$func_quote_for_eval_unquoted_result"
+    esac
+}
+
+
+# func_quote_for_expand arg
+# Aesthetically quote ARG to be evaled later; same as above,
+# but do not quote variable references.
+func_quote_for_expand ()
+{
+    case $1 in
+      *[\\\`\"]*)
+	my_arg=`$ECHO "$1" | $SED \
+	    -e "$double_quote_subst" -e "$sed_double_backslash"` ;;
+      *)
+        my_arg="$1" ;;
+    esac
+
+    case $my_arg in
+      # Double-quote args containing shell metacharacters to delay
+      # word splitting and command substitution for a subsequent eval.
+      # Many Bourne shells cannot handle close brackets correctly
+      # in scan sets, so we specify it separately.
+      *[\[\~\#\^\&\*\(\)\{\}\|\;\<\>\?\'\ \	]*|*]*|"")
+        my_arg="\"$my_arg\""
+        ;;
+    esac
+
+    func_quote_for_expand_result="$my_arg"
+}
+
+
+# func_show_eval cmd [fail_exp]
+# Unless opt_silent is true, then output CMD.  Then, if opt_dryrun is
+# not true, evaluate CMD.  If the evaluation of CMD fails, and FAIL_EXP
+# is given, then evaluate it.
+func_show_eval ()
+{
+    my_cmd="$1"
+    my_fail_exp="${2-:}"
+
+    ${opt_silent-false} || {
+      func_quote_for_expand "$my_cmd"
+      eval "func_echo $func_quote_for_expand_result"
+    }
+
+    if ${opt_dry_run-false}; then :; else
+      eval "$my_cmd"
+      my_status=$?
+      if test "$my_status" -eq 0; then :; else
+	eval "(exit $my_status); $my_fail_exp"
+      fi
+    fi
+}
+
+
+# func_show_eval_locale cmd [fail_exp]
+# Unless opt_silent is true, then output CMD.  Then, if opt_dryrun is
+# not true, evaluate CMD.  If the evaluation of CMD fails, and FAIL_EXP
+# is given, then evaluate it.  Use the saved locale for evaluation.
+func_show_eval_locale ()
+{
+    my_cmd="$1"
+    my_fail_exp="${2-:}"
+
+    ${opt_silent-false} || {
+      func_quote_for_expand "$my_cmd"
+      eval "func_echo $func_quote_for_expand_result"
+    }
+
+    if ${opt_dry_run-false}; then :; else
+      eval "$lt_user_locale
+	    $my_cmd"
+      my_status=$?
+      eval "$lt_safe_locale"
+      if test "$my_status" -eq 0; then :; else
+	eval "(exit $my_status); $my_fail_exp"
+      fi
+    fi
+}
+
+# func_tr_sh
+# Turn $1 into a string suitable for a shell variable name.
+# Result is stored in $func_tr_sh_result.  All characters
+# not in the set a-zA-Z0-9_ are replaced with '_'. Further,
+# if $1 begins with a digit, a '_' is prepended as well.
+func_tr_sh ()
+{
+  case $1 in
+  [0-9]* | *[!a-zA-Z0-9_]*)
+    func_tr_sh_result=`$ECHO "$1" | $SED 's/^\([0-9]\)/_\1/; s/[^a-zA-Z0-9_]/_/g'`
+    ;;
+  * )
+    func_tr_sh_result=$1
+    ;;
+  esac
+}
+
+
+# func_version
+# Echo version message to standard output and exit.
+func_version ()
+{
+    $opt_debug
+
+    $SED -n '/(C)/!b go
+	:more
+	/\./!{
+	  N
+	  s/\n# / /
+	  b more
+	}
+	:go
+	/^# '$PROGRAM' (GNU /,/# warranty; / {
+        s/^# //
+	s/^# *$//
+        s/\((C)\)[ 0-9,-]*\( [1-9][0-9]*\)/\1\2/
+        p
+     }' < "$progpath"
+     exit $?
+}
+
+# func_usage
+# Echo short help message to standard output and exit.
+func_usage ()
+{
+    $opt_debug
+
+    $SED -n '/^# Usage:/,/^#  *.*--help/ {
+        s/^# //
+	s/^# *$//
+	s/\$progname/'$progname'/
+	p
+    }' < "$progpath"
+    echo
+    $ECHO "run \`$progname --help | more' for full usage"
+    exit $?
+}
+
+# func_help [NOEXIT]
+# Echo long help message to standard output and exit,
+# unless 'noexit' is passed as argument.
+func_help ()
+{
+    $opt_debug
+
+    $SED -n '/^# Usage:/,/# Report bugs to/ {
+	:print
+        s/^# //
+	s/^# *$//
+	s*\$progname*'$progname'*
+	s*\$host*'"$host"'*
+	s*\$SHELL*'"$SHELL"'*
+	s*\$LTCC*'"$LTCC"'*
+	s*\$LTCFLAGS*'"$LTCFLAGS"'*
+	s*\$LD*'"$LD"'*
+	s/\$with_gnu_ld/'"$with_gnu_ld"'/
+	s/\$automake_version/'"`(${AUTOMAKE-automake} --version) 2>/dev/null |$SED 1q`"'/
+	s/\$autoconf_version/'"`(${AUTOCONF-autoconf} --version) 2>/dev/null |$SED 1q`"'/
+	p
+	d
+     }
+     /^# .* home page:/b print
+     /^# General help using/b print
+     ' < "$progpath"
+    ret=$?
+    if test -z "$1"; then
+      exit $ret
+    fi
+}
+
+# func_missing_arg argname
+# Echo program name prefixed message to standard error and set global
+# exit_cmd.
+func_missing_arg ()
+{
+    $opt_debug
+
+    func_error "missing argument for $1."
+    exit_cmd=exit
+}
+
+
+# func_split_short_opt shortopt
+# Set func_split_short_opt_name and func_split_short_opt_arg shell
+# variables after splitting SHORTOPT after the 2nd character.
+func_split_short_opt ()
+{
+    my_sed_short_opt='1s/^\(..\).*$/\1/;q'
+    my_sed_short_rest='1s/^..\(.*\)$/\1/;q'
+
+    func_split_short_opt_name=`$ECHO "$1" | $SED "$my_sed_short_opt"`
+    func_split_short_opt_arg=`$ECHO "$1" | $SED "$my_sed_short_rest"`
+} # func_split_short_opt may be replaced by extended shell implementation
+
+
+# func_split_long_opt longopt
+# Set func_split_long_opt_name and func_split_long_opt_arg shell
+# variables after splitting LONGOPT at the `=' sign.
+func_split_long_opt ()
+{
+    my_sed_long_opt='1s/^\(--[^=]*\)=.*/\1/;q'
+    my_sed_long_arg='1s/^--[^=]*=//'
+
+    func_split_long_opt_name=`$ECHO "$1" | $SED "$my_sed_long_opt"`
+    func_split_long_opt_arg=`$ECHO "$1" | $SED "$my_sed_long_arg"`
+} # func_split_long_opt may be replaced by extended shell implementation
+
+exit_cmd=:
+
+
+
+
+
+magic="%%%MAGIC variable%%%"
+magic_exe="%%%MAGIC EXE variable%%%"
+
+# Global variables.
+nonopt=
+preserve_args=
+lo2o="s/\\.lo\$/.${objext}/"
+o2lo="s/\\.${objext}\$/.lo/"
+extracted_archives=
+extracted_serial=0
+
+# If this variable is set in any of the actions, the command in it
+# will be execed at the end.  This prevents here-documents from being
+# left over by shells.
+exec_cmd=
+
+# func_append var value
+# Append VALUE to the end of shell variable VAR.
+func_append ()
+{
+    eval "${1}=\$${1}\${2}"
+} # func_append may be replaced by extended shell implementation
+
+# func_append_quoted var value
+# Quote VALUE and append to the end of shell variable VAR, separated
+# by a space.
+func_append_quoted ()
+{
+    func_quote_for_eval "${2}"
+    eval "${1}=\$${1}\\ \$func_quote_for_eval_result"
+} # func_append_quoted may be replaced by extended shell implementation
+
+
+# func_arith arithmetic-term...
+func_arith ()
+{
+    func_arith_result=`expr "${@}"`
+} # func_arith may be replaced by extended shell implementation
+
+
+# func_len string
+# STRING may not start with a hyphen.
+func_len ()
+{
+    func_len_result=`expr "${1}" : ".*" 2>/dev/null || echo $max_cmd_len`
+} # func_len may be replaced by extended shell implementation
+
+
+# func_lo2o object
+func_lo2o ()
+{
+    func_lo2o_result=`$ECHO "${1}" | $SED "$lo2o"`
+} # func_lo2o may be replaced by extended shell implementation
+
+
+# func_xform libobj-or-source
+func_xform ()
+{
+    func_xform_result=`$ECHO "${1}" | $SED 's/\.[^.]*$/.lo/'`
+} # func_xform may be replaced by extended shell implementation
+
+
+# func_fatal_configuration arg...
+# Echo program name prefixed message to standard error, followed by
+# a configuration failure hint, and exit.
+func_fatal_configuration ()
+{
+    func_error ${1+"$@"}
+    func_error "See the $PACKAGE documentation for more information."
+    func_fatal_error "Fatal configuration error."
+}
+
+
+# func_config
+# Display the configuration for all the tags in this script.
+func_config ()
+{
+    re_begincf='^# ### BEGIN LIBTOOL'
+    re_endcf='^# ### END LIBTOOL'
+
+    # Default configuration.
+    $SED "1,/$re_begincf CONFIG/d;/$re_endcf CONFIG/,\$d" < "$progpath"
+
+    # Now print the configurations for the tags.
+    for tagname in $taglist; do
+      $SED -n "/$re_begincf TAG CONFIG: $tagname\$/,/$re_endcf TAG CONFIG: $tagname\$/p" < "$progpath"
+    done
+
+    exit $?
+}
+
+# func_features
+# Display the features supported by this script.
+func_features ()
+{
+    echo "host: $host"
+    if test "$build_libtool_libs" = yes; then
+      echo "enable shared libraries"
+    else
+      echo "disable shared libraries"
+    fi
+    if test "$build_old_libs" = yes; then
+      echo "enable static libraries"
+    else
+      echo "disable static libraries"
+    fi
+
+    exit $?
+}
+
+# func_enable_tag tagname
+# Verify that TAGNAME is valid, and either flag an error and exit, or
+# enable the TAGNAME tag.  We also add TAGNAME to the global $taglist
+# variable here.
+func_enable_tag ()
+{
+  # Global variable:
+  tagname="$1"
+
+  re_begincf="^# ### BEGIN LIBTOOL TAG CONFIG: $tagname\$"
+  re_endcf="^# ### END LIBTOOL TAG CONFIG: $tagname\$"
+  sed_extractcf="/$re_begincf/,/$re_endcf/p"
+
+  # Validate tagname.
+  case $tagname in
+    *[!-_A-Za-z0-9,/]*)
+      func_fatal_error "invalid tag name: $tagname"
+      ;;
+  esac
+
+  # Don't test for the "default" C tag, as we know it's
+  # there but not specially marked.
+  case $tagname in
+    CC) ;;
+    *)
+      if $GREP "$re_begincf" "$progpath" >/dev/null 2>&1; then
+	taglist="$taglist $tagname"
+
+	# Evaluate the configuration.  Be careful to quote the path
+	# and the sed script, to avoid splitting on whitespace, but
+	# also don't use non-portable quotes within backquotes within
+	# quotes we have to do it in 2 steps:
+	extractedcf=`$SED -n -e "$sed_extractcf" < "$progpath"`
+	eval "$extractedcf"
+      else
+	func_error "ignoring unknown tag $tagname"
+      fi
+      ;;
+  esac
+}
+
+# func_check_version_match
+# Ensure that we are using m4 macros, and libtool script from the same
+# release of libtool.
+func_check_version_match ()
+{
+  if test "$package_revision" != "$macro_revision"; then
+    if test "$VERSION" != "$macro_version"; then
+      if test -z "$macro_version"; then
+        cat >&2 <<_LT_EOF
+$progname: Version mismatch error.  This is $PACKAGE $VERSION, but the
+$progname: definition of this LT_INIT comes from an older release.
+$progname: You should recreate aclocal.m4 with macros from $PACKAGE $VERSION
+$progname: and run autoconf again.
+_LT_EOF
+      else
+        cat >&2 <<_LT_EOF
+$progname: Version mismatch error.  This is $PACKAGE $VERSION, but the
+$progname: definition of this LT_INIT comes from $PACKAGE $macro_version.
+$progname: You should recreate aclocal.m4 with macros from $PACKAGE $VERSION
+$progname: and run autoconf again.
+_LT_EOF
+      fi
+    else
+      cat >&2 <<_LT_EOF
+$progname: Version mismatch error.  This is $PACKAGE $VERSION, revision $package_revision,
+$progname: but the definition of this LT_INIT comes from revision $macro_revision.
+$progname: You should recreate aclocal.m4 with macros from revision $package_revision
+$progname: of $PACKAGE $VERSION and run autoconf again.
+_LT_EOF
+    fi
+
+    exit $EXIT_MISMATCH
+  fi
+}
+
+
+# Shorthand for --mode=foo, only valid as the first argument
+case $1 in
+clean|clea|cle|cl)
+  shift; set dummy --mode clean ${1+"$@"}; shift
+  ;;
+compile|compil|compi|comp|com|co|c)
+  shift; set dummy --mode compile ${1+"$@"}; shift
+  ;;
+execute|execut|execu|exec|exe|ex|e)
+  shift; set dummy --mode execute ${1+"$@"}; shift
+  ;;
+finish|finis|fini|fin|fi|f)
+  shift; set dummy --mode finish ${1+"$@"}; shift
+  ;;
+install|instal|insta|inst|ins|in|i)
+  shift; set dummy --mode install ${1+"$@"}; shift
+  ;;
+link|lin|li|l)
+  shift; set dummy --mode link ${1+"$@"}; shift
+  ;;
+uninstall|uninstal|uninsta|uninst|unins|unin|uni|un|u)
+  shift; set dummy --mode uninstall ${1+"$@"}; shift
+  ;;
+esac
+
+
+
+# Option defaults:
+opt_debug=:
+opt_dry_run=false
+opt_config=false
+opt_preserve_dup_deps=false
+opt_features=false
+opt_finish=false
+opt_help=false
+opt_help_all=false
+opt_silent=:
+opt_warning=:
+opt_verbose=:
+opt_silent=false
+opt_verbose=false
+
+
+# Parse options once, thoroughly.  This comes as soon as possible in the
+# script to make things like `--version' happen as quickly as we can.
+{
+  # this just eases exit handling
+  while test $# -gt 0; do
+    opt="$1"
+    shift
+    case $opt in
+      --debug|-x)	opt_debug='set -x'
+			func_echo "enabling shell trace mode"
+			$opt_debug
+			;;
+      --dry-run|--dryrun|-n)
+			opt_dry_run=:
+			;;
+      --config)
+			opt_config=:
+func_config
+			;;
+      --dlopen|-dlopen)
+			optarg="$1"
+			opt_dlopen="${opt_dlopen+$opt_dlopen
+}$optarg"
+			shift
+			;;
+      --preserve-dup-deps)
+			opt_preserve_dup_deps=:
+			;;
+      --features)
+			opt_features=:
+func_features
+			;;
+      --finish)
+			opt_finish=:
+set dummy --mode finish ${1+"$@"}; shift
+			;;
+      --help)
+			opt_help=:
+			;;
+      --help-all)
+			opt_help_all=:
+opt_help=': help-all'
+			;;
+      --mode)
+			test $# = 0 && func_missing_arg $opt && break
+			optarg="$1"
+			opt_mode="$optarg"
+case $optarg in
+  # Valid mode arguments:
+  clean|compile|execute|finish|install|link|relink|uninstall) ;;
+
+  # Catch anything else as an error
+  *) func_error "invalid argument for $opt"
+     exit_cmd=exit
+     break
+     ;;
+esac
+			shift
+			;;
+      --no-silent|--no-quiet)
+			opt_silent=false
+func_append preserve_args " $opt"
+			;;
+      --no-warning|--no-warn)
+			opt_warning=false
+func_append preserve_args " $opt"
+			;;
+      --no-verbose)
+			opt_verbose=false
+func_append preserve_args " $opt"
+			;;
+      --silent|--quiet)
+			opt_silent=:
+func_append preserve_args " $opt"
+        opt_verbose=false
+			;;
+      --verbose|-v)
+			opt_verbose=:
+func_append preserve_args " $opt"
+opt_silent=false
+			;;
+      --tag)
+			test $# = 0 && func_missing_arg $opt && break
+			optarg="$1"
+			opt_tag="$optarg"
+func_append preserve_args " $opt $optarg"
+func_enable_tag "$optarg"
+			shift
+			;;
+
+      -\?|-h)		func_usage				;;
+      --help)		func_help				;;
+      --version)	func_version				;;
+
+      # Separate optargs to long options:
+      --*=*)
+			func_split_long_opt "$opt"
+			set dummy "$func_split_long_opt_name" "$func_split_long_opt_arg" ${1+"$@"}
+			shift
+			;;
+
+      # Separate non-argument short options:
+      -\?*|-h*|-n*|-v*)
+			func_split_short_opt "$opt"
+			set dummy "$func_split_short_opt_name" "-$func_split_short_opt_arg" ${1+"$@"}
+			shift
+			;;
+
+      --)		break					;;
+      -*)		func_fatal_help "unrecognized option \`$opt'" ;;
+      *)		set dummy "$opt" ${1+"$@"};	shift; break  ;;
+    esac
+  done
+
+  # Validate options:
+
+  # save first non-option argument
+  if test "$#" -gt 0; then
+    nonopt="$opt"
+    shift
+  fi
+
+  # preserve --debug
+  test "$opt_debug" = : || func_append preserve_args " --debug"
+
+  case $host in
+    *cygwin* | *mingw* | *pw32* | *cegcc*)
+      # don't eliminate duplications in $postdeps and $predeps
+      opt_duplicate_compiler_generated_deps=:
+      ;;
+    *)
+      opt_duplicate_compiler_generated_deps=$opt_preserve_dup_deps
+      ;;
+  esac
+
+  $opt_help || {
+    # Sanity checks first:
+    func_check_version_match
+
+    if test "$build_libtool_libs" != yes && test "$build_old_libs" != yes; then
+      func_fatal_configuration "not configured to build any kind of library"
+    fi
+
+    # Darwin sucks
+    eval std_shrext=\"$shrext_cmds\"
+
+    # Only execute mode is allowed to have -dlopen flags.
+    if test -n "$opt_dlopen" && test "$opt_mode" != execute; then
+      func_error "unrecognized option \`-dlopen'"
+      $ECHO "$help" 1>&2
+      exit $EXIT_FAILURE
+    fi
+
+    # Change the help message to a mode-specific one.
+    generic_help="$help"
+    help="Try \`$progname --help --mode=$opt_mode' for more information."
+  }
+
+
+  # Bail if the options were screwed
+  $exit_cmd $EXIT_FAILURE
+}
+
+
+
+
+## ----------- ##
+##    Main.    ##
+## ----------- ##
+
+# func_lalib_p file
+# True iff FILE is a libtool `.la' library or `.lo' object file.
+# This function is only a basic sanity check; it will hardly flush out
+# determined imposters.
+func_lalib_p ()
+{
+    test -f "$1" &&
+      $SED -e 4q "$1" 2>/dev/null \
+        | $GREP "^# Generated by .*$PACKAGE" > /dev/null 2>&1
+}
+
+# func_lalib_unsafe_p file
+# True iff FILE is a libtool `.la' library or `.lo' object file.
+# This function implements the same check as func_lalib_p without
+# resorting to external programs.  To this end, it redirects stdin and
+# closes it afterwards, without saving the original file descriptor.
+# As a safety measure, use it only where a negative result would be
+# fatal anyway.  Works if `file' does not exist.
+func_lalib_unsafe_p ()
+{
+    lalib_p=no
+    if test -f "$1" && test -r "$1" && exec 5<&0 <"$1"; then
+	for lalib_p_l in 1 2 3 4
+	do
+	    read lalib_p_line
+	    case "$lalib_p_line" in
+		\#\ Generated\ by\ *$PACKAGE* ) lalib_p=yes; break;;
+	    esac
+	done
+	exec 0<&5 5<&-
+    fi
+    test "$lalib_p" = yes
+}
+
+# func_ltwrapper_script_p file
+# True iff FILE is a libtool wrapper script
+# This function is only a basic sanity check; it will hardly flush out
+# determined imposters.
+func_ltwrapper_script_p ()
+{
+    func_lalib_p "$1"
+}
+
+# func_ltwrapper_executable_p file
+# True iff FILE is a libtool wrapper executable
+# This function is only a basic sanity check; it will hardly flush out
+# determined imposters.
+func_ltwrapper_executable_p ()
+{
+    func_ltwrapper_exec_suffix=
+    case $1 in
+    *.exe) ;;
+    *) func_ltwrapper_exec_suffix=.exe ;;
+    esac
+    $GREP "$magic_exe" "$1$func_ltwrapper_exec_suffix" >/dev/null 2>&1
+}
+
+# func_ltwrapper_scriptname file
+# Assumes file is an ltwrapper_executable
+# uses $file to determine the appropriate filename for a
+# temporary ltwrapper_script.
+func_ltwrapper_scriptname ()
+{
+    func_dirname_and_basename "$1" "" "."
+    func_stripname '' '.exe' "$func_basename_result"
+    func_ltwrapper_scriptname_result="$func_dirname_result/$objdir/${func_stripname_result}_ltshwrapper"
+}
+
+# func_ltwrapper_p file
+# True iff FILE is a libtool wrapper script or wrapper executable
+# This function is only a basic sanity check; it will hardly flush out
+# determined imposters.
+func_ltwrapper_p ()
+{
+    func_ltwrapper_script_p "$1" || func_ltwrapper_executable_p "$1"
+}
+
+
+# func_execute_cmds commands fail_cmd
+# Execute tilde-delimited COMMANDS.
+# If FAIL_CMD is given, eval that upon failure.
+# FAIL_CMD may read-access the current command in variable CMD!
+func_execute_cmds ()
+{
+    $opt_debug
+    save_ifs=$IFS; IFS='~'
+    for cmd in $1; do
+      IFS=$save_ifs
+      eval cmd=\"$cmd\"
+      func_show_eval "$cmd" "${2-:}"
+    done
+    IFS=$save_ifs
+}
+
+
+# func_source file
+# Source FILE, adding directory component if necessary.
+# Note that it is not necessary on cygwin/mingw to append a dot to
+# FILE even if both FILE and FILE.exe exist: automatic-append-.exe
+# behavior happens only for exec(3), not for open(2)!  Also, sourcing
+# `FILE.' does not work on cygwin managed mounts.
+func_source ()
+{
+    $opt_debug
+    case $1 in
+    */* | *\\*)	. "$1" ;;
+    *)		. "./$1" ;;
+    esac
+}
+
+
+# func_resolve_sysroot PATH
+# Replace a leading = in PATH with a sysroot.  Store the result into
+# func_resolve_sysroot_result
+func_resolve_sysroot ()
+{
+  func_resolve_sysroot_result=$1
+  case $func_resolve_sysroot_result in
+  =*)
+    func_stripname '=' '' "$func_resolve_sysroot_result"
+    func_resolve_sysroot_result=$lt_sysroot$func_stripname_result
+    ;;
+  esac
+}
+
+# func_replace_sysroot PATH
+# If PATH begins with the sysroot, replace it with = and
+# store the result into func_replace_sysroot_result.
+func_replace_sysroot ()
+{
+  case "$lt_sysroot:$1" in
+  ?*:"$lt_sysroot"*)
+    func_stripname "$lt_sysroot" '' "$1"
+    func_replace_sysroot_result="=$func_stripname_result"
+    ;;
+  *)
+    # Including no sysroot.
+    func_replace_sysroot_result=$1
+    ;;
+  esac
+}
+
+# func_infer_tag arg
+# Infer tagged configuration to use if any are available and
+# if one wasn't chosen via the "--tag" command line option.
+# Only attempt this if the compiler in the base compile
+# command doesn't match the default compiler.
+# arg is usually of the form 'gcc ...'
+func_infer_tag ()
+{
+    $opt_debug
+    if test -n "$available_tags" && test -z "$tagname"; then
+      CC_quoted=
+      for arg in $CC; do
+	func_append_quoted CC_quoted "$arg"
+      done
+      CC_expanded=`func_echo_all $CC`
+      CC_quoted_expanded=`func_echo_all $CC_quoted`
+      case $@ in
+      # Blanks in the command may have been stripped by the calling shell,
+      # but not from the CC environment variable when configure was run.
+      " $CC "* | "$CC "* | " $CC_expanded "* | "$CC_expanded "* | \
+      " $CC_quoted"* | "$CC_quoted "* | " $CC_quoted_expanded "* | "$CC_quoted_expanded "*) ;;
+      # Blanks at the start of $base_compile will cause this to fail
+      # if we don't check for them as well.
+      *)
+	for z in $available_tags; do
+	  if $GREP "^# ### BEGIN LIBTOOL TAG CONFIG: $z$" < "$progpath" > /dev/null; then
+	    # Evaluate the configuration.
+	    eval "`${SED} -n -e '/^# ### BEGIN LIBTOOL TAG CONFIG: '$z'$/,/^# ### END LIBTOOL TAG CONFIG: '$z'$/p' < $progpath`"
+	    CC_quoted=
+	    for arg in $CC; do
+	      # Double-quote args containing other shell metacharacters.
+	      func_append_quoted CC_quoted "$arg"
+	    done
+	    CC_expanded=`func_echo_all $CC`
+	    CC_quoted_expanded=`func_echo_all $CC_quoted`
+	    case "$@ " in
+	    " $CC "* | "$CC "* | " $CC_expanded "* | "$CC_expanded "* | \
+	    " $CC_quoted"* | "$CC_quoted "* | " $CC_quoted_expanded "* | "$CC_quoted_expanded "*)
+	      # The compiler in the base compile command matches
+	      # the one in the tagged configuration.
+	      # Assume this is the tagged configuration we want.
+	      tagname=$z
+	      break
+	      ;;
+	    esac
+	  fi
+	done
+	# If $tagname still isn't set, then no tagged configuration
+	# was found and let the user know that the "--tag" command
+	# line option must be used.
+	if test -z "$tagname"; then
+	  func_echo "unable to infer tagged configuration"
+	  func_fatal_error "specify a tag with \`--tag'"
+#	else
+#	  func_verbose "using $tagname tagged configuration"
+	fi
+	;;
+      esac
+    fi
+}
+
+
+
+# func_write_libtool_object output_name pic_name nonpic_name
+# Create a libtool object file (analogous to a ".la" file),
+# but don't create it if we're doing a dry run.
+func_write_libtool_object ()
+{
+    write_libobj=${1}
+    if test "$build_libtool_libs" = yes; then
+      write_lobj=\'${2}\'
+    else
+      write_lobj=none
+    fi
+
+    if test "$build_old_libs" = yes; then
+      write_oldobj=\'${3}\'
+    else
+      write_oldobj=none
+    fi
+
+    $opt_dry_run || {
+      cat >${write_libobj}T <<EOF
+# $write_libobj - a libtool object file
+# Generated by $PROGRAM (GNU $PACKAGE$TIMESTAMP) $VERSION
+#
+# Please DO NOT delete this file!
+# It is necessary for linking the library.
+
+# Name of the PIC object.
+pic_object=$write_lobj
+
+# Name of the non-PIC object
+non_pic_object=$write_oldobj
+
+EOF
+      $MV "${write_libobj}T" "${write_libobj}"
+    }
+}
+
+
+##################################################
+# FILE NAME AND PATH CONVERSION HELPER FUNCTIONS #
+##################################################
+
+# func_convert_core_file_wine_to_w32 ARG
+# Helper function used by file name conversion functions when $build is *nix,
+# and $host is mingw, cygwin, or some other w32 environment. Relies on a
+# correctly configured wine environment available, with the winepath program
+# in $build's $PATH.
+#
+# ARG is the $build file name to be converted to w32 format.
+# Result is available in $func_convert_core_file_wine_to_w32_result, and will
+# be empty on error (or when ARG is empty)
+func_convert_core_file_wine_to_w32 ()
+{
+  $opt_debug
+  func_convert_core_file_wine_to_w32_result="$1"
+  if test -n "$1"; then
+    # Unfortunately, winepath does not exit with a non-zero error code, so we
+    # are forced to check the contents of stdout. On the other hand, if the
+    # command is not found, the shell will set an exit code of 127 and print
+    # *an error message* to stdout. So we must check for both error code of
+    # zero AND non-empty stdout, which explains the odd construction:
+    func_convert_core_file_wine_to_w32_tmp=`winepath -w "$1" 2>/dev/null`
+    if test "$?" -eq 0 && test -n "${func_convert_core_file_wine_to_w32_tmp}"; then
+      func_convert_core_file_wine_to_w32_result=`$ECHO "$func_convert_core_file_wine_to_w32_tmp" |
+        $SED -e "$lt_sed_naive_backslashify"`
+    else
+      func_convert_core_file_wine_to_w32_result=
+    fi
+  fi
+}
+# end: func_convert_core_file_wine_to_w32
+
+
+# func_convert_core_path_wine_to_w32 ARG
+# Helper function used by path conversion functions when $build is *nix, and
+# $host is mingw, cygwin, or some other w32 environment. Relies on a correctly
+# configured wine environment available, with the winepath program in $build's
+# $PATH. Assumes ARG has no leading or trailing path separator characters.
+#
+# ARG is path to be converted from $build format to win32.
+# Result is available in $func_convert_core_path_wine_to_w32_result.
+# Unconvertible file (directory) names in ARG are skipped; if no directory names
+# are convertible, then the result may be empty.
+func_convert_core_path_wine_to_w32 ()
+{
+  $opt_debug
+  # unfortunately, winepath doesn't convert paths, only file names
+  func_convert_core_path_wine_to_w32_result=""
+  if test -n "$1"; then
+    oldIFS=$IFS
+    IFS=:
+    for func_convert_core_path_wine_to_w32_f in $1; do
+      IFS=$oldIFS
+      func_convert_core_file_wine_to_w32 "$func_convert_core_path_wine_to_w32_f"
+      if test -n "$func_convert_core_file_wine_to_w32_result" ; then
+        if test -z "$func_convert_core_path_wine_to_w32_result"; then
+          func_convert_core_path_wine_to_w32_result="$func_convert_core_file_wine_to_w32_result"
+        else
+          func_append func_convert_core_path_wine_to_w32_result ";$func_convert_core_file_wine_to_w32_result"
+        fi
+      fi
+    done
+    IFS=$oldIFS
+  fi
+}
+# end: func_convert_core_path_wine_to_w32
+
+
+# func_cygpath ARGS...
+# Wrapper around calling the cygpath program via LT_CYGPATH. This is used when
+# when (1) $build is *nix and Cygwin is hosted via a wine environment; or (2)
+# $build is MSYS and $host is Cygwin, or (3) $build is Cygwin. In case (1) or
+# (2), returns the Cygwin file name or path in func_cygpath_result (input
+# file name or path is assumed to be in w32 format, as previously converted
+# from $build's *nix or MSYS format). In case (3), returns the w32 file name
+# or path in func_cygpath_result (input file name or path is assumed to be in
+# Cygwin format). Returns an empty string on error.
+#
+# ARGS are passed to cygpath, with the last one being the file name or path to
+# be converted.
+#
+# Specify the absolute *nix (or w32) name to cygpath in the LT_CYGPATH
+# environment variable; do not put it in $PATH.
+func_cygpath ()
+{
+  $opt_debug
+  if test -n "$LT_CYGPATH" && test -f "$LT_CYGPATH"; then
+    func_cygpath_result=`$LT_CYGPATH "$@" 2>/dev/null`
+    if test "$?" -ne 0; then
+      # on failure, ensure result is empty
+      func_cygpath_result=
+    fi
+  else
+    func_cygpath_result=
+    func_error "LT_CYGPATH is empty or specifies non-existent file: \`$LT_CYGPATH'"
+  fi
+}
+#end: func_cygpath
+
+
+# func_convert_core_msys_to_w32 ARG
+# Convert file name or path ARG from MSYS format to w32 format.  Return
+# result in func_convert_core_msys_to_w32_result.
+func_convert_core_msys_to_w32 ()
+{
+  $opt_debug
+  # awkward: cmd appends spaces to result
+  func_convert_core_msys_to_w32_result=`( cmd //c echo "$1" ) 2>/dev/null |
+    $SED -e 's/[ ]*$//' -e "$lt_sed_naive_backslashify"`
+}
+#end: func_convert_core_msys_to_w32
+
+
+# func_convert_file_check ARG1 ARG2
+# Verify that ARG1 (a file name in $build format) was converted to $host
+# format in ARG2. Otherwise, emit an error message, but continue (resetting
+# func_to_host_file_result to ARG1).
+func_convert_file_check ()
+{
+  $opt_debug
+  if test -z "$2" && test -n "$1" ; then
+    func_error "Could not determine host file name corresponding to"
+    func_error "  \`$1'"
+    func_error "Continuing, but uninstalled executables may not work."
+    # Fallback:
+    func_to_host_file_result="$1"
+  fi
+}
+# end func_convert_file_check
+
+
+# func_convert_path_check FROM_PATHSEP TO_PATHSEP FROM_PATH TO_PATH
+# Verify that FROM_PATH (a path in $build format) was converted to $host
+# format in TO_PATH. Otherwise, emit an error message, but continue, resetting
+# func_to_host_file_result to a simplistic fallback value (see below).
+func_convert_path_check ()
+{
+  $opt_debug
+  if test -z "$4" && test -n "$3"; then
+    func_error "Could not determine the host path corresponding to"
+    func_error "  \`$3'"
+    func_error "Continuing, but uninstalled executables may not work."
+    # Fallback.  This is a deliberately simplistic "conversion" and
+    # should not be "improved".  See libtool.info.
+    if test "x$1" != "x$2"; then
+      lt_replace_pathsep_chars="s|$1|$2|g"
+      func_to_host_path_result=`echo "$3" |
+        $SED -e "$lt_replace_pathsep_chars"`
+    else
+      func_to_host_path_result="$3"
+    fi
+  fi
+}
+# end func_convert_path_check
+
+
+# func_convert_path_front_back_pathsep FRONTPAT BACKPAT REPL ORIG
+# Modifies func_to_host_path_result by prepending REPL if ORIG matches FRONTPAT
+# and appending REPL if ORIG matches BACKPAT.
+func_convert_path_front_back_pathsep ()
+{
+  $opt_debug
+  case $4 in
+  $1 ) func_to_host_path_result="$3$func_to_host_path_result"
+    ;;
+  esac
+  case $4 in
+  $2 ) func_append func_to_host_path_result "$3"
+    ;;
+  esac
+}
+# end func_convert_path_front_back_pathsep
+
+
+##################################################
+# $build to $host FILE NAME CONVERSION FUNCTIONS #
+##################################################
+# invoked via `$to_host_file_cmd ARG'
+#
+# In each case, ARG is the path to be converted from $build to $host format.
+# Result will be available in $func_to_host_file_result.
+
+
+# func_to_host_file ARG
+# Converts the file name ARG from $build format to $host format. Return result
+# in func_to_host_file_result.
+func_to_host_file ()
+{
+  $opt_debug
+  $to_host_file_cmd "$1"
+}
+# end func_to_host_file
+
+
+# func_to_tool_file ARG LAZY
+# converts the file name ARG from $build format to toolchain format. Return
+# result in func_to_tool_file_result.  If the conversion in use is listed
+# in (the comma separated) LAZY, no conversion takes place.
+func_to_tool_file ()
+{
+  $opt_debug
+  case ,$2, in
+    *,"$to_tool_file_cmd",*)
+      func_to_tool_file_result=$1
+      ;;
+    *)
+      $to_tool_file_cmd "$1"
+      func_to_tool_file_result=$func_to_host_file_result
+      ;;
+  esac
+}
+# end func_to_tool_file
+
+
+# func_convert_file_noop ARG
+# Copy ARG to func_to_host_file_result.
+func_convert_file_noop ()
+{
+  func_to_host_file_result="$1"
+}
+# end func_convert_file_noop
+
+
+# func_convert_file_msys_to_w32 ARG
+# Convert file name ARG from (mingw) MSYS to (mingw) w32 format; automatic
+# conversion to w32 is not available inside the cwrapper.  Returns result in
+# func_to_host_file_result.
+func_convert_file_msys_to_w32 ()
+{
+  $opt_debug
+  func_to_host_file_result="$1"
+  if test -n "$1"; then
+    func_convert_core_msys_to_w32 "$1"
+    func_to_host_file_result="$func_convert_core_msys_to_w32_result"
+  fi
+  func_convert_file_check "$1" "$func_to_host_file_result"
+}
+# end func_convert_file_msys_to_w32
+
+
+# func_convert_file_cygwin_to_w32 ARG
+# Convert file name ARG from Cygwin to w32 format.  Returns result in
+# func_to_host_file_result.
+func_convert_file_cygwin_to_w32 ()
+{
+  $opt_debug
+  func_to_host_file_result="$1"
+  if test -n "$1"; then
+    # because $build is cygwin, we call "the" cygpath in $PATH; no need to use
+    # LT_CYGPATH in this case.
+    func_to_host_file_result=`cygpath -m "$1"`
+  fi
+  func_convert_file_check "$1" "$func_to_host_file_result"
+}
+# end func_convert_file_cygwin_to_w32
+
+
+# func_convert_file_nix_to_w32 ARG
+# Convert file name ARG from *nix to w32 format.  Requires a wine environment
+# and a working winepath. Returns result in func_to_host_file_result.
+func_convert_file_nix_to_w32 ()
+{
+  $opt_debug
+  func_to_host_file_result="$1"
+  if test -n "$1"; then
+    func_convert_core_file_wine_to_w32 "$1"
+    func_to_host_file_result="$func_convert_core_file_wine_to_w32_result"
+  fi
+  func_convert_file_check "$1" "$func_to_host_file_result"
+}
+# end func_convert_file_nix_to_w32
+
+
+# func_convert_file_msys_to_cygwin ARG
+# Convert file name ARG from MSYS to Cygwin format.  Requires LT_CYGPATH set.
+# Returns result in func_to_host_file_result.
+func_convert_file_msys_to_cygwin ()
+{
+  $opt_debug
+  func_to_host_file_result="$1"
+  if test -n "$1"; then
+    func_convert_core_msys_to_w32 "$1"
+    func_cygpath -u "$func_convert_core_msys_to_w32_result"
+    func_to_host_file_result="$func_cygpath_result"
+  fi
+  func_convert_file_check "$1" "$func_to_host_file_result"
+}
+# end func_convert_file_msys_to_cygwin
+
+
+# func_convert_file_nix_to_cygwin ARG
+# Convert file name ARG from *nix to Cygwin format.  Requires Cygwin installed
+# in a wine environment, working winepath, and LT_CYGPATH set.  Returns result
+# in func_to_host_file_result.
+func_convert_file_nix_to_cygwin ()
+{
+  $opt_debug
+  func_to_host_file_result="$1"
+  if test -n "$1"; then
+    # convert from *nix to w32, then use cygpath to convert from w32 to cygwin.
+    func_convert_core_file_wine_to_w32 "$1"
+    func_cygpath -u "$func_convert_core_file_wine_to_w32_result"
+    func_to_host_file_result="$func_cygpath_result"
+  fi
+  func_convert_file_check "$1" "$func_to_host_file_result"
+}
+# end func_convert_file_nix_to_cygwin
+
+
+#############################################
+# $build to $host PATH CONVERSION FUNCTIONS #
+#############################################
+# invoked via `$to_host_path_cmd ARG'
+#
+# In each case, ARG is the path to be converted from $build to $host format.
+# The result will be available in $func_to_host_path_result.
+#
+# Path separators are also converted from $build format to $host format.  If
+# ARG begins or ends with a path separator character, it is preserved (but
+# converted to $host format) on output.
+#
+# All path conversion functions are named using the following convention:
+#   file name conversion function    : func_convert_file_X_to_Y ()
+#   path conversion function         : func_convert_path_X_to_Y ()
+# where, for any given $build/$host combination the 'X_to_Y' value is the
+# same.  If conversion functions are added for new $build/$host combinations,
+# the two new functions must follow this pattern, or func_init_to_host_path_cmd
+# will break.
+
+
+# func_init_to_host_path_cmd
+# Ensures that function "pointer" variable $to_host_path_cmd is set to the
+# appropriate value, based on the value of $to_host_file_cmd.
+to_host_path_cmd=
+func_init_to_host_path_cmd ()
+{
+  $opt_debug
+  if test -z "$to_host_path_cmd"; then
+    func_stripname 'func_convert_file_' '' "$to_host_file_cmd"
+    to_host_path_cmd="func_convert_path_${func_stripname_result}"
+  fi
+}
+
+
+# func_to_host_path ARG
+# Converts the path ARG from $build format to $host format. Return result
+# in func_to_host_path_result.
+func_to_host_path ()
+{
+  $opt_debug
+  func_init_to_host_path_cmd
+  $to_host_path_cmd "$1"
+}
+# end func_to_host_path
+
+
+# func_convert_path_noop ARG
+# Copy ARG to func_to_host_path_result.
+func_convert_path_noop ()
+{
+  func_to_host_path_result="$1"
+}
+# end func_convert_path_noop
+
+
+# func_convert_path_msys_to_w32 ARG
+# Convert path ARG from (mingw) MSYS to (mingw) w32 format; automatic
+# conversion to w32 is not available inside the cwrapper.  Returns result in
+# func_to_host_path_result.
+func_convert_path_msys_to_w32 ()
+{
+  $opt_debug
+  func_to_host_path_result="$1"
+  if test -n "$1"; then
+    # Remove leading and trailing path separator characters from ARG.  MSYS
+    # behavior is inconsistent here; cygpath turns them into '.;' and ';.';
+    # and winepath ignores them completely.
+    func_stripname : : "$1"
+    func_to_host_path_tmp1=$func_stripname_result
+    func_convert_core_msys_to_w32 "$func_to_host_path_tmp1"
+    func_to_host_path_result="$func_convert_core_msys_to_w32_result"
+    func_convert_path_check : ";" \
+      "$func_to_host_path_tmp1" "$func_to_host_path_result"
+    func_convert_path_front_back_pathsep ":*" "*:" ";" "$1"
+  fi
+}
+# end func_convert_path_msys_to_w32
+
+
+# func_convert_path_cygwin_to_w32 ARG
+# Convert path ARG from Cygwin to w32 format.  Returns result in
+# func_to_host_file_result.
+func_convert_path_cygwin_to_w32 ()
+{
+  $opt_debug
+  func_to_host_path_result="$1"
+  if test -n "$1"; then
+    # See func_convert_path_msys_to_w32:
+    func_stripname : : "$1"
+    func_to_host_path_tmp1=$func_stripname_result
+    func_to_host_path_result=`cygpath -m -p "$func_to_host_path_tmp1"`
+    func_convert_path_check : ";" \
+      "$func_to_host_path_tmp1" "$func_to_host_path_result"
+    func_convert_path_front_back_pathsep ":*" "*:" ";" "$1"
+  fi
+}
+# end func_convert_path_cygwin_to_w32
+
+
+# func_convert_path_nix_to_w32 ARG
+# Convert path ARG from *nix to w32 format.  Requires a wine environment and
+# a working winepath.  Returns result in func_to_host_file_result.
+func_convert_path_nix_to_w32 ()
+{
+  $opt_debug
+  func_to_host_path_result="$1"
+  if test -n "$1"; then
+    # See func_convert_path_msys_to_w32:
+    func_stripname : : "$1"
+    func_to_host_path_tmp1=$func_stripname_result
+    func_convert_core_path_wine_to_w32 "$func_to_host_path_tmp1"
+    func_to_host_path_result="$func_convert_core_path_wine_to_w32_result"
+    func_convert_path_check : ";" \
+      "$func_to_host_path_tmp1" "$func_to_host_path_result"
+    func_convert_path_front_back_pathsep ":*" "*:" ";" "$1"
+  fi
+}
+# end func_convert_path_nix_to_w32
+
+
+# func_convert_path_msys_to_cygwin ARG
+# Convert path ARG from MSYS to Cygwin format.  Requires LT_CYGPATH set.
+# Returns result in func_to_host_file_result.
+func_convert_path_msys_to_cygwin ()
+{
+  $opt_debug
+  func_to_host_path_result="$1"
+  if test -n "$1"; then
+    # See func_convert_path_msys_to_w32:
+    func_stripname : : "$1"
+    func_to_host_path_tmp1=$func_stripname_result
+    func_convert_core_msys_to_w32 "$func_to_host_path_tmp1"
+    func_cygpath -u -p "$func_convert_core_msys_to_w32_result"
+    func_to_host_path_result="$func_cygpath_result"
+    func_convert_path_check : : \
+      "$func_to_host_path_tmp1" "$func_to_host_path_result"
+    func_convert_path_front_back_pathsep ":*" "*:" : "$1"
+  fi
+}
+# end func_convert_path_msys_to_cygwin
+
+
+# func_convert_path_nix_to_cygwin ARG
+# Convert path ARG from *nix to Cygwin format.  Requires Cygwin installed in a
+# a wine environment, working winepath, and LT_CYGPATH set.  Returns result in
+# func_to_host_file_result.
+func_convert_path_nix_to_cygwin ()
+{
+  $opt_debug
+  func_to_host_path_result="$1"
+  if test -n "$1"; then
+    # Remove leading and trailing path separator characters from
+    # ARG. msys behavior is inconsistent here, cygpath turns them
+    # into '.;' and ';.', and winepath ignores them completely.
+    func_stripname : : "$1"
+    func_to_host_path_tmp1=$func_stripname_result
+    func_convert_core_path_wine_to_w32 "$func_to_host_path_tmp1"
+    func_cygpath -u -p "$func_convert_core_path_wine_to_w32_result"
+    func_to_host_path_result="$func_cygpath_result"
+    func_convert_path_check : : \
+      "$func_to_host_path_tmp1" "$func_to_host_path_result"
+    func_convert_path_front_back_pathsep ":*" "*:" : "$1"
+  fi
+}
+# end func_convert_path_nix_to_cygwin
+
+
+# func_mode_compile arg...
+func_mode_compile ()
+{
+    $opt_debug
+    # Get the compilation command and the source file.
+    base_compile=
+    srcfile="$nonopt"  #  always keep a non-empty value in "srcfile"
+    suppress_opt=yes
+    suppress_output=
+    arg_mode=normal
+    libobj=
+    later=
+    pie_flag=
+
+    for arg
+    do
+      case $arg_mode in
+      arg  )
+	# do not "continue".  Instead, add this to base_compile
+	lastarg="$arg"
+	arg_mode=normal
+	;;
+
+      target )
+	libobj="$arg"
+	arg_mode=normal
+	continue
+	;;
+
+      normal )
+	# Accept any command-line options.
+	case $arg in
+	-o)
+	  test -n "$libobj" && \
+	    func_fatal_error "you cannot specify \`-o' more than once"
+	  arg_mode=target
+	  continue
+	  ;;
+
+	-pie | -fpie | -fPIE)
+          func_append pie_flag " $arg"
+	  continue
+	  ;;
+
+	-shared | -static | -prefer-pic | -prefer-non-pic)
+	  func_append later " $arg"
+	  continue
+	  ;;
+
+	-no-suppress)
+	  suppress_opt=no
+	  continue
+	  ;;
+
+	-Xcompiler)
+	  arg_mode=arg  #  the next one goes into the "base_compile" arg list
+	  continue      #  The current "srcfile" will either be retained or
+	  ;;            #  replaced later.  I would guess that would be a bug.
+
+	-Wc,*)
+	  func_stripname '-Wc,' '' "$arg"
+	  args=$func_stripname_result
+	  lastarg=
+	  save_ifs="$IFS"; IFS=','
+	  for arg in $args; do
+	    IFS="$save_ifs"
+	    func_append_quoted lastarg "$arg"
+	  done
+	  IFS="$save_ifs"
+	  func_stripname ' ' '' "$lastarg"
+	  lastarg=$func_stripname_result
+
+	  # Add the arguments to base_compile.
+	  func_append base_compile " $lastarg"
+	  continue
+	  ;;
+
+	*)
+	  # Accept the current argument as the source file.
+	  # The previous "srcfile" becomes the current argument.
+	  #
+	  lastarg="$srcfile"
+	  srcfile="$arg"
+	  ;;
+	esac  #  case $arg
+	;;
+      esac    #  case $arg_mode
+
+      # Aesthetically quote the previous argument.
+      func_append_quoted base_compile "$lastarg"
+    done # for arg
+
+    case $arg_mode in
+    arg)
+      func_fatal_error "you must specify an argument for -Xcompile"
+      ;;
+    target)
+      func_fatal_error "you must specify a target with \`-o'"
+      ;;
+    *)
+      # Get the name of the library object.
+      test -z "$libobj" && {
+	func_basename "$srcfile"
+	libobj="$func_basename_result"
+      }
+      ;;
+    esac
+
+    # Recognize several different file suffixes.
+    # If the user specifies -o file.o, it is replaced with file.lo
+    case $libobj in
+    *.[cCFSifmso] | \
+    *.ada | *.adb | *.ads | *.asm | \
+    *.c++ | *.cc | *.ii | *.class | *.cpp | *.cxx | \
+    *.[fF][09]? | *.for | *.java | *.go | *.obj | *.sx | *.cu | *.cup)
+      func_xform "$libobj"
+      libobj=$func_xform_result
+      ;;
+    esac
+
+    case $libobj in
+    *.lo) func_lo2o "$libobj"; obj=$func_lo2o_result ;;
+    *)
+      func_fatal_error "cannot determine name of library object from \`$libobj'"
+      ;;
+    esac
+
+    func_infer_tag $base_compile
+
+    for arg in $later; do
+      case $arg in
+      -shared)
+	test "$build_libtool_libs" != yes && \
+	  func_fatal_configuration "can not build a shared library"
+	build_old_libs=no
+	continue
+	;;
+
+      -static)
+	build_libtool_libs=no
+	build_old_libs=yes
+	continue
+	;;
+
+      -prefer-pic)
+	pic_mode=yes
+	continue
+	;;
+
+      -prefer-non-pic)
+	pic_mode=no
+	continue
+	;;
+      esac
+    done
+
+    func_quote_for_eval "$libobj"
+    test "X$libobj" != "X$func_quote_for_eval_result" \
+      && $ECHO "X$libobj" | $GREP '[]~#^*{};<>?"'"'"'	 &()|`$[]' \
+      && func_warning "libobj name \`$libobj' may not contain shell special characters."
+    func_dirname_and_basename "$obj" "/" ""
+    objname="$func_basename_result"
+    xdir="$func_dirname_result"
+    lobj=${xdir}$objdir/$objname
+
+    test -z "$base_compile" && \
+      func_fatal_help "you must specify a compilation command"
+
+    # Delete any leftover library objects.
+    if test "$build_old_libs" = yes; then
+      removelist="$obj $lobj $libobj ${libobj}T"
+    else
+      removelist="$lobj $libobj ${libobj}T"
+    fi
+
+    # On Cygwin there's no "real" PIC flag so we must build both object types
+    case $host_os in
+    cygwin* | mingw* | pw32* | os2* | cegcc*)
+      pic_mode=default
+      ;;
+    esac
+    if test "$pic_mode" = no && test "$deplibs_check_method" != pass_all; then
+      # non-PIC code in shared libraries is not supported
+      pic_mode=default
+    fi
+
+    # Calculate the filename of the output object if compiler does
+    # not support -o with -c
+    if test "$compiler_c_o" = no; then
+      output_obj=`$ECHO "$srcfile" | $SED 's%^.*/%%; s%\.[^.]*$%%'`.${objext}
+      lockfile="$output_obj.lock"
+    else
+      output_obj=
+      need_locks=no
+      lockfile=
+    fi
+
+    # Lock this critical section if it is needed
+    # We use this script file to make the link, it avoids creating a new file
+    if test "$need_locks" = yes; then
+      until $opt_dry_run || ln "$progpath" "$lockfile" 2>/dev/null; do
+	func_echo "Waiting for $lockfile to be removed"
+	sleep 2
+      done
+    elif test "$need_locks" = warn; then
+      if test -f "$lockfile"; then
+	$ECHO "\
+*** ERROR, $lockfile exists and contains:
+`cat $lockfile 2>/dev/null`
+
+This indicates that another process is trying to use the same
+temporary object file, and libtool could not work around it because
+your compiler does not support \`-c' and \`-o' together.  If you
+repeat this compilation, it may succeed, by chance, but you had better
+avoid parallel builds (make -j) in this platform, or get a better
+compiler."
+
+	$opt_dry_run || $RM $removelist
+	exit $EXIT_FAILURE
+      fi
+      func_append removelist " $output_obj"
+      $ECHO "$srcfile" > "$lockfile"
+    fi
+
+    $opt_dry_run || $RM $removelist
+    func_append removelist " $lockfile"
+    trap '$opt_dry_run || $RM $removelist; exit $EXIT_FAILURE' 1 2 15
+
+    func_to_tool_file "$srcfile" func_convert_file_msys_to_w32
+    srcfile=$func_to_tool_file_result
+    func_quote_for_eval "$srcfile"
+    qsrcfile=$func_quote_for_eval_result
+
+    # Only build a PIC object if we are building libtool libraries.
+    if test "$build_libtool_libs" = yes; then
+      # Without this assignment, base_compile gets emptied.
+      fbsd_hideous_sh_bug=$base_compile
+
+      if test "$pic_mode" != no; then
+	command="$base_compile $qsrcfile $pic_flag"
+      else
+	# Don't build PIC code
+	command="$base_compile $qsrcfile"
+      fi
+
+      func_mkdir_p "$xdir$objdir"
+
+      if test -z "$output_obj"; then
+	# Place PIC objects in $objdir
+	func_append command " -o $lobj"
+      fi
+
+      func_show_eval_locale "$command"	\
+          'test -n "$output_obj" && $RM $removelist; exit $EXIT_FAILURE'
+
+      if test "$need_locks" = warn &&
+	 test "X`cat $lockfile 2>/dev/null`" != "X$srcfile"; then
+	$ECHO "\
+*** ERROR, $lockfile contains:
+`cat $lockfile 2>/dev/null`
+
+but it should contain:
+$srcfile
+
+This indicates that another process is trying to use the same
+temporary object file, and libtool could not work around it because
+your compiler does not support \`-c' and \`-o' together.  If you
+repeat this compilation, it may succeed, by chance, but you had better
+avoid parallel builds (make -j) in this platform, or get a better
+compiler."
+
+	$opt_dry_run || $RM $removelist
+	exit $EXIT_FAILURE
+      fi
+
+      # Just move the object if needed, then go on to compile the next one
+      if test -n "$output_obj" && test "X$output_obj" != "X$lobj"; then
+	func_show_eval '$MV "$output_obj" "$lobj"' \
+	  'error=$?; $opt_dry_run || $RM $removelist; exit $error'
+      fi
+
+      # Allow error messages only from the first compilation.
+      if test "$suppress_opt" = yes; then
+	suppress_output=' >/dev/null 2>&1'
+      fi
+    fi
+
+    # Only build a position-dependent object if we build old libraries.
+    if test "$build_old_libs" = yes; then
+      if test "$pic_mode" != yes; then
+	# Don't build PIC code
+	command="$base_compile $qsrcfile$pie_flag"
+      else
+	command="$base_compile $qsrcfile $pic_flag"
+      fi
+      if test "$compiler_c_o" = yes; then
+	func_append command " -o $obj"
+      fi
+
+      # Suppress compiler output if we already did a PIC compilation.
+      func_append command "$suppress_output"
+      func_show_eval_locale "$command" \
+        '$opt_dry_run || $RM $removelist; exit $EXIT_FAILURE'
+
+      if test "$need_locks" = warn &&
+	 test "X`cat $lockfile 2>/dev/null`" != "X$srcfile"; then
+	$ECHO "\
+*** ERROR, $lockfile contains:
+`cat $lockfile 2>/dev/null`
+
+but it should contain:
+$srcfile
+
+This indicates that another process is trying to use the same
+temporary object file, and libtool could not work around it because
+your compiler does not support \`-c' and \`-o' together.  If you
+repeat this compilation, it may succeed, by chance, but you had better
+avoid parallel builds (make -j) in this platform, or get a better
+compiler."
+
+	$opt_dry_run || $RM $removelist
+	exit $EXIT_FAILURE
+      fi
+
+      # Just move the object if needed
+      if test -n "$output_obj" && test "X$output_obj" != "X$obj"; then
+	func_show_eval '$MV "$output_obj" "$obj"' \
+	  'error=$?; $opt_dry_run || $RM $removelist; exit $error'
+      fi
+    fi
+
+    $opt_dry_run || {
+      func_write_libtool_object "$libobj" "$objdir/$objname" "$objname"
+
+      # Unlock the critical section if it was locked
+      if test "$need_locks" != no; then
+	removelist=$lockfile
+        $RM "$lockfile"
+      fi
+    }
+
+    exit $EXIT_SUCCESS
+}
+
+$opt_help || {
+  test "$opt_mode" = compile && func_mode_compile ${1+"$@"}
+}
+
+func_mode_help ()
+{
+    # We need to display help for each of the modes.
+    case $opt_mode in
+      "")
+        # Generic help is extracted from the usage comments
+        # at the start of this file.
+        func_help
+        ;;
+
+      clean)
+        $ECHO \
+"Usage: $progname [OPTION]... --mode=clean RM [RM-OPTION]... FILE...
+
+Remove files from the build directory.
+
+RM is the name of the program to use to delete files associated with each FILE
+(typically \`/bin/rm').  RM-OPTIONS are options (such as \`-f') to be passed
+to RM.
+
+If FILE is a libtool library, object or program, all the files associated
+with it are deleted. Otherwise, only FILE itself is deleted using RM."
+        ;;
+
+      compile)
+      $ECHO \
+"Usage: $progname [OPTION]... --mode=compile COMPILE-COMMAND... SOURCEFILE
+
+Compile a source file into a libtool library object.
+
+This mode accepts the following additional options:
+
+  -o OUTPUT-FILE    set the output file name to OUTPUT-FILE
+  -no-suppress      do not suppress compiler output for multiple passes
+  -prefer-pic       try to build PIC objects only
+  -prefer-non-pic   try to build non-PIC objects only
+  -shared           do not build a \`.o' file suitable for static linking
+  -static           only build a \`.o' file suitable for static linking
+  -Wc,FLAG          pass FLAG directly to the compiler
+
+COMPILE-COMMAND is a command to be used in creating a \`standard' object file
+from the given SOURCEFILE.
+
+The output file name is determined by removing the directory component from
+SOURCEFILE, then substituting the C source code suffix \`.c' with the
+library object suffix, \`.lo'."
+        ;;
+
+      execute)
+        $ECHO \
+"Usage: $progname [OPTION]... --mode=execute COMMAND [ARGS]...
+
+Automatically set library path, then run a program.
+
+This mode accepts the following additional options:
+
+  -dlopen FILE      add the directory containing FILE to the library path
+
+This mode sets the library path environment variable according to \`-dlopen'
+flags.
+
+If any of the ARGS are libtool executable wrappers, then they are translated
+into their corresponding uninstalled binary, and any of their required library
+directories are added to the library path.
+
+Then, COMMAND is executed, with ARGS as arguments."
+        ;;
+
+      finish)
+        $ECHO \
+"Usage: $progname [OPTION]... --mode=finish [LIBDIR]...
+
+Complete the installation of libtool libraries.
+
+Each LIBDIR is a directory that contains libtool libraries.
+
+The commands that this mode executes may require superuser privileges.  Use
+the \`--dry-run' option if you just want to see what would be executed."
+        ;;
+
+      install)
+        $ECHO \
+"Usage: $progname [OPTION]... --mode=install INSTALL-COMMAND...
+
+Install executables or libraries.
+
+INSTALL-COMMAND is the installation command.  The first component should be
+either the \`install' or \`cp' program.
+
+The following components of INSTALL-COMMAND are treated specially:
+
+  -inst-prefix-dir PREFIX-DIR  Use PREFIX-DIR as a staging area for installation
+
+The rest of the components are interpreted as arguments to that command (only
+BSD-compatible install options are recognized)."
+        ;;
+
+      link)
+        $ECHO \
+"Usage: $progname [OPTION]... --mode=link LINK-COMMAND...
+
+Link object files or libraries together to form another library, or to
+create an executable program.
+
+LINK-COMMAND is a command using the C compiler that you would use to create
+a program from several object files.
+
+The following components of LINK-COMMAND are treated specially:
+
+  -all-static       do not do any dynamic linking at all
+  -avoid-version    do not add a version suffix if possible
+  -bindir BINDIR    specify path to binaries directory (for systems where
+                    libraries must be found in the PATH setting at runtime)
+  -dlopen FILE      \`-dlpreopen' FILE if it cannot be dlopened at runtime
+  -dlpreopen FILE   link in FILE and add its symbols to lt_preloaded_symbols
+  -export-dynamic   allow symbols from OUTPUT-FILE to be resolved with dlsym(3)
+  -export-symbols SYMFILE
+                    try to export only the symbols listed in SYMFILE
+  -export-symbols-regex REGEX
+                    try to export only the symbols matching REGEX
+  -LLIBDIR          search LIBDIR for required installed libraries
+  -lNAME            OUTPUT-FILE requires the installed library libNAME
+  -module           build a library that can dlopened
+  -no-fast-install  disable the fast-install mode
+  -no-install       link a not-installable executable
+  -no-undefined     declare that a library does not refer to external symbols
+  -o OUTPUT-FILE    create OUTPUT-FILE from the specified objects
+  -objectlist FILE  Use a list of object files found in FILE to specify objects
+  -precious-files-regex REGEX
+                    don't remove output files matching REGEX
+  -release RELEASE  specify package release information
+  -rpath LIBDIR     the created library will eventually be installed in LIBDIR
+  -R[ ]LIBDIR       add LIBDIR to the runtime path of programs and libraries
+  -shared           only do dynamic linking of libtool libraries
+  -shrext SUFFIX    override the standard shared library file extension
+  -static           do not do any dynamic linking of uninstalled libtool libraries
+  -static-libtool-libs
+                    do not do any dynamic linking of libtool libraries
+  -version-info CURRENT[:REVISION[:AGE]]
+                    specify library version info [each variable defaults to 0]
+  -weak LIBNAME     declare that the target provides the LIBNAME interface
+  -Wc,FLAG
+  -Xcompiler FLAG   pass linker-specific FLAG directly to the compiler
+  -Wl,FLAG
+  -Xlinker FLAG     pass linker-specific FLAG directly to the linker
+  -XCClinker FLAG   pass link-specific FLAG to the compiler driver (CC)
+
+All other options (arguments beginning with \`-') are ignored.
+
+Every other argument is treated as a filename.  Files ending in \`.la' are
+treated as uninstalled libtool libraries, other files are standard or library
+object files.
+
+If the OUTPUT-FILE ends in \`.la', then a libtool library is created,
+only library objects (\`.lo' files) may be specified, and \`-rpath' is
+required, except when creating a convenience library.
+
+If OUTPUT-FILE ends in \`.a' or \`.lib', then a standard library is created
+using \`ar' and \`ranlib', or on Windows using \`lib'.
+
+If OUTPUT-FILE ends in \`.lo' or \`.${objext}', then a reloadable object file
+is created, otherwise an executable program is created."
+        ;;
+
+      uninstall)
+        $ECHO \
+"Usage: $progname [OPTION]... --mode=uninstall RM [RM-OPTION]... FILE...
+
+Remove libraries from an installation directory.
+
+RM is the name of the program to use to delete files associated with each FILE
+(typically \`/bin/rm').  RM-OPTIONS are options (such as \`-f') to be passed
+to RM.
+
+If FILE is a libtool library, all the files associated with it are deleted.
+Otherwise, only FILE itself is deleted using RM."
+        ;;
+
+      *)
+        func_fatal_help "invalid operation mode \`$opt_mode'"
+        ;;
+    esac
+
+    echo
+    $ECHO "Try \`$progname --help' for more information about other modes."
+}
+
+# Now that we've collected a possible --mode arg, show help if necessary
+if $opt_help; then
+  if test "$opt_help" = :; then
+    func_mode_help
+  else
+    {
+      func_help noexit
+      for opt_mode in compile link execute install finish uninstall clean; do
+	func_mode_help
+      done
+    } | sed -n '1p; 2,$s/^Usage:/  or: /p'
+    {
+      func_help noexit
+      for opt_mode in compile link execute install finish uninstall clean; do
+	echo
+	func_mode_help
+      done
+    } |
+    sed '1d
+      /^When reporting/,/^Report/{
+	H
+	d
+      }
+      $x
+      /information about other modes/d
+      /more detailed .*MODE/d
+      s/^Usage:.*--mode=\([^ ]*\) .*/Description of \1 mode:/'
+  fi
+  exit $?
+fi
+
+
+# func_mode_execute arg...
+func_mode_execute ()
+{
+    $opt_debug
+    # The first argument is the command name.
+    cmd="$nonopt"
+    test -z "$cmd" && \
+      func_fatal_help "you must specify a COMMAND"
+
+    # Handle -dlopen flags immediately.
+    for file in $opt_dlopen; do
+      test -f "$file" \
+	|| func_fatal_help "\`$file' is not a file"
+
+      dir=
+      case $file in
+      *.la)
+	func_resolve_sysroot "$file"
+	file=$func_resolve_sysroot_result
+
+	# Check to see that this really is a libtool archive.
+	func_lalib_unsafe_p "$file" \
+	  || func_fatal_help "\`$lib' is not a valid libtool archive"
+
+	# Read the libtool library.
+	dlname=
+	library_names=
+	func_source "$file"
+
+	# Skip this library if it cannot be dlopened.
+	if test -z "$dlname"; then
+	  # Warn if it was a shared library.
+	  test -n "$library_names" && \
+	    func_warning "\`$file' was not linked with \`-export-dynamic'"
+	  continue
+	fi
+
+	func_dirname "$file" "" "."
+	dir="$func_dirname_result"
+
+	if test -f "$dir/$objdir/$dlname"; then
+	  func_append dir "/$objdir"
+	else
+	  if test ! -f "$dir/$dlname"; then
+	    func_fatal_error "cannot find \`$dlname' in \`$dir' or \`$dir/$objdir'"
+	  fi
+	fi
+	;;
+
+      *.lo)
+	# Just add the directory containing the .lo file.
+	func_dirname "$file" "" "."
+	dir="$func_dirname_result"
+	;;
+
+      *)
+	func_warning "\`-dlopen' is ignored for non-libtool libraries and objects"
+	continue
+	;;
+      esac
+
+      # Get the absolute pathname.
+      absdir=`cd "$dir" && pwd`
+      test -n "$absdir" && dir="$absdir"
+
+      # Now add the directory to shlibpath_var.
+      if eval "test -z \"\$$shlibpath_var\""; then
+	eval "$shlibpath_var=\"\$dir\""
+      else
+	eval "$shlibpath_var=\"\$dir:\$$shlibpath_var\""
+      fi
+    done
+
+    # This variable tells wrapper scripts just to set shlibpath_var
+    # rather than running their programs.
+    libtool_execute_magic="$magic"
+
+    # Check if any of the arguments is a wrapper script.
+    args=
+    for file
+    do
+      case $file in
+      -* | *.la | *.lo ) ;;
+      *)
+	# Do a test to see if this is really a libtool program.
+	if func_ltwrapper_script_p "$file"; then
+	  func_source "$file"
+	  # Transform arg to wrapped name.
+	  file="$progdir/$program"
+	elif func_ltwrapper_executable_p "$file"; then
+	  func_ltwrapper_scriptname "$file"
+	  func_source "$func_ltwrapper_scriptname_result"
+	  # Transform arg to wrapped name.
+	  file="$progdir/$program"
+	fi
+	;;
+      esac
+      # Quote arguments (to preserve shell metacharacters).
+      func_append_quoted args "$file"
+    done
+
+    if test "X$opt_dry_run" = Xfalse; then
+      if test -n "$shlibpath_var"; then
+	# Export the shlibpath_var.
+	eval "export $shlibpath_var"
+      fi
+
+      # Restore saved environment variables
+      for lt_var in LANG LANGUAGE LC_ALL LC_CTYPE LC_COLLATE LC_MESSAGES
+      do
+	eval "if test \"\${save_$lt_var+set}\" = set; then
+                $lt_var=\$save_$lt_var; export $lt_var
+	      else
+		$lt_unset $lt_var
+	      fi"
+      done
+
+      # Now prepare to actually exec the command.
+      exec_cmd="\$cmd$args"
+    else
+      # Display what would be done.
+      if test -n "$shlibpath_var"; then
+	eval "\$ECHO \"\$shlibpath_var=\$$shlibpath_var\""
+	echo "export $shlibpath_var"
+      fi
+      $ECHO "$cmd$args"
+      exit $EXIT_SUCCESS
+    fi
+}
+
+test "$opt_mode" = execute && func_mode_execute ${1+"$@"}
+
+
+# func_mode_finish arg...
+func_mode_finish ()
+{
+    $opt_debug
+    libs=
+    libdirs=
+    admincmds=
+
+    for opt in "$nonopt" ${1+"$@"}
+    do
+      if test -d "$opt"; then
+	func_append libdirs " $opt"
+
+      elif test -f "$opt"; then
+	if func_lalib_unsafe_p "$opt"; then
+	  func_append libs " $opt"
+	else
+	  func_warning "\`$opt' is not a valid libtool archive"
+	fi
+
+      else
+	func_fatal_error "invalid argument \`$opt'"
+      fi
+    done
+
+    if test -n "$libs"; then
+      if test -n "$lt_sysroot"; then
+        sysroot_regex=`$ECHO "$lt_sysroot" | $SED "$sed_make_literal_regex"`
+        sysroot_cmd="s/\([ ']\)$sysroot_regex/\1/g;"
+      else
+        sysroot_cmd=
+      fi
+
+      # Remove sysroot references
+      if $opt_dry_run; then
+        for lib in $libs; do
+          echo "removing references to $lt_sysroot and \`=' prefixes from $lib"
+        done
+      else
+        tmpdir=`func_mktempdir`
+        for lib in $libs; do
+	  sed -e "${sysroot_cmd} s/\([ ']-[LR]\)=/\1/g; s/\([ ']\)=/\1/g" $lib \
+	    > $tmpdir/tmp-la
+	  mv -f $tmpdir/tmp-la $lib
+	done
+        ${RM}r "$tmpdir"
+      fi
+    fi
+
+    if test -n "$finish_cmds$finish_eval" && test -n "$libdirs"; then
+      for libdir in $libdirs; do
+	if test -n "$finish_cmds"; then
+	  # Do each command in the finish commands.
+	  func_execute_cmds "$finish_cmds" 'admincmds="$admincmds
+'"$cmd"'"'
+	fi
+	if test -n "$finish_eval"; then
+	  # Do the single finish_eval.
+	  eval cmds=\"$finish_eval\"
+	  $opt_dry_run || eval "$cmds" || func_append admincmds "
+       $cmds"
+	fi
+      done
+    fi
+
+    # Exit here if they wanted silent mode.
+    $opt_silent && exit $EXIT_SUCCESS
+
+    if test -n "$finish_cmds$finish_eval" && test -n "$libdirs"; then
+      echo "----------------------------------------------------------------------"
+      echo "Libraries have been installed in:"
+      for libdir in $libdirs; do
+	$ECHO "   $libdir"
+      done
+      echo
+      echo "If you ever happen to want to link against installed libraries"
+      echo "in a given directory, LIBDIR, you must either use libtool, and"
+      echo "specify the full pathname of the library, or use the \`-LLIBDIR'"
+      echo "flag during linking and do at least one of the following:"
+      if test -n "$shlibpath_var"; then
+	echo "   - add LIBDIR to the \`$shlibpath_var' environment variable"
+	echo "     during execution"
+      fi
+      if test -n "$runpath_var"; then
+	echo "   - add LIBDIR to the \`$runpath_var' environment variable"
+	echo "     during linking"
+      fi
+      if test -n "$hardcode_libdir_flag_spec"; then
+	libdir=LIBDIR
+	eval flag=\"$hardcode_libdir_flag_spec\"
+
+	$ECHO "   - use the \`$flag' linker flag"
+      fi
+      if test -n "$admincmds"; then
+	$ECHO "   - have your system administrator run these commands:$admincmds"
+      fi
+      if test -f /etc/ld.so.conf; then
+	echo "   - have your system administrator add LIBDIR to \`/etc/ld.so.conf'"
+      fi
+      echo
+
+      echo "See any operating system documentation about shared libraries for"
+      case $host in
+	solaris2.[6789]|solaris2.1[0-9])
+	  echo "more information, such as the ld(1), crle(1) and ld.so(8) manual"
+	  echo "pages."
+	  ;;
+	*)
+	  echo "more information, such as the ld(1) and ld.so(8) manual pages."
+	  ;;
+      esac
+      echo "----------------------------------------------------------------------"
+    fi
+    exit $EXIT_SUCCESS
+}
+
+test "$opt_mode" = finish && func_mode_finish ${1+"$@"}
+
+
+# func_mode_install arg...
+func_mode_install ()
+{
+    $opt_debug
+    # There may be an optional sh(1) argument at the beginning of
+    # install_prog (especially on Windows NT).
+    if test "$nonopt" = "$SHELL" || test "$nonopt" = /bin/sh ||
+       # Allow the use of GNU shtool's install command.
+       case $nonopt in *shtool*) :;; *) false;; esac; then
+      # Aesthetically quote it.
+      func_quote_for_eval "$nonopt"
+      install_prog="$func_quote_for_eval_result "
+      arg=$1
+      shift
+    else
+      install_prog=
+      arg=$nonopt
+    fi
+
+    # The real first argument should be the name of the installation program.
+    # Aesthetically quote it.
+    func_quote_for_eval "$arg"
+    func_append install_prog "$func_quote_for_eval_result"
+    install_shared_prog=$install_prog
+    case " $install_prog " in
+      *[\\\ /]cp\ *) install_cp=: ;;
+      *) install_cp=false ;;
+    esac
+
+    # We need to accept at least all the BSD install flags.
+    dest=
+    files=
+    opts=
+    prev=
+    install_type=
+    isdir=no
+    stripme=
+    no_mode=:
+    for arg
+    do
+      arg2=
+      if test -n "$dest"; then
+	func_append files " $dest"
+	dest=$arg
+	continue
+      fi
+
+      case $arg in
+      -d) isdir=yes ;;
+      -f)
+	if $install_cp; then :; else
+	  prev=$arg
+	fi
+	;;
+      -g | -m | -o)
+	prev=$arg
+	;;
+      -s)
+	stripme=" -s"
+	continue
+	;;
+      -*)
+	;;
+      *)
+	# If the previous option needed an argument, then skip it.
+	if test -n "$prev"; then
+	  if test "x$prev" = x-m && test -n "$install_override_mode"; then
+	    arg2=$install_override_mode
+	    no_mode=false
+	  fi
+	  prev=
+	else
+	  dest=$arg
+	  continue
+	fi
+	;;
+      esac
+
+      # Aesthetically quote the argument.
+      func_quote_for_eval "$arg"
+      func_append install_prog " $func_quote_for_eval_result"
+      if test -n "$arg2"; then
+	func_quote_for_eval "$arg2"
+      fi
+      func_append install_shared_prog " $func_quote_for_eval_result"
+    done
+
+    test -z "$install_prog" && \
+      func_fatal_help "you must specify an install program"
+
+    test -n "$prev" && \
+      func_fatal_help "the \`$prev' option requires an argument"
+
+    if test -n "$install_override_mode" && $no_mode; then
+      if $install_cp; then :; else
+	func_quote_for_eval "$install_override_mode"
+	func_append install_shared_prog " -m $func_quote_for_eval_result"
+      fi
+    fi
+
+    if test -z "$files"; then
+      if test -z "$dest"; then
+	func_fatal_help "no file or destination specified"
+      else
+	func_fatal_help "you must specify a destination"
+      fi
+    fi
+
+    # Strip any trailing slash from the destination.
+    func_stripname '' '/' "$dest"
+    dest=$func_stripname_result
+
+    # Check to see that the destination is a directory.
+    test -d "$dest" && isdir=yes
+    if test "$isdir" = yes; then
+      destdir="$dest"
+      destname=
+    else
+      func_dirname_and_basename "$dest" "" "."
+      destdir="$func_dirname_result"
+      destname="$func_basename_result"
+
+      # Not a directory, so check to see that there is only one file specified.
+      set dummy $files; shift
+      test "$#" -gt 1 && \
+	func_fatal_help "\`$dest' is not a directory"
+    fi
+    case $destdir in
+    [\\/]* | [A-Za-z]:[\\/]*) ;;
+    *)
+      for file in $files; do
+	case $file in
+	*.lo) ;;
+	*)
+	  func_fatal_help "\`$destdir' must be an absolute directory name"
+	  ;;
+	esac
+      done
+      ;;
+    esac
+
+    # This variable tells wrapper scripts just to set variables rather
+    # than running their programs.
+    libtool_install_magic="$magic"
+
+    staticlibs=
+    future_libdirs=
+    current_libdirs=
+    for file in $files; do
+
+      # Do each installation.
+      case $file in
+      *.$libext)
+	# Do the static libraries later.
+	func_append staticlibs " $file"
+	;;
+
+      *.la)
+	func_resolve_sysroot "$file"
+	file=$func_resolve_sysroot_result
+
+	# Check to see that this really is a libtool archive.
+	func_lalib_unsafe_p "$file" \
+	  || func_fatal_help "\`$file' is not a valid libtool archive"
+
+	library_names=
+	old_library=
+	relink_command=
+	func_source "$file"
+
+	# Add the libdir to current_libdirs if it is the destination.
+	if test "X$destdir" = "X$libdir"; then
+	  case "$current_libdirs " in
+	  *" $libdir "*) ;;
+	  *) func_append current_libdirs " $libdir" ;;
+	  esac
+	else
+	  # Note the libdir as a future libdir.
+	  case "$future_libdirs " in
+	  *" $libdir "*) ;;
+	  *) func_append future_libdirs " $libdir" ;;
+	  esac
+	fi
+
+	func_dirname "$file" "/" ""
+	dir="$func_dirname_result"
+	func_append dir "$objdir"
+
+	if test -n "$relink_command"; then
+	  # Determine the prefix the user has applied to our future dir.
+	  inst_prefix_dir=`$ECHO "$destdir" | $SED -e "s%$libdir\$%%"`
+
+	  # Don't allow the user to place us outside of our expected
+	  # location b/c this prevents finding dependent libraries that
+	  # are installed to the same prefix.
+	  # At present, this check doesn't affect windows .dll's that
+	  # are installed into $libdir/../bin (currently, that works fine)
+	  # but it's something to keep an eye on.
+	  test "$inst_prefix_dir" = "$destdir" && \
+	    func_fatal_error "error: cannot install \`$file' to a directory not ending in $libdir"
+
+	  if test -n "$inst_prefix_dir"; then
+	    # Stick the inst_prefix_dir data into the link command.
+	    relink_command=`$ECHO "$relink_command" | $SED "s%@inst_prefix_dir@%-inst-prefix-dir $inst_prefix_dir%"`
+	  else
+	    relink_command=`$ECHO "$relink_command" | $SED "s%@inst_prefix_dir@%%"`
+	  fi
+
+	  func_warning "relinking \`$file'"
+	  func_show_eval "$relink_command" \
+	    'func_fatal_error "error: relink \`$file'\'' with the above command before installing it"'
+	fi
+
+	# See the names of the shared library.
+	set dummy $library_names; shift
+	if test -n "$1"; then
+	  realname="$1"
+	  shift
+
+	  srcname="$realname"
+	  test -n "$relink_command" && srcname="$realname"T
+
+	  # Install the shared library and build the symlinks.
+	  func_show_eval "$install_shared_prog $dir/$srcname $destdir/$realname" \
+	      'exit $?'
+	  tstripme="$stripme"
+	  case $host_os in
+	  cygwin* | mingw* | pw32* | cegcc*)
+	    case $realname in
+	    *.dll.a)
+	      tstripme=""
+	      ;;
+	    esac
+	    ;;
+	  esac
+	  if test -n "$tstripme" && test -n "$striplib"; then
+	    func_show_eval "$striplib $destdir/$realname" 'exit $?'
+	  fi
+
+	  if test "$#" -gt 0; then
+	    # Delete the old symlinks, and create new ones.
+	    # Try `ln -sf' first, because the `ln' binary might depend on
+	    # the symlink we replace!  Solaris /bin/ln does not understand -f,
+	    # so we also need to try rm && ln -s.
+	    for linkname
+	    do
+	      test "$linkname" != "$realname" \
+		&& func_show_eval "(cd $destdir && { $LN_S -f $realname $linkname || { $RM $linkname && $LN_S $realname $linkname; }; })"
+	    done
+	  fi
+
+	  # Do each command in the postinstall commands.
+	  lib="$destdir/$realname"
+	  func_execute_cmds "$postinstall_cmds" 'exit $?'
+	fi
+
+	# Install the pseudo-library for information purposes.
+	func_basename "$file"
+	name="$func_basename_result"
+	instname="$dir/$name"i
+	func_show_eval "$install_prog $instname $destdir/$name" 'exit $?'
+
+	# Maybe install the static library, too.
+	test -n "$old_library" && func_append staticlibs " $dir/$old_library"
+	;;
+
+      *.lo)
+	# Install (i.e. copy) a libtool object.
+
+	# Figure out destination file name, if it wasn't already specified.
+	if test -n "$destname"; then
+	  destfile="$destdir/$destname"
+	else
+	  func_basename "$file"
+	  destfile="$func_basename_result"
+	  destfile="$destdir/$destfile"
+	fi
+
+	# Deduce the name of the destination old-style object file.
+	case $destfile in
+	*.lo)
+	  func_lo2o "$destfile"
+	  staticdest=$func_lo2o_result
+	  ;;
+	*.$objext)
+	  staticdest="$destfile"
+	  destfile=
+	  ;;
+	*)
+	  func_fatal_help "cannot copy a libtool object to \`$destfile'"
+	  ;;
+	esac
+
+	# Install the libtool object if requested.
+	test -n "$destfile" && \
+	  func_show_eval "$install_prog $file $destfile" 'exit $?'
+
+	# Install the old object if enabled.
+	if test "$build_old_libs" = yes; then
+	  # Deduce the name of the old-style object file.
+	  func_lo2o "$file"
+	  staticobj=$func_lo2o_result
+	  func_show_eval "$install_prog \$staticobj \$staticdest" 'exit $?'
+	fi
+	exit $EXIT_SUCCESS
+	;;
+
+      *)
+	# Figure out destination file name, if it wasn't already specified.
+	if test -n "$destname"; then
+	  destfile="$destdir/$destname"
+	else
+	  func_basename "$file"
+	  destfile="$func_basename_result"
+	  destfile="$destdir/$destfile"
+	fi
+
+	# If the file is missing, and there is a .exe on the end, strip it
+	# because it is most likely a libtool script we actually want to
+	# install
+	stripped_ext=""
+	case $file in
+	  *.exe)
+	    if test ! -f "$file"; then
+	      func_stripname '' '.exe' "$file"
+	      file=$func_stripname_result
+	      stripped_ext=".exe"
+	    fi
+	    ;;
+	esac
+
+	# Do a test to see if this is really a libtool program.
+	case $host in
+	*cygwin* | *mingw*)
+	    if func_ltwrapper_executable_p "$file"; then
+	      func_ltwrapper_scriptname "$file"
+	      wrapper=$func_ltwrapper_scriptname_result
+	    else
+	      func_stripname '' '.exe' "$file"
+	      wrapper=$func_stripname_result
+	    fi
+	    ;;
+	*)
+	    wrapper=$file
+	    ;;
+	esac
+	if func_ltwrapper_script_p "$wrapper"; then
+	  notinst_deplibs=
+	  relink_command=
+
+	  func_source "$wrapper"
+
+	  # Check the variables that should have been set.
+	  test -z "$generated_by_libtool_version" && \
+	    func_fatal_error "invalid libtool wrapper script \`$wrapper'"
+
+	  finalize=yes
+	  for lib in $notinst_deplibs; do
+	    # Check to see that each library is installed.
+	    libdir=
+	    if test -f "$lib"; then
+	      func_source "$lib"
+	    fi
+	    libfile="$libdir/"`$ECHO "$lib" | $SED 's%^.*/%%g'` ### testsuite: skip nested quoting test
+	    if test -n "$libdir" && test ! -f "$libfile"; then
+	      func_warning "\`$lib' has not been installed in \`$libdir'"
+	      finalize=no
+	    fi
+	  done
+
+	  relink_command=
+	  func_source "$wrapper"
+
+	  outputname=
+	  if test "$fast_install" = no && test -n "$relink_command"; then
+	    $opt_dry_run || {
+	      if test "$finalize" = yes; then
+	        tmpdir=`func_mktempdir`
+		func_basename "$file$stripped_ext"
+		file="$func_basename_result"
+	        outputname="$tmpdir/$file"
+	        # Replace the output file specification.
+	        relink_command=`$ECHO "$relink_command" | $SED 's%@OUTPUT@%'"$outputname"'%g'`
+
+	        $opt_silent || {
+	          func_quote_for_expand "$relink_command"
+		  eval "func_echo $func_quote_for_expand_result"
+	        }
+	        if eval "$relink_command"; then :
+	          else
+		  func_error "error: relink \`$file' with the above command before installing it"
+		  $opt_dry_run || ${RM}r "$tmpdir"
+		  continue
+	        fi
+	        file="$outputname"
+	      else
+	        func_warning "cannot relink \`$file'"
+	      fi
+	    }
+	  else
+	    # Install the binary that we compiled earlier.
+	    file=`$ECHO "$file$stripped_ext" | $SED "s%\([^/]*\)$%$objdir/\1%"`
+	  fi
+	fi
+
+	# remove .exe since cygwin /usr/bin/install will append another
+	# one anyway
+	case $install_prog,$host in
+	*/usr/bin/install*,*cygwin*)
+	  case $file:$destfile in
+	  *.exe:*.exe)
+	    # this is ok
+	    ;;
+	  *.exe:*)
+	    destfile=$destfile.exe
+	    ;;
+	  *:*.exe)
+	    func_stripname '' '.exe' "$destfile"
+	    destfile=$func_stripname_result
+	    ;;
+	  esac
+	  ;;
+	esac
+	func_show_eval "$install_prog\$stripme \$file \$destfile" 'exit $?'
+	$opt_dry_run || if test -n "$outputname"; then
+	  ${RM}r "$tmpdir"
+	fi
+	;;
+      esac
+    done
+
+    for file in $staticlibs; do
+      func_basename "$file"
+      name="$func_basename_result"
+
+      # Set up the ranlib parameters.
+      oldlib="$destdir/$name"
+      func_to_tool_file "$oldlib" func_convert_file_msys_to_w32
+      tool_oldlib=$func_to_tool_file_result
+
+      func_show_eval "$install_prog \$file \$oldlib" 'exit $?'
+
+      if test -n "$stripme" && test -n "$old_striplib"; then
+	func_show_eval "$old_striplib $tool_oldlib" 'exit $?'
+      fi
+
+      # Do each command in the postinstall commands.
+      func_execute_cmds "$old_postinstall_cmds" 'exit $?'
+    done
+
+    test -n "$future_libdirs" && \
+      func_warning "remember to run \`$progname --finish$future_libdirs'"
+
+    if test -n "$current_libdirs"; then
+      # Maybe just do a dry run.
+      $opt_dry_run && current_libdirs=" -n$current_libdirs"
+      exec_cmd='$SHELL $progpath $preserve_args --finish$current_libdirs'
+    else
+      exit $EXIT_SUCCESS
+    fi
+}
+
+test "$opt_mode" = install && func_mode_install ${1+"$@"}
+
+
+# func_generate_dlsyms outputname originator pic_p
+# Extract symbols from dlprefiles and create ${outputname}S.o with
+# a dlpreopen symbol table.
+func_generate_dlsyms ()
+{
+    $opt_debug
+    my_outputname="$1"
+    my_originator="$2"
+    my_pic_p="${3-no}"
+    my_prefix=`$ECHO "$my_originator" | sed 's%[^a-zA-Z0-9]%_%g'`
+    my_dlsyms=
+
+    if test -n "$dlfiles$dlprefiles" || test "$dlself" != no; then
+      if test -n "$NM" && test -n "$global_symbol_pipe"; then
+	my_dlsyms="${my_outputname}S.c"
+      else
+	func_error "not configured to extract global symbols from dlpreopened files"
+      fi
+    fi
+
+    if test -n "$my_dlsyms"; then
+      case $my_dlsyms in
+      "") ;;
+      *.c)
+	# Discover the nlist of each of the dlfiles.
+	nlist="$output_objdir/${my_outputname}.nm"
+
+	func_show_eval "$RM $nlist ${nlist}S ${nlist}T"
+
+	# Parse the name list into a source file.
+	func_verbose "creating $output_objdir/$my_dlsyms"
+
+	$opt_dry_run || $ECHO > "$output_objdir/$my_dlsyms" "\
+/* $my_dlsyms - symbol resolution table for \`$my_outputname' dlsym emulation. */
+/* Generated by $PROGRAM (GNU $PACKAGE$TIMESTAMP) $VERSION */
+
+#ifdef __cplusplus
+extern \"C\" {
+#endif
+
+#if defined(__GNUC__) && (((__GNUC__ == 4) && (__GNUC_MINOR__ >= 4)) || (__GNUC__ > 4))
+#pragma GCC diagnostic ignored \"-Wstrict-prototypes\"
+#endif
+
+/* Keep this code in sync between libtool.m4, ltmain, lt_system.h, and tests.  */
+#if defined(_WIN32) || defined(__CYGWIN__) || defined(_WIN32_WCE)
+/* DATA imports from DLLs on WIN32 con't be const, because runtime
+   relocations are performed -- see ld's documentation on pseudo-relocs.  */
+# define LT_DLSYM_CONST
+#elif defined(__osf__)
+/* This system does not cope well with relocations in const data.  */
+# define LT_DLSYM_CONST
+#else
+# define LT_DLSYM_CONST const
+#endif
+
+/* External symbol declarations for the compiler. */\
+"
+
+	if test "$dlself" = yes; then
+	  func_verbose "generating symbol list for \`$output'"
+
+	  $opt_dry_run || echo ': @PROGRAM@ ' > "$nlist"
+
+	  # Add our own program objects to the symbol list.
+	  progfiles=`$ECHO "$objs$old_deplibs" | $SP2NL | $SED "$lo2o" | $NL2SP`
+	  for progfile in $progfiles; do
+	    func_to_tool_file "$progfile" func_convert_file_msys_to_w32
+	    func_verbose "extracting global C symbols from \`$func_to_tool_file_result'"
+	    $opt_dry_run || eval "$NM $func_to_tool_file_result | $global_symbol_pipe >> '$nlist'"
+	  done
+
+	  if test -n "$exclude_expsyms"; then
+	    $opt_dry_run || {
+	      eval '$EGREP -v " ($exclude_expsyms)$" "$nlist" > "$nlist"T'
+	      eval '$MV "$nlist"T "$nlist"'
+	    }
+	  fi
+
+	  if test -n "$export_symbols_regex"; then
+	    $opt_dry_run || {
+	      eval '$EGREP -e "$export_symbols_regex" "$nlist" > "$nlist"T'
+	      eval '$MV "$nlist"T "$nlist"'
+	    }
+	  fi
+
+	  # Prepare the list of exported symbols
+	  if test -z "$export_symbols"; then
+	    export_symbols="$output_objdir/$outputname.exp"
+	    $opt_dry_run || {
+	      $RM $export_symbols
+	      eval "${SED} -n -e '/^: @PROGRAM@ $/d' -e 's/^.* \(.*\)$/\1/p' "'< "$nlist" > "$export_symbols"'
+	      case $host in
+	      *cygwin* | *mingw* | *cegcc* )
+                eval "echo EXPORTS "'> "$output_objdir/$outputname.def"'
+                eval 'cat "$export_symbols" >> "$output_objdir/$outputname.def"'
+	        ;;
+	      esac
+	    }
+	  else
+	    $opt_dry_run || {
+	      eval "${SED} -e 's/\([].[*^$]\)/\\\\\1/g' -e 's/^/ /' -e 's/$/$/'"' < "$export_symbols" > "$output_objdir/$outputname.exp"'
+	      eval '$GREP -f "$output_objdir/$outputname.exp" < "$nlist" > "$nlist"T'
+	      eval '$MV "$nlist"T "$nlist"'
+	      case $host in
+	        *cygwin* | *mingw* | *cegcc* )
+	          eval "echo EXPORTS "'> "$output_objdir/$outputname.def"'
+	          eval 'cat "$nlist" >> "$output_objdir/$outputname.def"'
+	          ;;
+	      esac
+	    }
+	  fi
+	fi
+
+	for dlprefile in $dlprefiles; do
+	  func_verbose "extracting global C symbols from \`$dlprefile'"
+	  func_basename "$dlprefile"
+	  name="$func_basename_result"
+          case $host in
+	    *cygwin* | *mingw* | *cegcc* )
+	      # if an import library, we need to obtain dlname
+	      if func_win32_import_lib_p "$dlprefile"; then
+	        func_tr_sh "$dlprefile"
+	        eval "curr_lafile=\$libfile_$func_tr_sh_result"
+	        dlprefile_dlbasename=""
+	        if test -n "$curr_lafile" && func_lalib_p "$curr_lafile"; then
+	          # Use subshell, to avoid clobbering current variable values
+	          dlprefile_dlname=`source "$curr_lafile" && echo "$dlname"`
+	          if test -n "$dlprefile_dlname" ; then
+	            func_basename "$dlprefile_dlname"
+	            dlprefile_dlbasename="$func_basename_result"
+	          else
+	            # no lafile. user explicitly requested -dlpreopen <import library>.
+	            $sharedlib_from_linklib_cmd "$dlprefile"
+	            dlprefile_dlbasename=$sharedlib_from_linklib_result
+	          fi
+	        fi
+	        $opt_dry_run || {
+	          if test -n "$dlprefile_dlbasename" ; then
+	            eval '$ECHO ": $dlprefile_dlbasename" >> "$nlist"'
+	          else
+	            func_warning "Could not compute DLL name from $name"
+	            eval '$ECHO ": $name " >> "$nlist"'
+	          fi
+	          func_to_tool_file "$dlprefile" func_convert_file_msys_to_w32
+	          eval "$NM \"$func_to_tool_file_result\" 2>/dev/null | $global_symbol_pipe |
+	            $SED -e '/I __imp/d' -e 's/I __nm_/D /;s/_nm__//' >> '$nlist'"
+	        }
+	      else # not an import lib
+	        $opt_dry_run || {
+	          eval '$ECHO ": $name " >> "$nlist"'
+	          func_to_tool_file "$dlprefile" func_convert_file_msys_to_w32
+	          eval "$NM \"$func_to_tool_file_result\" 2>/dev/null | $global_symbol_pipe >> '$nlist'"
+	        }
+	      fi
+	    ;;
+	    *)
+	      $opt_dry_run || {
+	        eval '$ECHO ": $name " >> "$nlist"'
+	        func_to_tool_file "$dlprefile" func_convert_file_msys_to_w32
+	        eval "$NM \"$func_to_tool_file_result\" 2>/dev/null | $global_symbol_pipe >> '$nlist'"
+	      }
+	    ;;
+          esac
+	done
+
+	$opt_dry_run || {
+	  # Make sure we have at least an empty file.
+	  test -f "$nlist" || : > "$nlist"
+
+	  if test -n "$exclude_expsyms"; then
+	    $EGREP -v " ($exclude_expsyms)$" "$nlist" > "$nlist"T
+	    $MV "$nlist"T "$nlist"
+	  fi
+
+	  # Try sorting and uniquifying the output.
+	  if $GREP -v "^: " < "$nlist" |
+	      if sort -k 3 </dev/null >/dev/null 2>&1; then
+		sort -k 3
+	      else
+		sort +2
+	      fi |
+	      uniq > "$nlist"S; then
+	    :
+	  else
+	    $GREP -v "^: " < "$nlist" > "$nlist"S
+	  fi
+
+	  if test -f "$nlist"S; then
+	    eval "$global_symbol_to_cdecl"' < "$nlist"S >> "$output_objdir/$my_dlsyms"'
+	  else
+	    echo '/* NONE */' >> "$output_objdir/$my_dlsyms"
+	  fi
+
+	  echo >> "$output_objdir/$my_dlsyms" "\
+
+/* The mapping between symbol names and symbols.  */
+typedef struct {
+  const char *name;
+  void *address;
+} lt_dlsymlist;
+extern LT_DLSYM_CONST lt_dlsymlist
+lt_${my_prefix}_LTX_preloaded_symbols[];
+LT_DLSYM_CONST lt_dlsymlist
+lt_${my_prefix}_LTX_preloaded_symbols[] =
+{\
+  { \"$my_originator\", (void *) 0 },"
+
+	  case $need_lib_prefix in
+	  no)
+	    eval "$global_symbol_to_c_name_address" < "$nlist" >> "$output_objdir/$my_dlsyms"
+	    ;;
+	  *)
+	    eval "$global_symbol_to_c_name_address_lib_prefix" < "$nlist" >> "$output_objdir/$my_dlsyms"
+	    ;;
+	  esac
+	  echo >> "$output_objdir/$my_dlsyms" "\
+  {0, (void *) 0}
+};
+
+/* This works around a problem in FreeBSD linker */
+#ifdef FREEBSD_WORKAROUND
+static const void *lt_preloaded_setup() {
+  return lt_${my_prefix}_LTX_preloaded_symbols;
+}
+#endif
+
+#ifdef __cplusplus
+}
+#endif\
+"
+	} # !$opt_dry_run
+
+	pic_flag_for_symtable=
+	case "$compile_command " in
+	*" -static "*) ;;
+	*)
+	  case $host in
+	  # compiling the symbol table file with pic_flag works around
+	  # a FreeBSD bug that causes programs to crash when -lm is
+	  # linked before any other PIC object.  But we must not use
+	  # pic_flag when linking with -static.  The problem exists in
+	  # FreeBSD 2.2.6 and is fixed in FreeBSD 3.1.
+	  *-*-freebsd2.*|*-*-freebsd3.0*|*-*-freebsdelf3.0*)
+	    pic_flag_for_symtable=" $pic_flag -DFREEBSD_WORKAROUND" ;;
+	  *-*-hpux*)
+	    pic_flag_for_symtable=" $pic_flag"  ;;
+	  *)
+	    if test "X$my_pic_p" != Xno; then
+	      pic_flag_for_symtable=" $pic_flag"
+	    fi
+	    ;;
+	  esac
+	  ;;
+	esac
+	symtab_cflags=
+	for arg in $LTCFLAGS; do
+	  case $arg in
+	  -pie | -fpie | -fPIE) ;;
+	  *) func_append symtab_cflags " $arg" ;;
+	  esac
+	done
+
+	# Now compile the dynamic symbol file.
+	func_show_eval '(cd $output_objdir && $LTCC$symtab_cflags -c$no_builtin_flag$pic_flag_for_symtable "$my_dlsyms")' 'exit $?'
+
+	# Clean up the generated files.
+	func_show_eval '$RM "$output_objdir/$my_dlsyms" "$nlist" "${nlist}S" "${nlist}T"'
+
+	# Transform the symbol file into the correct name.
+	symfileobj="$output_objdir/${my_outputname}S.$objext"
+	case $host in
+	*cygwin* | *mingw* | *cegcc* )
+	  if test -f "$output_objdir/$my_outputname.def"; then
+	    compile_command=`$ECHO "$compile_command" | $SED "s%@SYMFILE@%$output_objdir/$my_outputname.def $symfileobj%"`
+	    finalize_command=`$ECHO "$finalize_command" | $SED "s%@SYMFILE@%$output_objdir/$my_outputname.def $symfileobj%"`
+	  else
+	    compile_command=`$ECHO "$compile_command" | $SED "s%@SYMFILE@%$symfileobj%"`
+	    finalize_command=`$ECHO "$finalize_command" | $SED "s%@SYMFILE@%$symfileobj%"`
+	  fi
+	  ;;
+	*)
+	  compile_command=`$ECHO "$compile_command" | $SED "s%@SYMFILE@%$symfileobj%"`
+	  finalize_command=`$ECHO "$finalize_command" | $SED "s%@SYMFILE@%$symfileobj%"`
+	  ;;
+	esac
+	;;
+      *)
+	func_fatal_error "unknown suffix for \`$my_dlsyms'"
+	;;
+      esac
+    else
+      # We keep going just in case the user didn't refer to
+      # lt_preloaded_symbols.  The linker will fail if global_symbol_pipe
+      # really was required.
+
+      # Nullify the symbol file.
+      compile_command=`$ECHO "$compile_command" | $SED "s% @SYMFILE@%%"`
+      finalize_command=`$ECHO "$finalize_command" | $SED "s% @SYMFILE@%%"`
+    fi
+}
+
+# func_win32_libid arg
+# return the library type of file 'arg'
+#
+# Need a lot of goo to handle *both* DLLs and import libs
+# Has to be a shell function in order to 'eat' the argument
+# that is supplied when $file_magic_command is called.
+# Despite the name, also deal with 64 bit binaries.
+func_win32_libid ()
+{
+  $opt_debug
+  win32_libid_type="unknown"
+  win32_fileres=`file -L $1 2>/dev/null`
+  case $win32_fileres in
+  *ar\ archive\ import\ library*) # definitely import
+    win32_libid_type="x86 archive import"
+    ;;
+  *ar\ archive*) # could be an import, or static
+    # Keep the egrep pattern in sync with the one in _LT_CHECK_MAGIC_METHOD.
+    if eval $OBJDUMP -f $1 | $SED -e '10q' 2>/dev/null |
+       $EGREP 'file format (pei*-i386(.*architecture: i386)?|pe-arm-wince|pe-x86-64)' >/dev/null; then
+      func_to_tool_file "$1" func_convert_file_msys_to_w32
+      win32_nmres=`eval $NM -f posix -A \"$func_to_tool_file_result\" |
+	$SED -n -e '
+	    1,100{
+		/ I /{
+		    s,.*,import,
+		    p
+		    q
+		}
+	    }'`
+      case $win32_nmres in
+      import*)  win32_libid_type="x86 archive import";;
+      *)        win32_libid_type="x86 archive static";;
+      esac
+    fi
+    ;;
+  *DLL*)
+    win32_libid_type="x86 DLL"
+    ;;
+  *executable*) # but shell scripts are "executable" too...
+    case $win32_fileres in
+    *MS\ Windows\ PE\ Intel*)
+      win32_libid_type="x86 DLL"
+      ;;
+    esac
+    ;;
+  esac
+  $ECHO "$win32_libid_type"
+}
+
+# func_cygming_dll_for_implib ARG
+#
+# Platform-specific function to extract the
+# name of the DLL associated with the specified
+# import library ARG.
+# Invoked by eval'ing the libtool variable
+#    $sharedlib_from_linklib_cmd
+# Result is available in the variable
+#    $sharedlib_from_linklib_result
+func_cygming_dll_for_implib ()
+{
+  $opt_debug
+  sharedlib_from_linklib_result=`$DLLTOOL --identify-strict --identify "$1"`
+}
+
+# func_cygming_dll_for_implib_fallback_core SECTION_NAME LIBNAMEs
+#
+# The is the core of a fallback implementation of a
+# platform-specific function to extract the name of the
+# DLL associated with the specified import library LIBNAME.
+#
+# SECTION_NAME is either .idata$6 or .idata$7, depending
+# on the platform and compiler that created the implib.
+#
+# Echos the name of the DLL associated with the
+# specified import library.
+func_cygming_dll_for_implib_fallback_core ()
+{
+  $opt_debug
+  match_literal=`$ECHO "$1" | $SED "$sed_make_literal_regex"`
+  $OBJDUMP -s --section "$1" "$2" 2>/dev/null |
+    $SED '/^Contents of section '"$match_literal"':/{
+      # Place marker at beginning of archive member dllname section
+      s/.*/====MARK====/
+      p
+      d
+    }
+    # These lines can sometimes be longer than 43 characters, but
+    # are always uninteresting
+    /:[	 ]*file format pe[i]\{,1\}-/d
+    /^In archive [^:]*:/d
+    # Ensure marker is printed
+    /^====MARK====/p
+    # Remove all lines with less than 43 characters
+    /^.\{43\}/!d
+    # From remaining lines, remove first 43 characters
+    s/^.\{43\}//' |
+    $SED -n '
+      # Join marker and all lines until next marker into a single line
+      /^====MARK====/ b para
+      H
+      $ b para
+      b
+      :para
+      x
+      s/\n//g
+      # Remove the marker
+      s/^====MARK====//
+      # Remove trailing dots and whitespace
+      s/[\. \t]*$//
+      # Print
+      /./p' |
+    # we now have a list, one entry per line, of the stringified
+    # contents of the appropriate section of all members of the
+    # archive which possess that section. Heuristic: eliminate
+    # all those which have a first or second character that is
+    # a '.' (that is, objdump's representation of an unprintable
+    # character.) This should work for all archives with less than
+    # 0x302f exports -- but will fail for DLLs whose name actually
+    # begins with a literal '.' or a single character followed by
+    # a '.'.
+    #
+    # Of those that remain, print the first one.
+    $SED -e '/^\./d;/^.\./d;q'
+}
+
+# func_cygming_gnu_implib_p ARG
+# This predicate returns with zero status (TRUE) if
+# ARG is a GNU/binutils-style import library. Returns
+# with nonzero status (FALSE) otherwise.
+func_cygming_gnu_implib_p ()
+{
+  $opt_debug
+  func_to_tool_file "$1" func_convert_file_msys_to_w32
+  func_cygming_gnu_implib_tmp=`$NM "$func_to_tool_file_result" | eval "$global_symbol_pipe" | $EGREP ' (_head_[A-Za-z0-9_]+_[ad]l*|[A-Za-z0-9_]+_[ad]l*_iname)$'`
+  test -n "$func_cygming_gnu_implib_tmp"
+}
+
+# func_cygming_ms_implib_p ARG
+# This predicate returns with zero status (TRUE) if
+# ARG is an MS-style import library. Returns
+# with nonzero status (FALSE) otherwise.
+func_cygming_ms_implib_p ()
+{
+  $opt_debug
+  func_to_tool_file "$1" func_convert_file_msys_to_w32
+  func_cygming_ms_implib_tmp=`$NM "$func_to_tool_file_result" | eval "$global_symbol_pipe" | $GREP '_NULL_IMPORT_DESCRIPTOR'`
+  test -n "$func_cygming_ms_implib_tmp"
+}
+
+# func_cygming_dll_for_implib_fallback ARG
+# Platform-specific function to extract the
+# name of the DLL associated with the specified
+# import library ARG.
+#
+# This fallback implementation is for use when $DLLTOOL
+# does not support the --identify-strict option.
+# Invoked by eval'ing the libtool variable
+#    $sharedlib_from_linklib_cmd
+# Result is available in the variable
+#    $sharedlib_from_linklib_result
+func_cygming_dll_for_implib_fallback ()
+{
+  $opt_debug
+  if func_cygming_gnu_implib_p "$1" ; then
+    # binutils import library
+    sharedlib_from_linklib_result=`func_cygming_dll_for_implib_fallback_core '.idata$7' "$1"`
+  elif func_cygming_ms_implib_p "$1" ; then
+    # ms-generated import library
+    sharedlib_from_linklib_result=`func_cygming_dll_for_implib_fallback_core '.idata$6' "$1"`
+  else
+    # unknown
+    sharedlib_from_linklib_result=""
+  fi
+}
+
+
+# func_extract_an_archive dir oldlib
+func_extract_an_archive ()
+{
+    $opt_debug
+    f_ex_an_ar_dir="$1"; shift
+    f_ex_an_ar_oldlib="$1"
+    if test "$lock_old_archive_extraction" = yes; then
+      lockfile=$f_ex_an_ar_oldlib.lock
+      until $opt_dry_run || ln "$progpath" "$lockfile" 2>/dev/null; do
+	func_echo "Waiting for $lockfile to be removed"
+	sleep 2
+      done
+    fi
+    func_show_eval "(cd \$f_ex_an_ar_dir && $AR x \"\$f_ex_an_ar_oldlib\")" \
+		   'stat=$?; rm -f "$lockfile"; exit $stat'
+    if test "$lock_old_archive_extraction" = yes; then
+      $opt_dry_run || rm -f "$lockfile"
+    fi
+    if ($AR t "$f_ex_an_ar_oldlib" | sort | sort -uc >/dev/null 2>&1); then
+     :
+    else
+      func_fatal_error "object name conflicts in archive: $f_ex_an_ar_dir/$f_ex_an_ar_oldlib"
+    fi
+}
+
+
+# func_extract_archives gentop oldlib ...
+func_extract_archives ()
+{
+    $opt_debug
+    my_gentop="$1"; shift
+    my_oldlibs=${1+"$@"}
+    my_oldobjs=""
+    my_xlib=""
+    my_xabs=""
+    my_xdir=""
+
+    for my_xlib in $my_oldlibs; do
+      # Extract the objects.
+      case $my_xlib in
+	[\\/]* | [A-Za-z]:[\\/]*) my_xabs="$my_xlib" ;;
+	*) my_xabs=`pwd`"/$my_xlib" ;;
+      esac
+      func_basename "$my_xlib"
+      my_xlib="$func_basename_result"
+      my_xlib_u=$my_xlib
+      while :; do
+        case " $extracted_archives " in
+	*" $my_xlib_u "*)
+	  func_arith $extracted_serial + 1
+	  extracted_serial=$func_arith_result
+	  my_xlib_u=lt$extracted_serial-$my_xlib ;;
+	*) break ;;
+	esac
+      done
+      extracted_archives="$extracted_archives $my_xlib_u"
+      my_xdir="$my_gentop/$my_xlib_u"
+
+      func_mkdir_p "$my_xdir"
+
+      case $host in
+      *-darwin*)
+	func_verbose "Extracting $my_xabs"
+	# Do not bother doing anything if just a dry run
+	$opt_dry_run || {
+	  darwin_orig_dir=`pwd`
+	  cd $my_xdir || exit $?
+	  darwin_archive=$my_xabs
+	  darwin_curdir=`pwd`
+	  darwin_base_archive=`basename "$darwin_archive"`
+	  darwin_arches=`$LIPO -info "$darwin_archive" 2>/dev/null | $GREP Architectures 2>/dev/null || true`
+	  if test -n "$darwin_arches"; then
+	    darwin_arches=`$ECHO "$darwin_arches" | $SED -e 's/.*are://'`
+	    darwin_arch=
+	    func_verbose "$darwin_base_archive has multiple architectures $darwin_arches"
+	    for darwin_arch in  $darwin_arches ; do
+	      func_mkdir_p "unfat-$$/${darwin_base_archive}-${darwin_arch}"
+	      $LIPO -thin $darwin_arch -output "unfat-$$/${darwin_base_archive}-${darwin_arch}/${darwin_base_archive}" "${darwin_archive}"
+	      cd "unfat-$$/${darwin_base_archive}-${darwin_arch}"
+	      func_extract_an_archive "`pwd`" "${darwin_base_archive}"
+	      cd "$darwin_curdir"
+	      $RM "unfat-$$/${darwin_base_archive}-${darwin_arch}/${darwin_base_archive}"
+	    done # $darwin_arches
+            ## Okay now we've a bunch of thin objects, gotta fatten them up :)
+	    darwin_filelist=`find unfat-$$ -type f -name \*.o -print -o -name \*.lo -print | $SED -e "$basename" | sort -u`
+	    darwin_file=
+	    darwin_files=
+	    for darwin_file in $darwin_filelist; do
+	      darwin_files=`find unfat-$$ -name $darwin_file -print | sort | $NL2SP`
+	      $LIPO -create -output "$darwin_file" $darwin_files
+	    done # $darwin_filelist
+	    $RM -rf unfat-$$
+	    cd "$darwin_orig_dir"
+	  else
+	    cd $darwin_orig_dir
+	    func_extract_an_archive "$my_xdir" "$my_xabs"
+	  fi # $darwin_arches
+	} # !$opt_dry_run
+	;;
+      *)
+        func_extract_an_archive "$my_xdir" "$my_xabs"
+	;;
+      esac
+      my_oldobjs="$my_oldobjs "`find $my_xdir -name \*.$objext -print -o -name \*.lo -print | sort | $NL2SP`
+    done
+
+    func_extract_archives_result="$my_oldobjs"
+}
+
+
+# func_emit_wrapper [arg=no]
+#
+# Emit a libtool wrapper script on stdout.
+# Don't directly open a file because we may want to
+# incorporate the script contents within a cygwin/mingw
+# wrapper executable.  Must ONLY be called from within
+# func_mode_link because it depends on a number of variables
+# set therein.
+#
+# ARG is the value that the WRAPPER_SCRIPT_BELONGS_IN_OBJDIR
+# variable will take.  If 'yes', then the emitted script
+# will assume that the directory in which it is stored is
+# the $objdir directory.  This is a cygwin/mingw-specific
+# behavior.
+func_emit_wrapper ()
+{
+	func_emit_wrapper_arg1=${1-no}
+
+	$ECHO "\
+#! $SHELL
+
+# $output - temporary wrapper script for $objdir/$outputname
+# Generated by $PROGRAM (GNU $PACKAGE$TIMESTAMP) $VERSION
+#
+# The $output program cannot be directly executed until all the libtool
+# libraries that it depends on are installed.
+#
+# This wrapper script should never be moved out of the build directory.
+# If it is, it will not operate correctly.
+
+# Sed substitution that helps us do robust quoting.  It backslashifies
+# metacharacters that are still active within double-quoted strings.
+sed_quote_subst='$sed_quote_subst'
+
+# Be Bourne compatible
+if test -n \"\${ZSH_VERSION+set}\" && (emulate sh) >/dev/null 2>&1; then
+  emulate sh
+  NULLCMD=:
+  # Zsh 3.x and 4.x performs word splitting on \${1+\"\$@\"}, which
+  # is contrary to our usage.  Disable this feature.
+  alias -g '\${1+\"\$@\"}'='\"\$@\"'
+  setopt NO_GLOB_SUBST
+else
+  case \`(set -o) 2>/dev/null\` in *posix*) set -o posix;; esac
+fi
+BIN_SH=xpg4; export BIN_SH # for Tru64
+DUALCASE=1; export DUALCASE # for MKS sh
+
+# The HP-UX ksh and POSIX shell print the target directory to stdout
+# if CDPATH is set.
+(unset CDPATH) >/dev/null 2>&1 && unset CDPATH
+
+relink_command=\"$relink_command\"
+
+# This environment variable determines our operation mode.
+if test \"\$libtool_install_magic\" = \"$magic\"; then
+  # install mode needs the following variables:
+  generated_by_libtool_version='$macro_version'
+  notinst_deplibs='$notinst_deplibs'
+else
+  # When we are sourced in execute mode, \$file and \$ECHO are already set.
+  if test \"\$libtool_execute_magic\" != \"$magic\"; then
+    file=\"\$0\""
+
+    qECHO=`$ECHO "$ECHO" | $SED "$sed_quote_subst"`
+    $ECHO "\
+
+# A function that is used when there is no print builtin or printf.
+func_fallback_echo ()
+{
+  eval 'cat <<_LTECHO_EOF
+\$1
+_LTECHO_EOF'
+}
+    ECHO=\"$qECHO\"
+  fi
+
+# Very basic option parsing. These options are (a) specific to
+# the libtool wrapper, (b) are identical between the wrapper
+# /script/ and the wrapper /executable/ which is used only on
+# windows platforms, and (c) all begin with the string "--lt-"
+# (application programs are unlikely to have options which match
+# this pattern).
+#
+# There are only two supported options: --lt-debug and
+# --lt-dump-script. There is, deliberately, no --lt-help.
+#
+# The first argument to this parsing function should be the
+# script's $0 value, followed by "$@".
+lt_option_debug=
+func_parse_lt_options ()
+{
+  lt_script_arg0=\$0
+  shift
+  for lt_opt
+  do
+    case \"\$lt_opt\" in
+    --lt-debug) lt_option_debug=1 ;;
+    --lt-dump-script)
+        lt_dump_D=\`\$ECHO \"X\$lt_script_arg0\" | $SED -e 's/^X//' -e 's%/[^/]*$%%'\`
+        test \"X\$lt_dump_D\" = \"X\$lt_script_arg0\" && lt_dump_D=.
+        lt_dump_F=\`\$ECHO \"X\$lt_script_arg0\" | $SED -e 's/^X//' -e 's%^.*/%%'\`
+        cat \"\$lt_dump_D/\$lt_dump_F\"
+        exit 0
+      ;;
+    --lt-*)
+        \$ECHO \"Unrecognized --lt- option: '\$lt_opt'\" 1>&2
+        exit 1
+      ;;
+    esac
+  done
+
+  # Print the debug banner immediately:
+  if test -n \"\$lt_option_debug\"; then
+    echo \"${outputname}:${output}:\${LINENO}: libtool wrapper (GNU $PACKAGE$TIMESTAMP) $VERSION\" 1>&2
+  fi
+}
+
+# Used when --lt-debug. Prints its arguments to stdout
+# (redirection is the responsibility of the caller)
+func_lt_dump_args ()
+{
+  lt_dump_args_N=1;
+  for lt_arg
+  do
+    \$ECHO \"${outputname}:${output}:\${LINENO}: newargv[\$lt_dump_args_N]: \$lt_arg\"
+    lt_dump_args_N=\`expr \$lt_dump_args_N + 1\`
+  done
+}
+
+# Core function for launching the target application
+func_exec_program_core ()
+{
+"
+  case $host in
+  # Backslashes separate directories on plain windows
+  *-*-mingw | *-*-os2* | *-cegcc*)
+    $ECHO "\
+      if test -n \"\$lt_option_debug\"; then
+        \$ECHO \"${outputname}:${output}:\${LINENO}: newargv[0]: \$progdir\\\\\$program\" 1>&2
+        func_lt_dump_args \${1+\"\$@\"} 1>&2
+      fi
+      exec \"\$progdir\\\\\$program\" \${1+\"\$@\"}
+"
+    ;;
+
+  *)
+    $ECHO "\
+      if test -n \"\$lt_option_debug\"; then
+        \$ECHO \"${outputname}:${output}:\${LINENO}: newargv[0]: \$progdir/\$program\" 1>&2
+        func_lt_dump_args \${1+\"\$@\"} 1>&2
+      fi
+      exec \"\$progdir/\$program\" \${1+\"\$@\"}
+"
+    ;;
+  esac
+  $ECHO "\
+      \$ECHO \"\$0: cannot exec \$program \$*\" 1>&2
+      exit 1
+}
+
+# A function to encapsulate launching the target application
+# Strips options in the --lt-* namespace from \$@ and
+# launches target application with the remaining arguments.
+func_exec_program ()
+{
+  case \" \$* \" in
+  *\\ --lt-*)
+    for lt_wr_arg
+    do
+      case \$lt_wr_arg in
+      --lt-*) ;;
+      *) set x \"\$@\" \"\$lt_wr_arg\"; shift;;
+      esac
+      shift
+    done ;;
+  esac
+  func_exec_program_core \${1+\"\$@\"}
+}
+
+  # Parse options
+  func_parse_lt_options \"\$0\" \${1+\"\$@\"}
+
+  # Find the directory that this script lives in.
+  thisdir=\`\$ECHO \"\$file\" | $SED 's%/[^/]*$%%'\`
+  test \"x\$thisdir\" = \"x\$file\" && thisdir=.
+
+  # Follow symbolic links until we get to the real thisdir.
+  file=\`ls -ld \"\$file\" | $SED -n 's/.*-> //p'\`
+  while test -n \"\$file\"; do
+    destdir=\`\$ECHO \"\$file\" | $SED 's%/[^/]*\$%%'\`
+
+    # If there was a directory component, then change thisdir.
+    if test \"x\$destdir\" != \"x\$file\"; then
+      case \"\$destdir\" in
+      [\\\\/]* | [A-Za-z]:[\\\\/]*) thisdir=\"\$destdir\" ;;
+      *) thisdir=\"\$thisdir/\$destdir\" ;;
+      esac
+    fi
+
+    file=\`\$ECHO \"\$file\" | $SED 's%^.*/%%'\`
+    file=\`ls -ld \"\$thisdir/\$file\" | $SED -n 's/.*-> //p'\`
+  done
+
+  # Usually 'no', except on cygwin/mingw when embedded into
+  # the cwrapper.
+  WRAPPER_SCRIPT_BELONGS_IN_OBJDIR=$func_emit_wrapper_arg1
+  if test \"\$WRAPPER_SCRIPT_BELONGS_IN_OBJDIR\" = \"yes\"; then
+    # special case for '.'
+    if test \"\$thisdir\" = \".\"; then
+      thisdir=\`pwd\`
+    fi
+    # remove .libs from thisdir
+    case \"\$thisdir\" in
+    *[\\\\/]$objdir ) thisdir=\`\$ECHO \"\$thisdir\" | $SED 's%[\\\\/][^\\\\/]*$%%'\` ;;
+    $objdir )   thisdir=. ;;
+    esac
+  fi
+
+  # Try to get the absolute directory name.
+  absdir=\`cd \"\$thisdir\" && pwd\`
+  test -n \"\$absdir\" && thisdir=\"\$absdir\"
+"
+
+	if test "$fast_install" = yes; then
+	  $ECHO "\
+  program=lt-'$outputname'$exeext
+  progdir=\"\$thisdir/$objdir\"
+
+  if test ! -f \"\$progdir/\$program\" ||
+     { file=\`ls -1dt \"\$progdir/\$program\" \"\$progdir/../\$program\" 2>/dev/null | ${SED} 1q\`; \\
+       test \"X\$file\" != \"X\$progdir/\$program\"; }; then
+
+    file=\"\$\$-\$program\"
+
+    if test ! -d \"\$progdir\"; then
+      $MKDIR \"\$progdir\"
+    else
+      $RM \"\$progdir/\$file\"
+    fi"
+
+	  $ECHO "\
+
+    # relink executable if necessary
+    if test -n \"\$relink_command\"; then
+      if relink_command_output=\`eval \$relink_command 2>&1\`; then :
+      else
+	$ECHO \"\$relink_command_output\" >&2
+	$RM \"\$progdir/\$file\"
+	exit 1
+      fi
+    fi
+
+    $MV \"\$progdir/\$file\" \"\$progdir/\$program\" 2>/dev/null ||
+    { $RM \"\$progdir/\$program\";
+      $MV \"\$progdir/\$file\" \"\$progdir/\$program\"; }
+    $RM \"\$progdir/\$file\"
+  fi"
+	else
+	  $ECHO "\
+  program='$outputname'
+  progdir=\"\$thisdir/$objdir\"
+"
+	fi
+
+	$ECHO "\
+
+  if test -f \"\$progdir/\$program\"; then"
+
+	# fixup the dll searchpath if we need to.
+	#
+	# Fix the DLL searchpath if we need to.  Do this before prepending
+	# to shlibpath, because on Windows, both are PATH and uninstalled
+	# libraries must come first.
+	if test -n "$dllsearchpath"; then
+	  $ECHO "\
+    # Add the dll search path components to the executable PATH
+    PATH=$dllsearchpath:\$PATH
+"
+	fi
+
+	# Export our shlibpath_var if we have one.
+	if test "$shlibpath_overrides_runpath" = yes && test -n "$shlibpath_var" && test -n "$temp_rpath"; then
+	  $ECHO "\
+    # Add our own library path to $shlibpath_var
+    $shlibpath_var=\"$temp_rpath\$$shlibpath_var\"
+
+    # Some systems cannot cope with colon-terminated $shlibpath_var
+    # The second colon is a workaround for a bug in BeOS R4 sed
+    $shlibpath_var=\`\$ECHO \"\$$shlibpath_var\" | $SED 's/::*\$//'\`
+
+    export $shlibpath_var
+"
+	fi
+
+	$ECHO "\
+    if test \"\$libtool_execute_magic\" != \"$magic\"; then
+      # Run the actual program with our arguments.
+      func_exec_program \${1+\"\$@\"}
+    fi
+  else
+    # The program doesn't exist.
+    \$ECHO \"\$0: error: \\\`\$progdir/\$program' does not exist\" 1>&2
+    \$ECHO \"This script is just a wrapper for \$program.\" 1>&2
+    \$ECHO \"See the $PACKAGE documentation for more information.\" 1>&2
+    exit 1
+  fi
+fi\
+"
+}
+
+
+# func_emit_cwrapperexe_src
+# emit the source code for a wrapper executable on stdout
+# Must ONLY be called from within func_mode_link because
+# it depends on a number of variable set therein.
+func_emit_cwrapperexe_src ()
+{
+	cat <<EOF
+
+/* $cwrappersource - temporary wrapper executable for $objdir/$outputname
+   Generated by $PROGRAM (GNU $PACKAGE$TIMESTAMP) $VERSION
+
+   The $output program cannot be directly executed until all the libtool
+   libraries that it depends on are installed.
+
+   This wrapper executable should never be moved out of the build directory.
+   If it is, it will not operate correctly.
+*/
+EOF
+	    cat <<"EOF"
+#ifdef _MSC_VER
+# define _CRT_SECURE_NO_DEPRECATE 1
+#endif
+#include <stdio.h>
+#include <stdlib.h>
+#ifdef _MSC_VER
+# include <direct.h>
+# include <process.h>
+# include <io.h>
+#else
+# include <unistd.h>
+# include <stdint.h>
+# ifdef __CYGWIN__
+#  include <io.h>
+# endif
+#endif
+#include <malloc.h>
+#include <stdarg.h>
+#include <assert.h>
+#include <string.h>
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+
+/* declarations of non-ANSI functions */
+#if defined(__MINGW32__)
+# ifdef __STRICT_ANSI__
+int _putenv (const char *);
+# endif
+#elif defined(__CYGWIN__)
+# ifdef __STRICT_ANSI__
+char *realpath (const char *, char *);
+int putenv (char *);
+int setenv (const char *, const char *, int);
+# endif
+/* #elif defined (other platforms) ... */
+#endif
+
+/* portability defines, excluding path handling macros */
+#if defined(_MSC_VER)
+# define setmode _setmode
+# define stat    _stat
+# define chmod   _chmod
+# define getcwd  _getcwd
+# define putenv  _putenv
+# define S_IXUSR _S_IEXEC
+# ifndef _INTPTR_T_DEFINED
+#  define _INTPTR_T_DEFINED
+#  define intptr_t int
+# endif
+#elif defined(__MINGW32__)
+# define setmode _setmode
+# define stat    _stat
+# define chmod   _chmod
+# define getcwd  _getcwd
+# define putenv  _putenv
+#elif defined(__CYGWIN__)
+# define HAVE_SETENV
+# define FOPEN_WB "wb"
+/* #elif defined (other platforms) ... */
+#endif
+
+#if defined(PATH_MAX)
+# define LT_PATHMAX PATH_MAX
+#elif defined(MAXPATHLEN)
+# define LT_PATHMAX MAXPATHLEN
+#else
+# define LT_PATHMAX 1024
+#endif
+
+#ifndef S_IXOTH
+# define S_IXOTH 0
+#endif
+#ifndef S_IXGRP
+# define S_IXGRP 0
+#endif
+
+/* path handling portability macros */
+#ifndef DIR_SEPARATOR
+# define DIR_SEPARATOR '/'
+# define PATH_SEPARATOR ':'
+#endif
+
+#if defined (_WIN32) || defined (__MSDOS__) || defined (__DJGPP__) || \
+  defined (__OS2__)
+# define HAVE_DOS_BASED_FILE_SYSTEM
+# define FOPEN_WB "wb"
+# ifndef DIR_SEPARATOR_2
+#  define DIR_SEPARATOR_2 '\\'
+# endif
+# ifndef PATH_SEPARATOR_2
+#  define PATH_SEPARATOR_2 ';'
+# endif
+#endif
+
+#ifndef DIR_SEPARATOR_2
+# define IS_DIR_SEPARATOR(ch) ((ch) == DIR_SEPARATOR)
+#else /* DIR_SEPARATOR_2 */
+# define IS_DIR_SEPARATOR(ch) \
+	(((ch) == DIR_SEPARATOR) || ((ch) == DIR_SEPARATOR_2))
+#endif /* DIR_SEPARATOR_2 */
+
+#ifndef PATH_SEPARATOR_2
+# define IS_PATH_SEPARATOR(ch) ((ch) == PATH_SEPARATOR)
+#else /* PATH_SEPARATOR_2 */
+# define IS_PATH_SEPARATOR(ch) ((ch) == PATH_SEPARATOR_2)
+#endif /* PATH_SEPARATOR_2 */
+
+#ifndef FOPEN_WB
+# define FOPEN_WB "w"
+#endif
+#ifndef _O_BINARY
+# define _O_BINARY 0
+#endif
+
+#define XMALLOC(type, num)      ((type *) xmalloc ((num) * sizeof(type)))
+#define XFREE(stale) do { \
+  if (stale) { free ((void *) stale); stale = 0; } \
+} while (0)
+
+#if defined(LT_DEBUGWRAPPER)
+static int lt_debug = 1;
+#else
+static int lt_debug = 0;
+#endif
+
+const char *program_name = "libtool-wrapper"; /* in case xstrdup fails */
+
+void *xmalloc (size_t num);
+char *xstrdup (const char *string);
+const char *base_name (const char *name);
+char *find_executable (const char *wrapper);
+char *chase_symlinks (const char *pathspec);
+int make_executable (const char *path);
+int check_executable (const char *path);
+char *strendzap (char *str, const char *pat);
+void lt_debugprintf (const char *file, int line, const char *fmt, ...);
+void lt_fatal (const char *file, int line, const char *message, ...);
+static const char *nonnull (const char *s);
+static const char *nonempty (const char *s);
+void lt_setenv (const char *name, const char *value);
+char *lt_extend_str (const char *orig_value, const char *add, int to_end);
+void lt_update_exe_path (const char *name, const char *value);
+void lt_update_lib_path (const char *name, const char *value);
+char **prepare_spawn (char **argv);
+void lt_dump_script (FILE *f);
+EOF
+
+	    cat <<EOF
+volatile const char * MAGIC_EXE = "$magic_exe";
+const char * LIB_PATH_VARNAME = "$shlibpath_var";
+EOF
+
+	    if test "$shlibpath_overrides_runpath" = yes && test -n "$shlibpath_var" && test -n "$temp_rpath"; then
+              func_to_host_path "$temp_rpath"
+	      cat <<EOF
+const char * LIB_PATH_VALUE   = "$func_to_host_path_result";
+EOF
+	    else
+	      cat <<"EOF"
+const char * LIB_PATH_VALUE   = "";
+EOF
+	    fi
+
+	    if test -n "$dllsearchpath"; then
+              func_to_host_path "$dllsearchpath:"
+	      cat <<EOF
+const char * EXE_PATH_VARNAME = "PATH";
+const char * EXE_PATH_VALUE   = "$func_to_host_path_result";
+EOF
+	    else
+	      cat <<"EOF"
+const char * EXE_PATH_VARNAME = "";
+const char * EXE_PATH_VALUE   = "";
+EOF
+	    fi
+
+	    if test "$fast_install" = yes; then
+	      cat <<EOF
+const char * TARGET_PROGRAM_NAME = "lt-$outputname"; /* hopefully, no .exe */
+EOF
+	    else
+	      cat <<EOF
+const char * TARGET_PROGRAM_NAME = "$outputname"; /* hopefully, no .exe */
+EOF
+	    fi
+
+
+	    cat <<"EOF"
+
+#define LTWRAPPER_OPTION_PREFIX         "--lt-"
+
+static const char *ltwrapper_option_prefix = LTWRAPPER_OPTION_PREFIX;
+static const char *dumpscript_opt       = LTWRAPPER_OPTION_PREFIX "dump-script";
+static const char *debug_opt            = LTWRAPPER_OPTION_PREFIX "debug";
+
+int
+main (int argc, char *argv[])
+{
+  char **newargz;
+  int  newargc;
+  char *tmp_pathspec;
+  char *actual_cwrapper_path;
+  char *actual_cwrapper_name;
+  char *target_name;
+  char *lt_argv_zero;
+  intptr_t rval = 127;
+
+  int i;
+
+  program_name = (char *) xstrdup (base_name (argv[0]));
+  newargz = XMALLOC (char *, argc + 1);
+
+  /* very simple arg parsing; don't want to rely on getopt
+   * also, copy all non cwrapper options to newargz, except
+   * argz[0], which is handled differently
+   */
+  newargc=0;
+  for (i = 1; i < argc; i++)
+    {
+      if (strcmp (argv[i], dumpscript_opt) == 0)
+	{
+EOF
+	    case "$host" in
+	      *mingw* | *cygwin* )
+		# make stdout use "unix" line endings
+		echo "          setmode(1,_O_BINARY);"
+		;;
+	      esac
+
+	    cat <<"EOF"
+	  lt_dump_script (stdout);
+	  return 0;
+	}
+      if (strcmp (argv[i], debug_opt) == 0)
+	{
+          lt_debug = 1;
+          continue;
+	}
+      if (strcmp (argv[i], ltwrapper_option_prefix) == 0)
+        {
+          /* however, if there is an option in the LTWRAPPER_OPTION_PREFIX
+             namespace, but it is not one of the ones we know about and
+             have already dealt with, above (inluding dump-script), then
+             report an error. Otherwise, targets might begin to believe
+             they are allowed to use options in the LTWRAPPER_OPTION_PREFIX
+             namespace. The first time any user complains about this, we'll
+             need to make LTWRAPPER_OPTION_PREFIX a configure-time option
+             or a configure.ac-settable value.
+           */
+          lt_fatal (__FILE__, __LINE__,
+		    "unrecognized %s option: '%s'",
+                    ltwrapper_option_prefix, argv[i]);
+        }
+      /* otherwise ... */
+      newargz[++newargc] = xstrdup (argv[i]);
+    }
+  newargz[++newargc] = NULL;
+
+EOF
+	    cat <<EOF
+  /* The GNU banner must be the first non-error debug message */
+  lt_debugprintf (__FILE__, __LINE__, "libtool wrapper (GNU $PACKAGE$TIMESTAMP) $VERSION\n");
+EOF
+	    cat <<"EOF"
+  lt_debugprintf (__FILE__, __LINE__, "(main) argv[0]: %s\n", argv[0]);
+  lt_debugprintf (__FILE__, __LINE__, "(main) program_name: %s\n", program_name);
+
+  tmp_pathspec = find_executable (argv[0]);
+  if (tmp_pathspec == NULL)
+    lt_fatal (__FILE__, __LINE__, "couldn't find %s", argv[0]);
+  lt_debugprintf (__FILE__, __LINE__,
+                  "(main) found exe (before symlink chase) at: %s\n",
+		  tmp_pathspec);
+
+  actual_cwrapper_path = chase_symlinks (tmp_pathspec);
+  lt_debugprintf (__FILE__, __LINE__,
+                  "(main) found exe (after symlink chase) at: %s\n",
+		  actual_cwrapper_path);
+  XFREE (tmp_pathspec);
+
+  actual_cwrapper_name = xstrdup (base_name (actual_cwrapper_path));
+  strendzap (actual_cwrapper_path, actual_cwrapper_name);
+
+  /* wrapper name transforms */
+  strendzap (actual_cwrapper_name, ".exe");
+  tmp_pathspec = lt_extend_str (actual_cwrapper_name, ".exe", 1);
+  XFREE (actual_cwrapper_name);
+  actual_cwrapper_name = tmp_pathspec;
+  tmp_pathspec = 0;
+
+  /* target_name transforms -- use actual target program name; might have lt- prefix */
+  target_name = xstrdup (base_name (TARGET_PROGRAM_NAME));
+  strendzap (target_name, ".exe");
+  tmp_pathspec = lt_extend_str (target_name, ".exe", 1);
+  XFREE (target_name);
+  target_name = tmp_pathspec;
+  tmp_pathspec = 0;
+
+  lt_debugprintf (__FILE__, __LINE__,
+		  "(main) libtool target name: %s\n",
+		  target_name);
+EOF
+
+	    cat <<EOF
+  newargz[0] =
+    XMALLOC (char, (strlen (actual_cwrapper_path) +
+		    strlen ("$objdir") + 1 + strlen (actual_cwrapper_name) + 1));
+  strcpy (newargz[0], actual_cwrapper_path);
+  strcat (newargz[0], "$objdir");
+  strcat (newargz[0], "/");
+EOF
+
+	    cat <<"EOF"
+  /* stop here, and copy so we don't have to do this twice */
+  tmp_pathspec = xstrdup (newargz[0]);
+
+  /* do NOT want the lt- prefix here, so use actual_cwrapper_name */
+  strcat (newargz[0], actual_cwrapper_name);
+
+  /* DO want the lt- prefix here if it exists, so use target_name */
+  lt_argv_zero = lt_extend_str (tmp_pathspec, target_name, 1);
+  XFREE (tmp_pathspec);
+  tmp_pathspec = NULL;
+EOF
+
+	    case $host_os in
+	      mingw*)
+	    cat <<"EOF"
+  {
+    char* p;
+    while ((p = strchr (newargz[0], '\\')) != NULL)
+      {
+	*p = '/';
+      }
+    while ((p = strchr (lt_argv_zero, '\\')) != NULL)
+      {
+	*p = '/';
+      }
+  }
+EOF
+	    ;;
+	    esac
+
+	    cat <<"EOF"
+  XFREE (target_name);
+  XFREE (actual_cwrapper_path);
+  XFREE (actual_cwrapper_name);
+
+  lt_setenv ("BIN_SH", "xpg4"); /* for Tru64 */
+  lt_setenv ("DUALCASE", "1");  /* for MSK sh */
+  /* Update the DLL searchpath.  EXE_PATH_VALUE ($dllsearchpath) must
+     be prepended before (that is, appear after) LIB_PATH_VALUE ($temp_rpath)
+     because on Windows, both *_VARNAMEs are PATH but uninstalled
+     libraries must come first. */
+  lt_update_exe_path (EXE_PATH_VARNAME, EXE_PATH_VALUE);
+  lt_update_lib_path (LIB_PATH_VARNAME, LIB_PATH_VALUE);
+
+  lt_debugprintf (__FILE__, __LINE__, "(main) lt_argv_zero: %s\n",
+		  nonnull (lt_argv_zero));
+  for (i = 0; i < newargc; i++)
+    {
+      lt_debugprintf (__FILE__, __LINE__, "(main) newargz[%d]: %s\n",
+		      i, nonnull (newargz[i]));
+    }
+
+EOF
+
+	    case $host_os in
+	      mingw*)
+		cat <<"EOF"
+  /* execv doesn't actually work on mingw as expected on unix */
+  newargz = prepare_spawn (newargz);
+  rval = _spawnv (_P_WAIT, lt_argv_zero, (const char * const *) newargz);
+  if (rval == -1)
+    {
+      /* failed to start process */
+      lt_debugprintf (__FILE__, __LINE__,
+		      "(main) failed to launch target \"%s\": %s\n",
+		      lt_argv_zero, nonnull (strerror (errno)));
+      return 127;
+    }
+  return rval;
+EOF
+		;;
+	      *)
+		cat <<"EOF"
+  execv (lt_argv_zero, newargz);
+  return rval; /* =127, but avoids unused variable warning */
+EOF
+		;;
+	    esac
+
+	    cat <<"EOF"
+}
+
+void *
+xmalloc (size_t num)
+{
+  void *p = (void *) malloc (num);
+  if (!p)
+    lt_fatal (__FILE__, __LINE__, "memory exhausted");
+
+  return p;
+}
+
+char *
+xstrdup (const char *string)
+{
+  return string ? strcpy ((char *) xmalloc (strlen (string) + 1),
+			  string) : NULL;
+}
+
+const char *
+base_name (const char *name)
+{
+  const char *base;
+
+#if defined (HAVE_DOS_BASED_FILE_SYSTEM)
+  /* Skip over the disk name in MSDOS pathnames. */
+  if (isalpha ((unsigned char) name[0]) && name[1] == ':')
+    name += 2;
+#endif
+
+  for (base = name; *name; name++)
+    if (IS_DIR_SEPARATOR (*name))
+      base = name + 1;
+  return base;
+}
+
+int
+check_executable (const char *path)
+{
+  struct stat st;
+
+  lt_debugprintf (__FILE__, __LINE__, "(check_executable): %s\n",
+                  nonempty (path));
+  if ((!path) || (!*path))
+    return 0;
+
+  if ((stat (path, &st) >= 0)
+      && (st.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH)))
+    return 1;
+  else
+    return 0;
+}
+
+int
+make_executable (const char *path)
+{
+  int rval = 0;
+  struct stat st;
+
+  lt_debugprintf (__FILE__, __LINE__, "(make_executable): %s\n",
+                  nonempty (path));
+  if ((!path) || (!*path))
+    return 0;
+
+  if (stat (path, &st) >= 0)
+    {
+      rval = chmod (path, st.st_mode | S_IXOTH | S_IXGRP | S_IXUSR);
+    }
+  return rval;
+}
+
+/* Searches for the full path of the wrapper.  Returns
+   newly allocated full path name if found, NULL otherwise
+   Does not chase symlinks, even on platforms that support them.
+*/
+char *
+find_executable (const char *wrapper)
+{
+  int has_slash = 0;
+  const char *p;
+  const char *p_next;
+  /* static buffer for getcwd */
+  char tmp[LT_PATHMAX + 1];
+  int tmp_len;
+  char *concat_name;
+
+  lt_debugprintf (__FILE__, __LINE__, "(find_executable): %s\n",
+                  nonempty (wrapper));
+
+  if ((wrapper == NULL) || (*wrapper == '\0'))
+    return NULL;
+
+  /* Absolute path? */
+#if defined (HAVE_DOS_BASED_FILE_SYSTEM)
+  if (isalpha ((unsigned char) wrapper[0]) && wrapper[1] == ':')
+    {
+      concat_name = xstrdup (wrapper);
+      if (check_executable (concat_name))
+	return concat_name;
+      XFREE (concat_name);
+    }
+  else
+    {
+#endif
+      if (IS_DIR_SEPARATOR (wrapper[0]))
+	{
+	  concat_name = xstrdup (wrapper);
+	  if (check_executable (concat_name))
+	    return concat_name;
+	  XFREE (concat_name);
+	}
+#if defined (HAVE_DOS_BASED_FILE_SYSTEM)
+    }
+#endif
+
+  for (p = wrapper; *p; p++)
+    if (*p == '/')
+      {
+	has_slash = 1;
+	break;
+      }
+  if (!has_slash)
+    {
+      /* no slashes; search PATH */
+      const char *path = getenv ("PATH");
+      if (path != NULL)
+	{
+	  for (p = path; *p; p = p_next)
+	    {
+	      const char *q;
+	      size_t p_len;
+	      for (q = p; *q; q++)
+		if (IS_PATH_SEPARATOR (*q))
+		  break;
+	      p_len = q - p;
+	      p_next = (*q == '\0' ? q : q + 1);
+	      if (p_len == 0)
+		{
+		  /* empty path: current directory */
+		  if (getcwd (tmp, LT_PATHMAX) == NULL)
+		    lt_fatal (__FILE__, __LINE__, "getcwd failed: %s",
+                              nonnull (strerror (errno)));
+		  tmp_len = strlen (tmp);
+		  concat_name =
+		    XMALLOC (char, tmp_len + 1 + strlen (wrapper) + 1);
+		  memcpy (concat_name, tmp, tmp_len);
+		  concat_name[tmp_len] = '/';
+		  strcpy (concat_name + tmp_len + 1, wrapper);
+		}
+	      else
+		{
+		  concat_name =
+		    XMALLOC (char, p_len + 1 + strlen (wrapper) + 1);
+		  memcpy (concat_name, p, p_len);
+		  concat_name[p_len] = '/';
+		  strcpy (concat_name + p_len + 1, wrapper);
+		}
+	      if (check_executable (concat_name))
+		return concat_name;
+	      XFREE (concat_name);
+	    }
+	}
+      /* not found in PATH; assume curdir */
+    }
+  /* Relative path | not found in path: prepend cwd */
+  if (getcwd (tmp, LT_PATHMAX) == NULL)
+    lt_fatal (__FILE__, __LINE__, "getcwd failed: %s",
+              nonnull (strerror (errno)));
+  tmp_len = strlen (tmp);
+  concat_name = XMALLOC (char, tmp_len + 1 + strlen (wrapper) + 1);
+  memcpy (concat_name, tmp, tmp_len);
+  concat_name[tmp_len] = '/';
+  strcpy (concat_name + tmp_len + 1, wrapper);
+
+  if (check_executable (concat_name))
+    return concat_name;
+  XFREE (concat_name);
+  return NULL;
+}
+
+char *
+chase_symlinks (const char *pathspec)
+{
+#ifndef S_ISLNK
+  return xstrdup (pathspec);
+#else
+  char buf[LT_PATHMAX];
+  struct stat s;
+  char *tmp_pathspec = xstrdup (pathspec);
+  char *p;
+  int has_symlinks = 0;
+  while (strlen (tmp_pathspec) && !has_symlinks)
+    {
+      lt_debugprintf (__FILE__, __LINE__,
+		      "checking path component for symlinks: %s\n",
+		      tmp_pathspec);
+      if (lstat (tmp_pathspec, &s) == 0)
+	{
+	  if (S_ISLNK (s.st_mode) != 0)
+	    {
+	      has_symlinks = 1;
+	      break;
+	    }
+
+	  /* search backwards for last DIR_SEPARATOR */
+	  p = tmp_pathspec + strlen (tmp_pathspec) - 1;
+	  while ((p > tmp_pathspec) && (!IS_DIR_SEPARATOR (*p)))
+	    p--;
+	  if ((p == tmp_pathspec) && (!IS_DIR_SEPARATOR (*p)))
+	    {
+	      /* no more DIR_SEPARATORS left */
+	      break;
+	    }
+	  *p = '\0';
+	}
+      else
+	{
+	  lt_fatal (__FILE__, __LINE__,
+		    "error accessing file \"%s\": %s",
+		    tmp_pathspec, nonnull (strerror (errno)));
+	}
+    }
+  XFREE (tmp_pathspec);
+
+  if (!has_symlinks)
+    {
+      return xstrdup (pathspec);
+    }
+
+  tmp_pathspec = realpath (pathspec, buf);
+  if (tmp_pathspec == 0)
+    {
+      lt_fatal (__FILE__, __LINE__,
+		"could not follow symlinks for %s", pathspec);
+    }
+  return xstrdup (tmp_pathspec);
+#endif
+}
+
+char *
+strendzap (char *str, const char *pat)
+{
+  size_t len, patlen;
+
+  assert (str != NULL);
+  assert (pat != NULL);
+
+  len = strlen (str);
+  patlen = strlen (pat);
+
+  if (patlen <= len)
+    {
+      str += len - patlen;
+      if (strcmp (str, pat) == 0)
+	*str = '\0';
+    }
+  return str;
+}
+
+void
+lt_debugprintf (const char *file, int line, const char *fmt, ...)
+{
+  va_list args;
+  if (lt_debug)
+    {
+      (void) fprintf (stderr, "%s:%s:%d: ", program_name, file, line);
+      va_start (args, fmt);
+      (void) vfprintf (stderr, fmt, args);
+      va_end (args);
+    }
+}
+
+static void
+lt_error_core (int exit_status, const char *file,
+	       int line, const char *mode,
+	       const char *message, va_list ap)
+{
+  fprintf (stderr, "%s:%s:%d: %s: ", program_name, file, line, mode);
+  vfprintf (stderr, message, ap);
+  fprintf (stderr, ".\n");
+
+  if (exit_status >= 0)
+    exit (exit_status);
+}
+
+void
+lt_fatal (const char *file, int line, const char *message, ...)
+{
+  va_list ap;
+  va_start (ap, message);
+  lt_error_core (EXIT_FAILURE, file, line, "FATAL", message, ap);
+  va_end (ap);
+}
+
+static const char *
+nonnull (const char *s)
+{
+  return s ? s : "(null)";
+}
+
+static const char *
+nonempty (const char *s)
+{
+  return (s && !*s) ? "(empty)" : nonnull (s);
+}
+
+void
+lt_setenv (const char *name, const char *value)
+{
+  lt_debugprintf (__FILE__, __LINE__,
+		  "(lt_setenv) setting '%s' to '%s'\n",
+                  nonnull (name), nonnull (value));
+  {
+#ifdef HAVE_SETENV
+    /* always make a copy, for consistency with !HAVE_SETENV */
+    char *str = xstrdup (value);
+    setenv (name, str, 1);
+#else
+    int len = strlen (name) + 1 + strlen (value) + 1;
+    char *str = XMALLOC (char, len);
+    sprintf (str, "%s=%s", name, value);
+    if (putenv (str) != EXIT_SUCCESS)
+      {
+        XFREE (str);
+      }
+#endif
+  }
+}
+
+char *
+lt_extend_str (const char *orig_value, const char *add, int to_end)
+{
+  char *new_value;
+  if (orig_value && *orig_value)
+    {
+      int orig_value_len = strlen (orig_value);
+      int add_len = strlen (add);
+      new_value = XMALLOC (char, add_len + orig_value_len + 1);
+      if (to_end)
+        {
+          strcpy (new_value, orig_value);
+          strcpy (new_value + orig_value_len, add);
+        }
+      else
+        {
+          strcpy (new_value, add);
+          strcpy (new_value + add_len, orig_value);
+        }
+    }
+  else
+    {
+      new_value = xstrdup (add);
+    }
+  return new_value;
+}
+
+void
+lt_update_exe_path (const char *name, const char *value)
+{
+  lt_debugprintf (__FILE__, __LINE__,
+		  "(lt_update_exe_path) modifying '%s' by prepending '%s'\n",
+                  nonnull (name), nonnull (value));
+
+  if (name && *name && value && *value)
+    {
+      char *new_value = lt_extend_str (getenv (name), value, 0);
+      /* some systems can't cope with a ':'-terminated path #' */
+      int len = strlen (new_value);
+      while (((len = strlen (new_value)) > 0) && IS_PATH_SEPARATOR (new_value[len-1]))
+        {
+          new_value[len-1] = '\0';
+        }
+      lt_setenv (name, new_value);
+      XFREE (new_value);
+    }
+}
+
+void
+lt_update_lib_path (const char *name, const char *value)
+{
+  lt_debugprintf (__FILE__, __LINE__,
+		  "(lt_update_lib_path) modifying '%s' by prepending '%s'\n",
+                  nonnull (name), nonnull (value));
+
+  if (name && *name && value && *value)
+    {
+      char *new_value = lt_extend_str (getenv (name), value, 0);
+      lt_setenv (name, new_value);
+      XFREE (new_value);
+    }
+}
+
+EOF
+	    case $host_os in
+	      mingw*)
+		cat <<"EOF"
+
+/* Prepares an argument vector before calling spawn().
+   Note that spawn() does not by itself call the command interpreter
+     (getenv ("COMSPEC") != NULL ? getenv ("COMSPEC") :
+      ({ OSVERSIONINFO v; v.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
+         GetVersionEx(&v);
+         v.dwPlatformId == VER_PLATFORM_WIN32_NT;
+      }) ? "cmd.exe" : "command.com").
+   Instead it simply concatenates the arguments, separated by ' ', and calls
+   CreateProcess().  We must quote the arguments since Win32 CreateProcess()
+   interprets characters like ' ', '\t', '\\', '"' (but not '<' and '>') in a
+   special way:
+   - Space and tab are interpreted as delimiters. They are not treated as
+     delimiters if they are surrounded by double quotes: "...".
+   - Unescaped double quotes are removed from the input. Their only effect is
+     that within double quotes, space and tab are treated like normal
+     characters.
+   - Backslashes not followed by double quotes are not special.
+   - But 2*n+1 backslashes followed by a double quote become
+     n backslashes followed by a double quote (n >= 0):
+       \" -> "
+       \\\" -> \"
+       \\\\\" -> \\"
+ */
+#define SHELL_SPECIAL_CHARS "\"\\ \001\002\003\004\005\006\007\010\011\012\013\014\015\016\017\020\021\022\023\024\025\026\027\030\031\032\033\034\035\036\037"
+#define SHELL_SPACE_CHARS " \001\002\003\004\005\006\007\010\011\012\013\014\015\016\017\020\021\022\023\024\025\026\027\030\031\032\033\034\035\036\037"
+char **
+prepare_spawn (char **argv)
+{
+  size_t argc;
+  char **new_argv;
+  size_t i;
+
+  /* Count number of arguments.  */
+  for (argc = 0; argv[argc] != NULL; argc++)
+    ;
+
+  /* Allocate new argument vector.  */
+  new_argv = XMALLOC (char *, argc + 1);
+
+  /* Put quoted arguments into the new argument vector.  */
+  for (i = 0; i < argc; i++)
+    {
+      const char *string = argv[i];
+
+      if (string[0] == '\0')
+	new_argv[i] = xstrdup ("\"\"");
+      else if (strpbrk (string, SHELL_SPECIAL_CHARS) != NULL)
+	{
+	  int quote_around = (strpbrk (string, SHELL_SPACE_CHARS) != NULL);
+	  size_t length;
+	  unsigned int backslashes;
+	  const char *s;
+	  char *quoted_string;
+	  char *p;
+
+	  length = 0;
+	  backslashes = 0;
+	  if (quote_around)
+	    length++;
+	  for (s = string; *s != '\0'; s++)
+	    {
+	      char c = *s;
+	      if (c == '"')
+		length += backslashes + 1;
+	      length++;
+	      if (c == '\\')
+		backslashes++;
+	      else
+		backslashes = 0;
+	    }
+	  if (quote_around)
+	    length += backslashes + 1;
+
+	  quoted_string = XMALLOC (char, length + 1);
+
+	  p = quoted_string;
+	  backslashes = 0;
+	  if (quote_around)
+	    *p++ = '"';
+	  for (s = string; *s != '\0'; s++)
+	    {
+	      char c = *s;
+	      if (c == '"')
+		{
+		  unsigned int j;
+		  for (j = backslashes + 1; j > 0; j--)
+		    *p++ = '\\';
+		}
+	      *p++ = c;
+	      if (c == '\\')
+		backslashes++;
+	      else
+		backslashes = 0;
+	    }
+	  if (quote_around)
+	    {
+	      unsigned int j;
+	      for (j = backslashes; j > 0; j--)
+		*p++ = '\\';
+	      *p++ = '"';
+	    }
+	  *p = '\0';
+
+	  new_argv[i] = quoted_string;
+	}
+      else
+	new_argv[i] = (char *) string;
+    }
+  new_argv[argc] = NULL;
+
+  return new_argv;
+}
+EOF
+		;;
+	    esac
+
+            cat <<"EOF"
+void lt_dump_script (FILE* f)
+{
+EOF
+	    func_emit_wrapper yes |
+	      $SED -n -e '
+s/^\(.\{79\}\)\(..*\)/\1\
+\2/
+h
+s/\([\\"]\)/\\\1/g
+s/$/\\n/
+s/\([^\n]*\).*/  fputs ("\1", f);/p
+g
+D'
+            cat <<"EOF"
+}
+EOF
+}
+# end: func_emit_cwrapperexe_src
+
+# func_win32_import_lib_p ARG
+# True if ARG is an import lib, as indicated by $file_magic_cmd
+func_win32_import_lib_p ()
+{
+    $opt_debug
+    case `eval $file_magic_cmd \"\$1\" 2>/dev/null | $SED -e 10q` in
+    *import*) : ;;
+    *) false ;;
+    esac
+}
+
+# func_mode_link arg...
+func_mode_link ()
+{
+    $opt_debug
+    case $host in
+    *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-os2* | *-cegcc*)
+      # It is impossible to link a dll without this setting, and
+      # we shouldn't force the makefile maintainer to figure out
+      # which system we are compiling for in order to pass an extra
+      # flag for every libtool invocation.
+      # allow_undefined=no
+
+      # FIXME: Unfortunately, there are problems with the above when trying
+      # to make a dll which has undefined symbols, in which case not
+      # even a static library is built.  For now, we need to specify
+      # -no-undefined on the libtool link line when we can be certain
+      # that all symbols are satisfied, otherwise we get a static library.
+      allow_undefined=yes
+      ;;
+    *)
+      allow_undefined=yes
+      ;;
+    esac
+    libtool_args=$nonopt
+    base_compile="$nonopt $@"
+    compile_command=$nonopt
+    finalize_command=$nonopt
+
+    compile_rpath=
+    finalize_rpath=
+    compile_shlibpath=
+    finalize_shlibpath=
+    convenience=
+    old_convenience=
+    deplibs=
+    old_deplibs=
+    compiler_flags=
+    linker_flags=
+    dllsearchpath=
+    lib_search_path=`pwd`
+    inst_prefix_dir=
+    new_inherited_linker_flags=
+
+    avoid_version=no
+    bindir=
+    dlfiles=
+    dlprefiles=
+    dlself=no
+    export_dynamic=no
+    export_symbols=
+    export_symbols_regex=
+    generated=
+    libobjs=
+    ltlibs=
+    module=no
+    no_install=no
+    objs=
+    non_pic_objects=
+    precious_files_regex=
+    prefer_static_libs=no
+    preload=no
+    prev=
+    prevarg=
+    release=
+    rpath=
+    xrpath=
+    perm_rpath=
+    temp_rpath=
+    thread_safe=no
+    vinfo=
+    vinfo_number=no
+    weak_libs=
+    single_module="${wl}-single_module"
+    func_infer_tag $base_compile
+
+    # We need to know -static, to get the right output filenames.
+    for arg
+    do
+      case $arg in
+      -shared)
+	test "$build_libtool_libs" != yes && \
+	  func_fatal_configuration "can not build a shared library"
+	build_old_libs=no
+	break
+	;;
+      -all-static | -static | -static-libtool-libs)
+	case $arg in
+	-all-static)
+	  if test "$build_libtool_libs" = yes && test -z "$link_static_flag"; then
+	    func_warning "complete static linking is impossible in this configuration"
+	  fi
+	  if test -n "$link_static_flag"; then
+	    dlopen_self=$dlopen_self_static
+	  fi
+	  prefer_static_libs=yes
+	  ;;
+	-static)
+	  if test -z "$pic_flag" && test -n "$link_static_flag"; then
+	    dlopen_self=$dlopen_self_static
+	  fi
+	  prefer_static_libs=built
+	  ;;
+	-static-libtool-libs)
+	  if test -z "$pic_flag" && test -n "$link_static_flag"; then
+	    dlopen_self=$dlopen_self_static
+	  fi
+	  prefer_static_libs=yes
+	  ;;
+	esac
+	build_libtool_libs=no
+	build_old_libs=yes
+	break
+	;;
+      esac
+    done
+
+    # See if our shared archives depend on static archives.
+    test -n "$old_archive_from_new_cmds" && build_old_libs=yes
+
+    # Go through the arguments, transforming them on the way.
+    while test "$#" -gt 0; do
+      arg="$1"
+      shift
+      func_quote_for_eval "$arg"
+      qarg=$func_quote_for_eval_unquoted_result
+      func_append libtool_args " $func_quote_for_eval_result"
+
+      # If the previous option needs an argument, assign it.
+      if test -n "$prev"; then
+	case $prev in
+	output)
+	  func_append compile_command " @OUTPUT@"
+	  func_append finalize_command " @OUTPUT@"
+	  ;;
+	esac
+
+	case $prev in
+	bindir)
+	  bindir="$arg"
+	  prev=
+	  continue
+	  ;;
+	dlfiles|dlprefiles)
+	  if test "$preload" = no; then
+	    # Add the symbol object into the linking commands.
+	    func_append compile_command " @SYMFILE@"
+	    func_append finalize_command " @SYMFILE@"
+	    preload=yes
+	  fi
+	  case $arg in
+	  *.la | *.lo) ;;  # We handle these cases below.
+	  force)
+	    if test "$dlself" = no; then
+	      dlself=needless
+	      export_dynamic=yes
+	    fi
+	    prev=
+	    continue
+	    ;;
+	  self)
+	    if test "$prev" = dlprefiles; then
+	      dlself=yes
+	    elif test "$prev" = dlfiles && test "$dlopen_self" != yes; then
+	      dlself=yes
+	    else
+	      dlself=needless
+	      export_dynamic=yes
+	    fi
+	    prev=
+	    continue
+	    ;;
+	  *)
+	    if test "$prev" = dlfiles; then
+	      func_append dlfiles " $arg"
+	    else
+	      func_append dlprefiles " $arg"
+	    fi
+	    prev=
+	    continue
+	    ;;
+	  esac
+	  ;;
+	expsyms)
+	  export_symbols="$arg"
+	  test -f "$arg" \
+	    || func_fatal_error "symbol file \`$arg' does not exist"
+	  prev=
+	  continue
+	  ;;
+	expsyms_regex)
+	  export_symbols_regex="$arg"
+	  prev=
+	  continue
+	  ;;
+	framework)
+	  case $host in
+	    *-*-darwin*)
+	      case "$deplibs " in
+		*" $qarg.ltframework "*) ;;
+		*) func_append deplibs " $qarg.ltframework" # this is fixed later
+		   ;;
+	      esac
+	      ;;
+	  esac
+	  prev=
+	  continue
+	  ;;
+	inst_prefix)
+	  inst_prefix_dir="$arg"
+	  prev=
+	  continue
+	  ;;
+	objectlist)
+	  if test -f "$arg"; then
+	    save_arg=$arg
+	    moreargs=
+	    for fil in `cat "$save_arg"`
+	    do
+#	      func_append moreargs " $fil"
+	      arg=$fil
+	      # A libtool-controlled object.
+
+	      # Check to see that this really is a libtool object.
+	      if func_lalib_unsafe_p "$arg"; then
+		pic_object=
+		non_pic_object=
+
+		# Read the .lo file
+		func_source "$arg"
+
+		if test -z "$pic_object" ||
+		   test -z "$non_pic_object" ||
+		   test "$pic_object" = none &&
+		   test "$non_pic_object" = none; then
+		  func_fatal_error "cannot find name of object for \`$arg'"
+		fi
+
+		# Extract subdirectory from the argument.
+		func_dirname "$arg" "/" ""
+		xdir="$func_dirname_result"
+
+		if test "$pic_object" != none; then
+		  # Prepend the subdirectory the object is found in.
+		  pic_object="$xdir$pic_object"
+
+		  if test "$prev" = dlfiles; then
+		    if test "$build_libtool_libs" = yes && test "$dlopen_support" = yes; then
+		      func_append dlfiles " $pic_object"
+		      prev=
+		      continue
+		    else
+		      # If libtool objects are unsupported, then we need to preload.
+		      prev=dlprefiles
+		    fi
+		  fi
+
+		  # CHECK ME:  I think I busted this.  -Ossama
+		  if test "$prev" = dlprefiles; then
+		    # Preload the old-style object.
+		    func_append dlprefiles " $pic_object"
+		    prev=
+		  fi
+
+		  # A PIC object.
+		  func_append libobjs " $pic_object"
+		  arg="$pic_object"
+		fi
+
+		# Non-PIC object.
+		if test "$non_pic_object" != none; then
+		  # Prepend the subdirectory the object is found in.
+		  non_pic_object="$xdir$non_pic_object"
+
+		  # A standard non-PIC object
+		  func_append non_pic_objects " $non_pic_object"
+		  if test -z "$pic_object" || test "$pic_object" = none ; then
+		    arg="$non_pic_object"
+		  fi
+		else
+		  # If the PIC object exists, use it instead.
+		  # $xdir was prepended to $pic_object above.
+		  non_pic_object="$pic_object"
+		  func_append non_pic_objects " $non_pic_object"
+		fi
+	      else
+		# Only an error if not doing a dry-run.
+		if $opt_dry_run; then
+		  # Extract subdirectory from the argument.
+		  func_dirname "$arg" "/" ""
+		  xdir="$func_dirname_result"
+
+		  func_lo2o "$arg"
+		  pic_object=$xdir$objdir/$func_lo2o_result
+		  non_pic_object=$xdir$func_lo2o_result
+		  func_append libobjs " $pic_object"
+		  func_append non_pic_objects " $non_pic_object"
+	        else
+		  func_fatal_error "\`$arg' is not a valid libtool object"
+		fi
+	      fi
+	    done
+	  else
+	    func_fatal_error "link input file \`$arg' does not exist"
+	  fi
+	  arg=$save_arg
+	  prev=
+	  continue
+	  ;;
+	precious_regex)
+	  precious_files_regex="$arg"
+	  prev=
+	  continue
+	  ;;
+	release)
+	  release="-$arg"
+	  prev=
+	  continue
+	  ;;
+	rpath | xrpath)
+	  # We need an absolute path.
+	  case $arg in
+	  [\\/]* | [A-Za-z]:[\\/]*) ;;
+	  *)
+	    func_fatal_error "only absolute run-paths are allowed"
+	    ;;
+	  esac
+	  if test "$prev" = rpath; then
+	    case "$rpath " in
+	    *" $arg "*) ;;
+	    *) func_append rpath " $arg" ;;
+	    esac
+	  else
+	    case "$xrpath " in
+	    *" $arg "*) ;;
+	    *) func_append xrpath " $arg" ;;
+	    esac
+	  fi
+	  prev=
+	  continue
+	  ;;
+	shrext)
+	  shrext_cmds="$arg"
+	  prev=
+	  continue
+	  ;;
+	weak)
+	  func_append weak_libs " $arg"
+	  prev=
+	  continue
+	  ;;
+	xcclinker)
+	  func_append linker_flags " $qarg"
+	  func_append compiler_flags " $qarg"
+	  prev=
+	  func_append compile_command " $qarg"
+	  func_append finalize_command " $qarg"
+	  continue
+	  ;;
+	xcompiler)
+	  func_append compiler_flags " $qarg"
+	  prev=
+	  func_append compile_command " $qarg"
+	  func_append finalize_command " $qarg"
+	  continue
+	  ;;
+	xlinker)
+	  func_append linker_flags " $qarg"
+	  func_append compiler_flags " $wl$qarg"
+	  prev=
+	  func_append compile_command " $wl$qarg"
+	  func_append finalize_command " $wl$qarg"
+	  continue
+	  ;;
+	*)
+	  eval "$prev=\"\$arg\""
+	  prev=
+	  continue
+	  ;;
+	esac
+      fi # test -n "$prev"
+
+      prevarg="$arg"
+
+      case $arg in
+      -all-static)
+	if test -n "$link_static_flag"; then
+	  # See comment for -static flag below, for more details.
+	  func_append compile_command " $link_static_flag"
+	  func_append finalize_command " $link_static_flag"
+	fi
+	continue
+	;;
+
+      -allow-undefined)
+	# FIXME: remove this flag sometime in the future.
+	func_fatal_error "\`-allow-undefined' must not be used because it is the default"
+	;;
+
+      -avoid-version)
+	avoid_version=yes
+	continue
+	;;
+
+      -bindir)
+	prev=bindir
+	continue
+	;;
+
+      -dlopen)
+	prev=dlfiles
+	continue
+	;;
+
+      -dlpreopen)
+	prev=dlprefiles
+	continue
+	;;
+
+      -export-dynamic)
+	export_dynamic=yes
+	continue
+	;;
+
+      -export-symbols | -export-symbols-regex)
+	if test -n "$export_symbols" || test -n "$export_symbols_regex"; then
+	  func_fatal_error "more than one -exported-symbols argument is not allowed"
+	fi
+	if test "X$arg" = "X-export-symbols"; then
+	  prev=expsyms
+	else
+	  prev=expsyms_regex
+	fi
+	continue
+	;;
+
+      -framework)
+	prev=framework
+	continue
+	;;
+
+      -inst-prefix-dir)
+	prev=inst_prefix
+	continue
+	;;
+
+      # The native IRIX linker understands -LANG:*, -LIST:* and -LNO:*
+      # so, if we see these flags be careful not to treat them like -L
+      -L[A-Z][A-Z]*:*)
+	case $with_gcc/$host in
+	no/*-*-irix* | /*-*-irix*)
+	  func_append compile_command " $arg"
+	  func_append finalize_command " $arg"
+	  ;;
+	esac
+	continue
+	;;
+
+      -L*)
+	func_stripname "-L" '' "$arg"
+	if test -z "$func_stripname_result"; then
+	  if test "$#" -gt 0; then
+	    func_fatal_error "require no space between \`-L' and \`$1'"
+	  else
+	    func_fatal_error "need path for \`-L' option"
+	  fi
+	fi
+	func_resolve_sysroot "$func_stripname_result"
+	dir=$func_resolve_sysroot_result
+	# We need an absolute path.
+	case $dir in
+	[\\/]* | [A-Za-z]:[\\/]*) ;;
+	*)
+	  absdir=`cd "$dir" && pwd`
+	  test -z "$absdir" && \
+	    func_fatal_error "cannot determine absolute directory name of \`$dir'"
+	  dir="$absdir"
+	  ;;
+	esac
+	case "$deplibs " in
+	*" -L$dir "* | *" $arg "*)
+	  # Will only happen for absolute or sysroot arguments
+	  ;;
+	*)
+	  # Preserve sysroot, but never include relative directories
+	  case $dir in
+	    [\\/]* | [A-Za-z]:[\\/]* | =*) func_append deplibs " $arg" ;;
+	    *) func_append deplibs " -L$dir" ;;
+	  esac
+	  func_append lib_search_path " $dir"
+	  ;;
+	esac
+	case $host in
+	*-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-os2* | *-cegcc*)
+	  testbindir=`$ECHO "$dir" | $SED 's*/lib$*/bin*'`
+	  case :$dllsearchpath: in
+	  *":$dir:"*) ;;
+	  ::) dllsearchpath=$dir;;
+	  *) func_append dllsearchpath ":$dir";;
+	  esac
+	  case :$dllsearchpath: in
+	  *":$testbindir:"*) ;;
+	  ::) dllsearchpath=$testbindir;;
+	  *) func_append dllsearchpath ":$testbindir";;
+	  esac
+	  ;;
+	esac
+	continue
+	;;
+
+      -l*)
+	if test "X$arg" = "X-lc" || test "X$arg" = "X-lm"; then
+	  case $host in
+	  *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-beos* | *-cegcc* | *-*-haiku*)
+	    # These systems don't actually have a C or math library (as such)
+	    continue
+	    ;;
+	  *-*-os2*)
+	    # These systems don't actually have a C library (as such)
+	    test "X$arg" = "X-lc" && continue
+	    ;;
+	  *-*-openbsd* | *-*-freebsd* | *-*-dragonfly*)
+	    # Do not include libc due to us having libc/libc_r.
+	    test "X$arg" = "X-lc" && continue
+	    ;;
+	  *-*-rhapsody* | *-*-darwin1.[012])
+	    # Rhapsody C and math libraries are in the System framework
+	    func_append deplibs " System.ltframework"
+	    continue
+	    ;;
+	  *-*-sco3.2v5* | *-*-sco5v6*)
+	    # Causes problems with __ctype
+	    test "X$arg" = "X-lc" && continue
+	    ;;
+	  *-*-sysv4.2uw2* | *-*-sysv5* | *-*-unixware* | *-*-OpenUNIX*)
+	    # Compiler inserts libc in the correct place for threads to work
+	    test "X$arg" = "X-lc" && continue
+	    ;;
+	  esac
+	elif test "X$arg" = "X-lc_r"; then
+	 case $host in
+	 *-*-openbsd* | *-*-freebsd* | *-*-dragonfly*)
+	   # Do not include libc_r directly, use -pthread flag.
+	   continue
+	   ;;
+	 esac
+	fi
+	func_append deplibs " $arg"
+	continue
+	;;
+
+      -module)
+	module=yes
+	continue
+	;;
+
+      # Tru64 UNIX uses -model [arg] to determine the layout of C++
+      # classes, name mangling, and exception handling.
+      # Darwin uses the -arch flag to determine output architecture.
+      -model|-arch|-isysroot|--sysroot)
+	func_append compiler_flags " $arg"
+	func_append compile_command " $arg"
+	func_append finalize_command " $arg"
+	prev=xcompiler
+	continue
+	;;
+
+      -mt|-mthreads|-kthread|-Kthread|-pthread|-pthreads|--thread-safe \
+      |-threads|-fopenmp|-openmp|-mp|-xopenmp|-omp|-qsmp=*)
+	func_append compiler_flags " $arg"
+	func_append compile_command " $arg"
+	func_append finalize_command " $arg"
+	case "$new_inherited_linker_flags " in
+	    *" $arg "*) ;;
+	    * ) func_append new_inherited_linker_flags " $arg" ;;
+	esac
+	continue
+	;;
+
+      -multi_module)
+	single_module="${wl}-multi_module"
+	continue
+	;;
+
+      -no-fast-install)
+	fast_install=no
+	continue
+	;;
+
+      -no-install)
+	case $host in
+	*-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-os2* | *-*-darwin* | *-cegcc*)
+	  # The PATH hackery in wrapper scripts is required on Windows
+	  # and Darwin in order for the loader to find any dlls it needs.
+	  func_warning "\`-no-install' is ignored for $host"
+	  func_warning "assuming \`-no-fast-install' instead"
+	  fast_install=no
+	  ;;
+	*) no_install=yes ;;
+	esac
+	continue
+	;;
+
+      -no-undefined)
+	allow_undefined=no
+	continue
+	;;
+
+      -objectlist)
+	prev=objectlist
+	continue
+	;;
+
+      -o) prev=output ;;
+
+      -precious-files-regex)
+	prev=precious_regex
+	continue
+	;;
+
+      -release)
+	prev=release
+	continue
+	;;
+
+      -rpath)
+	prev=rpath
+	continue
+	;;
+
+      -R)
+	prev=xrpath
+	continue
+	;;
+
+      -R*)
+	func_stripname '-R' '' "$arg"
+	dir=$func_stripname_result
+	# We need an absolute path.
+	case $dir in
+	[\\/]* | [A-Za-z]:[\\/]*) ;;
+	=*)
+	  func_stripname '=' '' "$dir"
+	  dir=$lt_sysroot$func_stripname_result
+	  ;;
+	*)
+	  func_fatal_error "only absolute run-paths are allowed"
+	  ;;
+	esac
+	case "$xrpath " in
+	*" $dir "*) ;;
+	*) func_append xrpath " $dir" ;;
+	esac
+	continue
+	;;
+
+      -shared)
+	# The effects of -shared are defined in a previous loop.
+	continue
+	;;
+
+      -shrext)
+	prev=shrext
+	continue
+	;;
+
+      -static | -static-libtool-libs)
+	# The effects of -static are defined in a previous loop.
+	# We used to do the same as -all-static on platforms that
+	# didn't have a PIC flag, but the assumption that the effects
+	# would be equivalent was wrong.  It would break on at least
+	# Digital Unix and AIX.
+	continue
+	;;
+
+      -thread-safe)
+	thread_safe=yes
+	continue
+	;;
+
+      -version-info)
+	prev=vinfo
+	continue
+	;;
+
+      -version-number)
+	prev=vinfo
+	vinfo_number=yes
+	continue
+	;;
+
+      -weak)
+        prev=weak
+	continue
+	;;
+
+      -Wc,*)
+	func_stripname '-Wc,' '' "$arg"
+	args=$func_stripname_result
+	arg=
+	save_ifs="$IFS"; IFS=','
+	for flag in $args; do
+	  IFS="$save_ifs"
+          func_quote_for_eval "$flag"
+	  func_append arg " $func_quote_for_eval_result"
+	  func_append compiler_flags " $func_quote_for_eval_result"
+	done
+	IFS="$save_ifs"
+	func_stripname ' ' '' "$arg"
+	arg=$func_stripname_result
+	;;
+
+      -Wl,*)
+	func_stripname '-Wl,' '' "$arg"
+	args=$func_stripname_result
+	arg=
+	save_ifs="$IFS"; IFS=','
+	for flag in $args; do
+	  IFS="$save_ifs"
+          func_quote_for_eval "$flag"
+	  func_append arg " $wl$func_quote_for_eval_result"
+	  func_append compiler_flags " $wl$func_quote_for_eval_result"
+	  func_append linker_flags " $func_quote_for_eval_result"
+	done
+	IFS="$save_ifs"
+	func_stripname ' ' '' "$arg"
+	arg=$func_stripname_result
+	;;
+
+      -Xcompiler)
+	prev=xcompiler
+	continue
+	;;
+
+      -Xlinker)
+	prev=xlinker
+	continue
+	;;
+
+      -XCClinker)
+	prev=xcclinker
+	continue
+	;;
+
+      # -msg_* for osf cc
+      -msg_*)
+	func_quote_for_eval "$arg"
+	arg="$func_quote_for_eval_result"
+	;;
+
+      # Flags to be passed through unchanged, with rationale:
+      # -64, -mips[0-9]      enable 64-bit mode for the SGI compiler
+      # -r[0-9][0-9]*        specify processor for the SGI compiler
+      # -xarch=*, -xtarget=* enable 64-bit mode for the Sun compiler
+      # +DA*, +DD*           enable 64-bit mode for the HP compiler
+      # -q*                  compiler args for the IBM compiler
+      # -m*, -t[45]*, -txscale* architecture-specific flags for GCC
+      # -F/path              path to uninstalled frameworks, gcc on darwin
+      # -p, -pg, --coverage, -fprofile-*  profiling flags for GCC
+      # @file                GCC response files
+      # -tp=*                Portland pgcc target processor selection
+      # --sysroot=*          for sysroot support
+      # -O*, -flto*, -fwhopr*, -fuse-linker-plugin GCC link-time optimization
+      -64|-mips[0-9]|-r[0-9][0-9]*|-xarch=*|-xtarget=*|+DA*|+DD*|-q*|-m*| \
+      -t[45]*|-txscale*|-p|-pg|--coverage|-fprofile-*|-F*|@*|-tp=*|--sysroot=*| \
+      -O*|-flto*|-fwhopr*|-fuse-linker-plugin)
+        func_quote_for_eval "$arg"
+	arg="$func_quote_for_eval_result"
+        func_append compile_command " $arg"
+        func_append finalize_command " $arg"
+        func_append compiler_flags " $arg"
+        continue
+        ;;
+
+      # Some other compiler flag.
+      -* | +*)
+        func_quote_for_eval "$arg"
+	arg="$func_quote_for_eval_result"
+	;;
+
+      *.$objext)
+	# A standard object.
+	func_append objs " $arg"
+	;;
+
+      *.lo)
+	# A libtool-controlled object.
+
+	# Check to see that this really is a libtool object.
+	if func_lalib_unsafe_p "$arg"; then
+	  pic_object=
+	  non_pic_object=
+
+	  # Read the .lo file
+	  func_source "$arg"
+
+	  if test -z "$pic_object" ||
+	     test -z "$non_pic_object" ||
+	     test "$pic_object" = none &&
+	     test "$non_pic_object" = none; then
+	    func_fatal_error "cannot find name of object for \`$arg'"
+	  fi
+
+	  # Extract subdirectory from the argument.
+	  func_dirname "$arg" "/" ""
+	  xdir="$func_dirname_result"
+
+	  if test "$pic_object" != none; then
+	    # Prepend the subdirectory the object is found in.
+	    pic_object="$xdir$pic_object"
+
+	    if test "$prev" = dlfiles; then
+	      if test "$build_libtool_libs" = yes && test "$dlopen_support" = yes; then
+		func_append dlfiles " $pic_object"
+		prev=
+		continue
+	      else
+		# If libtool objects are unsupported, then we need to preload.
+		prev=dlprefiles
+	      fi
+	    fi
+
+	    # CHECK ME:  I think I busted this.  -Ossama
+	    if test "$prev" = dlprefiles; then
+	      # Preload the old-style object.
+	      func_append dlprefiles " $pic_object"
+	      prev=
+	    fi
+
+	    # A PIC object.
+	    func_append libobjs " $pic_object"
+	    arg="$pic_object"
+	  fi
+
+	  # Non-PIC object.
+	  if test "$non_pic_object" != none; then
+	    # Prepend the subdirectory the object is found in.
+	    non_pic_object="$xdir$non_pic_object"
+
+	    # A standard non-PIC object
+	    func_append non_pic_objects " $non_pic_object"
+	    if test -z "$pic_object" || test "$pic_object" = none ; then
+	      arg="$non_pic_object"
+	    fi
+	  else
+	    # If the PIC object exists, use it instead.
+	    # $xdir was prepended to $pic_object above.
+	    non_pic_object="$pic_object"
+	    func_append non_pic_objects " $non_pic_object"
+	  fi
+	else
+	  # Only an error if not doing a dry-run.
+	  if $opt_dry_run; then
+	    # Extract subdirectory from the argument.
+	    func_dirname "$arg" "/" ""
+	    xdir="$func_dirname_result"
+
+	    func_lo2o "$arg"
+	    pic_object=$xdir$objdir/$func_lo2o_result
+	    non_pic_object=$xdir$func_lo2o_result
+	    func_append libobjs " $pic_object"
+	    func_append non_pic_objects " $non_pic_object"
+	  else
+	    func_fatal_error "\`$arg' is not a valid libtool object"
+	  fi
+	fi
+	;;
+
+      *.$libext)
+	# An archive.
+	func_append deplibs " $arg"
+	func_append old_deplibs " $arg"
+	continue
+	;;
+
+      *.la)
+	# A libtool-controlled library.
+
+	func_resolve_sysroot "$arg"
+	if test "$prev" = dlfiles; then
+	  # This library was specified with -dlopen.
+	  func_append dlfiles " $func_resolve_sysroot_result"
+	  prev=
+	elif test "$prev" = dlprefiles; then
+	  # The library was specified with -dlpreopen.
+	  func_append dlprefiles " $func_resolve_sysroot_result"
+	  prev=
+	else
+	  func_append deplibs " $func_resolve_sysroot_result"
+	fi
+	continue
+	;;
+
+      # Some other compiler argument.
+      *)
+	# Unknown arguments in both finalize_command and compile_command need
+	# to be aesthetically quoted because they are evaled later.
+	func_quote_for_eval "$arg"
+	arg="$func_quote_for_eval_result"
+	;;
+      esac # arg
+
+      # Now actually substitute the argument into the commands.
+      if test -n "$arg"; then
+	func_append compile_command " $arg"
+	func_append finalize_command " $arg"
+      fi
+    done # argument parsing loop
+
+    test -n "$prev" && \
+      func_fatal_help "the \`$prevarg' option requires an argument"
+
+    if test "$export_dynamic" = yes && test -n "$export_dynamic_flag_spec"; then
+      eval arg=\"$export_dynamic_flag_spec\"
+      func_append compile_command " $arg"
+      func_append finalize_command " $arg"
+    fi
+
+    oldlibs=
+    # calculate the name of the file, without its directory
+    func_basename "$output"
+    outputname="$func_basename_result"
+    libobjs_save="$libobjs"
+
+    if test -n "$shlibpath_var"; then
+      # get the directories listed in $shlibpath_var
+      eval shlib_search_path=\`\$ECHO \"\${$shlibpath_var}\" \| \$SED \'s/:/ /g\'\`
+    else
+      shlib_search_path=
+    fi
+    eval sys_lib_search_path=\"$sys_lib_search_path_spec\"
+    eval sys_lib_dlsearch_path=\"$sys_lib_dlsearch_path_spec\"
+
+    func_dirname "$output" "/" ""
+    output_objdir="$func_dirname_result$objdir"
+    func_to_tool_file "$output_objdir/"
+    tool_output_objdir=$func_to_tool_file_result
+    # Create the object directory.
+    func_mkdir_p "$output_objdir"
+
+    # Determine the type of output
+    case $output in
+    "")
+      func_fatal_help "you must specify an output file"
+      ;;
+    *.$libext) linkmode=oldlib ;;
+    *.lo | *.$objext) linkmode=obj ;;
+    *.la) linkmode=lib ;;
+    *) linkmode=prog ;; # Anything else should be a program.
+    esac
+
+    specialdeplibs=
+
+    libs=
+    # Find all interdependent deplibs by searching for libraries
+    # that are linked more than once (e.g. -la -lb -la)
+    for deplib in $deplibs; do
+      if $opt_preserve_dup_deps ; then
+	case "$libs " in
+	*" $deplib "*) func_append specialdeplibs " $deplib" ;;
+	esac
+      fi
+      func_append libs " $deplib"
+    done
+
+    if test "$linkmode" = lib; then
+      libs="$predeps $libs $compiler_lib_search_path $postdeps"
+
+      # Compute libraries that are listed more than once in $predeps
+      # $postdeps and mark them as special (i.e., whose duplicates are
+      # not to be eliminated).
+      pre_post_deps=
+      if $opt_duplicate_compiler_generated_deps; then
+	for pre_post_dep in $predeps $postdeps; do
+	  case "$pre_post_deps " in
+	  *" $pre_post_dep "*) func_append specialdeplibs " $pre_post_deps" ;;
+	  esac
+	  func_append pre_post_deps " $pre_post_dep"
+	done
+      fi
+      pre_post_deps=
+    fi
+
+    deplibs=
+    newdependency_libs=
+    newlib_search_path=
+    need_relink=no # whether we're linking any uninstalled libtool libraries
+    notinst_deplibs= # not-installed libtool libraries
+    notinst_path= # paths that contain not-installed libtool libraries
+
+    case $linkmode in
+    lib)
+	passes="conv dlpreopen link"
+	for file in $dlfiles $dlprefiles; do
+	  case $file in
+	  *.la) ;;
+	  *)
+	    func_fatal_help "libraries can \`-dlopen' only libtool libraries: $file"
+	    ;;
+	  esac
+	done
+	;;
+    prog)
+	compile_deplibs=
+	finalize_deplibs=
+	alldeplibs=no
+	newdlfiles=
+	newdlprefiles=
+	passes="conv scan dlopen dlpreopen link"
+	;;
+    *)  passes="conv"
+	;;
+    esac
+
+    for pass in $passes; do
+      # The preopen pass in lib mode reverses $deplibs; put it back here
+      # so that -L comes before libs that need it for instance...
+      if test "$linkmode,$pass" = "lib,link"; then
+	## FIXME: Find the place where the list is rebuilt in the wrong
+	##        order, and fix it there properly
+        tmp_deplibs=
+	for deplib in $deplibs; do
+	  tmp_deplibs="$deplib $tmp_deplibs"
+	done
+	deplibs="$tmp_deplibs"
+      fi
+
+      if test "$linkmode,$pass" = "lib,link" ||
+	 test "$linkmode,$pass" = "prog,scan"; then
+	libs="$deplibs"
+	deplibs=
+      fi
+      if test "$linkmode" = prog; then
+	case $pass in
+	dlopen) libs="$dlfiles" ;;
+	dlpreopen) libs="$dlprefiles" ;;
+	link)
+	  libs="$deplibs %DEPLIBS%"
+	  test "X$link_all_deplibs" != Xno && libs="$libs $dependency_libs"
+	  ;;
+	esac
+      fi
+      if test "$linkmode,$pass" = "lib,dlpreopen"; then
+	# Collect and forward deplibs of preopened libtool libs
+	for lib in $dlprefiles; do
+	  # Ignore non-libtool-libs
+	  dependency_libs=
+	  func_resolve_sysroot "$lib"
+	  case $lib in
+	  *.la)	func_source "$func_resolve_sysroot_result" ;;
+	  esac
+
+	  # Collect preopened libtool deplibs, except any this library
+	  # has declared as weak libs
+	  for deplib in $dependency_libs; do
+	    func_basename "$deplib"
+            deplib_base=$func_basename_result
+	    case " $weak_libs " in
+	    *" $deplib_base "*) ;;
+	    *) func_append deplibs " $deplib" ;;
+	    esac
+	  done
+	done
+	libs="$dlprefiles"
+      fi
+      if test "$pass" = dlopen; then
+	# Collect dlpreopened libraries
+	save_deplibs="$deplibs"
+	deplibs=
+      fi
+
+      for deplib in $libs; do
+	lib=
+	found=no
+	case $deplib in
+	-mt|-mthreads|-kthread|-Kthread|-pthread|-pthreads|--thread-safe \
+        |-threads|-fopenmp|-openmp|-mp|-xopenmp|-omp|-qsmp=*)
+	  if test "$linkmode,$pass" = "prog,link"; then
+	    compile_deplibs="$deplib $compile_deplibs"
+	    finalize_deplibs="$deplib $finalize_deplibs"
+	  else
+	    func_append compiler_flags " $deplib"
+	    if test "$linkmode" = lib ; then
+		case "$new_inherited_linker_flags " in
+		    *" $deplib "*) ;;
+		    * ) func_append new_inherited_linker_flags " $deplib" ;;
+		esac
+	    fi
+	  fi
+	  continue
+	  ;;
+	-l*)
+	  if test "$linkmode" != lib && test "$linkmode" != prog; then
+	    func_warning "\`-l' is ignored for archives/objects"
+	    continue
+	  fi
+	  func_stripname '-l' '' "$deplib"
+	  name=$func_stripname_result
+	  if test "$linkmode" = lib; then
+	    searchdirs="$newlib_search_path $lib_search_path $compiler_lib_search_dirs $sys_lib_search_path $shlib_search_path"
+	  else
+	    searchdirs="$newlib_search_path $lib_search_path $sys_lib_search_path $shlib_search_path"
+	  fi
+	  for searchdir in $searchdirs; do
+	    for search_ext in .la $std_shrext .so .a; do
+	      # Search the libtool library
+	      lib="$searchdir/lib${name}${search_ext}"
+	      if test -f "$lib"; then
+		if test "$search_ext" = ".la"; then
+		  found=yes
+		else
+		  found=no
+		fi
+		break 2
+	      fi
+	    done
+	  done
+	  if test "$found" != yes; then
+	    # deplib doesn't seem to be a libtool library
+	    if test "$linkmode,$pass" = "prog,link"; then
+	      compile_deplibs="$deplib $compile_deplibs"
+	      finalize_deplibs="$deplib $finalize_deplibs"
+	    else
+	      deplibs="$deplib $deplibs"
+	      test "$linkmode" = lib && newdependency_libs="$deplib $newdependency_libs"
+	    fi
+	    continue
+	  else # deplib is a libtool library
+	    # If $allow_libtool_libs_with_static_runtimes && $deplib is a stdlib,
+	    # We need to do some special things here, and not later.
+	    if test "X$allow_libtool_libs_with_static_runtimes" = "Xyes" ; then
+	      case " $predeps $postdeps " in
+	      *" $deplib "*)
+		if func_lalib_p "$lib"; then
+		  library_names=
+		  old_library=
+		  func_source "$lib"
+		  for l in $old_library $library_names; do
+		    ll="$l"
+		  done
+		  if test "X$ll" = "X$old_library" ; then # only static version available
+		    found=no
+		    func_dirname "$lib" "" "."
+		    ladir="$func_dirname_result"
+		    lib=$ladir/$old_library
+		    if test "$linkmode,$pass" = "prog,link"; then
+		      compile_deplibs="$deplib $compile_deplibs"
+		      finalize_deplibs="$deplib $finalize_deplibs"
+		    else
+		      deplibs="$deplib $deplibs"
+		      test "$linkmode" = lib && newdependency_libs="$deplib $newdependency_libs"
+		    fi
+		    continue
+		  fi
+		fi
+		;;
+	      *) ;;
+	      esac
+	    fi
+	  fi
+	  ;; # -l
+	*.ltframework)
+	  if test "$linkmode,$pass" = "prog,link"; then
+	    compile_deplibs="$deplib $compile_deplibs"
+	    finalize_deplibs="$deplib $finalize_deplibs"
+	  else
+	    deplibs="$deplib $deplibs"
+	    if test "$linkmode" = lib ; then
+		case "$new_inherited_linker_flags " in
+		    *" $deplib "*) ;;
+		    * ) func_append new_inherited_linker_flags " $deplib" ;;
+		esac
+	    fi
+	  fi
+	  continue
+	  ;;
+	-L*)
+	  case $linkmode in
+	  lib)
+	    deplibs="$deplib $deplibs"
+	    test "$pass" = conv && continue
+	    newdependency_libs="$deplib $newdependency_libs"
+	    func_stripname '-L' '' "$deplib"
+	    func_resolve_sysroot "$func_stripname_result"
+	    func_append newlib_search_path " $func_resolve_sysroot_result"
+	    ;;
+	  prog)
+	    if test "$pass" = conv; then
+	      deplibs="$deplib $deplibs"
+	      continue
+	    fi
+	    if test "$pass" = scan; then
+	      deplibs="$deplib $deplibs"
+	    else
+	      compile_deplibs="$deplib $compile_deplibs"
+	      finalize_deplibs="$deplib $finalize_deplibs"
+	    fi
+	    func_stripname '-L' '' "$deplib"
+	    func_resolve_sysroot "$func_stripname_result"
+	    func_append newlib_search_path " $func_resolve_sysroot_result"
+	    ;;
+	  *)
+	    func_warning "\`-L' is ignored for archives/objects"
+	    ;;
+	  esac # linkmode
+	  continue
+	  ;; # -L
+	-R*)
+	  if test "$pass" = link; then
+	    func_stripname '-R' '' "$deplib"
+	    func_resolve_sysroot "$func_stripname_result"
+	    dir=$func_resolve_sysroot_result
+	    # Make sure the xrpath contains only unique directories.
+	    case "$xrpath " in
+	    *" $dir "*) ;;
+	    *) func_append xrpath " $dir" ;;
+	    esac
+	  fi
+	  deplibs="$deplib $deplibs"
+	  continue
+	  ;;
+	*.la)
+	  func_resolve_sysroot "$deplib"
+	  lib=$func_resolve_sysroot_result
+	  ;;
+	*.$libext)
+	  if test "$pass" = conv; then
+	    deplibs="$deplib $deplibs"
+	    continue
+	  fi
+	  case $linkmode in
+	  lib)
+	    # Linking convenience modules into shared libraries is allowed,
+	    # but linking other static libraries is non-portable.
+	    case " $dlpreconveniencelibs " in
+	    *" $deplib "*) ;;
+	    *)
+	      valid_a_lib=no
+	      case $deplibs_check_method in
+		match_pattern*)
+		  set dummy $deplibs_check_method; shift
+		  match_pattern_regex=`expr "$deplibs_check_method" : "$1 \(.*\)"`
+		  if eval "\$ECHO \"$deplib\"" 2>/dev/null | $SED 10q \
+		    | $EGREP "$match_pattern_regex" > /dev/null; then
+		    valid_a_lib=yes
+		  fi
+		;;
+		pass_all)
+		  valid_a_lib=yes
+		;;
+	      esac
+	      if test "$valid_a_lib" != yes; then
+		echo
+		$ECHO "*** Warning: Trying to link with static lib archive $deplib."
+		echo "*** I have the capability to make that library automatically link in when"
+		echo "*** you link to this library.  But I can only do this if you have a"
+		echo "*** shared version of the library, which you do not appear to have"
+		echo "*** because the file extensions .$libext of this argument makes me believe"
+		echo "*** that it is just a static archive that I should not use here."
+	      else
+		echo
+		$ECHO "*** Warning: Linking the shared library $output against the"
+		$ECHO "*** static library $deplib is not portable!"
+		deplibs="$deplib $deplibs"
+	      fi
+	      ;;
+	    esac
+	    continue
+	    ;;
+	  prog)
+	    if test "$pass" != link; then
+	      deplibs="$deplib $deplibs"
+	    else
+	      compile_deplibs="$deplib $compile_deplibs"
+	      finalize_deplibs="$deplib $finalize_deplibs"
+	    fi
+	    continue
+	    ;;
+	  esac # linkmode
+	  ;; # *.$libext
+	*.lo | *.$objext)
+	  if test "$pass" = conv; then
+	    deplibs="$deplib $deplibs"
+	  elif test "$linkmode" = prog; then
+	    if test "$pass" = dlpreopen || test "$dlopen_support" != yes || test "$build_libtool_libs" = no; then
+	      # If there is no dlopen support or we're linking statically,
+	      # we need to preload.
+	      func_append newdlprefiles " $deplib"
+	      compile_deplibs="$deplib $compile_deplibs"
+	      finalize_deplibs="$deplib $finalize_deplibs"
+	    else
+	      func_append newdlfiles " $deplib"
+	    fi
+	  fi
+	  continue
+	  ;;
+	%DEPLIBS%)
+	  alldeplibs=yes
+	  continue
+	  ;;
+	esac # case $deplib
+
+	if test "$found" = yes || test -f "$lib"; then :
+	else
+	  func_fatal_error "cannot find the library \`$lib' or unhandled argument \`$deplib'"
+	fi
+
+	# Check to see that this really is a libtool archive.
+	func_lalib_unsafe_p "$lib" \
+	  || func_fatal_error "\`$lib' is not a valid libtool archive"
+
+	func_dirname "$lib" "" "."
+	ladir="$func_dirname_result"
+
+	dlname=
+	dlopen=
+	dlpreopen=
+	libdir=
+	library_names=
+	old_library=
+	inherited_linker_flags=
+	# If the library was installed with an old release of libtool,
+	# it will not redefine variables installed, or shouldnotlink
+	installed=yes
+	shouldnotlink=no
+	avoidtemprpath=
+
+
+	# Read the .la file
+	func_source "$lib"
+
+	# Convert "-framework foo" to "foo.ltframework"
+	if test -n "$inherited_linker_flags"; then
+	  tmp_inherited_linker_flags=`$ECHO "$inherited_linker_flags" | $SED 's/-framework \([^ $]*\)/\1.ltframework/g'`
+	  for tmp_inherited_linker_flag in $tmp_inherited_linker_flags; do
+	    case " $new_inherited_linker_flags " in
+	      *" $tmp_inherited_linker_flag "*) ;;
+	      *) func_append new_inherited_linker_flags " $tmp_inherited_linker_flag";;
+	    esac
+	  done
+	fi
+	dependency_libs=`$ECHO " $dependency_libs" | $SED 's% \([^ $]*\).ltframework% -framework \1%g'`
+	if test "$linkmode,$pass" = "lib,link" ||
+	   test "$linkmode,$pass" = "prog,scan" ||
+	   { test "$linkmode" != prog && test "$linkmode" != lib; }; then
+	  test -n "$dlopen" && func_append dlfiles " $dlopen"
+	  test -n "$dlpreopen" && func_append dlprefiles " $dlpreopen"
+	fi
+
+	if test "$pass" = conv; then
+	  # Only check for convenience libraries
+	  deplibs="$lib $deplibs"
+	  if test -z "$libdir"; then
+	    if test -z "$old_library"; then
+	      func_fatal_error "cannot find name of link library for \`$lib'"
+	    fi
+	    # It is a libtool convenience library, so add in its objects.
+	    func_append convenience " $ladir/$objdir/$old_library"
+	    func_append old_convenience " $ladir/$objdir/$old_library"
+	    tmp_libs=
+	    for deplib in $dependency_libs; do
+	      deplibs="$deplib $deplibs"
+	      if $opt_preserve_dup_deps ; then
+		case "$tmp_libs " in
+		*" $deplib "*) func_append specialdeplibs " $deplib" ;;
+		esac
+	      fi
+	      func_append tmp_libs " $deplib"
+	    done
+	  elif test "$linkmode" != prog && test "$linkmode" != lib; then
+	    func_fatal_error "\`$lib' is not a convenience library"
+	  fi
+	  continue
+	fi # $pass = conv
+
+
+	# Get the name of the library we link against.
+	linklib=
+	if test -n "$old_library" &&
+	   { test "$prefer_static_libs" = yes ||
+	     test "$prefer_static_libs,$installed" = "built,no"; }; then
+	  linklib=$old_library
+	else
+	  for l in $old_library $library_names; do
+	    linklib="$l"
+	  done
+	fi
+	if test -z "$linklib"; then
+	  func_fatal_error "cannot find name of link library for \`$lib'"
+	fi
+
+	# This library was specified with -dlopen.
+	if test "$pass" = dlopen; then
+	  if test -z "$libdir"; then
+	    func_fatal_error "cannot -dlopen a convenience library: \`$lib'"
+	  fi
+	  if test -z "$dlname" ||
+	     test "$dlopen_support" != yes ||
+	     test "$build_libtool_libs" = no; then
+	    # If there is no dlname, no dlopen support or we're linking
+	    # statically, we need to preload.  We also need to preload any
+	    # dependent libraries so libltdl's deplib preloader doesn't
+	    # bomb out in the load deplibs phase.
+	    func_append dlprefiles " $lib $dependency_libs"
+	  else
+	    func_append newdlfiles " $lib"
+	  fi
+	  continue
+	fi # $pass = dlopen
+
+	# We need an absolute path.
+	case $ladir in
+	[\\/]* | [A-Za-z]:[\\/]*) abs_ladir="$ladir" ;;
+	*)
+	  abs_ladir=`cd "$ladir" && pwd`
+	  if test -z "$abs_ladir"; then
+	    func_warning "cannot determine absolute directory name of \`$ladir'"
+	    func_warning "passing it literally to the linker, although it might fail"
+	    abs_ladir="$ladir"
+	  fi
+	  ;;
+	esac
+	func_basename "$lib"
+	laname="$func_basename_result"
+
+	# Find the relevant object directory and library name.
+	if test "X$installed" = Xyes; then
+	  if test ! -f "$lt_sysroot$libdir/$linklib" && test -f "$abs_ladir/$linklib"; then
+	    func_warning "library \`$lib' was moved."
+	    dir="$ladir"
+	    absdir="$abs_ladir"
+	    libdir="$abs_ladir"
+	  else
+	    dir="$lt_sysroot$libdir"
+	    absdir="$lt_sysroot$libdir"
+	  fi
+	  test "X$hardcode_automatic" = Xyes && avoidtemprpath=yes
+	else
+	  if test ! -f "$ladir/$objdir/$linklib" && test -f "$abs_ladir/$linklib"; then
+	    dir="$ladir"
+	    absdir="$abs_ladir"
+	    # Remove this search path later
+	    func_append notinst_path " $abs_ladir"
+	  else
+	    dir="$ladir/$objdir"
+	    absdir="$abs_ladir/$objdir"
+	    # Remove this search path later
+	    func_append notinst_path " $abs_ladir"
+	  fi
+	fi # $installed = yes
+	func_stripname 'lib' '.la' "$laname"
+	name=$func_stripname_result
+
+	# This library was specified with -dlpreopen.
+	if test "$pass" = dlpreopen; then
+	  if test -z "$libdir" && test "$linkmode" = prog; then
+	    func_fatal_error "only libraries may -dlpreopen a convenience library: \`$lib'"
+	  fi
+	  case "$host" in
+	    # special handling for platforms with PE-DLLs.
+	    *cygwin* | *mingw* | *cegcc* )
+	      # Linker will automatically link against shared library if both
+	      # static and shared are present.  Therefore, ensure we extract
+	      # symbols from the import library if a shared library is present
+	      # (otherwise, the dlopen module name will be incorrect).  We do
+	      # this by putting the import library name into $newdlprefiles.
+	      # We recover the dlopen module name by 'saving' the la file
+	      # name in a special purpose variable, and (later) extracting the
+	      # dlname from the la file.
+	      if test -n "$dlname"; then
+	        func_tr_sh "$dir/$linklib"
+	        eval "libfile_$func_tr_sh_result=\$abs_ladir/\$laname"
+	        func_append newdlprefiles " $dir/$linklib"
+	      else
+	        func_append newdlprefiles " $dir/$old_library"
+	        # Keep a list of preopened convenience libraries to check
+	        # that they are being used correctly in the link pass.
+	        test -z "$libdir" && \
+	          func_append dlpreconveniencelibs " $dir/$old_library"
+	      fi
+	    ;;
+	    * )
+	      # Prefer using a static library (so that no silly _DYNAMIC symbols
+	      # are required to link).
+	      if test -n "$old_library"; then
+	        func_append newdlprefiles " $dir/$old_library"
+	        # Keep a list of preopened convenience libraries to check
+	        # that they are being used correctly in the link pass.
+	        test -z "$libdir" && \
+	          func_append dlpreconveniencelibs " $dir/$old_library"
+	      # Otherwise, use the dlname, so that lt_dlopen finds it.
+	      elif test -n "$dlname"; then
+	        func_append newdlprefiles " $dir/$dlname"
+	      else
+	        func_append newdlprefiles " $dir/$linklib"
+	      fi
+	    ;;
+	  esac
+	fi # $pass = dlpreopen
+
+	if test -z "$libdir"; then
+	  # Link the convenience library
+	  if test "$linkmode" = lib; then
+	    deplibs="$dir/$old_library $deplibs"
+	  elif test "$linkmode,$pass" = "prog,link"; then
+	    compile_deplibs="$dir/$old_library $compile_deplibs"
+	    finalize_deplibs="$dir/$old_library $finalize_deplibs"
+	  else
+	    deplibs="$lib $deplibs" # used for prog,scan pass
+	  fi
+	  continue
+	fi
+
+
+	if test "$linkmode" = prog && test "$pass" != link; then
+	  func_append newlib_search_path " $ladir"
+	  deplibs="$lib $deplibs"
+
+	  linkalldeplibs=no
+	  if test "$link_all_deplibs" != no || test -z "$library_names" ||
+	     test "$build_libtool_libs" = no; then
+	    linkalldeplibs=yes
+	  fi
+
+	  tmp_libs=
+	  for deplib in $dependency_libs; do
+	    case $deplib in
+	    -L*) func_stripname '-L' '' "$deplib"
+	         func_resolve_sysroot "$func_stripname_result"
+	         func_append newlib_search_path " $func_resolve_sysroot_result"
+		 ;;
+	    esac
+	    # Need to link against all dependency_libs?
+	    if test "$linkalldeplibs" = yes; then
+	      deplibs="$deplib $deplibs"
+	    else
+	      # Need to hardcode shared library paths
+	      # or/and link against static libraries
+	      newdependency_libs="$deplib $newdependency_libs"
+	    fi
+	    if $opt_preserve_dup_deps ; then
+	      case "$tmp_libs " in
+	      *" $deplib "*) func_append specialdeplibs " $deplib" ;;
+	      esac
+	    fi
+	    func_append tmp_libs " $deplib"
+	  done # for deplib
+	  continue
+	fi # $linkmode = prog...
+
+	if test "$linkmode,$pass" = "prog,link"; then
+	  if test -n "$library_names" &&
+	     { { test "$prefer_static_libs" = no ||
+	         test "$prefer_static_libs,$installed" = "built,yes"; } ||
+	       test -z "$old_library"; }; then
+	    # We need to hardcode the library path
+	    if test -n "$shlibpath_var" && test -z "$avoidtemprpath" ; then
+	      # Make sure the rpath contains only unique directories.
+	      case "$temp_rpath:" in
+	      *"$absdir:"*) ;;
+	      *) func_append temp_rpath "$absdir:" ;;
+	      esac
+	    fi
+
+	    # Hardcode the library path.
+	    # Skip directories that are in the system default run-time
+	    # search path.
+	    case " $sys_lib_dlsearch_path " in
+	    *" $absdir "*) ;;
+	    *)
+	      case "$compile_rpath " in
+	      *" $absdir "*) ;;
+	      *) func_append compile_rpath " $absdir" ;;
+	      esac
+	      ;;
+	    esac
+	    case " $sys_lib_dlsearch_path " in
+	    *" $libdir "*) ;;
+	    *)
+	      case "$finalize_rpath " in
+	      *" $libdir "*) ;;
+	      *) func_append finalize_rpath " $libdir" ;;
+	      esac
+	      ;;
+	    esac
+	  fi # $linkmode,$pass = prog,link...
+
+	  if test "$alldeplibs" = yes &&
+	     { test "$deplibs_check_method" = pass_all ||
+	       { test "$build_libtool_libs" = yes &&
+		 test -n "$library_names"; }; }; then
+	    # We only need to search for static libraries
+	    continue
+	  fi
+	fi
+
+	link_static=no # Whether the deplib will be linked statically
+	use_static_libs=$prefer_static_libs
+	if test "$use_static_libs" = built && test "$installed" = yes; then
+	  use_static_libs=no
+	fi
+	if test -n "$library_names" &&
+	   { test "$use_static_libs" = no || test -z "$old_library"; }; then
+	  case $host in
+	  *cygwin* | *mingw* | *cegcc*)
+	      # No point in relinking DLLs because paths are not encoded
+	      func_append notinst_deplibs " $lib"
+	      need_relink=no
+	    ;;
+	  *)
+	    if test "$installed" = no; then
+	      func_append notinst_deplibs " $lib"
+	      need_relink=yes
+	    fi
+	    ;;
+	  esac
+	  # This is a shared library
+
+	  # Warn about portability, can't link against -module's on some
+	  # systems (darwin).  Don't bleat about dlopened modules though!
+	  dlopenmodule=""
+	  for dlpremoduletest in $dlprefiles; do
+	    if test "X$dlpremoduletest" = "X$lib"; then
+	      dlopenmodule="$dlpremoduletest"
+	      break
+	    fi
+	  done
+	  if test -z "$dlopenmodule" && test "$shouldnotlink" = yes && test "$pass" = link; then
+	    echo
+	    if test "$linkmode" = prog; then
+	      $ECHO "*** Warning: Linking the executable $output against the loadable module"
+	    else
+	      $ECHO "*** Warning: Linking the shared library $output against the loadable module"
+	    fi
+	    $ECHO "*** $linklib is not portable!"
+	  fi
+	  if test "$linkmode" = lib &&
+	     test "$hardcode_into_libs" = yes; then
+	    # Hardcode the library path.
+	    # Skip directories that are in the system default run-time
+	    # search path.
+	    case " $sys_lib_dlsearch_path " in
+	    *" $absdir "*) ;;
+	    *)
+	      case "$compile_rpath " in
+	      *" $absdir "*) ;;
+	      *) func_append compile_rpath " $absdir" ;;
+	      esac
+	      ;;
+	    esac
+	    case " $sys_lib_dlsearch_path " in
+	    *" $libdir "*) ;;
+	    *)
+	      case "$finalize_rpath " in
+	      *" $libdir "*) ;;
+	      *) func_append finalize_rpath " $libdir" ;;
+	      esac
+	      ;;
+	    esac
+	  fi
+
+	  if test -n "$old_archive_from_expsyms_cmds"; then
+	    # figure out the soname
+	    set dummy $library_names
+	    shift
+	    realname="$1"
+	    shift
+	    libname=`eval "\\$ECHO \"$libname_spec\""`
+	    # use dlname if we got it. it's perfectly good, no?
+	    if test -n "$dlname"; then
+	      soname="$dlname"
+	    elif test -n "$soname_spec"; then
+	      # bleh windows
+	      case $host in
+	      *cygwin* | mingw* | *cegcc*)
+	        func_arith $current - $age
+		major=$func_arith_result
+		versuffix="-$major"
+		;;
+	      esac
+	      eval soname=\"$soname_spec\"
+	    else
+	      soname="$realname"
+	    fi
+
+	    # Make a new name for the extract_expsyms_cmds to use
+	    soroot="$soname"
+	    func_basename "$soroot"
+	    soname="$func_basename_result"
+	    func_stripname 'lib' '.dll' "$soname"
+	    newlib=libimp-$func_stripname_result.a
+
+	    # If the library has no export list, then create one now
+	    if test -f "$output_objdir/$soname-def"; then :
+	    else
+	      func_verbose "extracting exported symbol list from \`$soname'"
+	      func_execute_cmds "$extract_expsyms_cmds" 'exit $?'
+	    fi
+
+	    # Create $newlib
+	    if test -f "$output_objdir/$newlib"; then :; else
+	      func_verbose "generating import library for \`$soname'"
+	      func_execute_cmds "$old_archive_from_expsyms_cmds" 'exit $?'
+	    fi
+	    # make sure the library variables are pointing to the new library
+	    dir=$output_objdir
+	    linklib=$newlib
+	  fi # test -n "$old_archive_from_expsyms_cmds"
+
+	  if test "$linkmode" = prog || test "$opt_mode" != relink; then
+	    add_shlibpath=
+	    add_dir=
+	    add=
+	    lib_linked=yes
+	    case $hardcode_action in
+	    immediate | unsupported)
+	      if test "$hardcode_direct" = no; then
+		add="$dir/$linklib"
+		case $host in
+		  *-*-sco3.2v5.0.[024]*) add_dir="-L$dir" ;;
+		  *-*-sysv4*uw2*) add_dir="-L$dir" ;;
+		  *-*-sysv5OpenUNIX* | *-*-sysv5UnixWare7.[01].[10]* | \
+		    *-*-unixware7*) add_dir="-L$dir" ;;
+		  *-*-darwin* )
+		    # if the lib is a (non-dlopened) module then we can not
+		    # link against it, someone is ignoring the earlier warnings
+		    if /usr/bin/file -L $add 2> /dev/null |
+			 $GREP ": [^:]* bundle" >/dev/null ; then
+		      if test "X$dlopenmodule" != "X$lib"; then
+			$ECHO "*** Warning: lib $linklib is a module, not a shared library"
+			if test -z "$old_library" ; then
+			  echo
+			  echo "*** And there doesn't seem to be a static archive available"
+			  echo "*** The link will probably fail, sorry"
+			else
+			  add="$dir/$old_library"
+			fi
+		      elif test -n "$old_library"; then
+			add="$dir/$old_library"
+		      fi
+		    fi
+		esac
+	      elif test "$hardcode_minus_L" = no; then
+		case $host in
+		*-*-sunos*) add_shlibpath="$dir" ;;
+		esac
+		add_dir="-L$dir"
+		add="-l$name"
+	      elif test "$hardcode_shlibpath_var" = no; then
+		add_shlibpath="$dir"
+		add="-l$name"
+	      else
+		lib_linked=no
+	      fi
+	      ;;
+	    relink)
+	      if test "$hardcode_direct" = yes &&
+	         test "$hardcode_direct_absolute" = no; then
+		add="$dir/$linklib"
+	      elif test "$hardcode_minus_L" = yes; then
+		add_dir="-L$absdir"
+		# Try looking first in the location we're being installed to.
+		if test -n "$inst_prefix_dir"; then
+		  case $libdir in
+		    [\\/]*)
+		      func_append add_dir " -L$inst_prefix_dir$libdir"
+		      ;;
+		  esac
+		fi
+		add="-l$name"
+	      elif test "$hardcode_shlibpath_var" = yes; then
+		add_shlibpath="$dir"
+		add="-l$name"
+	      else
+		lib_linked=no
+	      fi
+	      ;;
+	    *) lib_linked=no ;;
+	    esac
+
+	    if test "$lib_linked" != yes; then
+	      func_fatal_configuration "unsupported hardcode properties"
+	    fi
+
+	    if test -n "$add_shlibpath"; then
+	      case :$compile_shlibpath: in
+	      *":$add_shlibpath:"*) ;;
+	      *) func_append compile_shlibpath "$add_shlibpath:" ;;
+	      esac
+	    fi
+	    if test "$linkmode" = prog; then
+	      test -n "$add_dir" && compile_deplibs="$add_dir $compile_deplibs"
+	      test -n "$add" && compile_deplibs="$add $compile_deplibs"
+	    else
+	      test -n "$add_dir" && deplibs="$add_dir $deplibs"
+	      test -n "$add" && deplibs="$add $deplibs"
+	      if test "$hardcode_direct" != yes &&
+		 test "$hardcode_minus_L" != yes &&
+		 test "$hardcode_shlibpath_var" = yes; then
+		case :$finalize_shlibpath: in
+		*":$libdir:"*) ;;
+		*) func_append finalize_shlibpath "$libdir:" ;;
+		esac
+	      fi
+	    fi
+	  fi
+
+	  if test "$linkmode" = prog || test "$opt_mode" = relink; then
+	    add_shlibpath=
+	    add_dir=
+	    add=
+	    # Finalize command for both is simple: just hardcode it.
+	    if test "$hardcode_direct" = yes &&
+	       test "$hardcode_direct_absolute" = no; then
+	      add="$libdir/$linklib"
+	    elif test "$hardcode_minus_L" = yes; then
+	      add_dir="-L$libdir"
+	      add="-l$name"
+	    elif test "$hardcode_shlibpath_var" = yes; then
+	      case :$finalize_shlibpath: in
+	      *":$libdir:"*) ;;
+	      *) func_append finalize_shlibpath "$libdir:" ;;
+	      esac
+	      add="-l$name"
+	    elif test "$hardcode_automatic" = yes; then
+	      if test -n "$inst_prefix_dir" &&
+		 test -f "$inst_prefix_dir$libdir/$linklib" ; then
+		add="$inst_prefix_dir$libdir/$linklib"
+	      else
+		add="$libdir/$linklib"
+	      fi
+	    else
+	      # We cannot seem to hardcode it, guess we'll fake it.
+	      add_dir="-L$libdir"
+	      # Try looking first in the location we're being installed to.
+	      if test -n "$inst_prefix_dir"; then
+		case $libdir in
+		  [\\/]*)
+		    func_append add_dir " -L$inst_prefix_dir$libdir"
+		    ;;
+		esac
+	      fi
+	      add="-l$name"
+	    fi
+
+	    if test "$linkmode" = prog; then
+	      test -n "$add_dir" && finalize_deplibs="$add_dir $finalize_deplibs"
+	      test -n "$add" && finalize_deplibs="$add $finalize_deplibs"
+	    else
+	      test -n "$add_dir" && deplibs="$add_dir $deplibs"
+	      test -n "$add" && deplibs="$add $deplibs"
+	    fi
+	  fi
+	elif test "$linkmode" = prog; then
+	  # Here we assume that one of hardcode_direct or hardcode_minus_L
+	  # is not unsupported.  This is valid on all known static and
+	  # shared platforms.
+	  if test "$hardcode_direct" != unsupported; then
+	    test -n "$old_library" && linklib="$old_library"
+	    compile_deplibs="$dir/$linklib $compile_deplibs"
+	    finalize_deplibs="$dir/$linklib $finalize_deplibs"
+	  else
+	    compile_deplibs="-l$name -L$dir $compile_deplibs"
+	    finalize_deplibs="-l$name -L$dir $finalize_deplibs"
+	  fi
+	elif test "$build_libtool_libs" = yes; then
+	  # Not a shared library
+	  if test "$deplibs_check_method" != pass_all; then
+	    # We're trying link a shared library against a static one
+	    # but the system doesn't support it.
+
+	    # Just print a warning and add the library to dependency_libs so
+	    # that the program can be linked against the static library.
+	    echo
+	    $ECHO "*** Warning: This system can not link to static lib archive $lib."
+	    echo "*** I have the capability to make that library automatically link in when"
+	    echo "*** you link to this library.  But I can only do this if you have a"
+	    echo "*** shared version of the library, which you do not appear to have."
+	    if test "$module" = yes; then
+	      echo "*** But as you try to build a module library, libtool will still create "
+	      echo "*** a static module, that should work as long as the dlopening application"
+	      echo "*** is linked with the -dlopen flag to resolve symbols at runtime."
+	      if test -z "$global_symbol_pipe"; then
+		echo
+		echo "*** However, this would only work if libtool was able to extract symbol"
+		echo "*** lists from a program, using \`nm' or equivalent, but libtool could"
+		echo "*** not find such a program.  So, this module is probably useless."
+		echo "*** \`nm' from GNU binutils and a full rebuild may help."
+	      fi
+	      if test "$build_old_libs" = no; then
+		build_libtool_libs=module
+		build_old_libs=yes
+	      else
+		build_libtool_libs=no
+	      fi
+	    fi
+	  else
+	    deplibs="$dir/$old_library $deplibs"
+	    link_static=yes
+	  fi
+	fi # link shared/static library?
+
+	if test "$linkmode" = lib; then
+	  if test -n "$dependency_libs" &&
+	     { test "$hardcode_into_libs" != yes ||
+	       test "$build_old_libs" = yes ||
+	       test "$link_static" = yes; }; then
+	    # Extract -R from dependency_libs
+	    temp_deplibs=
+	    for libdir in $dependency_libs; do
+	      case $libdir in
+	      -R*) func_stripname '-R' '' "$libdir"
+	           temp_xrpath=$func_stripname_result
+		   case " $xrpath " in
+		   *" $temp_xrpath "*) ;;
+		   *) func_append xrpath " $temp_xrpath";;
+		   esac;;
+	      *) func_append temp_deplibs " $libdir";;
+	      esac
+	    done
+	    dependency_libs="$temp_deplibs"
+	  fi
+
+	  func_append newlib_search_path " $absdir"
+	  # Link against this library
+	  test "$link_static" = no && newdependency_libs="$abs_ladir/$laname $newdependency_libs"
+	  # ... and its dependency_libs
+	  tmp_libs=
+	  for deplib in $dependency_libs; do
+	    newdependency_libs="$deplib $newdependency_libs"
+	    case $deplib in
+              -L*) func_stripname '-L' '' "$deplib"
+                   func_resolve_sysroot "$func_stripname_result";;
+              *) func_resolve_sysroot "$deplib" ;;
+            esac
+	    if $opt_preserve_dup_deps ; then
+	      case "$tmp_libs " in
+	      *" $func_resolve_sysroot_result "*)
+                func_append specialdeplibs " $func_resolve_sysroot_result" ;;
+	      esac
+	    fi
+	    func_append tmp_libs " $func_resolve_sysroot_result"
+	  done
+
+	  if test "$link_all_deplibs" != no; then
+	    # Add the search paths of all dependency libraries
+	    for deplib in $dependency_libs; do
+	      path=
+	      case $deplib in
+	      -L*) path="$deplib" ;;
+	      *.la)
+	        func_resolve_sysroot "$deplib"
+	        deplib=$func_resolve_sysroot_result
+	        func_dirname "$deplib" "" "."
+		dir=$func_dirname_result
+		# We need an absolute path.
+		case $dir in
+		[\\/]* | [A-Za-z]:[\\/]*) absdir="$dir" ;;
+		*)
+		  absdir=`cd "$dir" && pwd`
+		  if test -z "$absdir"; then
+		    func_warning "cannot determine absolute directory name of \`$dir'"
+		    absdir="$dir"
+		  fi
+		  ;;
+		esac
+		if $GREP "^installed=no" $deplib > /dev/null; then
+		case $host in
+		*-*-darwin*)
+		  depdepl=
+		  eval deplibrary_names=`${SED} -n -e 's/^library_names=\(.*\)$/\1/p' $deplib`
+		  if test -n "$deplibrary_names" ; then
+		    for tmp in $deplibrary_names ; do
+		      depdepl=$tmp
+		    done
+		    if test -f "$absdir/$objdir/$depdepl" ; then
+		      depdepl="$absdir/$objdir/$depdepl"
+		      darwin_install_name=`${OTOOL} -L $depdepl | awk '{if (NR == 2) {print $1;exit}}'`
+                      if test -z "$darwin_install_name"; then
+                          darwin_install_name=`${OTOOL64} -L $depdepl  | awk '{if (NR == 2) {print $1;exit}}'`
+                      fi
+		      func_append compiler_flags " ${wl}-dylib_file ${wl}${darwin_install_name}:${depdepl}"
+		      func_append linker_flags " -dylib_file ${darwin_install_name}:${depdepl}"
+		      path=
+		    fi
+		  fi
+		  ;;
+		*)
+		  path="-L$absdir/$objdir"
+		  ;;
+		esac
+		else
+		  eval libdir=`${SED} -n -e 's/^libdir=\(.*\)$/\1/p' $deplib`
+		  test -z "$libdir" && \
+		    func_fatal_error "\`$deplib' is not a valid libtool archive"
+		  test "$absdir" != "$libdir" && \
+		    func_warning "\`$deplib' seems to be moved"
+
+		  path="-L$absdir"
+		fi
+		;;
+	      esac
+	      case " $deplibs " in
+	      *" $path "*) ;;
+	      *) deplibs="$path $deplibs" ;;
+	      esac
+	    done
+	  fi # link_all_deplibs != no
+	fi # linkmode = lib
+      done # for deplib in $libs
+      if test "$pass" = link; then
+	if test "$linkmode" = "prog"; then
+	  compile_deplibs="$new_inherited_linker_flags $compile_deplibs"
+	  finalize_deplibs="$new_inherited_linker_flags $finalize_deplibs"
+	else
+	  compiler_flags="$compiler_flags "`$ECHO " $new_inherited_linker_flags" | $SED 's% \([^ $]*\).ltframework% -framework \1%g'`
+	fi
+      fi
+      dependency_libs="$newdependency_libs"
+      if test "$pass" = dlpreopen; then
+	# Link the dlpreopened libraries before other libraries
+	for deplib in $save_deplibs; do
+	  deplibs="$deplib $deplibs"
+	done
+      fi
+      if test "$pass" != dlopen; then
+	if test "$pass" != conv; then
+	  # Make sure lib_search_path contains only unique directories.
+	  lib_search_path=
+	  for dir in $newlib_search_path; do
+	    case "$lib_search_path " in
+	    *" $dir "*) ;;
+	    *) func_append lib_search_path " $dir" ;;
+	    esac
+	  done
+	  newlib_search_path=
+	fi
+
+	if test "$linkmode,$pass" != "prog,link"; then
+	  vars="deplibs"
+	else
+	  vars="compile_deplibs finalize_deplibs"
+	fi
+	for var in $vars dependency_libs; do
+	  # Add libraries to $var in reverse order
+	  eval tmp_libs=\"\$$var\"
+	  new_libs=
+	  for deplib in $tmp_libs; do
+	    # FIXME: Pedantically, this is the right thing to do, so
+	    #        that some nasty dependency loop isn't accidentally
+	    #        broken:
+	    #new_libs="$deplib $new_libs"
+	    # Pragmatically, this seems to cause very few problems in
+	    # practice:
+	    case $deplib in
+	    -L*) new_libs="$deplib $new_libs" ;;
+	    -R*) ;;
+	    *)
+	      # And here is the reason: when a library appears more
+	      # than once as an explicit dependence of a library, or
+	      # is implicitly linked in more than once by the
+	      # compiler, it is considered special, and multiple
+	      # occurrences thereof are not removed.  Compare this
+	      # with having the same library being listed as a
+	      # dependency of multiple other libraries: in this case,
+	      # we know (pedantically, we assume) the library does not
+	      # need to be listed more than once, so we keep only the
+	      # last copy.  This is not always right, but it is rare
+	      # enough that we require users that really mean to play
+	      # such unportable linking tricks to link the library
+	      # using -Wl,-lname, so that libtool does not consider it
+	      # for duplicate removal.
+	      case " $specialdeplibs " in
+	      *" $deplib "*) new_libs="$deplib $new_libs" ;;
+	      *)
+		case " $new_libs " in
+		*" $deplib "*) ;;
+		*) new_libs="$deplib $new_libs" ;;
+		esac
+		;;
+	      esac
+	      ;;
+	    esac
+	  done
+	  tmp_libs=
+	  for deplib in $new_libs; do
+	    case $deplib in
+	    -L*)
+	      case " $tmp_libs " in
+	      *" $deplib "*) ;;
+	      *) func_append tmp_libs " $deplib" ;;
+	      esac
+	      ;;
+	    *) func_append tmp_libs " $deplib" ;;
+	    esac
+	  done
+	  eval $var=\"$tmp_libs\"
+	done # for var
+      fi
+      # Last step: remove runtime libs from dependency_libs
+      # (they stay in deplibs)
+      tmp_libs=
+      for i in $dependency_libs ; do
+	case " $predeps $postdeps $compiler_lib_search_path " in
+	*" $i "*)
+	  i=""
+	  ;;
+	esac
+	if test -n "$i" ; then
+	  func_append tmp_libs " $i"
+	fi
+      done
+      dependency_libs=$tmp_libs
+    done # for pass
+    if test "$linkmode" = prog; then
+      dlfiles="$newdlfiles"
+    fi
+    if test "$linkmode" = prog || test "$linkmode" = lib; then
+      dlprefiles="$newdlprefiles"
+    fi
+
+    case $linkmode in
+    oldlib)
+      if test -n "$dlfiles$dlprefiles" || test "$dlself" != no; then
+	func_warning "\`-dlopen' is ignored for archives"
+      fi
+
+      case " $deplibs" in
+      *\ -l* | *\ -L*)
+	func_warning "\`-l' and \`-L' are ignored for archives" ;;
+      esac
+
+      test -n "$rpath" && \
+	func_warning "\`-rpath' is ignored for archives"
+
+      test -n "$xrpath" && \
+	func_warning "\`-R' is ignored for archives"
+
+      test -n "$vinfo" && \
+	func_warning "\`-version-info/-version-number' is ignored for archives"
+
+      test -n "$release" && \
+	func_warning "\`-release' is ignored for archives"
+
+      test -n "$export_symbols$export_symbols_regex" && \
+	func_warning "\`-export-symbols' is ignored for archives"
+
+      # Now set the variables for building old libraries.
+      build_libtool_libs=no
+      oldlibs="$output"
+      func_append objs "$old_deplibs"
+      ;;
+
+    lib)
+      # Make sure we only generate libraries of the form `libNAME.la'.
+      case $outputname in
+      lib*)
+	func_stripname 'lib' '.la' "$outputname"
+	name=$func_stripname_result
+	eval shared_ext=\"$shrext_cmds\"
+	eval libname=\"$libname_spec\"
+	;;
+      *)
+	test "$module" = no && \
+	  func_fatal_help "libtool library \`$output' must begin with \`lib'"
+
+	if test "$need_lib_prefix" != no; then
+	  # Add the "lib" prefix for modules if required
+	  func_stripname '' '.la' "$outputname"
+	  name=$func_stripname_result
+	  eval shared_ext=\"$shrext_cmds\"
+	  eval libname=\"$libname_spec\"
+	else
+	  func_stripname '' '.la' "$outputname"
+	  libname=$func_stripname_result
+	fi
+	;;
+      esac
+
+      if test -n "$objs"; then
+	if test "$deplibs_check_method" != pass_all; then
+	  func_fatal_error "cannot build libtool library \`$output' from non-libtool objects on this host:$objs"
+	else
+	  echo
+	  $ECHO "*** Warning: Linking the shared library $output against the non-libtool"
+	  $ECHO "*** objects $objs is not portable!"
+	  func_append libobjs " $objs"
+	fi
+      fi
+
+      test "$dlself" != no && \
+	func_warning "\`-dlopen self' is ignored for libtool libraries"
+
+      set dummy $rpath
+      shift
+      test "$#" -gt 1 && \
+	func_warning "ignoring multiple \`-rpath's for a libtool library"
+
+      install_libdir="$1"
+
+      oldlibs=
+      if test -z "$rpath"; then
+	if test "$build_libtool_libs" = yes; then
+	  # Building a libtool convenience library.
+	  # Some compilers have problems with a `.al' extension so
+	  # convenience libraries should have the same extension an
+	  # archive normally would.
+	  oldlibs="$output_objdir/$libname.$libext $oldlibs"
+	  build_libtool_libs=convenience
+	  build_old_libs=yes
+	fi
+
+	test -n "$vinfo" && \
+	  func_warning "\`-version-info/-version-number' is ignored for convenience libraries"
+
+	test -n "$release" && \
+	  func_warning "\`-release' is ignored for convenience libraries"
+      else
+
+	# Parse the version information argument.
+	save_ifs="$IFS"; IFS=':'
+	set dummy $vinfo 0 0 0
+	shift
+	IFS="$save_ifs"
+
+	test -n "$7" && \
+	  func_fatal_help "too many parameters to \`-version-info'"
+
+	# convert absolute version numbers to libtool ages
+	# this retains compatibility with .la files and attempts
+	# to make the code below a bit more comprehensible
+
+	case $vinfo_number in
+	yes)
+	  number_major="$1"
+	  number_minor="$2"
+	  number_revision="$3"
+	  #
+	  # There are really only two kinds -- those that
+	  # use the current revision as the major version
+	  # and those that subtract age and use age as
+	  # a minor version.  But, then there is irix
+	  # which has an extra 1 added just for fun
+	  #
+	  case $version_type in
+	  # correct linux to gnu/linux during the next big refactor
+	  darwin|linux|osf|windows|none)
+	    func_arith $number_major + $number_minor
+	    current=$func_arith_result
+	    age="$number_minor"
+	    revision="$number_revision"
+	    ;;
+	  freebsd-aout|freebsd-elf|qnx|sunos)
+	    current="$number_major"
+	    revision="$number_minor"
+	    age="0"
+	    ;;
+	  irix|nonstopux)
+	    func_arith $number_major + $number_minor
+	    current=$func_arith_result
+	    age="$number_minor"
+	    revision="$number_minor"
+	    lt_irix_increment=no
+	    ;;
+	  *)
+	    func_fatal_configuration "$modename: unknown library version type \`$version_type'"
+	    ;;
+	  esac
+	  ;;
+	no)
+	  current="$1"
+	  revision="$2"
+	  age="$3"
+	  ;;
+	esac
+
+	# Check that each of the things are valid numbers.
+	case $current in
+	0|[1-9]|[1-9][0-9]|[1-9][0-9][0-9]|[1-9][0-9][0-9][0-9]|[1-9][0-9][0-9][0-9][0-9]) ;;
+	*)
+	  func_error "CURRENT \`$current' must be a nonnegative integer"
+	  func_fatal_error "\`$vinfo' is not valid version information"
+	  ;;
+	esac
+
+	case $revision in
+	0|[1-9]|[1-9][0-9]|[1-9][0-9][0-9]|[1-9][0-9][0-9][0-9]|[1-9][0-9][0-9][0-9][0-9]) ;;
+	*)
+	  func_error "REVISION \`$revision' must be a nonnegative integer"
+	  func_fatal_error "\`$vinfo' is not valid version information"
+	  ;;
+	esac
+
+	case $age in
+	0|[1-9]|[1-9][0-9]|[1-9][0-9][0-9]|[1-9][0-9][0-9][0-9]|[1-9][0-9][0-9][0-9][0-9]) ;;
+	*)
+	  func_error "AGE \`$age' must be a nonnegative integer"
+	  func_fatal_error "\`$vinfo' is not valid version information"
+	  ;;
+	esac
+
+	if test "$age" -gt "$current"; then
+	  func_error "AGE \`$age' is greater than the current interface number \`$current'"
+	  func_fatal_error "\`$vinfo' is not valid version information"
+	fi
+
+	# Calculate the version variables.
+	major=
+	versuffix=
+	verstring=
+	case $version_type in
+	none) ;;
+
+	darwin)
+	  # Like Linux, but with the current version available in
+	  # verstring for coding it into the library header
+	  func_arith $current - $age
+	  major=.$func_arith_result
+	  versuffix="$major.$age.$revision"
+	  # Darwin ld doesn't like 0 for these options...
+	  func_arith $current + 1
+	  minor_current=$func_arith_result
+	  xlcverstring="${wl}-compatibility_version ${wl}$minor_current ${wl}-current_version ${wl}$minor_current.$revision"
+	  verstring="-compatibility_version $minor_current -current_version $minor_current.$revision"
+	  ;;
+
+	freebsd-aout)
+	  major=".$current"
+	  versuffix=".$current.$revision";
+	  ;;
+
+	freebsd-elf)
+	  major=".$current"
+	  versuffix=".$current"
+	  ;;
+
+	irix | nonstopux)
+	  if test "X$lt_irix_increment" = "Xno"; then
+	    func_arith $current - $age
+	  else
+	    func_arith $current - $age + 1
+	  fi
+	  major=$func_arith_result
+
+	  case $version_type in
+	    nonstopux) verstring_prefix=nonstopux ;;
+	    *)         verstring_prefix=sgi ;;
+	  esac
+	  verstring="$verstring_prefix$major.$revision"
+
+	  # Add in all the interfaces that we are compatible with.
+	  loop=$revision
+	  while test "$loop" -ne 0; do
+	    func_arith $revision - $loop
+	    iface=$func_arith_result
+	    func_arith $loop - 1
+	    loop=$func_arith_result
+	    verstring="$verstring_prefix$major.$iface:$verstring"
+	  done
+
+	  # Before this point, $major must not contain `.'.
+	  major=.$major
+	  versuffix="$major.$revision"
+	  ;;
+
+	linux) # correct to gnu/linux during the next big refactor
+	  func_arith $current - $age
+	  major=.$func_arith_result
+	  versuffix="$major.$age.$revision"
+	  ;;
+
+	osf)
+	  func_arith $current - $age
+	  major=.$func_arith_result
+	  versuffix=".$current.$age.$revision"
+	  verstring="$current.$age.$revision"
+
+	  # Add in all the interfaces that we are compatible with.
+	  loop=$age
+	  while test "$loop" -ne 0; do
+	    func_arith $current - $loop
+	    iface=$func_arith_result
+	    func_arith $loop - 1
+	    loop=$func_arith_result
+	    verstring="$verstring:${iface}.0"
+	  done
+
+	  # Make executables depend on our current version.
+	  func_append verstring ":${current}.0"
+	  ;;
+
+	qnx)
+	  major=".$current"
+	  versuffix=".$current"
+	  ;;
+
+	sunos)
+	  major=".$current"
+	  versuffix=".$current.$revision"
+	  ;;
+
+	windows)
+	  # Use '-' rather than '.', since we only want one
+	  # extension on DOS 8.3 filesystems.
+	  func_arith $current - $age
+	  major=$func_arith_result
+	  versuffix="-$major"
+	  ;;
+
+	*)
+	  func_fatal_configuration "unknown library version type \`$version_type'"
+	  ;;
+	esac
+
+	# Clear the version info if we defaulted, and they specified a release.
+	if test -z "$vinfo" && test -n "$release"; then
+	  major=
+	  case $version_type in
+	  darwin)
+	    # we can't check for "0.0" in archive_cmds due to quoting
+	    # problems, so we reset it completely
+	    verstring=
+	    ;;
+	  *)
+	    verstring="0.0"
+	    ;;
+	  esac
+	  if test "$need_version" = no; then
+	    versuffix=
+	  else
+	    versuffix=".0.0"
+	  fi
+	fi
+
+	# Remove version info from name if versioning should be avoided
+	if test "$avoid_version" = yes && test "$need_version" = no; then
+	  major=
+	  versuffix=
+	  verstring=""
+	fi
+
+	# Check to see if the archive will have undefined symbols.
+	if test "$allow_undefined" = yes; then
+	  if test "$allow_undefined_flag" = unsupported; then
+	    func_warning "undefined symbols not allowed in $host shared libraries"
+	    build_libtool_libs=no
+	    build_old_libs=yes
+	  fi
+	else
+	  # Don't allow undefined symbols.
+	  allow_undefined_flag="$no_undefined_flag"
+	fi
+
+      fi
+
+      func_generate_dlsyms "$libname" "$libname" "yes"
+      func_append libobjs " $symfileobj"
+      test "X$libobjs" = "X " && libobjs=
+
+      if test "$opt_mode" != relink; then
+	# Remove our outputs, but don't remove object files since they
+	# may have been created when compiling PIC objects.
+	removelist=
+	tempremovelist=`$ECHO "$output_objdir/*"`
+	for p in $tempremovelist; do
+	  case $p in
+	    *.$objext | *.gcno)
+	       ;;
+	    $output_objdir/$outputname | $output_objdir/$libname.* | $output_objdir/${libname}${release}.*)
+	       if test "X$precious_files_regex" != "X"; then
+		 if $ECHO "$p" | $EGREP -e "$precious_files_regex" >/dev/null 2>&1
+		 then
+		   continue
+		 fi
+	       fi
+	       func_append removelist " $p"
+	       ;;
+	    *) ;;
+	  esac
+	done
+	test -n "$removelist" && \
+	  func_show_eval "${RM}r \$removelist"
+      fi
+
+      # Now set the variables for building old libraries.
+      if test "$build_old_libs" = yes && test "$build_libtool_libs" != convenience ; then
+	func_append oldlibs " $output_objdir/$libname.$libext"
+
+	# Transform .lo files to .o files.
+	oldobjs="$objs "`$ECHO "$libobjs" | $SP2NL | $SED "/\.${libext}$/d; $lo2o" | $NL2SP`
+      fi
+
+      # Eliminate all temporary directories.
+      #for path in $notinst_path; do
+      #	lib_search_path=`$ECHO "$lib_search_path " | $SED "s% $path % %g"`
+      #	deplibs=`$ECHO "$deplibs " | $SED "s% -L$path % %g"`
+      #	dependency_libs=`$ECHO "$dependency_libs " | $SED "s% -L$path % %g"`
+      #done
+
+      if test -n "$xrpath"; then
+	# If the user specified any rpath flags, then add them.
+	temp_xrpath=
+	for libdir in $xrpath; do
+	  func_replace_sysroot "$libdir"
+	  func_append temp_xrpath " -R$func_replace_sysroot_result"
+	  case "$finalize_rpath " in
+	  *" $libdir "*) ;;
+	  *) func_append finalize_rpath " $libdir" ;;
+	  esac
+	done
+	if test "$hardcode_into_libs" != yes || test "$build_old_libs" = yes; then
+	  dependency_libs="$temp_xrpath $dependency_libs"
+	fi
+      fi
+
+      # Make sure dlfiles contains only unique files that won't be dlpreopened
+      old_dlfiles="$dlfiles"
+      dlfiles=
+      for lib in $old_dlfiles; do
+	case " $dlprefiles $dlfiles " in
+	*" $lib "*) ;;
+	*) func_append dlfiles " $lib" ;;
+	esac
+      done
+
+      # Make sure dlprefiles contains only unique files
+      old_dlprefiles="$dlprefiles"
+      dlprefiles=
+      for lib in $old_dlprefiles; do
+	case "$dlprefiles " in
+	*" $lib "*) ;;
+	*) func_append dlprefiles " $lib" ;;
+	esac
+      done
+
+      if test "$build_libtool_libs" = yes; then
+	if test -n "$rpath"; then
+	  case $host in
+	  *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-os2* | *-*-beos* | *-cegcc* | *-*-haiku*)
+	    # these systems don't actually have a c library (as such)!
+	    ;;
+	  *-*-rhapsody* | *-*-darwin1.[012])
+	    # Rhapsody C library is in the System framework
+	    func_append deplibs " System.ltframework"
+	    ;;
+	  *-*-netbsd*)
+	    # Don't link with libc until the a.out ld.so is fixed.
+	    ;;
+	  *-*-openbsd* | *-*-freebsd* | *-*-dragonfly*)
+	    # Do not include libc due to us having libc/libc_r.
+	    ;;
+	  *-*-sco3.2v5* | *-*-sco5v6*)
+	    # Causes problems with __ctype
+	    ;;
+	  *-*-sysv4.2uw2* | *-*-sysv5* | *-*-unixware* | *-*-OpenUNIX*)
+	    # Compiler inserts libc in the correct place for threads to work
+	    ;;
+	  *)
+	    # Add libc to deplibs on all other systems if necessary.
+	    if test "$build_libtool_need_lc" = "yes"; then
+	      func_append deplibs " -lc"
+	    fi
+	    ;;
+	  esac
+	fi
+
+	# Transform deplibs into only deplibs that can be linked in shared.
+	name_save=$name
+	libname_save=$libname
+	release_save=$release
+	versuffix_save=$versuffix
+	major_save=$major
+	# I'm not sure if I'm treating the release correctly.  I think
+	# release should show up in the -l (ie -lgmp5) so we don't want to
+	# add it in twice.  Is that correct?
+	release=""
+	versuffix=""
+	major=""
+	newdeplibs=
+	droppeddeps=no
+	case $deplibs_check_method in
+	pass_all)
+	  # Don't check for shared/static.  Everything works.
+	  # This might be a little naive.  We might want to check
+	  # whether the library exists or not.  But this is on
+	  # osf3 & osf4 and I'm not really sure... Just
+	  # implementing what was already the behavior.
+	  newdeplibs=$deplibs
+	  ;;
+	test_compile)
+	  # This code stresses the "libraries are programs" paradigm to its
+	  # limits. Maybe even breaks it.  We compile a program, linking it
+	  # against the deplibs as a proxy for the library.  Then we can check
+	  # whether they linked in statically or dynamically with ldd.
+	  $opt_dry_run || $RM conftest.c
+	  cat > conftest.c <<EOF
+	  int main() { return 0; }
+EOF
+	  $opt_dry_run || $RM conftest
+	  if $LTCC $LTCFLAGS -o conftest conftest.c $deplibs; then
+	    ldd_output=`ldd conftest`
+	    for i in $deplibs; do
+	      case $i in
+	      -l*)
+		func_stripname -l '' "$i"
+		name=$func_stripname_result
+		if test "X$allow_libtool_libs_with_static_runtimes" = "Xyes" ; then
+		  case " $predeps $postdeps " in
+		  *" $i "*)
+		    func_append newdeplibs " $i"
+		    i=""
+		    ;;
+		  esac
+		fi
+		if test -n "$i" ; then
+		  libname=`eval "\\$ECHO \"$libname_spec\""`
+		  deplib_matches=`eval "\\$ECHO \"$library_names_spec\""`
+		  set dummy $deplib_matches; shift
+		  deplib_match=$1
+		  if test `expr "$ldd_output" : ".*$deplib_match"` -ne 0 ; then
+		    func_append newdeplibs " $i"
+		  else
+		    droppeddeps=yes
+		    echo
+		    $ECHO "*** Warning: dynamic linker does not accept needed library $i."
+		    echo "*** I have the capability to make that library automatically link in when"
+		    echo "*** you link to this library.  But I can only do this if you have a"
+		    echo "*** shared version of the library, which I believe you do not have"
+		    echo "*** because a test_compile did reveal that the linker did not use it for"
+		    echo "*** its dynamic dependency list that programs get resolved with at runtime."
+		  fi
+		fi
+		;;
+	      *)
+		func_append newdeplibs " $i"
+		;;
+	      esac
+	    done
+	  else
+	    # Error occurred in the first compile.  Let's try to salvage
+	    # the situation: Compile a separate program for each library.
+	    for i in $deplibs; do
+	      case $i in
+	      -l*)
+		func_stripname -l '' "$i"
+		name=$func_stripname_result
+		$opt_dry_run || $RM conftest
+		if $LTCC $LTCFLAGS -o conftest conftest.c $i; then
+		  ldd_output=`ldd conftest`
+		  if test "X$allow_libtool_libs_with_static_runtimes" = "Xyes" ; then
+		    case " $predeps $postdeps " in
+		    *" $i "*)
+		      func_append newdeplibs " $i"
+		      i=""
+		      ;;
+		    esac
+		  fi
+		  if test -n "$i" ; then
+		    libname=`eval "\\$ECHO \"$libname_spec\""`
+		    deplib_matches=`eval "\\$ECHO \"$library_names_spec\""`
+		    set dummy $deplib_matches; shift
+		    deplib_match=$1
+		    if test `expr "$ldd_output" : ".*$deplib_match"` -ne 0 ; then
+		      func_append newdeplibs " $i"
+		    else
+		      droppeddeps=yes
+		      echo
+		      $ECHO "*** Warning: dynamic linker does not accept needed library $i."
+		      echo "*** I have the capability to make that library automatically link in when"
+		      echo "*** you link to this library.  But I can only do this if you have a"
+		      echo "*** shared version of the library, which you do not appear to have"
+		      echo "*** because a test_compile did reveal that the linker did not use this one"
+		      echo "*** as a dynamic dependency that programs can get resolved with at runtime."
+		    fi
+		  fi
+		else
+		  droppeddeps=yes
+		  echo
+		  $ECHO "*** Warning!  Library $i is needed by this library but I was not able to"
+		  echo "*** make it link in!  You will probably need to install it or some"
+		  echo "*** library that it depends on before this library will be fully"
+		  echo "*** functional.  Installing it before continuing would be even better."
+		fi
+		;;
+	      *)
+		func_append newdeplibs " $i"
+		;;
+	      esac
+	    done
+	  fi
+	  ;;
+	file_magic*)
+	  set dummy $deplibs_check_method; shift
+	  file_magic_regex=`expr "$deplibs_check_method" : "$1 \(.*\)"`
+	  for a_deplib in $deplibs; do
+	    case $a_deplib in
+	    -l*)
+	      func_stripname -l '' "$a_deplib"
+	      name=$func_stripname_result
+	      if test "X$allow_libtool_libs_with_static_runtimes" = "Xyes" ; then
+		case " $predeps $postdeps " in
+		*" $a_deplib "*)
+		  func_append newdeplibs " $a_deplib"
+		  a_deplib=""
+		  ;;
+		esac
+	      fi
+	      if test -n "$a_deplib" ; then
+		libname=`eval "\\$ECHO \"$libname_spec\""`
+		if test -n "$file_magic_glob"; then
+		  libnameglob=`func_echo_all "$libname" | $SED -e $file_magic_glob`
+		else
+		  libnameglob=$libname
+		fi
+		test "$want_nocaseglob" = yes && nocaseglob=`shopt -p nocaseglob`
+		for i in $lib_search_path $sys_lib_search_path $shlib_search_path; do
+		  if test "$want_nocaseglob" = yes; then
+		    shopt -s nocaseglob
+		    potential_libs=`ls $i/$libnameglob[.-]* 2>/dev/null`
+		    $nocaseglob
+		  else
+		    potential_libs=`ls $i/$libnameglob[.-]* 2>/dev/null`
+		  fi
+		  for potent_lib in $potential_libs; do
+		      # Follow soft links.
+		      if ls -lLd "$potent_lib" 2>/dev/null |
+			 $GREP " -> " >/dev/null; then
+			continue
+		      fi
+		      # The statement above tries to avoid entering an
+		      # endless loop below, in case of cyclic links.
+		      # We might still enter an endless loop, since a link
+		      # loop can be closed while we follow links,
+		      # but so what?
+		      potlib="$potent_lib"
+		      while test -h "$potlib" 2>/dev/null; do
+			potliblink=`ls -ld $potlib | ${SED} 's/.* -> //'`
+			case $potliblink in
+			[\\/]* | [A-Za-z]:[\\/]*) potlib="$potliblink";;
+			*) potlib=`$ECHO "$potlib" | $SED 's,[^/]*$,,'`"$potliblink";;
+			esac
+		      done
+		      if eval $file_magic_cmd \"\$potlib\" 2>/dev/null |
+			 $SED -e 10q |
+			 $EGREP "$file_magic_regex" > /dev/null; then
+			func_append newdeplibs " $a_deplib"
+			a_deplib=""
+			break 2
+		      fi
+		  done
+		done
+	      fi
+	      if test -n "$a_deplib" ; then
+		droppeddeps=yes
+		echo
+		$ECHO "*** Warning: linker path does not have real file for library $a_deplib."
+		echo "*** I have the capability to make that library automatically link in when"
+		echo "*** you link to this library.  But I can only do this if you have a"
+		echo "*** shared version of the library, which you do not appear to have"
+		echo "*** because I did check the linker path looking for a file starting"
+		if test -z "$potlib" ; then
+		  $ECHO "*** with $libname but no candidates were found. (...for file magic test)"
+		else
+		  $ECHO "*** with $libname and none of the candidates passed a file format test"
+		  $ECHO "*** using a file magic. Last file checked: $potlib"
+		fi
+	      fi
+	      ;;
+	    *)
+	      # Add a -L argument.
+	      func_append newdeplibs " $a_deplib"
+	      ;;
+	    esac
+	  done # Gone through all deplibs.
+	  ;;
+	match_pattern*)
+	  set dummy $deplibs_check_method; shift
+	  match_pattern_regex=`expr "$deplibs_check_method" : "$1 \(.*\)"`
+	  for a_deplib in $deplibs; do
+	    case $a_deplib in
+	    -l*)
+	      func_stripname -l '' "$a_deplib"
+	      name=$func_stripname_result
+	      if test "X$allow_libtool_libs_with_static_runtimes" = "Xyes" ; then
+		case " $predeps $postdeps " in
+		*" $a_deplib "*)
+		  func_append newdeplibs " $a_deplib"
+		  a_deplib=""
+		  ;;
+		esac
+	      fi
+	      if test -n "$a_deplib" ; then
+		libname=`eval "\\$ECHO \"$libname_spec\""`
+		for i in $lib_search_path $sys_lib_search_path $shlib_search_path; do
+		  potential_libs=`ls $i/$libname[.-]* 2>/dev/null`
+		  for potent_lib in $potential_libs; do
+		    potlib="$potent_lib" # see symlink-check above in file_magic test
+		    if eval "\$ECHO \"$potent_lib\"" 2>/dev/null | $SED 10q | \
+		       $EGREP "$match_pattern_regex" > /dev/null; then
+		      func_append newdeplibs " $a_deplib"
+		      a_deplib=""
+		      break 2
+		    fi
+		  done
+		done
+	      fi
+	      if test -n "$a_deplib" ; then
+		droppeddeps=yes
+		echo
+		$ECHO "*** Warning: linker path does not have real file for library $a_deplib."
+		echo "*** I have the capability to make that library automatically link in when"
+		echo "*** you link to this library.  But I can only do this if you have a"
+		echo "*** shared version of the library, which you do not appear to have"
+		echo "*** because I did check the linker path looking for a file starting"
+		if test -z "$potlib" ; then
+		  $ECHO "*** with $libname but no candidates were found. (...for regex pattern test)"
+		else
+		  $ECHO "*** with $libname and none of the candidates passed a file format test"
+		  $ECHO "*** using a regex pattern. Last file checked: $potlib"
+		fi
+	      fi
+	      ;;
+	    *)
+	      # Add a -L argument.
+	      func_append newdeplibs " $a_deplib"
+	      ;;
+	    esac
+	  done # Gone through all deplibs.
+	  ;;
+	none | unknown | *)
+	  newdeplibs=""
+	  tmp_deplibs=`$ECHO " $deplibs" | $SED 's/ -lc$//; s/ -[LR][^ ]*//g'`
+	  if test "X$allow_libtool_libs_with_static_runtimes" = "Xyes" ; then
+	    for i in $predeps $postdeps ; do
+	      # can't use Xsed below, because $i might contain '/'
+	      tmp_deplibs=`$ECHO " $tmp_deplibs" | $SED "s,$i,,"`
+	    done
+	  fi
+	  case $tmp_deplibs in
+	  *[!\	\ ]*)
+	    echo
+	    if test "X$deplibs_check_method" = "Xnone"; then
+	      echo "*** Warning: inter-library dependencies are not supported in this platform."
+	    else
+	      echo "*** Warning: inter-library dependencies are not known to be supported."
+	    fi
+	    echo "*** All declared inter-library dependencies are being dropped."
+	    droppeddeps=yes
+	    ;;
+	  esac
+	  ;;
+	esac
+	versuffix=$versuffix_save
+	major=$major_save
+	release=$release_save
+	libname=$libname_save
+	name=$name_save
+
+	case $host in
+	*-*-rhapsody* | *-*-darwin1.[012])
+	  # On Rhapsody replace the C library with the System framework
+	  newdeplibs=`$ECHO " $newdeplibs" | $SED 's/ -lc / System.ltframework /'`
+	  ;;
+	esac
+
+	if test "$droppeddeps" = yes; then
+	  if test "$module" = yes; then
+	    echo
+	    echo "*** Warning: libtool could not satisfy all declared inter-library"
+	    $ECHO "*** dependencies of module $libname.  Therefore, libtool will create"
+	    echo "*** a static module, that should work as long as the dlopening"
+	    echo "*** application is linked with the -dlopen flag."
+	    if test -z "$global_symbol_pipe"; then
+	      echo
+	      echo "*** However, this would only work if libtool was able to extract symbol"
+	      echo "*** lists from a program, using \`nm' or equivalent, but libtool could"
+	      echo "*** not find such a program.  So, this module is probably useless."
+	      echo "*** \`nm' from GNU binutils and a full rebuild may help."
+	    fi
+	    if test "$build_old_libs" = no; then
+	      oldlibs="$output_objdir/$libname.$libext"
+	      build_libtool_libs=module
+	      build_old_libs=yes
+	    else
+	      build_libtool_libs=no
+	    fi
+	  else
+	    echo "*** The inter-library dependencies that have been dropped here will be"
+	    echo "*** automatically added whenever a program is linked with this library"
+	    echo "*** or is declared to -dlopen it."
+
+	    if test "$allow_undefined" = no; then
+	      echo
+	      echo "*** Since this library must not contain undefined symbols,"
+	      echo "*** because either the platform does not support them or"
+	      echo "*** it was explicitly requested with -no-undefined,"
+	      echo "*** libtool will only create a static version of it."
+	      if test "$build_old_libs" = no; then
+		oldlibs="$output_objdir/$libname.$libext"
+		build_libtool_libs=module
+		build_old_libs=yes
+	      else
+		build_libtool_libs=no
+	      fi
+	    fi
+	  fi
+	fi
+	# Done checking deplibs!
+	deplibs=$newdeplibs
+      fi
+      # Time to change all our "foo.ltframework" stuff back to "-framework foo"
+      case $host in
+	*-*-darwin*)
+	  newdeplibs=`$ECHO " $newdeplibs" | $SED 's% \([^ $]*\).ltframework% -framework \1%g'`
+	  new_inherited_linker_flags=`$ECHO " $new_inherited_linker_flags" | $SED 's% \([^ $]*\).ltframework% -framework \1%g'`
+	  deplibs=`$ECHO " $deplibs" | $SED 's% \([^ $]*\).ltframework% -framework \1%g'`
+	  ;;
+      esac
+
+      # move library search paths that coincide with paths to not yet
+      # installed libraries to the beginning of the library search list
+      new_libs=
+      for path in $notinst_path; do
+	case " $new_libs " in
+	*" -L$path/$objdir "*) ;;
+	*)
+	  case " $deplibs " in
+	  *" -L$path/$objdir "*)
+	    func_append new_libs " -L$path/$objdir" ;;
+	  esac
+	  ;;
+	esac
+      done
+      for deplib in $deplibs; do
+	case $deplib in
+	-L*)
+	  case " $new_libs " in
+	  *" $deplib "*) ;;
+	  *) func_append new_libs " $deplib" ;;
+	  esac
+	  ;;
+	*) func_append new_libs " $deplib" ;;
+	esac
+      done
+      deplibs="$new_libs"
+
+      # All the library-specific variables (install_libdir is set above).
+      library_names=
+      old_library=
+      dlname=
+
+      # Test again, we may have decided not to build it any more
+      if test "$build_libtool_libs" = yes; then
+	# Remove ${wl} instances when linking with ld.
+	# FIXME: should test the right _cmds variable.
+	case $archive_cmds in
+	  *\$LD\ *) wl= ;;
+        esac
+	if test "$hardcode_into_libs" = yes; then
+	  # Hardcode the library paths
+	  hardcode_libdirs=
+	  dep_rpath=
+	  rpath="$finalize_rpath"
+	  test "$opt_mode" != relink && rpath="$compile_rpath$rpath"
+	  for libdir in $rpath; do
+	    if test -n "$hardcode_libdir_flag_spec"; then
+	      if test -n "$hardcode_libdir_separator"; then
+		func_replace_sysroot "$libdir"
+		libdir=$func_replace_sysroot_result
+		if test -z "$hardcode_libdirs"; then
+		  hardcode_libdirs="$libdir"
+		else
+		  # Just accumulate the unique libdirs.
+		  case $hardcode_libdir_separator$hardcode_libdirs$hardcode_libdir_separator in
+		  *"$hardcode_libdir_separator$libdir$hardcode_libdir_separator"*)
+		    ;;
+		  *)
+		    func_append hardcode_libdirs "$hardcode_libdir_separator$libdir"
+		    ;;
+		  esac
+		fi
+	      else
+		eval flag=\"$hardcode_libdir_flag_spec\"
+		func_append dep_rpath " $flag"
+	      fi
+	    elif test -n "$runpath_var"; then
+	      case "$perm_rpath " in
+	      *" $libdir "*) ;;
+	      *) func_append perm_rpath " $libdir" ;;
+	      esac
+	    fi
+	  done
+	  # Substitute the hardcoded libdirs into the rpath.
+	  if test -n "$hardcode_libdir_separator" &&
+	     test -n "$hardcode_libdirs"; then
+	    libdir="$hardcode_libdirs"
+	    eval "dep_rpath=\"$hardcode_libdir_flag_spec\""
+	  fi
+	  if test -n "$runpath_var" && test -n "$perm_rpath"; then
+	    # We should set the runpath_var.
+	    rpath=
+	    for dir in $perm_rpath; do
+	      func_append rpath "$dir:"
+	    done
+	    eval "$runpath_var='$rpath\$$runpath_var'; export $runpath_var"
+	  fi
+	  test -n "$dep_rpath" && deplibs="$dep_rpath $deplibs"
+	fi
+
+	shlibpath="$finalize_shlibpath"
+	test "$opt_mode" != relink && shlibpath="$compile_shlibpath$shlibpath"
+	if test -n "$shlibpath"; then
+	  eval "$shlibpath_var='$shlibpath\$$shlibpath_var'; export $shlibpath_var"
+	fi
+
+	# Get the real and link names of the library.
+	eval shared_ext=\"$shrext_cmds\"
+	eval library_names=\"$library_names_spec\"
+	set dummy $library_names
+	shift
+	realname="$1"
+	shift
+
+	if test -n "$soname_spec"; then
+	  eval soname=\"$soname_spec\"
+	else
+	  soname="$realname"
+	fi
+	if test -z "$dlname"; then
+	  dlname=$soname
+	fi
+
+	lib="$output_objdir/$realname"
+	linknames=
+	for link
+	do
+	  func_append linknames " $link"
+	done
+
+	# Use standard objects if they are pic
+	test -z "$pic_flag" && libobjs=`$ECHO "$libobjs" | $SP2NL | $SED "$lo2o" | $NL2SP`
+	test "X$libobjs" = "X " && libobjs=
+
+	delfiles=
+	if test -n "$export_symbols" && test -n "$include_expsyms"; then
+	  $opt_dry_run || cp "$export_symbols" "$output_objdir/$libname.uexp"
+	  export_symbols="$output_objdir/$libname.uexp"
+	  func_append delfiles " $export_symbols"
+	fi
+
+	orig_export_symbols=
+	case $host_os in
+	cygwin* | mingw* | cegcc*)
+	  if test -n "$export_symbols" && test -z "$export_symbols_regex"; then
+	    # exporting using user supplied symfile
+	    if test "x`$SED 1q $export_symbols`" != xEXPORTS; then
+	      # and it's NOT already a .def file. Must figure out
+	      # which of the given symbols are data symbols and tag
+	      # them as such. So, trigger use of export_symbols_cmds.
+	      # export_symbols gets reassigned inside the "prepare
+	      # the list of exported symbols" if statement, so the
+	      # include_expsyms logic still works.
+	      orig_export_symbols="$export_symbols"
+	      export_symbols=
+	      always_export_symbols=yes
+	    fi
+	  fi
+	  ;;
+	esac
+
+	# Prepare the list of exported symbols
+	if test -z "$export_symbols"; then
+	  if test "$always_export_symbols" = yes || test -n "$export_symbols_regex"; then
+	    func_verbose "generating symbol list for \`$libname.la'"
+	    export_symbols="$output_objdir/$libname.exp"
+	    $opt_dry_run || $RM $export_symbols
+	    cmds=$export_symbols_cmds
+	    save_ifs="$IFS"; IFS='~'
+	    for cmd1 in $cmds; do
+	      IFS="$save_ifs"
+	      # Take the normal branch if the nm_file_list_spec branch
+	      # doesn't work or if tool conversion is not needed.
+	      case $nm_file_list_spec~$to_tool_file_cmd in
+		*~func_convert_file_noop | *~func_convert_file_msys_to_w32 | ~*)
+		  try_normal_branch=yes
+		  eval cmd=\"$cmd1\"
+		  func_len " $cmd"
+		  len=$func_len_result
+		  ;;
+		*)
+		  try_normal_branch=no
+		  ;;
+	      esac
+	      if test "$try_normal_branch" = yes \
+		 && { test "$len" -lt "$max_cmd_len" \
+		      || test "$max_cmd_len" -le -1; }
+	      then
+		func_show_eval "$cmd" 'exit $?'
+		skipped_export=false
+	      elif test -n "$nm_file_list_spec"; then
+		func_basename "$output"
+		output_la=$func_basename_result
+		save_libobjs=$libobjs
+		save_output=$output
+		output=${output_objdir}/${output_la}.nm
+		func_to_tool_file "$output"
+		libobjs=$nm_file_list_spec$func_to_tool_file_result
+		func_append delfiles " $output"
+		func_verbose "creating $NM input file list: $output"
+		for obj in $save_libobjs; do
+		  func_to_tool_file "$obj"
+		  $ECHO "$func_to_tool_file_result"
+		done > "$output"
+		eval cmd=\"$cmd1\"
+		func_show_eval "$cmd" 'exit $?'
+		output=$save_output
+		libobjs=$save_libobjs
+		skipped_export=false
+	      else
+		# The command line is too long to execute in one step.
+		func_verbose "using reloadable object file for export list..."
+		skipped_export=:
+		# Break out early, otherwise skipped_export may be
+		# set to false by a later but shorter cmd.
+		break
+	      fi
+	    done
+	    IFS="$save_ifs"
+	    if test -n "$export_symbols_regex" && test "X$skipped_export" != "X:"; then
+	      func_show_eval '$EGREP -e "$export_symbols_regex" "$export_symbols" > "${export_symbols}T"'
+	      func_show_eval '$MV "${export_symbols}T" "$export_symbols"'
+	    fi
+	  fi
+	fi
+
+	if test -n "$export_symbols" && test -n "$include_expsyms"; then
+	  tmp_export_symbols="$export_symbols"
+	  test -n "$orig_export_symbols" && tmp_export_symbols="$orig_export_symbols"
+	  $opt_dry_run || eval '$ECHO "$include_expsyms" | $SP2NL >> "$tmp_export_symbols"'
+	fi
+
+	if test "X$skipped_export" != "X:" && test -n "$orig_export_symbols"; then
+	  # The given exports_symbols file has to be filtered, so filter it.
+	  func_verbose "filter symbol list for \`$libname.la' to tag DATA exports"
+	  # FIXME: $output_objdir/$libname.filter potentially contains lots of
+	  # 's' commands which not all seds can handle. GNU sed should be fine
+	  # though. Also, the filter scales superlinearly with the number of
+	  # global variables. join(1) would be nice here, but unfortunately
+	  # isn't a blessed tool.
+	  $opt_dry_run || $SED -e '/[ ,]DATA/!d;s,\(.*\)\([ \,].*\),s|^\1$|\1\2|,' < $export_symbols > $output_objdir/$libname.filter
+	  func_append delfiles " $export_symbols $output_objdir/$libname.filter"
+	  export_symbols=$output_objdir/$libname.def
+	  $opt_dry_run || $SED -f $output_objdir/$libname.filter < $orig_export_symbols > $export_symbols
+	fi
+
+	tmp_deplibs=
+	for test_deplib in $deplibs; do
+	  case " $convenience " in
+	  *" $test_deplib "*) ;;
+	  *)
+	    func_append tmp_deplibs " $test_deplib"
+	    ;;
+	  esac
+	done
+	deplibs="$tmp_deplibs"
+
+	if test -n "$convenience"; then
+	  if test -n "$whole_archive_flag_spec" &&
+	    test "$compiler_needs_object" = yes &&
+	    test -z "$libobjs"; then
+	    # extract the archives, so we have objects to list.
+	    # TODO: could optimize this to just extract one archive.
+	    whole_archive_flag_spec=
+	  fi
+	  if test -n "$whole_archive_flag_spec"; then
+	    save_libobjs=$libobjs
+	    eval libobjs=\"\$libobjs $whole_archive_flag_spec\"
+	    test "X$libobjs" = "X " && libobjs=
+	  else
+	    gentop="$output_objdir/${outputname}x"
+	    func_append generated " $gentop"
+
+	    func_extract_archives $gentop $convenience
+	    func_append libobjs " $func_extract_archives_result"
+	    test "X$libobjs" = "X " && libobjs=
+	  fi
+	fi
+
+	if test "$thread_safe" = yes && test -n "$thread_safe_flag_spec"; then
+	  eval flag=\"$thread_safe_flag_spec\"
+	  func_append linker_flags " $flag"
+	fi
+
+	# Make a backup of the uninstalled library when relinking
+	if test "$opt_mode" = relink; then
+	  $opt_dry_run || eval '(cd $output_objdir && $RM ${realname}U && $MV $realname ${realname}U)' || exit $?
+	fi
+
+	# Do each of the archive commands.
+	if test "$module" = yes && test -n "$module_cmds" ; then
+	  if test -n "$export_symbols" && test -n "$module_expsym_cmds"; then
+	    eval test_cmds=\"$module_expsym_cmds\"
+	    cmds=$module_expsym_cmds
+	  else
+	    eval test_cmds=\"$module_cmds\"
+	    cmds=$module_cmds
+	  fi
+	else
+	  if test -n "$export_symbols" && test -n "$archive_expsym_cmds"; then
+	    eval test_cmds=\"$archive_expsym_cmds\"
+	    cmds=$archive_expsym_cmds
+	  else
+	    eval test_cmds=\"$archive_cmds\"
+	    cmds=$archive_cmds
+	  fi
+	fi
+
+	if test "X$skipped_export" != "X:" &&
+	   func_len " $test_cmds" &&
+	   len=$func_len_result &&
+	   test "$len" -lt "$max_cmd_len" || test "$max_cmd_len" -le -1; then
+	  :
+	else
+	  # The command line is too long to link in one step, link piecewise
+	  # or, if using GNU ld and skipped_export is not :, use a linker
+	  # script.
+
+	  # Save the value of $output and $libobjs because we want to
+	  # use them later.  If we have whole_archive_flag_spec, we
+	  # want to use save_libobjs as it was before
+	  # whole_archive_flag_spec was expanded, because we can't
+	  # assume the linker understands whole_archive_flag_spec.
+	  # This may have to be revisited, in case too many
+	  # convenience libraries get linked in and end up exceeding
+	  # the spec.
+	  if test -z "$convenience" || test -z "$whole_archive_flag_spec"; then
+	    save_libobjs=$libobjs
+	  fi
+	  save_output=$output
+	  func_basename "$output"
+	  output_la=$func_basename_result
+
+	  # Clear the reloadable object creation command queue and
+	  # initialize k to one.
+	  test_cmds=
+	  concat_cmds=
+	  objlist=
+	  last_robj=
+	  k=1
+
+	  if test -n "$save_libobjs" && test "X$skipped_export" != "X:" && test "$with_gnu_ld" = yes; then
+	    output=${output_objdir}/${output_la}.lnkscript
+	    func_verbose "creating GNU ld script: $output"
+	    echo 'INPUT (' > $output
+	    for obj in $save_libobjs
+	    do
+	      func_to_tool_file "$obj"
+	      $ECHO "$func_to_tool_file_result" >> $output
+	    done
+	    echo ')' >> $output
+	    func_append delfiles " $output"
+	    func_to_tool_file "$output"
+	    output=$func_to_tool_file_result
+	  elif test -n "$save_libobjs" && test "X$skipped_export" != "X:" && test "X$file_list_spec" != X; then
+	    output=${output_objdir}/${output_la}.lnk
+	    func_verbose "creating linker input file list: $output"
+	    : > $output
+	    set x $save_libobjs
+	    shift
+	    firstobj=
+	    if test "$compiler_needs_object" = yes; then
+	      firstobj="$1 "
+	      shift
+	    fi
+	    for obj
+	    do
+	      func_to_tool_file "$obj"
+	      $ECHO "$func_to_tool_file_result" >> $output
+	    done
+	    func_append delfiles " $output"
+	    func_to_tool_file "$output"
+	    output=$firstobj\"$file_list_spec$func_to_tool_file_result\"
+	  else
+	    if test -n "$save_libobjs"; then
+	      func_verbose "creating reloadable object files..."
+	      output=$output_objdir/$output_la-${k}.$objext
+	      eval test_cmds=\"$reload_cmds\"
+	      func_len " $test_cmds"
+	      len0=$func_len_result
+	      len=$len0
+
+	      # Loop over the list of objects to be linked.
+	      for obj in $save_libobjs
+	      do
+		func_len " $obj"
+		func_arith $len + $func_len_result
+		len=$func_arith_result
+		if test "X$objlist" = X ||
+		   test "$len" -lt "$max_cmd_len"; then
+		  func_append objlist " $obj"
+		else
+		  # The command $test_cmds is almost too long, add a
+		  # command to the queue.
+		  if test "$k" -eq 1 ; then
+		    # The first file doesn't have a previous command to add.
+		    reload_objs=$objlist
+		    eval concat_cmds=\"$reload_cmds\"
+		  else
+		    # All subsequent reloadable object files will link in
+		    # the last one created.
+		    reload_objs="$objlist $last_robj"
+		    eval concat_cmds=\"\$concat_cmds~$reload_cmds~\$RM $last_robj\"
+		  fi
+		  last_robj=$output_objdir/$output_la-${k}.$objext
+		  func_arith $k + 1
+		  k=$func_arith_result
+		  output=$output_objdir/$output_la-${k}.$objext
+		  objlist=" $obj"
+		  func_len " $last_robj"
+		  func_arith $len0 + $func_len_result
+		  len=$func_arith_result
+		fi
+	      done
+	      # Handle the remaining objects by creating one last
+	      # reloadable object file.  All subsequent reloadable object
+	      # files will link in the last one created.
+	      test -z "$concat_cmds" || concat_cmds=$concat_cmds~
+	      reload_objs="$objlist $last_robj"
+	      eval concat_cmds=\"\${concat_cmds}$reload_cmds\"
+	      if test -n "$last_robj"; then
+	        eval concat_cmds=\"\${concat_cmds}~\$RM $last_robj\"
+	      fi
+	      func_append delfiles " $output"
+
+	    else
+	      output=
+	    fi
+
+	    if ${skipped_export-false}; then
+	      func_verbose "generating symbol list for \`$libname.la'"
+	      export_symbols="$output_objdir/$libname.exp"
+	      $opt_dry_run || $RM $export_symbols
+	      libobjs=$output
+	      # Append the command to create the export file.
+	      test -z "$concat_cmds" || concat_cmds=$concat_cmds~
+	      eval concat_cmds=\"\$concat_cmds$export_symbols_cmds\"
+	      if test -n "$last_robj"; then
+		eval concat_cmds=\"\$concat_cmds~\$RM $last_robj\"
+	      fi
+	    fi
+
+	    test -n "$save_libobjs" &&
+	      func_verbose "creating a temporary reloadable object file: $output"
+
+	    # Loop through the commands generated above and execute them.
+	    save_ifs="$IFS"; IFS='~'
+	    for cmd in $concat_cmds; do
+	      IFS="$save_ifs"
+	      $opt_silent || {
+		  func_quote_for_expand "$cmd"
+		  eval "func_echo $func_quote_for_expand_result"
+	      }
+	      $opt_dry_run || eval "$cmd" || {
+		lt_exit=$?
+
+		# Restore the uninstalled library and exit
+		if test "$opt_mode" = relink; then
+		  ( cd "$output_objdir" && \
+		    $RM "${realname}T" && \
+		    $MV "${realname}U" "$realname" )
+		fi
+
+		exit $lt_exit
+	      }
+	    done
+	    IFS="$save_ifs"
+
+	    if test -n "$export_symbols_regex" && ${skipped_export-false}; then
+	      func_show_eval '$EGREP -e "$export_symbols_regex" "$export_symbols" > "${export_symbols}T"'
+	      func_show_eval '$MV "${export_symbols}T" "$export_symbols"'
+	    fi
+	  fi
+
+          if ${skipped_export-false}; then
+	    if test -n "$export_symbols" && test -n "$include_expsyms"; then
+	      tmp_export_symbols="$export_symbols"
+	      test -n "$orig_export_symbols" && tmp_export_symbols="$orig_export_symbols"
+	      $opt_dry_run || eval '$ECHO "$include_expsyms" | $SP2NL >> "$tmp_export_symbols"'
+	    fi
+
+	    if test -n "$orig_export_symbols"; then
+	      # The given exports_symbols file has to be filtered, so filter it.
+	      func_verbose "filter symbol list for \`$libname.la' to tag DATA exports"
+	      # FIXME: $output_objdir/$libname.filter potentially contains lots of
+	      # 's' commands which not all seds can handle. GNU sed should be fine
+	      # though. Also, the filter scales superlinearly with the number of
+	      # global variables. join(1) would be nice here, but unfortunately
+	      # isn't a blessed tool.
+	      $opt_dry_run || $SED -e '/[ ,]DATA/!d;s,\(.*\)\([ \,].*\),s|^\1$|\1\2|,' < $export_symbols > $output_objdir/$libname.filter
+	      func_append delfiles " $export_symbols $output_objdir/$libname.filter"
+	      export_symbols=$output_objdir/$libname.def
+	      $opt_dry_run || $SED -f $output_objdir/$libname.filter < $orig_export_symbols > $export_symbols
+	    fi
+	  fi
+
+	  libobjs=$output
+	  # Restore the value of output.
+	  output=$save_output
+
+	  if test -n "$convenience" && test -n "$whole_archive_flag_spec"; then
+	    eval libobjs=\"\$libobjs $whole_archive_flag_spec\"
+	    test "X$libobjs" = "X " && libobjs=
+	  fi
+	  # Expand the library linking commands again to reset the
+	  # value of $libobjs for piecewise linking.
+
+	  # Do each of the archive commands.
+	  if test "$module" = yes && test -n "$module_cmds" ; then
+	    if test -n "$export_symbols" && test -n "$module_expsym_cmds"; then
+	      cmds=$module_expsym_cmds
+	    else
+	      cmds=$module_cmds
+	    fi
+	  else
+	    if test -n "$export_symbols" && test -n "$archive_expsym_cmds"; then
+	      cmds=$archive_expsym_cmds
+	    else
+	      cmds=$archive_cmds
+	    fi
+	  fi
+	fi
+
+	if test -n "$delfiles"; then
+	  # Append the command to remove temporary files to $cmds.
+	  eval cmds=\"\$cmds~\$RM $delfiles\"
+	fi
+
+	# Add any objects from preloaded convenience libraries
+	if test -n "$dlprefiles"; then
+	  gentop="$output_objdir/${outputname}x"
+	  func_append generated " $gentop"
+
+	  func_extract_archives $gentop $dlprefiles
+	  func_append libobjs " $func_extract_archives_result"
+	  test "X$libobjs" = "X " && libobjs=
+	fi
+
+	save_ifs="$IFS"; IFS='~'
+	for cmd in $cmds; do
+	  IFS="$save_ifs"
+	  eval cmd=\"$cmd\"
+	  $opt_silent || {
+	    func_quote_for_expand "$cmd"
+	    eval "func_echo $func_quote_for_expand_result"
+	  }
+	  $opt_dry_run || eval "$cmd" || {
+	    lt_exit=$?
+
+	    # Restore the uninstalled library and exit
+	    if test "$opt_mode" = relink; then
+	      ( cd "$output_objdir" && \
+	        $RM "${realname}T" && \
+		$MV "${realname}U" "$realname" )
+	    fi
+
+	    exit $lt_exit
+	  }
+	done
+	IFS="$save_ifs"
+
+	# Restore the uninstalled library and exit
+	if test "$opt_mode" = relink; then
+	  $opt_dry_run || eval '(cd $output_objdir && $RM ${realname}T && $MV $realname ${realname}T && $MV ${realname}U $realname)' || exit $?
+
+	  if test -n "$convenience"; then
+	    if test -z "$whole_archive_flag_spec"; then
+	      func_show_eval '${RM}r "$gentop"'
+	    fi
+	  fi
+
+	  exit $EXIT_SUCCESS
+	fi
+
+	# Create links to the real library.
+	for linkname in $linknames; do
+	  if test "$realname" != "$linkname"; then
+	    func_show_eval '(cd "$output_objdir" && $RM "$linkname" && $LN_S "$realname" "$linkname")' 'exit $?'
+	  fi
+	done
+
+	# If -module or -export-dynamic was specified, set the dlname.
+	if test "$module" = yes || test "$export_dynamic" = yes; then
+	  # On all known operating systems, these are identical.
+	  dlname="$soname"
+	fi
+      fi
+      ;;
+
+    obj)
+      if test -n "$dlfiles$dlprefiles" || test "$dlself" != no; then
+	func_warning "\`-dlopen' is ignored for objects"
+      fi
+
+      case " $deplibs" in
+      *\ -l* | *\ -L*)
+	func_warning "\`-l' and \`-L' are ignored for objects" ;;
+      esac
+
+      test -n "$rpath" && \
+	func_warning "\`-rpath' is ignored for objects"
+
+      test -n "$xrpath" && \
+	func_warning "\`-R' is ignored for objects"
+
+      test -n "$vinfo" && \
+	func_warning "\`-version-info' is ignored for objects"
+
+      test -n "$release" && \
+	func_warning "\`-release' is ignored for objects"
+
+      case $output in
+      *.lo)
+	test -n "$objs$old_deplibs" && \
+	  func_fatal_error "cannot build library object \`$output' from non-libtool objects"
+
+	libobj=$output
+	func_lo2o "$libobj"
+	obj=$func_lo2o_result
+	;;
+      *)
+	libobj=
+	obj="$output"
+	;;
+      esac
+
+      # Delete the old objects.
+      $opt_dry_run || $RM $obj $libobj
+
+      # Objects from convenience libraries.  This assumes
+      # single-version convenience libraries.  Whenever we create
+      # different ones for PIC/non-PIC, this we'll have to duplicate
+      # the extraction.
+      reload_conv_objs=
+      gentop=
+      # reload_cmds runs $LD directly, so let us get rid of
+      # -Wl from whole_archive_flag_spec and hope we can get by with
+      # turning comma into space..
+      wl=
+
+      if test -n "$convenience"; then
+	if test -n "$whole_archive_flag_spec"; then
+	  eval tmp_whole_archive_flags=\"$whole_archive_flag_spec\"
+	  reload_conv_objs=$reload_objs\ `$ECHO "$tmp_whole_archive_flags" | $SED 's|,| |g'`
+	else
+	  gentop="$output_objdir/${obj}x"
+	  func_append generated " $gentop"
+
+	  func_extract_archives $gentop $convenience
+	  reload_conv_objs="$reload_objs $func_extract_archives_result"
+	fi
+      fi
+
+      # If we're not building shared, we need to use non_pic_objs
+      test "$build_libtool_libs" != yes && libobjs="$non_pic_objects"
+
+      # Create the old-style object.
+      reload_objs="$objs$old_deplibs "`$ECHO "$libobjs" | $SP2NL | $SED "/\.${libext}$/d; /\.lib$/d; $lo2o" | $NL2SP`" $reload_conv_objs" ### testsuite: skip nested quoting test
+
+      output="$obj"
+      func_execute_cmds "$reload_cmds" 'exit $?'
+
+      # Exit if we aren't doing a library object file.
+      if test -z "$libobj"; then
+	if test -n "$gentop"; then
+	  func_show_eval '${RM}r "$gentop"'
+	fi
+
+	exit $EXIT_SUCCESS
+      fi
+
+      if test "$build_libtool_libs" != yes; then
+	if test -n "$gentop"; then
+	  func_show_eval '${RM}r "$gentop"'
+	fi
+
+	# Create an invalid libtool object if no PIC, so that we don't
+	# accidentally link it into a program.
+	# $show "echo timestamp > $libobj"
+	# $opt_dry_run || eval "echo timestamp > $libobj" || exit $?
+	exit $EXIT_SUCCESS
+      fi
+
+      if test -n "$pic_flag" || test "$pic_mode" != default; then
+	# Only do commands if we really have different PIC objects.
+	reload_objs="$libobjs $reload_conv_objs"
+	output="$libobj"
+	func_execute_cmds "$reload_cmds" 'exit $?'
+      fi
+
+      if test -n "$gentop"; then
+	func_show_eval '${RM}r "$gentop"'
+      fi
+
+      exit $EXIT_SUCCESS
+      ;;
+
+    prog)
+      case $host in
+	*cygwin*) func_stripname '' '.exe' "$output"
+	          output=$func_stripname_result.exe;;
+      esac
+      test -n "$vinfo" && \
+	func_warning "\`-version-info' is ignored for programs"
+
+      test -n "$release" && \
+	func_warning "\`-release' is ignored for programs"
+
+      test "$preload" = yes \
+        && test "$dlopen_support" = unknown \
+	&& test "$dlopen_self" = unknown \
+	&& test "$dlopen_self_static" = unknown && \
+	  func_warning "\`LT_INIT([dlopen])' not used. Assuming no dlopen support."
+
+      case $host in
+      *-*-rhapsody* | *-*-darwin1.[012])
+	# On Rhapsody replace the C library is the System framework
+	compile_deplibs=`$ECHO " $compile_deplibs" | $SED 's/ -lc / System.ltframework /'`
+	finalize_deplibs=`$ECHO " $finalize_deplibs" | $SED 's/ -lc / System.ltframework /'`
+	;;
+      esac
+
+      case $host in
+      *-*-darwin*)
+	# Don't allow lazy linking, it breaks C++ global constructors
+	# But is supposedly fixed on 10.4 or later (yay!).
+	if test "$tagname" = CXX ; then
+	  case ${MACOSX_DEPLOYMENT_TARGET-10.0} in
+	    10.[0123])
+	      func_append compile_command " ${wl}-bind_at_load"
+	      func_append finalize_command " ${wl}-bind_at_load"
+	    ;;
+	  esac
+	fi
+	# Time to change all our "foo.ltframework" stuff back to "-framework foo"
+	compile_deplibs=`$ECHO " $compile_deplibs" | $SED 's% \([^ $]*\).ltframework% -framework \1%g'`
+	finalize_deplibs=`$ECHO " $finalize_deplibs" | $SED 's% \([^ $]*\).ltframework% -framework \1%g'`
+	;;
+      esac
+
+
+      # move library search paths that coincide with paths to not yet
+      # installed libraries to the beginning of the library search list
+      new_libs=
+      for path in $notinst_path; do
+	case " $new_libs " in
+	*" -L$path/$objdir "*) ;;
+	*)
+	  case " $compile_deplibs " in
+	  *" -L$path/$objdir "*)
+	    func_append new_libs " -L$path/$objdir" ;;
+	  esac
+	  ;;
+	esac
+      done
+      for deplib in $compile_deplibs; do
+	case $deplib in
+	-L*)
+	  case " $new_libs " in
+	  *" $deplib "*) ;;
+	  *) func_append new_libs " $deplib" ;;
+	  esac
+	  ;;
+	*) func_append new_libs " $deplib" ;;
+	esac
+      done
+      compile_deplibs="$new_libs"
+
+
+      func_append compile_command " $compile_deplibs"
+      func_append finalize_command " $finalize_deplibs"
+
+      if test -n "$rpath$xrpath"; then
+	# If the user specified any rpath flags, then add them.
+	for libdir in $rpath $xrpath; do
+	  # This is the magic to use -rpath.
+	  case "$finalize_rpath " in
+	  *" $libdir "*) ;;
+	  *) func_append finalize_rpath " $libdir" ;;
+	  esac
+	done
+      fi
+
+      # Now hardcode the library paths
+      rpath=
+      hardcode_libdirs=
+      for libdir in $compile_rpath $finalize_rpath; do
+	if test -n "$hardcode_libdir_flag_spec"; then
+	  if test -n "$hardcode_libdir_separator"; then
+	    if test -z "$hardcode_libdirs"; then
+	      hardcode_libdirs="$libdir"
+	    else
+	      # Just accumulate the unique libdirs.
+	      case $hardcode_libdir_separator$hardcode_libdirs$hardcode_libdir_separator in
+	      *"$hardcode_libdir_separator$libdir$hardcode_libdir_separator"*)
+		;;
+	      *)
+		func_append hardcode_libdirs "$hardcode_libdir_separator$libdir"
+		;;
+	      esac
+	    fi
+	  else
+	    eval flag=\"$hardcode_libdir_flag_spec\"
+	    func_append rpath " $flag"
+	  fi
+	elif test -n "$runpath_var"; then
+	  case "$perm_rpath " in
+	  *" $libdir "*) ;;
+	  *) func_append perm_rpath " $libdir" ;;
+	  esac
+	fi
+	case $host in
+	*-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-os2* | *-cegcc*)
+	  testbindir=`${ECHO} "$libdir" | ${SED} -e 's*/lib$*/bin*'`
+	  case :$dllsearchpath: in
+	  *":$libdir:"*) ;;
+	  ::) dllsearchpath=$libdir;;
+	  *) func_append dllsearchpath ":$libdir";;
+	  esac
+	  case :$dllsearchpath: in
+	  *":$testbindir:"*) ;;
+	  ::) dllsearchpath=$testbindir;;
+	  *) func_append dllsearchpath ":$testbindir";;
+	  esac
+	  ;;
+	esac
+      done
+      # Substitute the hardcoded libdirs into the rpath.
+      if test -n "$hardcode_libdir_separator" &&
+	 test -n "$hardcode_libdirs"; then
+	libdir="$hardcode_libdirs"
+	eval rpath=\" $hardcode_libdir_flag_spec\"
+      fi
+      compile_rpath="$rpath"
+
+      rpath=
+      hardcode_libdirs=
+      for libdir in $finalize_rpath; do
+	if test -n "$hardcode_libdir_flag_spec"; then
+	  if test -n "$hardcode_libdir_separator"; then
+	    if test -z "$hardcode_libdirs"; then
+	      hardcode_libdirs="$libdir"
+	    else
+	      # Just accumulate the unique libdirs.
+	      case $hardcode_libdir_separator$hardcode_libdirs$hardcode_libdir_separator in
+	      *"$hardcode_libdir_separator$libdir$hardcode_libdir_separator"*)
+		;;
+	      *)
+		func_append hardcode_libdirs "$hardcode_libdir_separator$libdir"
+		;;
+	      esac
+	    fi
+	  else
+	    eval flag=\"$hardcode_libdir_flag_spec\"
+	    func_append rpath " $flag"
+	  fi
+	elif test -n "$runpath_var"; then
+	  case "$finalize_perm_rpath " in
+	  *" $libdir "*) ;;
+	  *) func_append finalize_perm_rpath " $libdir" ;;
+	  esac
+	fi
+      done
+      # Substitute the hardcoded libdirs into the rpath.
+      if test -n "$hardcode_libdir_separator" &&
+	 test -n "$hardcode_libdirs"; then
+	libdir="$hardcode_libdirs"
+	eval rpath=\" $hardcode_libdir_flag_spec\"
+      fi
+      finalize_rpath="$rpath"
+
+      if test -n "$libobjs" && test "$build_old_libs" = yes; then
+	# Transform all the library objects into standard objects.
+	compile_command=`$ECHO "$compile_command" | $SP2NL | $SED "$lo2o" | $NL2SP`
+	finalize_command=`$ECHO "$finalize_command" | $SP2NL | $SED "$lo2o" | $NL2SP`
+      fi
+
+      func_generate_dlsyms "$outputname" "@PROGRAM@" "no"
+
+      # template prelinking step
+      if test -n "$prelink_cmds"; then
+	func_execute_cmds "$prelink_cmds" 'exit $?'
+      fi
+
+      wrappers_required=yes
+      case $host in
+      *cegcc* | *mingw32ce*)
+        # Disable wrappers for cegcc and mingw32ce hosts, we are cross compiling anyway.
+        wrappers_required=no
+        ;;
+      *cygwin* | *mingw* )
+        if test "$build_libtool_libs" != yes; then
+          wrappers_required=no
+        fi
+        ;;
+      *)
+        if test "$need_relink" = no || test "$build_libtool_libs" != yes; then
+          wrappers_required=no
+        fi
+        ;;
+      esac
+      if test "$wrappers_required" = no; then
+	# Replace the output file specification.
+	compile_command=`$ECHO "$compile_command" | $SED 's%@OUTPUT@%'"$output"'%g'`
+	link_command="$compile_command$compile_rpath"
+
+	# We have no uninstalled library dependencies, so finalize right now.
+	exit_status=0
+	func_show_eval "$link_command" 'exit_status=$?'
+
+	if test -n "$postlink_cmds"; then
+	  func_to_tool_file "$output"
+	  postlink_cmds=`func_echo_all "$postlink_cmds" | $SED -e 's%@OUTPUT@%'"$output"'%g' -e 's%@TOOL_OUTPUT@%'"$func_to_tool_file_result"'%g'`
+	  func_execute_cmds "$postlink_cmds" 'exit $?'
+	fi
+
+	# Delete the generated files.
+	if test -f "$output_objdir/${outputname}S.${objext}"; then
+	  func_show_eval '$RM "$output_objdir/${outputname}S.${objext}"'
+	fi
+
+	exit $exit_status
+      fi
+
+      if test -n "$compile_shlibpath$finalize_shlibpath"; then
+	compile_command="$shlibpath_var=\"$compile_shlibpath$finalize_shlibpath\$$shlibpath_var\" $compile_command"
+      fi
+      if test -n "$finalize_shlibpath"; then
+	finalize_command="$shlibpath_var=\"$finalize_shlibpath\$$shlibpath_var\" $finalize_command"
+      fi
+
+      compile_var=
+      finalize_var=
+      if test -n "$runpath_var"; then
+	if test -n "$perm_rpath"; then
+	  # We should set the runpath_var.
+	  rpath=
+	  for dir in $perm_rpath; do
+	    func_append rpath "$dir:"
+	  done
+	  compile_var="$runpath_var=\"$rpath\$$runpath_var\" "
+	fi
+	if test -n "$finalize_perm_rpath"; then
+	  # We should set the runpath_var.
+	  rpath=
+	  for dir in $finalize_perm_rpath; do
+	    func_append rpath "$dir:"
+	  done
+	  finalize_var="$runpath_var=\"$rpath\$$runpath_var\" "
+	fi
+      fi
+
+      if test "$no_install" = yes; then
+	# We don't need to create a wrapper script.
+	link_command="$compile_var$compile_command$compile_rpath"
+	# Replace the output file specification.
+	link_command=`$ECHO "$link_command" | $SED 's%@OUTPUT@%'"$output"'%g'`
+	# Delete the old output file.
+	$opt_dry_run || $RM $output
+	# Link the executable and exit
+	func_show_eval "$link_command" 'exit $?'
+
+	if test -n "$postlink_cmds"; then
+	  func_to_tool_file "$output"
+	  postlink_cmds=`func_echo_all "$postlink_cmds" | $SED -e 's%@OUTPUT@%'"$output"'%g' -e 's%@TOOL_OUTPUT@%'"$func_to_tool_file_result"'%g'`
+	  func_execute_cmds "$postlink_cmds" 'exit $?'
+	fi
+
+	exit $EXIT_SUCCESS
+      fi
+
+      if test "$hardcode_action" = relink; then
+	# Fast installation is not supported
+	link_command="$compile_var$compile_command$compile_rpath"
+	relink_command="$finalize_var$finalize_command$finalize_rpath"
+
+	func_warning "this platform does not like uninstalled shared libraries"
+	func_warning "\`$output' will be relinked during installation"
+      else
+	if test "$fast_install" != no; then
+	  link_command="$finalize_var$compile_command$finalize_rpath"
+	  if test "$fast_install" = yes; then
+	    relink_command=`$ECHO "$compile_var$compile_command$compile_rpath" | $SED 's%@OUTPUT@%\$progdir/\$file%g'`
+	  else
+	    # fast_install is set to needless
+	    relink_command=
+	  fi
+	else
+	  link_command="$compile_var$compile_command$compile_rpath"
+	  relink_command="$finalize_var$finalize_command$finalize_rpath"
+	fi
+      fi
+
+      # Replace the output file specification.
+      link_command=`$ECHO "$link_command" | $SED 's%@OUTPUT@%'"$output_objdir/$outputname"'%g'`
+
+      # Delete the old output files.
+      $opt_dry_run || $RM $output $output_objdir/$outputname $output_objdir/lt-$outputname
+
+      func_show_eval "$link_command" 'exit $?'
+
+      if test -n "$postlink_cmds"; then
+	func_to_tool_file "$output_objdir/$outputname"
+	postlink_cmds=`func_echo_all "$postlink_cmds" | $SED -e 's%@OUTPUT@%'"$output_objdir/$outputname"'%g' -e 's%@TOOL_OUTPUT@%'"$func_to_tool_file_result"'%g'`
+	func_execute_cmds "$postlink_cmds" 'exit $?'
+      fi
+
+      # Now create the wrapper script.
+      func_verbose "creating $output"
+
+      # Quote the relink command for shipping.
+      if test -n "$relink_command"; then
+	# Preserve any variables that may affect compiler behavior
+	for var in $variables_saved_for_relink; do
+	  if eval test -z \"\${$var+set}\"; then
+	    relink_command="{ test -z \"\${$var+set}\" || $lt_unset $var || { $var=; export $var; }; }; $relink_command"
+	  elif eval var_value=\$$var; test -z "$var_value"; then
+	    relink_command="$var=; export $var; $relink_command"
+	  else
+	    func_quote_for_eval "$var_value"
+	    relink_command="$var=$func_quote_for_eval_result; export $var; $relink_command"
+	  fi
+	done
+	relink_command="(cd `pwd`; $relink_command)"
+	relink_command=`$ECHO "$relink_command" | $SED "$sed_quote_subst"`
+      fi
+
+      # Only actually do things if not in dry run mode.
+      $opt_dry_run || {
+	# win32 will think the script is a binary if it has
+	# a .exe suffix, so we strip it off here.
+	case $output in
+	  *.exe) func_stripname '' '.exe' "$output"
+	         output=$func_stripname_result ;;
+	esac
+	# test for cygwin because mv fails w/o .exe extensions
+	case $host in
+	  *cygwin*)
+	    exeext=.exe
+	    func_stripname '' '.exe' "$outputname"
+	    outputname=$func_stripname_result ;;
+	  *) exeext= ;;
+	esac
+	case $host in
+	  *cygwin* | *mingw* )
+	    func_dirname_and_basename "$output" "" "."
+	    output_name=$func_basename_result
+	    output_path=$func_dirname_result
+	    cwrappersource="$output_path/$objdir/lt-$output_name.c"
+	    cwrapper="$output_path/$output_name.exe"
+	    $RM $cwrappersource $cwrapper
+	    trap "$RM $cwrappersource $cwrapper; exit $EXIT_FAILURE" 1 2 15
+
+	    func_emit_cwrapperexe_src > $cwrappersource
+
+	    # The wrapper executable is built using the $host compiler,
+	    # because it contains $host paths and files. If cross-
+	    # compiling, it, like the target executable, must be
+	    # executed on the $host or under an emulation environment.
+	    $opt_dry_run || {
+	      $LTCC $LTCFLAGS -o $cwrapper $cwrappersource
+	      $STRIP $cwrapper
+	    }
+
+	    # Now, create the wrapper script for func_source use:
+	    func_ltwrapper_scriptname $cwrapper
+	    $RM $func_ltwrapper_scriptname_result
+	    trap "$RM $func_ltwrapper_scriptname_result; exit $EXIT_FAILURE" 1 2 15
+	    $opt_dry_run || {
+	      # note: this script will not be executed, so do not chmod.
+	      if test "x$build" = "x$host" ; then
+		$cwrapper --lt-dump-script > $func_ltwrapper_scriptname_result
+	      else
+		func_emit_wrapper no > $func_ltwrapper_scriptname_result
+	      fi
+	    }
+	  ;;
+	  * )
+	    $RM $output
+	    trap "$RM $output; exit $EXIT_FAILURE" 1 2 15
+
+	    func_emit_wrapper no > $output
+	    chmod +x $output
+	  ;;
+	esac
+      }
+      exit $EXIT_SUCCESS
+      ;;
+    esac
+
+    # See if we need to build an old-fashioned archive.
+    for oldlib in $oldlibs; do
+
+      if test "$build_libtool_libs" = convenience; then
+	oldobjs="$libobjs_save $symfileobj"
+	addlibs="$convenience"
+	build_libtool_libs=no
+      else
+	if test "$build_libtool_libs" = module; then
+	  oldobjs="$libobjs_save"
+	  build_libtool_libs=no
+	else
+	  oldobjs="$old_deplibs $non_pic_objects"
+	  if test "$preload" = yes && test -f "$symfileobj"; then
+	    func_append oldobjs " $symfileobj"
+	  fi
+	fi
+	addlibs="$old_convenience"
+      fi
+
+      if test -n "$addlibs"; then
+	gentop="$output_objdir/${outputname}x"
+	func_append generated " $gentop"
+
+	func_extract_archives $gentop $addlibs
+	func_append oldobjs " $func_extract_archives_result"
+      fi
+
+      # Do each command in the archive commands.
+      if test -n "$old_archive_from_new_cmds" && test "$build_libtool_libs" = yes; then
+	cmds=$old_archive_from_new_cmds
+      else
+
+	# Add any objects from preloaded convenience libraries
+	if test -n "$dlprefiles"; then
+	  gentop="$output_objdir/${outputname}x"
+	  func_append generated " $gentop"
+
+	  func_extract_archives $gentop $dlprefiles
+	  func_append oldobjs " $func_extract_archives_result"
+	fi
+
+	# POSIX demands no paths to be encoded in archives.  We have
+	# to avoid creating archives with duplicate basenames if we
+	# might have to extract them afterwards, e.g., when creating a
+	# static archive out of a convenience library, or when linking
+	# the entirety of a libtool archive into another (currently
+	# not supported by libtool).
+	if (for obj in $oldobjs
+	    do
+	      func_basename "$obj"
+	      $ECHO "$func_basename_result"
+	    done | sort | sort -uc >/dev/null 2>&1); then
+	  :
+	else
+	  echo "copying selected object files to avoid basename conflicts..."
+	  gentop="$output_objdir/${outputname}x"
+	  func_append generated " $gentop"
+	  func_mkdir_p "$gentop"
+	  save_oldobjs=$oldobjs
+	  oldobjs=
+	  counter=1
+	  for obj in $save_oldobjs
+	  do
+	    func_basename "$obj"
+	    objbase="$func_basename_result"
+	    case " $oldobjs " in
+	    " ") oldobjs=$obj ;;
+	    *[\ /]"$objbase "*)
+	      while :; do
+		# Make sure we don't pick an alternate name that also
+		# overlaps.
+		newobj=lt$counter-$objbase
+		func_arith $counter + 1
+		counter=$func_arith_result
+		case " $oldobjs " in
+		*[\ /]"$newobj "*) ;;
+		*) if test ! -f "$gentop/$newobj"; then break; fi ;;
+		esac
+	      done
+	      func_show_eval "ln $obj $gentop/$newobj || cp $obj $gentop/$newobj"
+	      func_append oldobjs " $gentop/$newobj"
+	      ;;
+	    *) func_append oldobjs " $obj" ;;
+	    esac
+	  done
+	fi
+	func_to_tool_file "$oldlib" func_convert_file_msys_to_w32
+	tool_oldlib=$func_to_tool_file_result
+	eval cmds=\"$old_archive_cmds\"
+
+	func_len " $cmds"
+	len=$func_len_result
+	if test "$len" -lt "$max_cmd_len" || test "$max_cmd_len" -le -1; then
+	  cmds=$old_archive_cmds
+	elif test -n "$archiver_list_spec"; then
+	  func_verbose "using command file archive linking..."
+	  for obj in $oldobjs
+	  do
+	    func_to_tool_file "$obj"
+	    $ECHO "$func_to_tool_file_result"
+	  done > $output_objdir/$libname.libcmd
+	  func_to_tool_file "$output_objdir/$libname.libcmd"
+	  oldobjs=" $archiver_list_spec$func_to_tool_file_result"
+	  cmds=$old_archive_cmds
+	else
+	  # the command line is too long to link in one step, link in parts
+	  func_verbose "using piecewise archive linking..."
+	  save_RANLIB=$RANLIB
+	  RANLIB=:
+	  objlist=
+	  concat_cmds=
+	  save_oldobjs=$oldobjs
+	  oldobjs=
+	  # Is there a better way of finding the last object in the list?
+	  for obj in $save_oldobjs
+	  do
+	    last_oldobj=$obj
+	  done
+	  eval test_cmds=\"$old_archive_cmds\"
+	  func_len " $test_cmds"
+	  len0=$func_len_result
+	  len=$len0
+	  for obj in $save_oldobjs
+	  do
+	    func_len " $obj"
+	    func_arith $len + $func_len_result
+	    len=$func_arith_result
+	    func_append objlist " $obj"
+	    if test "$len" -lt "$max_cmd_len"; then
+	      :
+	    else
+	      # the above command should be used before it gets too long
+	      oldobjs=$objlist
+	      if test "$obj" = "$last_oldobj" ; then
+		RANLIB=$save_RANLIB
+	      fi
+	      test -z "$concat_cmds" || concat_cmds=$concat_cmds~
+	      eval concat_cmds=\"\${concat_cmds}$old_archive_cmds\"
+	      objlist=
+	      len=$len0
+	    fi
+	  done
+	  RANLIB=$save_RANLIB
+	  oldobjs=$objlist
+	  if test "X$oldobjs" = "X" ; then
+	    eval cmds=\"\$concat_cmds\"
+	  else
+	    eval cmds=\"\$concat_cmds~\$old_archive_cmds\"
+	  fi
+	fi
+      fi
+      func_execute_cmds "$cmds" 'exit $?'
+    done
+
+    test -n "$generated" && \
+      func_show_eval "${RM}r$generated"
+
+    # Now create the libtool archive.
+    case $output in
+    *.la)
+      old_library=
+      test "$build_old_libs" = yes && old_library="$libname.$libext"
+      func_verbose "creating $output"
+
+      # Preserve any variables that may affect compiler behavior
+      for var in $variables_saved_for_relink; do
+	if eval test -z \"\${$var+set}\"; then
+	  relink_command="{ test -z \"\${$var+set}\" || $lt_unset $var || { $var=; export $var; }; }; $relink_command"
+	elif eval var_value=\$$var; test -z "$var_value"; then
+	  relink_command="$var=; export $var; $relink_command"
+	else
+	  func_quote_for_eval "$var_value"
+	  relink_command="$var=$func_quote_for_eval_result; export $var; $relink_command"
+	fi
+      done
+      # Quote the link command for shipping.
+      relink_command="(cd `pwd`; $SHELL $progpath $preserve_args --mode=relink $libtool_args @inst_prefix_dir@)"
+      relink_command=`$ECHO "$relink_command" | $SED "$sed_quote_subst"`
+      if test "$hardcode_automatic" = yes ; then
+	relink_command=
+      fi
+
+      # Only create the output if not a dry run.
+      $opt_dry_run || {
+	for installed in no yes; do
+	  if test "$installed" = yes; then
+	    if test -z "$install_libdir"; then
+	      break
+	    fi
+	    output="$output_objdir/$outputname"i
+	    # Replace all uninstalled libtool libraries with the installed ones
+	    newdependency_libs=
+	    for deplib in $dependency_libs; do
+	      case $deplib in
+	      *.la)
+		func_basename "$deplib"
+		name="$func_basename_result"
+		func_resolve_sysroot "$deplib"
+		eval libdir=`${SED} -n -e 's/^libdir=\(.*\)$/\1/p' $func_resolve_sysroot_result`
+		test -z "$libdir" && \
+		  func_fatal_error "\`$deplib' is not a valid libtool archive"
+		func_append newdependency_libs " ${lt_sysroot:+=}$libdir/$name"
+		;;
+	      -L*)
+		func_stripname -L '' "$deplib"
+		func_replace_sysroot "$func_stripname_result"
+		func_append newdependency_libs " -L$func_replace_sysroot_result"
+		;;
+	      -R*)
+		func_stripname -R '' "$deplib"
+		func_replace_sysroot "$func_stripname_result"
+		func_append newdependency_libs " -R$func_replace_sysroot_result"
+		;;
+	      *) func_append newdependency_libs " $deplib" ;;
+	      esac
+	    done
+	    dependency_libs="$newdependency_libs"
+	    newdlfiles=
+
+	    for lib in $dlfiles; do
+	      case $lib in
+	      *.la)
+	        func_basename "$lib"
+		name="$func_basename_result"
+		eval libdir=`${SED} -n -e 's/^libdir=\(.*\)$/\1/p' $lib`
+		test -z "$libdir" && \
+		  func_fatal_error "\`$lib' is not a valid libtool archive"
+		func_append newdlfiles " ${lt_sysroot:+=}$libdir/$name"
+		;;
+	      *) func_append newdlfiles " $lib" ;;
+	      esac
+	    done
+	    dlfiles="$newdlfiles"
+	    newdlprefiles=
+	    for lib in $dlprefiles; do
+	      case $lib in
+	      *.la)
+		# Only pass preopened files to the pseudo-archive (for
+		# eventual linking with the app. that links it) if we
+		# didn't already link the preopened objects directly into
+		# the library:
+		func_basename "$lib"
+		name="$func_basename_result"
+		eval libdir=`${SED} -n -e 's/^libdir=\(.*\)$/\1/p' $lib`
+		test -z "$libdir" && \
+		  func_fatal_error "\`$lib' is not a valid libtool archive"
+		func_append newdlprefiles " ${lt_sysroot:+=}$libdir/$name"
+		;;
+	      esac
+	    done
+	    dlprefiles="$newdlprefiles"
+	  else
+	    newdlfiles=
+	    for lib in $dlfiles; do
+	      case $lib in
+		[\\/]* | [A-Za-z]:[\\/]*) abs="$lib" ;;
+		*) abs=`pwd`"/$lib" ;;
+	      esac
+	      func_append newdlfiles " $abs"
+	    done
+	    dlfiles="$newdlfiles"
+	    newdlprefiles=
+	    for lib in $dlprefiles; do
+	      case $lib in
+		[\\/]* | [A-Za-z]:[\\/]*) abs="$lib" ;;
+		*) abs=`pwd`"/$lib" ;;
+	      esac
+	      func_append newdlprefiles " $abs"
+	    done
+	    dlprefiles="$newdlprefiles"
+	  fi
+	  $RM $output
+	  # place dlname in correct position for cygwin
+	  # In fact, it would be nice if we could use this code for all target
+	  # systems that can't hard-code library paths into their executables
+	  # and that have no shared library path variable independent of PATH,
+	  # but it turns out we can't easily determine that from inspecting
+	  # libtool variables, so we have to hard-code the OSs to which it
+	  # applies here; at the moment, that means platforms that use the PE
+	  # object format with DLL files.  See the long comment at the top of
+	  # tests/bindir.at for full details.
+	  tdlname=$dlname
+	  case $host,$output,$installed,$module,$dlname in
+	    *cygwin*,*lai,yes,no,*.dll | *mingw*,*lai,yes,no,*.dll | *cegcc*,*lai,yes,no,*.dll)
+	      # If a -bindir argument was supplied, place the dll there.
+	      if test "x$bindir" != x ;
+	      then
+		func_relative_path "$install_libdir" "$bindir"
+		tdlname=$func_relative_path_result$dlname
+	      else
+		# Otherwise fall back on heuristic.
+		tdlname=../bin/$dlname
+	      fi
+	      ;;
+	  esac
+	  $ECHO > $output "\
+# $outputname - a libtool library file
+# Generated by $PROGRAM (GNU $PACKAGE$TIMESTAMP) $VERSION
+#
+# Please DO NOT delete this file!
+# It is necessary for linking the library.
+
+# The name that we can dlopen(3).
+dlname='$tdlname'
+
+# Names of this library.
+library_names='$library_names'
+
+# The name of the static archive.
+old_library='$old_library'
+
+# Linker flags that can not go in dependency_libs.
+inherited_linker_flags='$new_inherited_linker_flags'
+
+# Libraries that this one depends upon.
+dependency_libs='$dependency_libs'
+
+# Names of additional weak libraries provided by this library
+weak_library_names='$weak_libs'
+
+# Version information for $libname.
+current=$current
+age=$age
+revision=$revision
+
+# Is this an already installed library?
+installed=$installed
+
+# Should we warn about portability when linking against -modules?
+shouldnotlink=$module
+
+# Files to dlopen/dlpreopen
+dlopen='$dlfiles'
+dlpreopen='$dlprefiles'
+
+# Directory that this library needs to be installed in:
+libdir='$install_libdir'"
+	  if test "$installed" = no && test "$need_relink" = yes; then
+	    $ECHO >> $output "\
+relink_command=\"$relink_command\""
+	  fi
+	done
+      }
+
+      # Do a symbolic link so that the libtool archive can be found in
+      # LD_LIBRARY_PATH before the program is installed.
+      func_show_eval '( cd "$output_objdir" && $RM "$outputname" && $LN_S "../$outputname" "$outputname" )' 'exit $?'
+      ;;
+    esac
+    exit $EXIT_SUCCESS
+}
+
+{ test "$opt_mode" = link || test "$opt_mode" = relink; } &&
+    func_mode_link ${1+"$@"}
+
+
+# func_mode_uninstall arg...
+func_mode_uninstall ()
+{
+    $opt_debug
+    RM="$nonopt"
+    files=
+    rmforce=
+    exit_status=0
+
+    # This variable tells wrapper scripts just to set variables rather
+    # than running their programs.
+    libtool_install_magic="$magic"
+
+    for arg
+    do
+      case $arg in
+      -f) func_append RM " $arg"; rmforce=yes ;;
+      -*) func_append RM " $arg" ;;
+      *) func_append files " $arg" ;;
+      esac
+    done
+
+    test -z "$RM" && \
+      func_fatal_help "you must specify an RM program"
+
+    rmdirs=
+
+    for file in $files; do
+      func_dirname "$file" "" "."
+      dir="$func_dirname_result"
+      if test "X$dir" = X.; then
+	odir="$objdir"
+      else
+	odir="$dir/$objdir"
+      fi
+      func_basename "$file"
+      name="$func_basename_result"
+      test "$opt_mode" = uninstall && odir="$dir"
+
+      # Remember odir for removal later, being careful to avoid duplicates
+      if test "$opt_mode" = clean; then
+	case " $rmdirs " in
+	  *" $odir "*) ;;
+	  *) func_append rmdirs " $odir" ;;
+	esac
+      fi
+
+      # Don't error if the file doesn't exist and rm -f was used.
+      if { test -L "$file"; } >/dev/null 2>&1 ||
+	 { test -h "$file"; } >/dev/null 2>&1 ||
+	 test -f "$file"; then
+	:
+      elif test -d "$file"; then
+	exit_status=1
+	continue
+      elif test "$rmforce" = yes; then
+	continue
+      fi
+
+      rmfiles="$file"
+
+      case $name in
+      *.la)
+	# Possibly a libtool archive, so verify it.
+	if func_lalib_p "$file"; then
+	  func_source $dir/$name
+
+	  # Delete the libtool libraries and symlinks.
+	  for n in $library_names; do
+	    func_append rmfiles " $odir/$n"
+	  done
+	  test -n "$old_library" && func_append rmfiles " $odir/$old_library"
+
+	  case "$opt_mode" in
+	  clean)
+	    case " $library_names " in
+	    *" $dlname "*) ;;
+	    *) test -n "$dlname" && func_append rmfiles " $odir/$dlname" ;;
+	    esac
+	    test -n "$libdir" && func_append rmfiles " $odir/$name $odir/${name}i"
+	    ;;
+	  uninstall)
+	    if test -n "$library_names"; then
+	      # Do each command in the postuninstall commands.
+	      func_execute_cmds "$postuninstall_cmds" 'test "$rmforce" = yes || exit_status=1'
+	    fi
+
+	    if test -n "$old_library"; then
+	      # Do each command in the old_postuninstall commands.
+	      func_execute_cmds "$old_postuninstall_cmds" 'test "$rmforce" = yes || exit_status=1'
+	    fi
+	    # FIXME: should reinstall the best remaining shared library.
+	    ;;
+	  esac
+	fi
+	;;
+
+      *.lo)
+	# Possibly a libtool object, so verify it.
+	if func_lalib_p "$file"; then
+
+	  # Read the .lo file
+	  func_source $dir/$name
+
+	  # Add PIC object to the list of files to remove.
+	  if test -n "$pic_object" &&
+	     test "$pic_object" != none; then
+	    func_append rmfiles " $dir/$pic_object"
+	  fi
+
+	  # Add non-PIC object to the list of files to remove.
+	  if test -n "$non_pic_object" &&
+	     test "$non_pic_object" != none; then
+	    func_append rmfiles " $dir/$non_pic_object"
+	  fi
+	fi
+	;;
+
+      *)
+	if test "$opt_mode" = clean ; then
+	  noexename=$name
+	  case $file in
+	  *.exe)
+	    func_stripname '' '.exe' "$file"
+	    file=$func_stripname_result
+	    func_stripname '' '.exe' "$name"
+	    noexename=$func_stripname_result
+	    # $file with .exe has already been added to rmfiles,
+	    # add $file without .exe
+	    func_append rmfiles " $file"
+	    ;;
+	  esac
+	  # Do a test to see if this is a libtool program.
+	  if func_ltwrapper_p "$file"; then
+	    if func_ltwrapper_executable_p "$file"; then
+	      func_ltwrapper_scriptname "$file"
+	      relink_command=
+	      func_source $func_ltwrapper_scriptname_result
+	      func_append rmfiles " $func_ltwrapper_scriptname_result"
+	    else
+	      relink_command=
+	      func_source $dir/$noexename
+	    fi
+
+	    # note $name still contains .exe if it was in $file originally
+	    # as does the version of $file that was added into $rmfiles
+	    func_append rmfiles " $odir/$name $odir/${name}S.${objext}"
+	    if test "$fast_install" = yes && test -n "$relink_command"; then
+	      func_append rmfiles " $odir/lt-$name"
+	    fi
+	    if test "X$noexename" != "X$name" ; then
+	      func_append rmfiles " $odir/lt-${noexename}.c"
+	    fi
+	  fi
+	fi
+	;;
+      esac
+      func_show_eval "$RM $rmfiles" 'exit_status=1'
+    done
+
+    # Try to remove the ${objdir}s in the directories where we deleted files
+    for dir in $rmdirs; do
+      if test -d "$dir"; then
+	func_show_eval "rmdir $dir >/dev/null 2>&1"
+      fi
+    done
+
+    exit $exit_status
+}
+
+{ test "$opt_mode" = uninstall || test "$opt_mode" = clean; } &&
+    func_mode_uninstall ${1+"$@"}
+
+test -z "$opt_mode" && {
+  help="$generic_help"
+  func_fatal_help "you must specify a MODE"
+}
+
+test -z "$exec_cmd" && \
+  func_fatal_help "invalid operation mode \`$opt_mode'"
+
+if test -n "$exec_cmd"; then
+  eval exec "$exec_cmd"
+  exit $EXIT_FAILURE
+fi
+
+exit $exit_status
+
+
+# The TAGs below are defined such that we never get into a situation
+# in which we disable both kinds of libraries.  Given conflicting
+# choices, we go for a static library, that is the most portable,
+# since we can't tell whether shared libraries were disabled because
+# the user asked for that or because the platform doesn't support
+# them.  This is particularly important on AIX, because we don't
+# support having both static and shared libraries enabled at the same
+# time on that platform, so we default to a shared-only configuration.
+# If a disable-shared tag is given, we'll fallback to a static-only
+# configuration.  But we'll never go from static-only to shared-only.
+
+# ### BEGIN LIBTOOL TAG CONFIG: disable-shared
+build_libtool_libs=no
+build_old_libs=yes
+# ### END LIBTOOL TAG CONFIG: disable-shared
+
+# ### BEGIN LIBTOOL TAG CONFIG: disable-static
+build_old_libs=`case $build_libtool_libs in yes) echo no;; *) echo yes;; esac`
+# ### END LIBTOOL TAG CONFIG: disable-static
+
+# Local Variables:
+# mode:shell-script
+# sh-indentation:2
+# End:
+# vi:sw=2
+
diff --git a/bluez/missing b/bluez/missing
new file mode 100755
index 0000000..86a8fc3
--- /dev/null
+++ b/bluez/missing
@@ -0,0 +1,331 @@
+#! /bin/sh
+# Common stub for a few missing GNU programs while installing.
+
+scriptversion=2012-01-06.13; # UTC
+
+# Copyright (C) 1996, 1997, 1999, 2000, 2002, 2003, 2004, 2005, 2006,
+# 2008, 2009, 2010, 2011, 2012 Free Software Foundation, Inc.
+# Originally by Fran,cois Pinard <pinard@iro.umontreal.ca>, 1996.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2, or (at your option)
+# any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+# As a special exception to the GNU General Public License, if you
+# distribute this file as part of a program that contains a
+# configuration script generated by Autoconf, you may include it under
+# the same distribution terms that you use for the rest of that program.
+
+if test $# -eq 0; then
+  echo 1>&2 "Try \`$0 --help' for more information"
+  exit 1
+fi
+
+run=:
+sed_output='s/.* --output[ =]\([^ ]*\).*/\1/p'
+sed_minuso='s/.* -o \([^ ]*\).*/\1/p'
+
+# In the cases where this matters, `missing' is being run in the
+# srcdir already.
+if test -f configure.ac; then
+  configure_ac=configure.ac
+else
+  configure_ac=configure.in
+fi
+
+msg="missing on your system"
+
+case $1 in
+--run)
+  # Try to run requested program, and just exit if it succeeds.
+  run=
+  shift
+  "$@" && exit 0
+  # Exit code 63 means version mismatch.  This often happens
+  # when the user try to use an ancient version of a tool on
+  # a file that requires a minimum version.  In this case we
+  # we should proceed has if the program had been absent, or
+  # if --run hadn't been passed.
+  if test $? = 63; then
+    run=:
+    msg="probably too old"
+  fi
+  ;;
+
+  -h|--h|--he|--hel|--help)
+    echo "\
+$0 [OPTION]... PROGRAM [ARGUMENT]...
+
+Handle \`PROGRAM [ARGUMENT]...' for when PROGRAM is missing, or return an
+error status if there is no known handling for PROGRAM.
+
+Options:
+  -h, --help      display this help and exit
+  -v, --version   output version information and exit
+  --run           try to run the given command, and emulate it if it fails
+
+Supported PROGRAM values:
+  aclocal      touch file \`aclocal.m4'
+  autoconf     touch file \`configure'
+  autoheader   touch file \`config.h.in'
+  autom4te     touch the output file, or create a stub one
+  automake     touch all \`Makefile.in' files
+  bison        create \`y.tab.[ch]', if possible, from existing .[ch]
+  flex         create \`lex.yy.c', if possible, from existing .c
+  help2man     touch the output file
+  lex          create \`lex.yy.c', if possible, from existing .c
+  makeinfo     touch the output file
+  yacc         create \`y.tab.[ch]', if possible, from existing .[ch]
+
+Version suffixes to PROGRAM as well as the prefixes \`gnu-', \`gnu', and
+\`g' are ignored when checking the name.
+
+Send bug reports to <bug-automake@gnu.org>."
+    exit $?
+    ;;
+
+  -v|--v|--ve|--ver|--vers|--versi|--versio|--version)
+    echo "missing $scriptversion (GNU Automake)"
+    exit $?
+    ;;
+
+  -*)
+    echo 1>&2 "$0: Unknown \`$1' option"
+    echo 1>&2 "Try \`$0 --help' for more information"
+    exit 1
+    ;;
+
+esac
+
+# normalize program name to check for.
+program=`echo "$1" | sed '
+  s/^gnu-//; t
+  s/^gnu//; t
+  s/^g//; t'`
+
+# Now exit if we have it, but it failed.  Also exit now if we
+# don't have it and --version was passed (most likely to detect
+# the program).  This is about non-GNU programs, so use $1 not
+# $program.
+case $1 in
+  lex*|yacc*)
+    # Not GNU programs, they don't have --version.
+    ;;
+
+  *)
+    if test -z "$run" && ($1 --version) > /dev/null 2>&1; then
+       # We have it, but it failed.
+       exit 1
+    elif test "x$2" = "x--version" || test "x$2" = "x--help"; then
+       # Could not run --version or --help.  This is probably someone
+       # running `$TOOL --version' or `$TOOL --help' to check whether
+       # $TOOL exists and not knowing $TOOL uses missing.
+       exit 1
+    fi
+    ;;
+esac
+
+# If it does not exist, or fails to run (possibly an outdated version),
+# try to emulate it.
+case $program in
+  aclocal*)
+    echo 1>&2 "\
+WARNING: \`$1' is $msg.  You should only need it if
+         you modified \`acinclude.m4' or \`${configure_ac}'.  You might want
+         to install the \`Automake' and \`Perl' packages.  Grab them from
+         any GNU archive site."
+    touch aclocal.m4
+    ;;
+
+  autoconf*)
+    echo 1>&2 "\
+WARNING: \`$1' is $msg.  You should only need it if
+         you modified \`${configure_ac}'.  You might want to install the
+         \`Autoconf' and \`GNU m4' packages.  Grab them from any GNU
+         archive site."
+    touch configure
+    ;;
+
+  autoheader*)
+    echo 1>&2 "\
+WARNING: \`$1' is $msg.  You should only need it if
+         you modified \`acconfig.h' or \`${configure_ac}'.  You might want
+         to install the \`Autoconf' and \`GNU m4' packages.  Grab them
+         from any GNU archive site."
+    files=`sed -n 's/^[ ]*A[CM]_CONFIG_HEADER(\([^)]*\)).*/\1/p' ${configure_ac}`
+    test -z "$files" && files="config.h"
+    touch_files=
+    for f in $files; do
+      case $f in
+      *:*) touch_files="$touch_files "`echo "$f" |
+				       sed -e 's/^[^:]*://' -e 's/:.*//'`;;
+      *) touch_files="$touch_files $f.in";;
+      esac
+    done
+    touch $touch_files
+    ;;
+
+  automake*)
+    echo 1>&2 "\
+WARNING: \`$1' is $msg.  You should only need it if
+         you modified \`Makefile.am', \`acinclude.m4' or \`${configure_ac}'.
+         You might want to install the \`Automake' and \`Perl' packages.
+         Grab them from any GNU archive site."
+    find . -type f -name Makefile.am -print |
+	   sed 's/\.am$/.in/' |
+	   while read f; do touch "$f"; done
+    ;;
+
+  autom4te*)
+    echo 1>&2 "\
+WARNING: \`$1' is needed, but is $msg.
+         You might have modified some files without having the
+         proper tools for further handling them.
+         You can get \`$1' as part of \`Autoconf' from any GNU
+         archive site."
+
+    file=`echo "$*" | sed -n "$sed_output"`
+    test -z "$file" && file=`echo "$*" | sed -n "$sed_minuso"`
+    if test -f "$file"; then
+	touch $file
+    else
+	test -z "$file" || exec >$file
+	echo "#! /bin/sh"
+	echo "# Created by GNU Automake missing as a replacement of"
+	echo "#  $ $@"
+	echo "exit 0"
+	chmod +x $file
+	exit 1
+    fi
+    ;;
+
+  bison*|yacc*)
+    echo 1>&2 "\
+WARNING: \`$1' $msg.  You should only need it if
+         you modified a \`.y' file.  You may need the \`Bison' package
+         in order for those modifications to take effect.  You can get
+         \`Bison' from any GNU archive site."
+    rm -f y.tab.c y.tab.h
+    if test $# -ne 1; then
+        eval LASTARG=\${$#}
+	case $LASTARG in
+	*.y)
+	    SRCFILE=`echo "$LASTARG" | sed 's/y$/c/'`
+	    if test -f "$SRCFILE"; then
+	         cp "$SRCFILE" y.tab.c
+	    fi
+	    SRCFILE=`echo "$LASTARG" | sed 's/y$/h/'`
+	    if test -f "$SRCFILE"; then
+	         cp "$SRCFILE" y.tab.h
+	    fi
+	  ;;
+	esac
+    fi
+    if test ! -f y.tab.h; then
+	echo >y.tab.h
+    fi
+    if test ! -f y.tab.c; then
+	echo 'main() { return 0; }' >y.tab.c
+    fi
+    ;;
+
+  lex*|flex*)
+    echo 1>&2 "\
+WARNING: \`$1' is $msg.  You should only need it if
+         you modified a \`.l' file.  You may need the \`Flex' package
+         in order for those modifications to take effect.  You can get
+         \`Flex' from any GNU archive site."
+    rm -f lex.yy.c
+    if test $# -ne 1; then
+        eval LASTARG=\${$#}
+	case $LASTARG in
+	*.l)
+	    SRCFILE=`echo "$LASTARG" | sed 's/l$/c/'`
+	    if test -f "$SRCFILE"; then
+	         cp "$SRCFILE" lex.yy.c
+	    fi
+	  ;;
+	esac
+    fi
+    if test ! -f lex.yy.c; then
+	echo 'main() { return 0; }' >lex.yy.c
+    fi
+    ;;
+
+  help2man*)
+    echo 1>&2 "\
+WARNING: \`$1' is $msg.  You should only need it if
+	 you modified a dependency of a manual page.  You may need the
+	 \`Help2man' package in order for those modifications to take
+	 effect.  You can get \`Help2man' from any GNU archive site."
+
+    file=`echo "$*" | sed -n "$sed_output"`
+    test -z "$file" && file=`echo "$*" | sed -n "$sed_minuso"`
+    if test -f "$file"; then
+	touch $file
+    else
+	test -z "$file" || exec >$file
+	echo ".ab help2man is required to generate this page"
+	exit $?
+    fi
+    ;;
+
+  makeinfo*)
+    echo 1>&2 "\
+WARNING: \`$1' is $msg.  You should only need it if
+         you modified a \`.texi' or \`.texinfo' file, or any other file
+         indirectly affecting the aspect of the manual.  The spurious
+         call might also be the consequence of using a buggy \`make' (AIX,
+         DU, IRIX).  You might want to install the \`Texinfo' package or
+         the \`GNU make' package.  Grab either from any GNU archive site."
+    # The file to touch is that specified with -o ...
+    file=`echo "$*" | sed -n "$sed_output"`
+    test -z "$file" && file=`echo "$*" | sed -n "$sed_minuso"`
+    if test -z "$file"; then
+      # ... or it is the one specified with @setfilename ...
+      infile=`echo "$*" | sed 's/.* \([^ ]*\) *$/\1/'`
+      file=`sed -n '
+	/^@setfilename/{
+	  s/.* \([^ ]*\) *$/\1/
+	  p
+	  q
+	}' $infile`
+      # ... or it is derived from the source name (dir/f.texi becomes f.info)
+      test -z "$file" && file=`echo "$infile" | sed 's,.*/,,;s,.[^.]*$,,'`.info
+    fi
+    # If the file does not exist, the user really needs makeinfo;
+    # let's fail without touching anything.
+    test -f $file || exit 1
+    touch $file
+    ;;
+
+  *)
+    echo 1>&2 "\
+WARNING: \`$1' is needed, and is $msg.
+         You might have modified some files without having the
+         proper tools for further handling them.  Check the \`README' file,
+         it often tells you about the needed prerequisites for installing
+         this package.  You may also peek at any GNU archive site, in case
+         some other package would contain this missing \`$1' program."
+    exit 1
+    ;;
+esac
+
+exit 0
+
+# Local variables:
+# eval: (add-hook 'write-file-hooks 'time-stamp)
+# time-stamp-start: "scriptversion="
+# time-stamp-format: "%:y-%02m-%02d.%02H"
+# time-stamp-time-zone: "UTC"
+# time-stamp-end: "; # UTC"
+# End:
diff --git a/bluez/monitor/analyze.c b/bluez/monitor/analyze.c
new file mode 100644
index 0000000..a747938
--- /dev/null
+++ b/bluez/monitor/analyze.c
@@ -0,0 +1,325 @@
+/*
+ *
+ *  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"
+
+#define MAX_PACKET_SIZE		(1486 + 4)
+
+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 type;
+
+	btsnoop_file = btsnoop_open(path, BTSNOOP_FLAG_PKLG_SUPPORT);
+	if (!btsnoop_file)
+		return;
+
+	type = btsnoop_get_type(btsnoop_file);
+
+	switch (type) {
+	case BTSNOOP_TYPE_HCI:
+	case BTSNOOP_TYPE_UART:
+	case BTSNOOP_TYPE_MONITOR:
+		break;
+	default:
+		fprintf(stderr, "Unsupported packet format\n");
+		return;
+	}
+
+	dev_list = queue_new();
+	if (!dev_list) {
+		fprintf(stderr, "Failed to allocate device list\n");
+		goto done;
+	}
+
+	while (1) {
+		unsigned char buf[MAX_PACKET_SIZE];
+		struct timeval tv;
+		uint16_t index, opcode, pktlen;
+
+		if (btsnoop_read_hci(btsnoop_file, &tv, &index, &opcode,
+							buf, &pktlen) < 0)
+			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;
+		}
+
+		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/monitor/analyze.h b/bluez/monitor/analyze.h
new file mode 100644
index 0000000..c643d35
--- /dev/null
+++ b/bluez/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/monitor/bt.h b/bluez/monitor/bt.h
new file mode 100644
index 0000000..1a41d9e
--- /dev/null
+++ b/bluez/monitor/bt.h
@@ -0,0 +1,2749 @@
+/*
+ *
+ *  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 LMP_ESC4(x) ((127 << 8) | (x))
+
+#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_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_UNSNIFF_REQ		24
+
+#define BT_LMP_MAX_POWER		33
+
+#define BT_LMP_MIN_POWER		34
+
+#define BT_LMP_AUTO_RATE		35
+
+#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_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_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_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_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_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_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
+
+#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_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_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_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
+
+#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_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_REMOTE_CONN_PARAM_REQUEST	0x06
+struct bt_hci_evt_le_remote_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_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;
+} __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));
+
+struct bt_sdp_hdr {
+	uint8_t  pdu;
+	uint16_t tid;
+	uint16_t plen;
+} __attribute__ ((packed));
diff --git a/bluez/monitor/control.c b/bluez/monitor/control.c
new file mode 100644
index 0000000..8197813
--- /dev/null
+++ b/bluez/monitor/control.c
@@ -0,0 +1,952 @@
+/*
+ *
+ *  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 "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;
+
+#define MAX_PACKET_SIZE		(1486 + 4)
+
+struct control_data {
+	uint16_t channel;
+	int fd;
+	unsigned char buf[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_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 *settings_str[] = {
+	"powered", "connectable", "fast-connectable", "discoverable",
+	"pairable", "link-security", "ssp", "br/edr", "hs", "le",
+	"advertising", "secure-conn", "debug-keys", "privacy",
+};
+
+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);
+
+	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->class_of_dev[2],
+						ev->class_of_dev[1],
+						ev->class_of_dev[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;
+	char str[18];
+
+	if (len < sizeof(*ev)) {
+		printf("* Malformed New Link Key control\n");
+		return;
+	}
+
+	ba2str(&ev->key.addr.bdaddr, str);
+
+	printf("@ New Link Key: %s (%d)\n", str, ev->key.addr.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;
+	char str[18];
+
+	if (len < sizeof(*ev)) {
+		printf("* Malformed New Long Term Key control\n");
+		return;
+	}
+
+	ba2str(&ev->key.addr.bdaddr, str);
+
+	printf("@ New Long Term Key: %s (%d) %s\n", str, ev->key.addr.type,
+					ev->key.master ? "Master" : "Slave");
+
+	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("@ PIN 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;
+	char addr[18];
+
+	if (len < sizeof(*ev)) {
+		printf("* Malformed New CSRK control\n");
+		return;
+	}
+
+	ba2str(&ev->key.addr.bdaddr, addr);
+
+	printf("@ New CSRK: %s (%d) %s\n", addr, ev->key.addr.type,
+					ev->key.master ? "Master" : "Slave");
+
+	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;
+	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[32];
+	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;
+		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;
+			}
+		}
+
+		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, index, opcode, data->buf, pktlen);
+			break;
+		case HCI_CHANNEL_MONITOR:
+			packet_monitor(tv, index, opcode, data->buf, pktlen);
+			btsnoop_write_hci(btsnoop_file, tv, index, opcode,
+							data->buf, pktlen);
+			ellisys_inject_hci(tv, 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;
+	}
+
+	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, 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;
+}
+
+void control_writer(const char *path)
+{
+	btsnoop_file = btsnoop_create(path, BTSNOOP_TYPE_MONITOR);
+}
+
+void control_reader(const char *path)
+{
+	unsigned char buf[MAX_PACKET_SIZE];
+	uint16_t pktlen;
+	uint32_t type;
+	struct timeval tv;
+
+	btsnoop_file = btsnoop_open(path, BTSNOOP_FLAG_PKLG_SUPPORT);
+	if (!btsnoop_file)
+		return;
+
+	type = btsnoop_get_type(btsnoop_file);
+
+	switch (type) {
+	case BTSNOOP_TYPE_HCI:
+	case BTSNOOP_TYPE_UART:
+	case BTSNOOP_TYPE_SIMULATOR:
+		packet_del_filter(PACKET_FILTER_SHOW_INDEX);
+		break;
+
+	case BTSNOOP_TYPE_MONITOR:
+		packet_add_filter(PACKET_FILTER_SHOW_INDEX);
+		break;
+	}
+
+	open_pager();
+
+	switch (type) {
+	case BTSNOOP_TYPE_HCI:
+	case BTSNOOP_TYPE_UART:
+	case BTSNOOP_TYPE_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, index, opcode, buf, pktlen);
+			ellisys_inject_hci(&tv, index, opcode, buf, pktlen);
+		}
+		break;
+
+	case BTSNOOP_TYPE_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/monitor/control.h b/bluez/monitor/control.h
new file mode 100644
index 0000000..8de4c6c
--- /dev/null
+++ b/bluez/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>
+
+void 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/monitor/crc.c b/bluez/monitor/crc.c
new file mode 100644
index 0000000..912b37e
--- /dev/null
+++ b/bluez/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/monitor/crc.h b/bluez/monitor/crc.h
new file mode 100644
index 0000000..772388b
--- /dev/null
+++ b/bluez/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/monitor/display.c b/bluez/monitor/display.c
new file mode 100644
index 0000000..411af94
--- /dev/null
+++ b/bluez/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/monitor/display.h b/bluez/monitor/display.h
new file mode 100644
index 0000000..e627401
--- /dev/null
+++ b/bluez/monitor/display.h
@@ -0,0 +1,62 @@
+/*
+ *
+ *  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;47m"
+#define COLOR_HIGHLIGHT	"\x1B[1;39m"
+
+#define COLOR_ERROR	"\x1B[1;31m"
+
+#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/monitor/ellisys.c b/bluez/monitor/ellisys.c
new file mode 100644
index 0000000..bafbb5d
--- /dev/null
+++ b/bluez/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/monitor/ellisys.h b/bluez/monitor/ellisys.h
new file mode 100644
index 0000000..8be888d
--- /dev/null
+++ b/bluez/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/monitor/hcidump.c b/bluez/monitor/hcidump.c
new file mode 100644
index 0000000..1515f93
--- /dev/null
+++ b/bluez/monitor/hcidump.c
@@ -0,0 +1,409 @@
+/*
+ *
+ *  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 <bluetooth/bluetooth.h>
+#include <bluetooth/hci.h>
+#include <bluetooth/hci_lib.h>
+
+#include "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, data->index, buf + 1, len - 1);
+			break;
+		case HCI_EVENT_PKT:
+			packet_hci_event(tv, data->index, buf + 1, len - 1);
+			break;
+		case HCI_ACLDATA_PKT:
+			packet_hci_acldata(tv, data->index, !!dir,
+							buf + 1, len - 1);
+			break;
+		case HCI_SCODATA_PKT:
+			packet_hci_scodata(tv, 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/monitor/hcidump.h b/bluez/monitor/hcidump.h
new file mode 100644
index 0000000..c908650
--- /dev/null
+++ b/bluez/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/monitor/hwdb.c b/bluez/monitor/hwdb.c
new file mode 100644
index 0000000..6931660
--- /dev/null
+++ b/bluez/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/monitor/hwdb.h b/bluez/monitor/hwdb.h
new file mode 100644
index 0000000..79f505a
--- /dev/null
+++ b/bluez/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/monitor/keys.c b/bluez/monitor/keys.c
new file mode 100644
index 0000000..4ccef22
--- /dev/null
+++ b/bluez/monitor/keys.c
@@ -0,0 +1,144 @@
+/*
+ *
+ *  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);
+	}
+}
+
+struct resolve_data {
+	bool found;
+	uint8_t addr[6];
+	uint8_t ident[6];
+	uint8_t ident_type;
+};
+
+static void try_resolve_irk(void *data, void *user_data)
+{
+	struct irk_data *irk = data;
+	struct resolve_data *result = user_data;
+	uint8_t local_hash[3];
+
+	if (result->found)
+		return;
+
+	bt_crypto_ah(crypto, irk->key, result->addr + 3, local_hash);
+
+	if (!memcmp(result->addr, local_hash, 3)) {
+		result->found = true;
+		memcpy(result->ident, irk->addr, 6);
+		result->ident_type = irk->addr_type;
+	}
+}
+
+bool keys_resolve_identity(const uint8_t addr[6], uint8_t ident[6],
+							uint8_t *ident_type)
+{
+	struct resolve_data result;
+
+	result.found = false;
+	memcpy(result.addr, addr, 6);
+
+	queue_foreach(irk_list, try_resolve_irk, &result);
+
+	if (result.found) {
+		memcpy(ident, result.ident, 6);
+		*ident_type = result.ident_type;
+		return true;
+	}
+
+	return false;
+}
diff --git a/bluez/monitor/keys.h b/bluez/monitor/keys.h
new file mode 100644
index 0000000..61ec50a
--- /dev/null
+++ b/bluez/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/monitor/l2cap.c b/bluez/monitor/l2cap.c
new file mode 100644
index 0000000..993aa8b
--- /dev/null
+++ b/bluez/monitor/l2cap.c
@@ -0,0 +1,2770 @@
+/*
+ *
+ *  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 <bluetooth/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"
+
+#define MAX_CHAN 64
+
+struct chan_data {
+	uint16_t index;
+	uint16_t handle;
+	uint16_t scid;
+	uint16_t dcid;
+	uint16_t psm;
+	uint8_t  ctrlid;
+	uint8_t  mode;
+};
+
+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;
+
+	for (i = 0; i < MAX_CHAN; i++) {
+		if (n < 0 && chan_list[i].handle == 0x0000)
+			n = i;
+
+		if (chan_list[i].index != frame->index)
+			continue;
+
+		if (chan_list[i].handle != frame->handle)
+			continue;
+
+		if (frame->in) {
+			if (chan_list[i].dcid == scid) {
+				n = i;
+				break;
+			}
+		} else {
+			if (chan_list[i].scid == scid) {
+				n = i;
+				break;
+			}
+		}
+	}
+
+	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;
+
+	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;
+}
+
+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->in) {
+			if (chan_list[i].scid == scid) {
+				chan_list[i].dcid = dcid;
+				break;
+			}
+		} else {
+			if (chan_list[i].dcid == 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 uint16_t get_psm(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].handle != frame->handle &&
+					chan_list[i].ctrlid != frame->index)
+			continue;
+
+		if (frame->in) {
+			if (chan_list[i].scid == frame->cid)
+				return chan_list[i].psm;
+		} else {
+			if (chan_list[i].dcid == frame->cid)
+				return chan_list[i].psm;
+		}
+	}
+
+	return 0;
+}
+
+static uint8_t get_mode(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].handle != frame->handle &&
+					chan_list[i].ctrlid != frame->index)
+			continue;
+
+		if (frame->in) {
+			if (chan_list[i].scid == frame->cid)
+				return chan_list[i].mode;
+		} else {
+			if (chan_list[i].dcid == frame->cid)
+				return chan_list[i].mode;
+		}
+	}
+
+	return 0;
+}
+
+static uint16_t get_chan(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].handle != frame->handle &&
+					chan_list[i].ctrlid != frame->index)
+			continue;
+
+		if (frame->in) {
+			if (chan_list[i].scid == frame->cid)
+				return i;
+		} else {
+			if (chan_list[i].dcid == frame->cid)
+				return i;
+		}
+	}
+
+	return 0;
+}
+
+#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];
+
+static void clear_fragment_buffer(uint16_t index)
+{
+	free(index_list[index].frag_buf);
+	index_list[index].frag_buf = NULL;
+	index_list[index].frag_pos = 0;
+	index_list[index].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 0x0005:
+		str = "Insufficient Authentication";
+		break;
+	case 0x0006:
+		str = "Insufficient Authorization";
+		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 dcid, 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];
+		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)", str, type);
+
+		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], dcid);
+
+			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("  Max window size: %d",
+					get_le16(data + consumed + 2));
+			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"		},
+	{ 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_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_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_conn_result(pdu->result);
+
+	/*assign_dcid(frame, le16_to_cpu(pdu->dcid), le16_to_cpu(pdu->scid));*/
+}
+
+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 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, 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, 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, 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, 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;
+	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 uint16_t *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 uint16_t *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 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_data_list("Attribute data 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);
+}
+
+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"		},
+	{ }
+};
+
+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, 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 *str;
+
+	switch (auth_req & 0x03) {
+	case 0x00:
+		str = "No bonding";
+		break;
+	case 0x01:
+		str = "Bonding";
+		break;
+	default:
+		str = "Reserved";
+		break;
+	}
+
+	print_field("Authentication requirement: %s - %s (0x%2.2x)",
+			str, (auth_req & 0x04) ? "MITM" : "No MITM", auth_req);
+}
+
+static void print_smp_key_dist(const char *label, uint8_t dist)
+{
+	char str[19];
+
+	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 ");
+	}
+
+	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;
+	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);
+}
+
+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 },
+	{ }
+};
+
+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, "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, 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;
+	uint16_t psm, chan;
+	uint8_t mode;
+
+	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:
+		smp_packet(index, in, handle, cid, data, size);
+		break;
+	default:
+		l2cap_frame_init(&frame, index, in, handle, cid, data, size);
+		psm = get_psm(&frame);
+		mode = get_mode(&frame);
+		chan = get_chan(&frame);
+
+		print_indent(6, COLOR_CYAN, "Channel:", "", COLOR_OFF,
+				" %d len %d [PSM %d mode %d] {chan %d}",
+						cid, size, psm, mode, chan);
+
+		switch (psm) {
+		case 0x0001:
+			sdp_packet(&frame, chan);
+			break;
+		case 0x001f:
+			att_packet(index, in, handle, cid, data, size);
+			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].frag_len) {
+			print_text(COLOR_ERROR, "unexpected start frame");
+			packet_hexdump(data, size);
+			clear_fragment_buffer(index);
+			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].frag_buf = malloc(len);
+		if (!index_list[index].frag_buf) {
+			print_text(COLOR_ERROR, "failed buffer allocation");
+			packet_hexdump(data, size);
+			return;
+		}
+
+		memcpy(index_list[index].frag_buf, data, size);
+		index_list[index].frag_pos = size;
+		index_list[index].frag_len = len - size;
+		index_list[index].frag_cid = cid;
+		break;
+
+	case 0x01:	/* continuing fragment */
+		if (!index_list[index].frag_len) {
+			print_text(COLOR_ERROR, "unexpected continuation");
+			packet_hexdump(data, size);
+			return;
+		}
+
+		if (size > index_list[index].frag_len) {
+			print_text(COLOR_ERROR, "fragment too long");
+			packet_hexdump(data, size);
+			clear_fragment_buffer(index);
+			return;
+		}
+
+		memcpy(index_list[index].frag_buf +
+				index_list[index].frag_pos, data, size);
+		index_list[index].frag_pos += size;
+		index_list[index].frag_len -= size;
+
+		if (!index_list[index].frag_len) {
+			/* complete frame */
+			l2cap_frame(index, in, handle,
+					index_list[index].frag_cid,
+					index_list[index].frag_buf,
+					index_list[index].frag_pos);
+			clear_fragment_buffer(index);
+			return;
+		}
+		break;
+
+	case 0x03:	/* complete automatically-flushable PDU */
+		if (index_list[index].frag_len) {
+			print_text(COLOR_ERROR, "unexpected complete frame");
+			packet_hexdump(data, size);
+			clear_fragment_buffer(index);
+			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/monitor/l2cap.h b/bluez/monitor/l2cap.h
new file mode 100644
index 0000000..0d18478
--- /dev/null
+++ b/bluez/monitor/l2cap.h
@@ -0,0 +1,61 @@
+/*
+ *
+ *  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;
+	uint16_t cid;
+	const void *data;
+	uint16_t size;
+};
+
+static inline void l2cap_frame_init(struct l2cap_frame *frame,
+				uint16_t index, bool in, uint16_t handle,
+				uint16_t cid, const void *data, uint16_t size)
+{
+	frame->index  = index;
+	frame->in     = in;
+	frame->handle = handle;
+	frame->cid    = cid;
+	frame->data   = data;
+	frame->size   = size;
+}
+
+static inline void l2cap_frame_pull(struct l2cap_frame *frame,
+				const struct l2cap_frame *source, uint16_t len)
+{
+	frame->index   = source->index;
+	frame->in      = source->in;
+	frame->handle  = source->handle;
+	frame->cid     = source->cid;
+	frame->data    = source->data + len;
+	frame->size    = source->size - len;
+}
+
+void l2cap_packet(uint16_t index, bool in, uint16_t handle, uint8_t flags,
+					const void *data, uint16_t size);
diff --git a/bluez/monitor/ll.c b/bluez/monitor/ll.c
new file mode 100644
index 0000000..e9d0cf6
--- /dev/null
+++ b/bluez/monitor/ll.c
@@ -0,0 +1,551 @@
+/*
+ *
+ *  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)
+{
+	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);
+		break;
+
+	default:
+		packet_hexdump(data + 2, size - 2);
+		break;
+	}
+}
+
+void ll_packet(uint16_t frequency, const void *data, uint8_t size)
+{
+	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);
+}
+
+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);
+}
+
+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 },
+	{ 0x12, "LL_PING_REQ",              null_pdu,         0, true },
+	{ 0x13, "LL_PING_RSP",              null_pdu,         0, 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)
+{
+	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) {
+		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/monitor/ll.h b/bluez/monitor/ll.h
new file mode 100644
index 0000000..ee28f40
--- /dev/null
+++ b/bluez/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);
+void llcp_packet(const void *data, uint8_t size);
diff --git a/bluez/monitor/lmp.c b/bluez/monitor/lmp.c
new file mode 100644
index 0000000..4a05973
--- /dev/null
+++ b/bluez/monitor/lmp.c
@@ -0,0 +1,819 @@
+/*
+ *
+ *  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 "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 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 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 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 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 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("Features: 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" },
+	{  2, "LMP_name_res" },
+	{  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" },
+	{  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" },
+	{ 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" },
+	{ 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" },
+	{ 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)
+{
+	const struct lmp_data *lmp_data = NULL;
+	const char *opcode_color, *opcode_str;
+	uint16_t opcode;
+	uint8_t tid, off;
+	int i;
+
+	tid = ((const uint8_t *) data)[0] & 0x01;
+	opcode = (((const uint8_t *) data)[0] & 0xfe) >> 1;
+
+	switch (opcode) {
+	case 127:
+		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) TID %u", opcode >> 8, opcode & 0xff, tid);
+	else
+		print_indent(6, opcode_color, "", opcode_str, COLOR_OFF,
+					" (%u) TID %d", opcode, tid);
+
+	if (!lmp_data || !lmp_data->func) {
+		packet_hexdump(data + off, size - off);
+		return;
+	}
+
+	if (lmp_data->fixed) {
+		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/monitor/lmp.h b/bluez/monitor/lmp.h
new file mode 100644
index 0000000..9b5393b
--- /dev/null
+++ b/bluez/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);
+
+void lmp_todo(void);
diff --git a/bluez/monitor/main.c b/bluez/monitor/main.c
new file mode 100644
index 0000000..d4e8e6d
--- /dev/null
+++ b/bluez/monitor/main.c
@@ -0,0 +1,215 @@
+/*
+ *
+ *  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 "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-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' },
+	{ "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: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 '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);
+
+	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/monitor/mainloop.c b/bluez/monitor/mainloop.c
new file mode 100644
index 0000000..8d4b391
--- /dev/null
+++ b/bluez/monitor/mainloop.c
@@ -0,0 +1,387 @@
+/*
+ *
+ *  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;
+
+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;
+}
+
+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 1;
+
+		signal_data->fd = signalfd(-1, &signal_data->mask,
+						SFD_NONBLOCK | SFD_CLOEXEC);
+		if (signal_data->fd < 0)
+			return 1;
+
+		if (mainloop_add_fd(signal_data->fd, EPOLLIN,
+				signal_callback, signal_data, NULL) < 0) {
+			close(signal_data->fd);
+			return 1;
+		}
+	}
+
+	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 0;
+}
+
+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/monitor/mainloop.h b/bluez/monitor/mainloop.h
new file mode 100644
index 0000000..dafec8b
--- /dev/null
+++ b/bluez/monitor/mainloop.h
@@ -0,0 +1,49 @@
+/*
+ *
+ *  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);
+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/monitor/packet.c b/bluez/monitor/packet.c
new file mode 100644
index 0000000..d8ff2da
--- /dev/null
+++ b/bluez/monitor/packet.c
@@ -0,0 +1,7787 @@
+/*
+ *
+ *  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 <bluetooth/bluetooth.h>
+#include <bluetooth/hci.h>
+#include <bluetooth/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 "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_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 unsigned long filter_mask = 0;
+static bool index_filter = false;
+static uint16_t index_number = 0;
+static uint16_t index_current = 0;
+
+#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_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, 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) {
+		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);
+	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", 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"				},
+	{ 0x1f, "Unspecified Error"					},
+	{ 0x20, "Unsupported LMP 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 Interval"			},
+	{ 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_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:
+		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:
+		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"	},
+	{ 1152, true,  "Cycling"		},
+	{ 1216, true,  "Undefined"		},
+
+	{ 3136, true,  "Pulse Oximeter"		},
+	{ 3200, true,  "Undefined"		},
+
+	{ 5184, true,  "Outdoor Sports Activity"},
+	{ 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)
+{
+	print_field("TX power: %d dBm", 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("  Enabled 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_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_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_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_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_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;
+	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 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"					},
+	{ }
+};
+
+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);
+}
+
+static const struct {
+	uint8_t bit;
+	const char *str;
+} le_states_table[] = {
+	{  0, "Non-connectable Advertising State"			},
+	{  1, "Scannable Advertising State"				},
+	{  2, "Connectable Advertising State"				},
+	{  3, "Directed Advertising State"				},
+	{  4, "Passive Scanning State"					},
+	{  5, "Active Scanning State"					},
+	{  6, "Initiating State and Connection State in Master Role"	},
+	{  7, "Connection State in Slave Role"				},
+	{  8, "Non-connectable Advertising State and "
+				"Passive Scanning State combination"	},
+	{  9, "Scannable Advertising State and "
+				"Passive Scanning State combination"	},
+	{ 10, "Connectable Advertising State and "
+				"Passive Scanning State combination"	},
+	{ 11, "Directed Advertising State and "
+				"Passive Scanning State combination"	},
+	{ 12, "Non-connectable Advertising State and "
+				"Active Scanning State combination"	},
+	{ 13, "Scannable Advertising State and "
+				"Active Scanning State combination"	},
+	{ 14, "Connectable Advertising State and "
+				"Active Scanning State combination"	},
+	{ 15, "Directed Advertising State and "
+				"Active Scanning State combination"	},
+	{ 16, "Non-connectable Advertising State and "
+				"Initiating State combination"		},
+	{ 17, "Scannable Advertising State and "
+				"Initiating State combination"		},
+	{ 18, "Non-connectable Advertising State and "
+				"Mater Role combination"		},
+	{ 19, "Scannable Advertising State and "
+				"Master Role combination"		},
+	{ 20, "Non-connectable Advertising State and "
+				"Slave Role combination"		},
+	{ 21, "Scannable Advertising State and "
+				"Slave Role combination"		},
+	{ 22, "Passive Scanning State and Initiating State combination"	},
+	{ 23, "Active Scanning State and Initiating State combination"	},
+	{ 24, "Passive Scanning State and Master Role combination"	},
+	{ 25, "Active Scanning State and Master Role combination"	},
+	{ 26, "Passive Scanning State and Slave Role combination"	},
+	{ 27, "Active Scanning State and Slave Role combination"	},
+	{ 28, "Initiating State and Master Role combination"		},
+	{ }
+};
+
+static void print_le_states(const uint8_t *states_array)
+{
+	uint64_t mask, states = 0;
+	int i;
+
+	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_table[i].str; i++) {
+		if (states & (((uint64_t) 1) << le_states_table[i].bit)) {
+			print_field("  %s", le_states_table[i].str);
+			mask &= ~(((uint64_t) 1) << le_states_table[i].bit);
+		}
+	}
+
+	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"		},
+	{  4, "LE Long Term Key Request"		},
+	{  5, "LE Remote Connection Parameter Request"	},
+	{ }
+};
+
+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);
+}
+
+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, 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];
+};
+
+static struct index_data index_list[MAX_INDEX];
+
+void packet_monitor(struct timeval *tv, uint16_t index, uint16_t opcode,
+					const void *data, uint16_t size)
+{
+	const struct btsnoop_opcode_new_index *ni;
+	char str[18], extra_str[24];
+
+	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);
+		}
+
+		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, index, data, size);
+		break;
+	case BTSNOOP_OPCODE_EVENT_PKT:
+		packet_hci_event(tv, index, data, size);
+		break;
+	case BTSNOOP_OPCODE_ACL_TX_PKT:
+		packet_hci_acldata(tv, index, false, data, size);
+		break;
+	case BTSNOOP_OPCODE_ACL_RX_PKT:
+		packet_hci_acldata(tv, index, true, data, size);
+		break;
+	case BTSNOOP_OPCODE_SCO_TX_PKT:
+		packet_hci_scodata(tv, index, false, data, size);
+		break;
+	case BTSNOOP_OPCODE_SCO_RX_PKT:
+		packet_hci_scodata(tv, index, true, data, size);
+		break;
+	default:
+		sprintf(extra_str, "(code %d len %d)", opcode, size);
+		print_packet(tv, 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, 0, '*', COLOR_PHY_PACKET,
+					"Physical packet:", NULL, str);
+
+	ll_packet(frequency, data, size);
+}
+
+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 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);
+}
+
+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 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);
+}
+
+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);
+}
+
+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_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_local_version_rsp(const void *data, uint8_t size)
+{
+	const struct bt_hci_rsp_read_local_version *rsp = data;
+
+	print_status(rsp->status);
+	print_hci_version(rsp->hci_ver, rsp->hci_rev);
+
+	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;
+	}
+
+	print_manufacturer(rsp->manufacturer);
+}
+
+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);
+}
+
+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_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 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 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 undirect - 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_addr_type("Own address 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);
+}
+
+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_addr_type("Own address 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_addr_type("Peer address type", cmd->peer_addr_type);
+	print_addr("Peer address", cmd->peer_addr, cmd->peer_addr_type);
+	print_addr_type("Own address 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));
+}
+
+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" },
+	{ 0x043e, 236, "Enhanced Accept Synchronous Connection Request" },
+	{ 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, "Holde 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" },
+	{ 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 Reporting" },
+	{ 0x0c5b, 147, "Write Default Erroneous Reporting" },
+	{ 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" },
+	{ 0x0c69, 194, "Read Best Effort Flush Timeout" },
+	{ 0x0c6a, 195, "Write Best Effort Flush Timeout" },
+	{ 0x0c6b, 196, "Short Range Mode" },
+	{ 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" },
+	{ 0x0c7f, 265, "Write Extended Page Timeout" },
+	{ 0x0c80, 266, "Read Extended Inquiry Length" },
+	{ 0x0c81, 267, "Write Extended Inquiry Length" },
+
+	/* 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" },
+
+	/* 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" },
+	{ 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" },
+	{ 0x1802, 129, "Write Loopback Mode" },
+	{ 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" },
+	{ 0x2021, 269, "LE Remote Connection Parameter Request Negative Reply" },
+	{ }
+};
+
+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 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);
+}
+
+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);
+	const struct opcode_data *opcode_data = NULL;
+	const char *opcode_color, *opcode_str;
+	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) {
+			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;
+	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) {
+			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)
+{
+	uint8_t num_keys = *((uint8_t *) data);
+
+	print_field("Num keys: %d", num_keys);
+
+	packet_hexdump(data + 1, size - 1);
+}
+
+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(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 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);
+
+	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_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;
+	const char *str;
+	uint8_t evt_len;
+	int8_t *rssi;
+
+	print_num_reports(evt->num_reports);
+
+report:
+	switch (evt->event_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, evt->event_type);
+	print_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);
+}
+
+struct subevent_data {
+	uint8_t subevent;
+	const char *str;
+	void (*func) (const void *data, uint8_t size);
+	uint8_t size;
+	bool fixed;
+};
+
+static const struct subevent_data subevent_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" },
+	{ }
+};
+
+static void le_meta_event_evt(const void *data, uint8_t size)
+{
+	uint8_t subevent = *((const uint8_t *) data);
+	const struct subevent_data *subevent_data = NULL;
+	const char *subevent_color, *subevent_str;
+	int i;
+
+	for (i = 0; subevent_table[i].str; i++) {
+		if (subevent_table[i].subevent == subevent) {
+			subevent_data = &subevent_table[i];
+			break;
+		}
+	}
+
+	if (subevent_data) {
+		if (subevent_data->func)
+			subevent_color = COLOR_HCI_EVENT;
+		else
+			subevent_color = COLOR_HCI_EVENT_UNKNOWN;
+		subevent_str = subevent_data->str;
+	} else {
+		subevent_color = COLOR_HCI_EVENT_UNKNOWN;
+		subevent_str = "Unknown";
+	}
+
+	print_indent(6, subevent_color, "", subevent_str, COLOR_OFF,
+						" (0x%2.2x)", subevent);
+
+	if (!subevent_data || !subevent_data->func) {
+		packet_hexdump(data + 1, size - 1);
+		return;
+	}
+
+	if (subevent_data->fixed) {
+		if (size - 1 != subevent_data->size) {
+			print_text(COLOR_ERROR, "invalid packet size");
+			packet_hexdump(data + 1, size - 1);
+			return;
+		}
+	} else {
+		if (size - 1 < subevent_data->size) {
+			print_text(COLOR_ERROR, "too short packet");
+			packet_hexdump(data + 1, size - 1);
+			return;
+		}
+	}
+
+	subevent_data->func(data + 1, size - 1);
+}
+
+static void vendor_evt(const void *data, uint8_t size)
+{
+	vendor_event(0xffff, 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" },
+	{ 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, index, '=', COLOR_NEW_INDEX, "New Index",
+							label, details);
+}
+
+void packet_del_index(struct timeval *tv, uint16_t index, const char *label)
+{
+	print_packet(tv, index, '=', COLOR_DEL_INDEX, "Delete Index",
+							label, NULL);
+}
+
+void packet_hci_command(struct timeval *tv, 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);
+	const struct opcode_data *opcode_data = NULL;
+	const char *opcode_color, *opcode_str;
+	char extra_str[25];
+	int i;
+
+	if (size < HCI_COMMAND_HDR_SIZE) {
+		sprintf(extra_str, "(len %d)", size);
+		print_packet(tv, 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) {
+			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, 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, 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, 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, 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, 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, index, '*', COLOR_ERROR,
+				"Malformed ACL Data RX packet", NULL, NULL);
+		else
+			print_packet(tv, 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, 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, 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, index, '*', COLOR_ERROR,
+				"Malformed SCO Data RX packet", NULL, NULL);
+		else
+			print_packet(tv, 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, 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; subevent_table[i].str; i++) {
+		if (subevent_table[i].func)
+			continue;
+
+		printf("\t%s\n", subevent_table[i].str);
+	}
+}
diff --git a/bluez/monitor/packet.h b/bluez/monitor/packet.h
new file mode 100644
index 0000000..c39816b
--- /dev/null
+++ b/bluez/monitor/packet.h
@@ -0,0 +1,76 @@
+/*
+ *
+ *  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>
+
+#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_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, uint16_t index, uint16_t opcode,
+					const void *data, uint16_t size);
+void packet_monitor(struct timeval *tv, 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_hci_command(struct timeval *tv, uint16_t index,
+					const void *data, uint16_t size);
+void packet_hci_event(struct timeval *tv, uint16_t index,
+					const void *data, uint16_t size);
+void packet_hci_acldata(struct timeval *tv, uint16_t index, bool in,
+					const void *data, uint16_t size);
+void packet_hci_scodata(struct timeval *tv, uint16_t index, bool in,
+					const void *data, uint16_t size);
+
+void packet_todo(void);
diff --git a/bluez/monitor/rfcomm.h b/bluez/monitor/rfcomm.h
new file mode 100644
index 0000000..c157352
--- /dev/null
+++ b/bluez/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/monitor/sdp.c b/bluez/monitor/sdp.c
new file mode 100644
index 0000000..a0ab314
--- /dev/null
+++ b/bluez/monitor/sdp.c
@@ -0,0 +1,749 @@
+/*
+ *
+ *  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 <bluetooth/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, uint16_t channel)
+{
+	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;
+
+	if (frame->size < 5) {
+		print_text(COLOR_ERROR, "frame too short");
+		packet_hexdump(frame->data, frame->size);
+		return;
+	}
+
+	pdu = *((uint8_t *) frame->data);
+	tid = get_be16(frame->data + 1);
+	plen = get_be16(frame->data + 3);
+
+	if (frame->size != plen + 5) {
+		print_text(COLOR_ERROR, "invalid frame size");
+		packet_hexdump(frame->data, 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);
+
+	if (!sdp_data || !sdp_data->func) {
+		packet_hexdump(frame->data + 5, frame->size - 5);
+		return;
+	}
+
+	tid_info = get_tid(tid, channel);
+
+	l2cap_frame_pull(&sdp_frame, frame, 5);
+	sdp_data->func(&sdp_frame, tid_info);
+}
diff --git a/bluez/monitor/sdp.h b/bluez/monitor/sdp.h
new file mode 100644
index 0000000..ca2cd45
--- /dev/null
+++ b/bluez/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, uint16_t channel);
diff --git a/bluez/monitor/uuid.c b/bluez/monitor/uuid.c
new file mode 100644
index 0000000..4b7448e
--- /dev/null
+++ b/bluez/monitor/uuid.c
@@ -0,0 +1,284 @@
+/*
+ *
+ *  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"					},
+	{ 0x1000, "Service Discovery Server Service Class"	},
+	{ 0x1001, "Browse Group Descriptor Service Class"	},
+	{ 0x1002, "Public Browse Root"				},
+	{ 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"				},
+	{ 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"					},
+	{ 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"				},
+	{ 0x1400, "HDP"						},
+	{ 0x1401, "HDP Source"					},
+	{ 0x1402, "HDP Sink"					},
+	{ 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 undefined */
+	{ 0x1816, "Cycling Speed and Cadence"			},
+	{ 0x2800, "Primary Service"				},
+	{ 0x2801, "Secondary Service"				},
+	{ 0x2802, "Include"					},
+	{ 0x2803, "Characteristic"				},
+	{ 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"				},
+	{ 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 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 to 0x2a5a undefined */
+	{ 0x2a5b, "CSC Measurement"				},
+	{ 0x2a5c, "CSC Feature"					},
+	{ 0x2a5d, "Sensor Location"				},
+	{ }
+};
+
+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/monitor/uuid.h b/bluez/monitor/uuid.h
new file mode 100644
index 0000000..f467f51
--- /dev/null
+++ b/bluez/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/monitor/vendor.c b/bluez/monitor/vendor.c
new file mode 100644
index 0000000..8d3b614
--- /dev/null
+++ b/bluez/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/monitor/vendor.h b/bluez/monitor/vendor.h
new file mode 100644
index 0000000..9ac9a4b
--- /dev/null
+++ b/bluez/monitor/vendor.h
@@ -0,0 +1,27 @@
+/*
+ *
+ *  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 vendor_event(uint16_t manufacturer, const void *data, uint8_t size);
diff --git a/bluez/obexd/client/bluetooth.c b/bluez/obexd/client/bluetooth.c
new file mode 100644
index 0000000..e89a92b
--- /dev/null
+++ b/bluez/obexd/client/bluetooth.c
@@ -0,0 +1,514 @@
+/*
+ *
+ *  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 <errno.h>
+#include <inttypes.h>
+
+#include <glib.h>
+#include <gdbus/gdbus.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/rfcomm.h>
+#include <bluetooth/sdp.h>
+#include <bluetooth/sdp_lib.h>
+
+#include "btio/btio.h"
+
+#include "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;
+
+		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/obexd/client/bluetooth.h b/bluez/obexd/client/bluetooth.h
new file mode 100644
index 0000000..968e131
--- /dev/null
+++ b/bluez/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/obexd/client/dbus.c b/bluez/obexd/client/dbus.c
new file mode 100644
index 0000000..243af59
--- /dev/null
+++ b/bluez/obexd/client/dbus.c
@@ -0,0 +1,93 @@
+/*
+ *
+ *  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 "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/obexd/client/dbus.h b/bluez/obexd/client/dbus.h
new file mode 100644
index 0000000..6136bf5
--- /dev/null
+++ b/bluez/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/obexd/client/driver.c b/bluez/obexd/client/driver.c
new file mode 100644
index 0000000..a98dcf4
--- /dev/null
+++ b/bluez/obexd/client/driver.c
@@ -0,0 +1,87 @@
+/*
+ *
+ *  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 "transfer.h"
+#include "session.h"
+#include "driver.h"
+#include "log.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/obexd/client/driver.h b/bluez/obexd/client/driver.h
new file mode 100644
index 0000000..f1c0646
--- /dev/null
+++ b/bluez/obexd/client/driver.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 obc_driver {
+	const char *service;
+	const char *uuid;
+	void *target;
+	gsize target_len;
+	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/obexd/client/ftp.c b/bluez/obexd/client/ftp.c
new file mode 100644
index 0000000..fa85708
--- /dev/null
+++ b/bluez/obexd/client/ftp.c
@@ -0,0 +1,511 @@
+/*
+ *
+ *  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 "dbus.h"
+#include "log.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/obexd/client/ftp.h b/bluez/obexd/client/ftp.h
new file mode 100644
index 0000000..3d90968
--- /dev/null
+++ b/bluez/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/obexd/client/manager.c b/bluez/obexd/client/manager.c
new file mode 100644
index 0000000..8efe1f2
--- /dev/null
+++ b/bluez/obexd/client/manager.c
@@ -0,0 +1,315 @@
+/*
+ *
+ *  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 "log.h"
+#include "transfer.h"
+#include "session.h"
+#include "manager.h"
+#include "bluetooth.h"
+#include "opp.h"
+#include "ftp.h"
+#include "pbap.h"
+#include "sync.h"
+#include "map.h"
+#include "obexd/src/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_str_equal(obc_session_get_path(session), path) == TRUE)
+			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/obexd/client/manager.h b/bluez/obexd/client/manager.h
new file mode 100644
index 0000000..e4068de
--- /dev/null
+++ b/bluez/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/obexd/client/map-event.c b/bluez/obexd/client/map-event.c
new file mode 100644
index 0000000..d424924
--- /dev/null
+++ b/bluez/obexd/client/map-event.c
@@ -0,0 +1,112 @@
+/*
+ *
+ *  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 "log.h"
+#include "map-event.h"
+
+#include <gdbus/gdbus.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/obexd/client/map-event.h b/bluez/obexd/client/map-event.h
new file mode 100644
index 0000000..ba5d5d2
--- /dev/null
+++ b/bluez/obexd/client/map-event.h
@@ -0,0 +1,67 @@
+/*
+ *
+ *  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;
+};
+
+/* 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/obexd/client/map.c b/bluez/obexd/client/map.c
new file mode 100644
index 0000000..d2d3d81
--- /dev/null
+++ b/bluez/obexd/client/map.c
@@ -0,0 +1,2080 @@
+/*
+ *
+ *  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 <gdbus/gdbus.h>
+#include <gobex/gobex-apparam.h>
+#include <bluetooth/sdp.h>
+
+#include "dbus.h"
+#include "log.h"
+#include "map_ap.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;
+};
+
+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)
+		return;
+
+	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);
+	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);
+
+	if (snprintf(handle, sizeof(handle), "%" PRIx64, msg->handle) < 0)
+		goto fail;
+
+	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;
+
+	if (snprintf(handle, sizeof(handle), "%" PRIx64, msg->handle) < 0)
+		goto fail;
+
+	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) {
+				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)
+		return;
+
+	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);
+	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;
+	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");
+}
+
+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/obexd/client/map.h b/bluez/obexd/client/map.h
new file mode 100644
index 0000000..86f6b95
--- /dev/null
+++ b/bluez/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/obexd/client/mns.c b/bluez/obexd/client/mns.c
new file mode 100644
index 0000000..d638886
--- /dev/null
+++ b/bluez/obexd/client/mns.c
@@ -0,0 +1,370 @@
+/*
+ *
+ *  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.h"
+#include "plugin.h"
+#include "log.h"
+#include "obex.h"
+#include "service.h"
+#include "mimetype.h"
+#include "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)
+{
+	if (!value)
+		return;
+
+	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)
+{
+	if (!value)
+		return;
+
+	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)
+{
+	event->msg_type = 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 },
+		{ }
+};
+
+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) {
+				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);
+}
+
+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/obexd/client/opp.c b/bluez/obexd/client/opp.c
new file mode 100644
index 0000000..d6fd1c6
--- /dev/null
+++ b/bluez/obexd/client/opp.c
@@ -0,0 +1,214 @@
+/*
+ *
+ *  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 "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/obexd/client/opp.h b/bluez/obexd/client/opp.h
new file mode 100644
index 0000000..a23e94e
--- /dev/null
+++ b/bluez/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/obexd/client/pbap.c b/bluez/obexd/client/pbap.c
new file mode 100644
index 0000000..2089860
--- /dev/null
+++ b/bluez/obexd/client/pbap.c
@@ -0,0 +1,1031 @@
+/*
+ *
+ *  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 <gdbus/gdbus.h>
+
+#include <bluetooth/bluetooth.h>
+#include <gobex/gobex-apparam.h>
+
+#include "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
+
+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",
+	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;
+};
+
+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;
+
+	if (!g_ascii_strcasecmp(location, "int") ||
+			!g_ascii_strcasecmp(location, "internal"))
+		path = g_strdup("/telecom");
+	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")) {
+		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);
+
+	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_return_apparam(struct obc_transfer *transfer,
+				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);
+}
+
+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, &phone_book_size, &new_missed_calls);
+
+	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 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) },
+	{ }
+};
+
+static void pbap_free(void *data)
+{
+	struct pbap_data *pbap = data;
+
+	obc_session_unref(pbap->session);
+	g_free(pbap->path);
+	g_free(pbap);
+}
+
+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);
+
+	if (!g_dbus_register_interface(conn, path, PBAP_INTERFACE, pbap_methods,
+						NULL, NULL, 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,
+	.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/obexd/client/pbap.h b/bluez/obexd/client/pbap.h
new file mode 100644
index 0000000..ce56258
--- /dev/null
+++ b/bluez/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/obexd/client/session.c b/bluez/obexd/client/session.c
new file mode 100644
index 0000000..cb176e4
--- /dev/null
+++ b/bluez/obexd/client/session.c
@@ -0,0 +1,1368 @@
+/*
+ *
+ *  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 "dbus.h"
+#include "log.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;
+	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;
+};
+
+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 != 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 connect_cb(GObex *obex, GError *err, GObexPacket *rsp,
+							gpointer user_data)
+{
+	struct callback_data *callback = user_data;
+	GError *gerr = NULL;
+	uint8_t rsp_code;
+
+	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->func(callback->session, NULL, gerr, callback->data);
+	if (gerr != NULL)
+		g_error_free(gerr);
+	obc_session_unref(callback->session);
+	g_free(callback);
+}
+
+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;
+	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);
+
+	if (driver->target != NULL)
+		g_obex_connect(obex, connect_cb, callback, &err,
+			G_OBEX_HDR_TARGET, driver->target, driver->target_len,
+			G_OBEX_HDR_INVALID);
+	else
+		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->func(callback->session, NULL, err, callback->data);
+	obc_session_unref(callback->session);
+	g_free(callback);
+}
+
+static void owner_disconnected(DBusConnection *connection, void *user_data)
+{
+	struct obc_session *session = user_data;
+
+	DBG("");
+
+	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;
+	}
+
+	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)
+		goto fail;
+
+	p->session->p = p;
+
+	return 0;
+
+fail:
+	pending_request_free(p);
+	return (*err)->code;
+}
+
+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);
+
+	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)
+		goto fail;
+
+	p->session->p = p;
+
+	return 0;
+
+fail:
+	pending_request_free(p);
+	return (*err)->code;
+}
+
+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)
+		goto fail;
+
+	p->session->p = p;
+
+	return 0;
+
+fail:
+	pending_request_free(p);
+	return (*err)->code;
+}
+
+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)
+		goto fail;
+
+	p->session->p = p;
+
+	return 0;
+
+fail:
+	pending_request_free(p);
+	return (*err)->code;
+}
+
+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)
+		goto fail;
+
+	p->session->p = p;
+
+	return 0;
+
+fail:
+	pending_request_free(p);
+	return (*err)->code;
+}
+
+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/obexd/client/session.h b/bluez/obexd/client/session.h
new file mode 100644
index 0000000..35899bc
--- /dev/null
+++ b/bluez/obexd/client/session.h
@@ -0,0 +1,83 @@
+/*
+ *
+ *  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>
+#include <gdbus/gdbus.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/obexd/client/sync.c b/bluez/obexd/client/sync.c
new file mode 100644
index 0000000..1a4b482
--- /dev/null
+++ b/bluez/obexd/client/sync.c
@@ -0,0 +1,261 @@
+/*
+ *
+ *  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 "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/obexd/client/sync.h b/bluez/obexd/client/sync.h
new file mode 100644
index 0000000..8adc5f8
--- /dev/null
+++ b/bluez/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/obexd/client/transfer.c b/bluez/obexd/client/transfer.c
new file mode 100644
index 0000000..3564a34
--- /dev/null
+++ b/bluez/obexd/client/transfer.c
@@ -0,0 +1,941 @@
+/*
+ *
+ *  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 "dbus.h"
+#include "log.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_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->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;
+
+	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->xfer == 0)
+		return g_dbus_create_error(message,
+				ERROR_INTERFACE ".NotInProgress",
+				"Not in progress");
+
+	g_obex_suspend(transfer->obex);
+
+	transfer_set_status(transfer, TRANSFER_STATUS_SUSPENDED);
+
+	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;
+
+	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->xfer == 0)
+		return g_dbus_create_error(message,
+				ERROR_INTERFACE ".NotInProgress",
+				"Not in progress");
+
+	g_obex_resume(transfer->obex);
+
+	transfer_set_status(transfer, TRANSFER_STATUS_ACTIVE);
+
+	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:
+		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->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 (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) {
+		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)
+{
+	transfer->obex = g_obex_ref(obex);
+
+	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/obexd/client/transfer.h b/bluez/obexd/client/transfer.h
new file mode 100644
index 0000000..b6b835d
--- /dev/null
+++ b/bluez/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/obexd/client/transport.c b/bluez/obexd/client/transport.c
new file mode 100644
index 0000000..77e52f7
--- /dev/null
+++ b/bluez/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 "transport.h"
+#include "log.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/obexd/client/transport.h b/bluez/obexd/client/transport.h
new file mode 100644
index 0000000..b035cfc
--- /dev/null
+++ b/bluez/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/obexd/plugins/bluetooth.c b/bluez/obexd/plugins/bluetooth.c
new file mode 100644
index 0000000..cf326cc
--- /dev/null
+++ b/bluez/obexd/plugins/bluetooth.c
@@ -0,0 +1,495 @@
+/*
+ *
+ *  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 <gdbus/gdbus.h>
+
+#include "btio/btio.h"
+#include "lib/uuid.h"
+
+#include "obexd.h"
+#include "plugin.h"
+#include "server.h"
+#include "obex.h"
+#include "transport.h"
+#include "service.h"
+#include "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/obexd/plugins/filesystem.c b/bluez/obexd/plugins/filesystem.c
new file mode 100644
index 0000000..1132a34
--- /dev/null
+++ b/bluez/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 <wait.h>
+#include <inttypes.h>
+
+#include <glib.h>
+
+#include "obexd.h"
+#include "plugin.h"
+#include "log.h"
+#include "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/obexd/plugins/filesystem.h b/bluez/obexd/plugins/filesystem.h
new file mode 100644
index 0000000..f95773b
--- /dev/null
+++ b/bluez/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/obexd/plugins/ftp.c b/bluez/obexd/plugins/ftp.c
new file mode 100644
index 0000000..773861d
--- /dev/null
+++ b/bluez/obexd/plugins/ftp.c
@@ -0,0 +1,502 @@
+/*
+ *
+ *  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.h"
+#include "plugin.h"
+#include "log.h"
+#include "obex.h"
+#include "manager.h"
+#include "mimetype.h"
+#include "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;
+	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;
+
+	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;
+
+	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);
+
+	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);
+
+	g_free(ftp->folder);
+	g_free(ftp);
+}
+
+static struct obex_service_driver ftp = {
+	.name = "File Transfer server",
+	.service = OBEX_FTP,
+	.target = FTP_TARGET,
+	.target_size = TARGET_SIZE,
+	.connect = ftp_connect,
+	.get = ftp_get,
+	.put = ftp_put,
+	.chkput = ftp_chkput,
+	.setpath = ftp_setpath,
+	.action = ftp_action,
+	.disconnect = ftp_disconnect
+};
+
+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/obexd/plugins/ftp.h b/bluez/obexd/plugins/ftp.h
new file mode 100644
index 0000000..f06de84
--- /dev/null
+++ b/bluez/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/obexd/plugins/irmc.c b/bluez/obexd/plugins/irmc.c
new file mode 100644
index 0000000..d343977
--- /dev/null
+++ b/bluez/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.h"
+#include "plugin.h"
+#include "log.h"
+#include "obex.h"
+#include "service.h"
+#include "phonebook.h"
+#include "mimetype.h"
+#include "filesystem.h"
+#include "manager.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/obexd/plugins/mas.c b/bluez/obexd/plugins/mas.c
new file mode 100644
index 0000000..5729c22
--- /dev/null
+++ b/bluez/obexd/plugins/mas.c
@@ -0,0 +1,853 @@
+/*
+ *
+ *  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 <gobex/gobex.h>
+#include <gobex/gobex-apparam.h>
+
+#include "obexd.h"
+#include "plugin.h"
+#include "log.h"
+#include "obex.h"
+#include "service.h"
+#include "mimetype.h"
+#include "filesystem.h"
+#include "manager.h"
+#include "map_ap.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;
+};
+
+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)
+		size = 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 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;
+
+	if (err < 0 && err != -EAGAIN) {
+		obex_object_set_io_flags(mas, G_IO_ERR, err);
+		return;
+	}
+
+	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);
+	}
+
+	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;
+	}
+
+	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);
+
+	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;
+	}
+
+	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);
+
+	*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;
+	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 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 = any_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/obexd/plugins/messages-dummy.c b/bluez/obexd/plugins/messages-dummy.c
new file mode 100644
index 0000000..bb0627f
--- /dev/null
+++ b/bluez/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 "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/obexd/plugins/messages.h b/bluez/obexd/plugins/messages.h
new file mode 100644
index 0000000..00a16b1
--- /dev/null
+++ b/bluez/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/obexd/plugins/opp.c b/bluez/obexd/plugins/opp.c
new file mode 100644
index 0000000..ee0204c
--- /dev/null
+++ b/bluez/obexd/plugins/opp.c
@@ -0,0 +1,189 @@
+/*
+ *
+ *  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.h"
+#include "plugin.h"
+#include "obex.h"
+#include "service.h"
+#include "log.h"
+#include "manager.h"
+#include "filesystem.h"
+
+#define VCARD_TYPE "text/x-vcard"
+#define VCARD_FILE CONFIGDIR "/vcard.vcf"
+
+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;
+	int32_t time;
+	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;
+	}
+
+	time = 0;
+	err = manager_request_authorization(user_data, time, &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;
+
+	if (obex_get_name(os))
+		return -EPERM;
+
+	type = obex_get_type(os);
+
+	if (type == NULL)
+		return -EPERM;
+
+	if (g_ascii_strcasecmp(type, VCARD_TYPE) == 0) {
+		if (obex_get_stream_start(os, VCARD_FILE) < 0)
+			return -ENOENT;
+
+	} else
+		return -EPERM;
+
+	return 0;
+}
+
+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/obexd/plugins/pbap.c b/bluez/obexd/plugins/pbap.c
new file mode 100644
index 0000000..acac3aa
--- /dev/null
+++ b/bluez/obexd/plugins/pbap.c
@@ -0,0 +1,991 @@
+/*
+ *
+ *  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-apparam.h>
+
+#include "obexd.h"
+#include "plugin.h"
+#include "log.h"
+#include "obex.h"
+#include "service.h"
+#include "phonebook.h"
+#include "mimetype.h"
+#include "filesystem.h"
+#include "manager.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);
+
+	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);
+
+	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;
+
+	DBG("name %s context %p valid %d", name, context, pbap->cache.valid);
+
+	if (oflag != O_RDONLY) {
+		ret = -EPERM;
+		goto fail;
+	}
+
+	if (name == NULL) {
+		ret = -EBADR;
+		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;
+	struct pbap_session *pbap = obj->session;
+
+	if (!obj->buffer && !obj->apparam)
+		return -EAGAIN;
+
+	*hi = G_OBEX_HDR_APPARAM;
+
+	if (pbap->params->maxlistcount == 0 || 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/obexd/plugins/pcsuite.c b/bluez/obexd/plugins/pcsuite.c
new file mode 100644
index 0000000..6460aa9
--- /dev/null
+++ b/bluez/obexd/plugins/pcsuite.c
@@ -0,0 +1,512 @@
+/*
+ *
+ *  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.h"
+#include "plugin.h"
+#include "log.h"
+#include "obex.h"
+#include "mimetype.h"
+#include "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/obexd/plugins/phonebook-dummy.c b/bluez/obexd/plugins/phonebook-dummy.c
new file mode 100644
index 0000000..6b9d040
--- /dev/null
+++ b/bluez/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 "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/obexd/plugins/phonebook.h b/bluez/obexd/plugins/phonebook.h
new file mode 100644
index 0000000..fff33c1
--- /dev/null
+++ b/bluez/obexd/plugins/phonebook.h
@@ -0,0 +1,162 @@
+/*
+ *
+ *  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_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_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/obexd/plugins/vcard.c b/bluez/obexd/plugins/vcard.c
new file mode 100644
index 0000000..b36e4bf
--- /dev/null
+++ b/bluez/obexd/plugins/vcard.c
@@ -0,0 +1,924 @@
+/*
+ * 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;
+	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;
+	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/obexd/plugins/vcard.h b/bluez/obexd/plugins/vcard.h
new file mode 100644
index 0000000..22c3f68
--- /dev/null
+++ b/bluez/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/obexd/src/genbuiltin b/bluez/obexd/src/genbuiltin
new file mode 100755
index 0000000..39f7735
--- /dev/null
+++ b/bluez/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/obexd/src/log.c b/bluez/obexd/src/log.c
new file mode 100644
index 0000000..ace7ab6
--- /dev/null
+++ b/bluez/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);
+
+	syslog(LOG_INFO, "OBEX daemon %s", VERSION);
+}
+
+void __obex_log_cleanup(void)
+{
+	closelog();
+
+	g_strfreev(enabled);
+}
diff --git a/bluez/obexd/src/log.h b/bluez/obexd/src/log.h
new file mode 100644
index 0000000..d9fb867
--- /dev/null
+++ b/bluez/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/obexd/src/main.c b/bluez/obexd/src/main.c
new file mode 100644
index 0000000..80645f8
--- /dev/null
+++ b/bluez/obexd/src/main.c
@@ -0,0 +1,340 @@
+/*
+ *
+ *  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] != '/') {
+		char *old_root = option_root, *home = getenv("HOME");
+		if (home) {
+			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/obexd/src/manager.c b/bluez/obexd/src/manager.c
new file mode 100644
index 0000000..326e56f
--- /dev/null
+++ b/bluez/obexd/src/manager.c
@@ -0,0 +1,807 @@
+/*
+ *
+ *  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 <gdbus/gdbus.h>
+#include <sys/socket.h>
+#include <inttypes.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? */
+
+	if (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, int32_t time,
+					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/obexd/src/manager.h b/bluez/obexd/src/manager.h
new file mode 100644
index 0000000..669b223
--- /dev/null
+++ b/bluez/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, int32_t time,
+					char **new_folder, char **new_name);
+
+DBusConnection *manager_dbus_get_connection(void);
diff --git a/bluez/obexd/src/map_ap.h b/bluez/obexd/src/map_ap.h
new file mode 100644
index 0000000..da108fe
--- /dev/null
+++ b/bluez/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/obexd/src/mimetype.c b/bluez/obexd/src/mimetype.c
new file mode 100644
index 0000000..833ddc7
--- /dev/null
+++ b/bluez/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/obexd/src/mimetype.h b/bluez/obexd/src/mimetype.h
new file mode 100644
index 0000000..79529b8
--- /dev/null
+++ b/bluez/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/obexd/src/obex-priv.h b/bluez/obexd/src/obex-priv.h
new file mode 100644
index 0000000..355a7f8
--- /dev/null
+++ b/bluez/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/obexd/src/obex.c b/bluez/obexd/src/obex.c
new file mode 100644
index 0000000..8823063
--- /dev/null
+++ b/bluez/obexd/src/obex.c
@@ -0,0 +1,1257 @@
+/*
+ *
+ *  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"
+
+/* Challenge request */
+#define NONCE_TAG 0x00
+#define OPTIONS_TAG 0x01 /* Optional */
+#define REALM_TAG 0x02 /* Optional */
+
+#define NONCE_LEN 16
+
+/* Challenge response */
+#define DIGEST_TAG 0x00
+#define USER_ID_TAG 0x01 /* Optional */
+#define DIGEST_NONCE_TAG 0x02 /* Optional */
+
+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 uint8_t *extract_nonce(const uint8_t *buffer, unsigned int hlen)
+{
+	struct auth_header *hdr;
+	uint8_t *nonce = NULL;
+	uint32_t len = 0;
+
+	while (len < hlen) {
+		hdr = (void *) buffer + len;
+
+		switch (hdr->tag) {
+		case NONCE_TAG:
+			if (hdr->len != NONCE_LEN)
+				return NULL;
+
+			nonce = hdr->val;
+			break;
+		}
+
+		len += hdr->len + sizeof(struct auth_header);
+	}
+
+	return nonce;
+}
+
+static uint8_t *challenge_response(const uint8_t *nonce)
+{
+	GChecksum *md5;
+	uint8_t *result;
+	size_t size;
+
+	result = g_new0(uint8_t, 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, (uint8_t *) ":BlueZ", 6);
+
+	size = NONCE_LEN;
+	g_checksum_get_digest(md5, result, &size);
+
+	g_checksum_free(md5);
+
+	return result;
+}
+
+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 parse_authchal(struct obex_session *session, GObexPacket *req,
+							GObexPacket *rsp)
+{
+	GObexHeader *hdr;
+	const guint8 *data, *nonce = NULL;
+	gsize len;
+	uint8_t challenge[18];
+	struct auth_header *auth = (struct auth_header *) challenge;
+	uint8_t *response;
+
+	hdr = g_obex_packet_get_header(req, G_OBEX_HDR_AUTHCHAL);
+	if (hdr == NULL)
+		return;
+
+	if (!g_obex_header_get_bytes(hdr, &data, &len))
+		return;
+
+	nonce = extract_nonce(data, len);
+	DBG("AUTH CHALLENGE REQUEST");
+
+	response = challenge_response(nonce);
+	auth->tag = DIGEST_TAG;
+	auth->len = NONCE_LEN;
+	memcpy(auth->val, response, NONCE_LEN);
+
+	hdr = g_obex_header_new_bytes(G_OBEX_HDR_AUTHRESP, challenge,
+							sizeof(challenge));
+	g_obex_packet_add_header(rsp, hdr);
+}
+
+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);
+
+	parse_authchal(os, req, rsp);
+
+	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;
+
+	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/obexd/src/obex.h b/bluez/obexd/src/obex.h
new file mode 100644
index 0000000..fc16747
--- /dev/null
+++ b/bluez/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/obexd/src/obex.service.in b/bluez/obexd/src/obex.service.in
new file mode 100644
index 0000000..bca3aef
--- /dev/null
+++ b/bluez/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/obexd/src/obexd.h b/bluez/obexd/src/obexd.h
new file mode 100644
index 0000000..42c3c4d
--- /dev/null
+++ b/bluez/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/obexd/src/org.bluez.obex.service b/bluez/obexd/src/org.bluez.obex.service
new file mode 100644
index 0000000..a538088
--- /dev/null
+++ b/bluez/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/obexd/src/plugin.c b/bluez/obexd/src/plugin.c
new file mode 100644
index 0000000..7d971b6
--- /dev/null
+++ b/bluez/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/obexd/src/plugin.h b/bluez/obexd/src/plugin.h
new file mode 100644
index 0000000..13d7769
--- /dev/null
+++ b/bluez/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/obexd/src/server.c b/bluez/obexd/src/server.c
new file mode 100644
index 0000000..007c27e
--- /dev/null
+++ b/bluez/obexd/src/server.c
@@ -0,0 +1,127 @@
+/*
+ *
+ *  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/obexd/src/server.h b/bluez/obexd/src/server.h
new file mode 100644
index 0000000..278c35f
--- /dev/null
+++ b/bluez/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/obexd/src/service.c b/bluez/obexd/src/service.c
new file mode 100644
index 0000000..c088535
--- /dev/null
+++ b/bluez/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/obexd/src/service.h b/bluez/obexd/src/service.h
new file mode 100644
index 0000000..5d9d325
--- /dev/null
+++ b/bluez/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/obexd/src/transport.c b/bluez/obexd/src/transport.c
new file mode 100644
index 0000000..4984643
--- /dev/null
+++ b/bluez/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/obexd/src/transport.h b/bluez/obexd/src/transport.h
new file mode 100644
index 0000000..97e10d0
--- /dev/null
+++ b/bluez/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/plugins/autopair.c b/bluez/plugins/autopair.c
new file mode 100644
index 0000000..d63da93
--- /dev/null
+++ b/bluez/plugins/autopair.c
@@ -0,0 +1,194 @@
+/*
+ *
+ *  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 <bluetooth/bluetooth.h>
+#include <glib.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",
+								sizeof(seed));
+		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/plugins/dropcam.c b/bluez/plugins/dropcam.c
new file mode 100644
index 0000000..f79901c
--- /dev/null
+++ b/bluez/plugins/dropcam.c
@@ -0,0 +1,452 @@
+/*
+ *
+ *  Dropcam BTLE integration for BlueZ
+ *
+ *  Copyright (C) 2013 Dropcam 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
+ *
+ */
+
+/*  This module implements the Dropcam BTLE profile for mobile setup.
+ *
+ *  Notes:
+ *   - General structure and "watcher" implementation is modeled after the
+ *     cyclingspeed and thermometer profile implementations.
+ *   - This module exposes a new DBus interface (com.dropcam.BTLEMessageDispatch1)
+ *     which can be used to send and receive messages from a connected Dropcam BTLE setup
+ *     device.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdbool.h>
+#include <glib.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <stdint.h>
+
+#include <gdbus/gdbus.h>
+#include "src/dbus-common.h"
+#include "src/adapter.h"
+
+#include "lib/uuid.h"
+#include "src/plugin.h"
+#include "src/hcid.h"
+#include "src/log.h"
+#include "src/device.h"
+#include "src/error.h"
+#include "attrib/gattrib.h"
+#include "attrib/gatt-service.h"
+#include "attrib/att.h"
+#include "attrib/gatt.h"
+#include "attrib/att-database.h"
+
+#include "dropcam_btle_profile_def.h"
+
+#define DROPCAM_OBJECT_PATH	"/com/dropcam"
+#define DROPCAM_INTERFACE	"com.dropcam.BTLEMessageDispatch1"
+#define DROPCAM_WATCHER_INTERFACE "com.dropcam.BTLEDispatchWatcher1"
+
+#define MTU_SIZE 20
+
+struct pending_read {
+	int payload_len;
+	uint8_t payload[MTU_SIZE];
+};
+
+struct message {
+	uint8_t *bytes;
+	int len;
+};
+
+struct watcher {
+	guint id;
+	char *srv;
+	char *path;
+};
+
+struct dropcam_adapter {
+	struct btd_adapter *adapter;
+
+	uint8_t last_read_seqnum;
+	uint8_t last_write_seqnum;
+
+	int cur_message_pos;
+	uint8_t message_payload[0xffff];
+	GSList *pending_read_payloads;
+};
+
+static GSList *adapters = NULL;
+static GSList *watchers = NULL;
+
+static int adapter_cmp(gconstpointer a, gconstpointer b)
+{
+	const struct dropcam_adapter *dc_adapter = a;
+	const struct btd_adapter *adapter = b;
+
+	return dc_adapter->adapter == adapter ? 0 : -1;
+}
+
+static struct dropcam_adapter *find_dropcam_adapter(struct btd_adapter *adapter) {
+	GSList *l = g_slist_find_custom(adapters, adapter, adapter_cmp);
+
+	return l ? l->data : NULL;
+}
+
+static void update_watcher(gpointer data, gpointer user_data) {
+	struct watcher *w = data;
+	struct dropcam_adapter *dc_adapter = user_data;
+	const char *path = DROPCAM_OBJECT_PATH;
+	DBusMessageIter iter;
+	DBusMessageIter array;
+	DBusMessage *msg;
+
+	msg = dbus_message_new_method_call(w->srv, w->path, DROPCAM_WATCHER_INTERFACE, "MessageReceived");
+	if (msg == NULL)
+		return;
+
+	dbus_message_iter_init_append(msg, &iter);
+
+	dbus_message_iter_append_basic(&iter, DBUS_TYPE_OBJECT_PATH , &path);
+
+	uint8_t *payload_ptr = dc_adapter->message_payload;
+	dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "y", &array);
+	dbus_message_iter_append_fixed_array(&array, DBUS_TYPE_BYTE,
+	                                     &payload_ptr, dc_adapter->cur_message_pos);
+	dbus_message_iter_close_container(&iter, &array);
+
+	dbus_message_set_no_reply(msg, TRUE);
+
+	DBG("Sending MessageReceived to: %s: %s", w->srv, w->path);
+	g_dbus_send_message(btd_get_dbus_connection(), msg);
+}
+
+static uint8_t dropcam_tx_read(struct attribute *a, struct btd_device *device, gpointer user_data) {
+	struct dropcam_adapter *dc_adapter = user_data;
+
+	if (dc_adapter->pending_read_payloads == NULL) {
+		/* nothing to send so increment the sequence number and return an empty message */
+		uint8_t seq_num = dc_adapter->last_read_seqnum + 1;
+		attrib_db_update(dc_adapter->adapter, a->handle, NULL, &seq_num, sizeof(seq_num), NULL);
+		dc_adapter->last_read_seqnum = seq_num;
+	} else {
+		/* return the next pending read */
+		struct pending_read* pr = dc_adapter->pending_read_payloads->data;
+		DBG("Sending %d byte payload", pr->payload_len);
+		attrib_db_update(dc_adapter->adapter, a->handle, NULL, pr->payload, pr->payload_len, NULL);
+
+		dc_adapter->pending_read_payloads = g_slist_remove(dc_adapter->pending_read_payloads, pr);
+		g_free(pr);
+	}
+
+	return 0;
+}
+
+static uint8_t dropcam_tx_write(struct attribute *a, struct btd_device *device, gpointer user_data) {
+	struct dropcam_adapter *dc_adapter = user_data;
+
+	if (a->len > 0) {
+		uint8_t expected_seq_num = dc_adapter->last_write_seqnum + 1;
+
+		struct DropcamBTLEPayloadHeader *payload_header = (struct DropcamBTLEPayloadHeader *)a->data;
+		uint8_t *payload_bytes = a->data + sizeof(struct DropcamBTLEPayloadHeader);
+		int payload_bytes_len = a->len - sizeof(struct DropcamBTLEPayloadHeader);
+
+		DBG("Received payload with sequence number: %d, length: %d", payload_header->seqnum, payload_bytes_len);
+
+		if (payload_header->seqnum != expected_seq_num) {
+			warn("Received out of order write!  Expected: %d, received: %d", expected_seq_num, payload_header->seqnum);
+		}
+
+		if (a->len > 1) {
+			if (dc_adapter->cur_message_pos == -1) {
+				/* we received data so we're starting a new message */
+				dc_adapter->cur_message_pos = 0;
+			}
+
+			if (dc_adapter->cur_message_pos + payload_bytes_len >= sizeof(dc_adapter->message_payload)) {
+				DBG("Message too large!");
+				return;
+			}
+
+			uint8_t *cur_pos = dc_adapter->message_payload + dc_adapter->cur_message_pos;
+			memcpy(cur_pos, payload_bytes, payload_bytes_len);
+
+			dc_adapter->cur_message_pos += payload_bytes_len;
+		} else {
+			if (dc_adapter->cur_message_pos != -1) {
+				/* received an empty payload - pending message is complete */
+				DBG("Received message -- length: %d", dc_adapter->cur_message_pos);
+
+				/* send the message to the watchers and reset the current message */
+				g_slist_foreach(watchers, update_watcher, dc_adapter);
+				dc_adapter->cur_message_pos = -1;
+			}
+		}
+
+		dc_adapter->last_write_seqnum = payload_header->seqnum;
+	}
+	return 0;
+}
+
+static void queue_pending_read_packet(struct dropcam_adapter *dc_adapter, uint8_t *packet, int packet_len) {
+	uint8_t seq_num = dc_adapter->last_read_seqnum + 1;
+	struct pending_read *pr = g_new0(struct pending_read, 1);
+	pr->payload_len = packet_len + 1;
+	struct DropcamBTLEPayloadHeader *pkt_header = (struct DropcamBTLEPayloadHeader *)pr->payload;
+	pkt_header->seqnum = seq_num;
+
+	if (packet_len > 0) {
+		memcpy(pr->payload + 1, packet, packet_len);
+	}
+
+	DBG("Queuing pending read: %d, %d", seq_num, packet_len + 1);
+	dc_adapter->pending_read_payloads = g_slist_append(dc_adapter->pending_read_payloads, pr);
+
+	dc_adapter->last_read_seqnum = seq_num;
+}
+
+static void queue_pending_read_message(struct dropcam_adapter *dc_adapter, struct message *msg) {
+	uint8_t *packet = msg->bytes;
+	int bytes_remaining = msg->len;
+
+	/* Split the message into smaller packets */
+	while (bytes_remaining > 0) {
+		int next_packet_len = MIN(MTU_SIZE - sizeof(struct DropcamBTLEPayloadHeader), bytes_remaining);
+
+		queue_pending_read_packet(dc_adapter, packet, next_packet_len);
+		packet += next_packet_len;
+		bytes_remaining -= next_packet_len;
+	}
+
+	/* Queue a final empty packet to signal that the message is complete */
+	queue_pending_read_packet(dc_adapter, NULL, 0);
+}
+
+static gboolean register_dropcam_service(struct dropcam_adapter *dc_adapter) {
+	bt_uuid_t svc_uuid;
+	bt_string_to_uuid(&svc_uuid, DROPCAM_SVC_UUID);
+
+	bt_uuid_t tx_read_chr_uuid;
+	bt_string_to_uuid(&tx_read_chr_uuid, DROPCAM_TRANSFER_READ_CHR_UUID);
+
+	bt_uuid_t tx_write_chr_uuid;
+	bt_string_to_uuid(&tx_write_chr_uuid, DROPCAM_TRANSFER_WRITE_CHR_UUID);
+
+	/* Dropcam Service */
+	gboolean svc_added = gatt_service_add(
+	                         dc_adapter->adapter,
+	                         GATT_PRIM_SVC_UUID, &svc_uuid,
+
+	                         /* Dropcam Transfer Write */
+	                         GATT_OPT_CHR_UUID, &tx_write_chr_uuid,
+	                         GATT_OPT_CHR_PROPS,	GATT_CHR_PROP_WRITE,
+	                         GATT_OPT_CHR_VALUE_CB, ATTRIB_WRITE,
+	                         dropcam_tx_write, dc_adapter,
+
+	                         /* Dropcam Transfer Read */
+	                         GATT_OPT_CHR_UUID, &tx_read_chr_uuid,
+	                         GATT_OPT_CHR_PROPS, GATT_CHR_PROP_READ,
+	                         GATT_OPT_CHR_VALUE_CB, ATTRIB_READ,
+	                         dropcam_tx_read, dc_adapter,
+
+	                         GATT_OPT_INVALID);
+}
+
+static void remove_watcher(gpointer user_data) {
+	struct watcher *watcher = user_data;
+
+	g_dbus_remove_watch(btd_get_dbus_connection(), watcher->id);
+}
+
+static void dropcam_destroy(gpointer user_data) {
+	g_slist_free_full(watchers, remove_watcher);
+	watchers = NULL;
+}
+
+static void watcher_exit_cb(DBusConnection *conn, void *user_data) {
+	struct watcher *watcher = user_data;
+
+	DBG("Dropcam watcher [%s] disconnected", watcher->path);
+
+	watchers = g_slist_remove(watchers, watcher);
+	g_dbus_remove_watch(conn, watcher->id);
+}
+
+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 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 queue_message_for_adapter(gpointer data, gpointer user_data) {
+	struct dropcam_adapter *dc_adapter = data;
+	struct message *msg = user_data;
+
+	queue_pending_read_message(dc_adapter, msg);
+}
+
+static DBusMessage *send_message(DBusConnection *conn, DBusMessage *msg, void *data) {
+	struct message msg_data;
+
+	if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, &msg_data.bytes, &msg_data.len, DBUS_TYPE_INVALID))
+		return btd_error_invalid_args(msg);
+
+	DBG("Received message of length: %d", msg_data.len);
+
+	g_slist_foreach(adapters, queue_message_for_adapter, &msg_data);
+
+	return dbus_message_new_method_return(msg);
+}
+
+static DBusMessage *register_watcher(DBusConnection *conn, DBusMessage *msg, void *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(watchers, sender, path);
+	if (watcher != NULL)
+		return btd_error_already_exists(msg);
+
+	watcher = g_new0(struct watcher, 1);
+	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);
+
+	watchers = g_slist_prepend(watchers, watcher);
+
+	DBG("Dropcam watcher [%s] registered", path);
+
+	return dbus_message_new_method_return(msg);
+}
+
+static DBusMessage *unregister_watcher(DBusConnection *conn, DBusMessage *msg, void *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(watchers, sender, path);
+	if (watcher == NULL)
+		return btd_error_does_not_exist(msg);
+
+	watchers = g_slist_remove(watchers, watcher);
+	g_dbus_remove_watch(conn, watcher->id);
+
+	DBG("Dropcam watcher [%s] unregistered", path);
+
+	return dbus_message_new_method_return(msg);
+}
+
+static const GDBusMethodTable dropcam_methods[] = {
+	{ GDBUS_METHOD("RegisterWatcher",    GDBUS_ARGS({ "agent", "o" }),    NULL, register_watcher) },
+	{ GDBUS_METHOD("UnregisterWatcher",  GDBUS_ARGS({ "agent", "o" }),    NULL, unregister_watcher) },
+	{ GDBUS_METHOD("SendMessage",        GDBUS_ARGS({ "message", "ay" }), NULL, send_message) },
+	{ }
+};
+
+static int dropcam_adapter_probe(struct btd_adapter *adapter) {
+	struct dropcam_adapter *dc_adapter = g_new0(struct dropcam_adapter, 1);
+	dc_adapter->adapter = btd_adapter_ref(adapter);
+	dc_adapter->last_write_seqnum = 0xff;
+	dc_adapter->last_read_seqnum = 0xff;
+
+	register_dropcam_service(dc_adapter);
+
+	adapters = g_slist_append(adapters, dc_adapter);
+
+	return 0;
+}
+
+static void dropcam_adapter_remove(struct btd_adapter *adapter) {
+	struct dropcam_adapter *dc_adapter;
+
+	dc_adapter = find_dropcam_adapter(adapter);
+	if (!dc_adapter)
+		return;
+
+	adapters = g_slist_remove(adapters, dc_adapter);
+	btd_adapter_unref(dc_adapter->adapter);
+
+	g_free(dc_adapter);
+}
+
+static struct btd_adapter_driver dropcam_adapter_driver = {
+	.name	= "dropcam-adapter-driver",
+	.probe	= dropcam_adapter_probe,
+	.remove	= dropcam_adapter_remove,
+};
+
+static int dropcam_init(void) {
+	if (!g_dbus_register_interface(btd_get_dbus_connection(),
+	                               DROPCAM_OBJECT_PATH, DROPCAM_INTERFACE,
+	                               dropcam_methods, NULL, NULL, NULL,
+	                               dropcam_destroy)) {
+		error("D-Bus failed to register %s interface", DROPCAM_INTERFACE);
+		return -EIO;
+	}
+
+	return btd_register_adapter_driver(&dropcam_adapter_driver);
+}
+
+static void dropcam_exit(void) {
+	g_dbus_unregister_interface(btd_get_dbus_connection(),
+	                            DROPCAM_OBJECT_PATH, DROPCAM_INTERFACE);
+
+	btd_unregister_adapter_driver(&dropcam_adapter_driver);
+}
+
+BLUETOOTH_PLUGIN_DEFINE(dropcam, VERSION,
+                        BLUETOOTH_PLUGIN_PRIORITY_LOW,
+                        dropcam_init, dropcam_exit)
diff --git a/bluez/plugins/dropcam_btle_profile_def.h b/bluez/plugins/dropcam_btle_profile_def.h
new file mode 100644
index 0000000..5db7c7e
--- /dev/null
+++ b/bluez/plugins/dropcam_btle_profile_def.h
@@ -0,0 +1,25 @@
+//
+//  dropcam_btle_profile_def.h
+//
+//  Created by Loren Kirkby on 5/29/13.
+//  Copyright (c) 2013 Loren Kirkby. All rights reserved.
+//
+
+#ifndef BTLESetup_DropcamBTDefines_h
+#define BTLESetup_DropcamBTDefines_h
+
+#define DROPCAM_SVC_UUID "d2d3f8ef-9c99-4d9c-a2b3-91c85d44326c"
+#define DROPCAM_TRANSFER_READ_CHR_UUID "0fde7f14-864a-4f7f-8dbe-7ac1f4ea4b91"
+#define DROPCAM_TRANSFER_WRITE_CHR_UUID "7606123e-4282-4ed4-aca1-2374de7fdb61"
+
+struct DropcamBTLEPayloadHeader {
+  uint8_t seqnum;
+}  __attribute__((packed));
+
+#define PAYLOAD_HEADER_SIZE (sizeof struct DropcamBTLEPayloadHeader)
+
+struct DropcamBTLEMessageHeader {
+  uint8_t type;
+}  __attribute__((packed));
+
+#endif
diff --git a/bluez/plugins/external-dummy.c b/bluez/plugins/external-dummy.c
new file mode 100644
index 0000000..536ad06
--- /dev/null
+++ b/bluez/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/plugins/gatt-example.c b/bluez/plugins/gatt-example.c
new file mode 100644
index 0000000..1e1483c
--- /dev/null
+++ b/bluez/plugins/gatt-example.c
@@ -0,0 +1,576 @@
+/*
+ *
+ *  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/uuid.h"
+#include "src/plugin.h"
+#include "src/adapter.h"
+#include "src/hcid.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/plugins/hostname.c b/bluez/plugins/hostname.c
new file mode 100644
index 0000000..d4d72d3
--- /dev/null
+++ b/bluez/plugins/hostname.c
@@ -0,0 +1,323 @@
+/*
+ *
+ *  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 <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/plugins/neard.c b/bluez/plugins/neard.c
new file mode 100644
index 0000000..137d601
--- /dev/null
+++ b/bluez/plugins/neard.c
@@ -0,0 +1,909 @@
+/*
+ *
+ *  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 <gdbus/gdbus.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/hci.h>
+#include <bluetooth/sdp.h>
+
+#include "src/plugin.h"
+#include "src/log.h"
+#include "src/dbus-common.h"
+#include "src/adapter.h"
+#include "src/device.h"
+#include "src/eir.h"
+#include "src/agent.h"
+#include "src/hcid.h"
+
+#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/plugins/policy.c b/bluez/plugins/policy.c
new file mode 100644
index 0000000..0292482
--- /dev/null
+++ b/bluez/plugins/policy.c
@@ -0,0 +1,437 @@
+/*
+ *
+ *  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 <glib.h>
+
+#include "lib/uuid.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"
+
+#define CONTROL_CONNECT_TIMEOUT 2
+#define SOURCE_RETRY_TIMEOUT 2
+#define SINK_RETRY_TIMEOUT SOURCE_RETRY_TIMEOUT
+#define SOURCE_RETRIES 1
+#define SINK_RETRIES SOURCE_RETRIES
+
+static unsigned int service_id = 0;
+static GSList *devices = NULL;
+
+struct policy_data {
+	struct btd_device *dev;
+
+	guint source_timer;
+	uint8_t source_retries;
+	guint sink_timer;
+	uint8_t sink_retries;
+	guint ct_timer;
+	guint tg_timer;
+};
+
+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;
+
+	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)
+{
+	if (data->ct_timer > 0)
+		g_source_remove(data->ct_timer);
+
+	data->ct_timer = g_timeout_add_seconds(CONTROL_CONNECT_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:
+	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);
+		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;
+
+	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)
+{
+	if (data->tg_timer > 0)
+		g_source_remove(data->tg_timer);
+
+	data->tg_timer = g_timeout_add_seconds(CONTROL_CONNECT_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:
+	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);
+		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:
+	case BTD_SERVICE_STATE_DISCONNECTED:
+		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:
+	case BTD_SERVICE_STATE_DISCONNECTED:
+		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 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);
+
+	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);
+}
+
+static int policy_init(void)
+{
+	service_id = btd_service_add_state_cb(service_cb, NULL);
+
+	return 0;
+}
+
+static void policy_exit(void)
+{
+	g_slist_free_full(devices, policy_remove);
+
+	btd_service_remove_state_cb(service_id);
+}
+
+BLUETOOTH_PLUGIN_DEFINE(policy, VERSION, BLUETOOTH_PLUGIN_PRIORITY_DEFAULT,
+						policy_init, policy_exit)
diff --git a/bluez/plugins/sixaxis.c b/bluez/plugins/sixaxis.c
new file mode 100644
index 0000000..8045448
--- /dev/null
+++ b/bluez/plugins/sixaxis.c
@@ -0,0 +1,434 @@
+/*
+ *
+ *  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/uuid.h"
+#include "src/adapter.h"
+#include "src/device.h"
+#include "src/plugin.h"
+#include "src/log.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,
+	},
+};
+
+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 gboolean setup_leds(GIOChannel *channel, GIOCondition cond,
+							gpointer user_data)
+{
+	/*
+	 * 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 number = GPOINTER_TO_INT(user_data);
+	int ret;
+	int fd;
+
+	if (cond & (G_IO_HUP | G_IO_ERR | G_IO_NVAL))
+		return FALSE;
+
+	DBG("number %d", number);
+
+	/* TODO we could support up to 10 (1 + 2 + 3 + 4) */
+	if (number > 7)
+		return FALSE;
+
+	if (number > 4) {
+		leds_report[10] |= 0x10;
+		number -= 4;
+	}
+
+	leds_report[10] |= 0x01 << number;
+
+	fd = g_io_channel_unix_get_fd(channel);
+
+	ret = write(fd, leds_report, sizeof(leds_report));
+	if (ret == sizeof(leds_report))
+		return FALSE;
+
+	if (ret < 0)
+		error("sixaxis: failed to set LEDS (%s)", strerror(errno));
+	else
+		error("sixaxis: failed to set LEDS (%d bytes written)", ret);
+
+	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_phys;
+	int number = 0;
+
+	hid_parent = udev_device_get_parent_with_subsystem_devtype(udevice,
+								"hid", NULL);
+
+	hid_phys = udev_device_get_property_value(hid_parent, "HID_PHYS");
+	hidraw_node = udev_device_get_devnode(udevice);
+	if (!hid_phys || !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_phys;
+		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_phys = udev_device_get_sysattr_value(input_parent,
+									"phys");
+		if (!input_phys)
+			goto next;
+
+		if (!strcmp(input_phys, hid_phys)) {
+			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 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,
+				GINT_TO_POINTER(get_js_number(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/plugins/wiimote.c b/bluez/plugins/wiimote.c
new file mode 100644
index 0000000..bd8820e
--- /dev/null
+++ b/bluez/plugins/wiimote.c
@@ -0,0 +1,141 @@
+/*
+ *
+ *  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 <bluetooth/bluetooth.h>
+#include <glib.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/profiles/alert/server.c b/bluez/profiles/alert/server.c
new file mode 100644
index 0000000..1612d6c
--- /dev/null
+++ b/bluez/profiles/alert/server.c
@@ -0,0 +1,1033 @@
+/*
+ *
+ *  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 <gdbus/gdbus.h>
+#include <glib.h>
+#include <stdlib.h>
+
+#include "lib/uuid.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;
+	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/profiles/audio/a2dp-codecs.h b/bluez/profiles/audio/a2dp-codecs.h
new file mode 100644
index 0000000..3dc31cb
--- /dev/null
+++ b/bluez/profiles/audio/a2dp-codecs.h
@@ -0,0 +1,139 @@
+/*
+ *
+ *  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
+
+#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;
+
+#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;
+
+#else
+#error "Unknown byte order"
+#endif
+
+typedef struct {
+	uint8_t vendor_id[4];
+	uint8_t codec_id[2];
+} __attribute__ ((packed)) a2dp_vendor_codec_t;
diff --git a/bluez/profiles/audio/a2dp.c b/bluez/profiles/audio/a2dp.c
new file mode 100644
index 0000000..cabdd66
--- /dev/null
+++ b/bluez/profiles/audio/a2dp.c
@@ -0,0 +1,2086 @@
+/*
+ *
+ *  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 <bluetooth/bluetooth.h>
+#include <bluetooth/sdp.h>
+#include <bluetooth/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 "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
+
+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_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 *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;
+};
+
+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 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 = avdtp_get_device(setup->session);
+	struct btd_service *service;
+
+	/* Check if configuration was aborted */
+	if (setup->sep->stream == NULL)
+		return FALSE;
+
+	if (setup->err != NULL)
+		goto done;
+
+	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_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 = find_setup_by_session(session);
+	if (!setup)
+		return TRUE;
+
+	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 = {
+	.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 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);
+	servers = g_slist_append(servers, server);
+
+	return server;
+}
+
+static void a2dp_unregister_sep(struct a2dp_sep *sep)
+{
+	if (sep->destroy) {
+		sep->destroy(sep->user_data);
+		sep->endpoint = NULL;
+	}
+
+	avdtp_unregister_sep(sep->lsep);
+	g_free(sep);
+}
+
+static void a2dp_server_unregister(struct a2dp_server *server)
+{
+	servers = g_slist_remove(servers, server);
+	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(adapter, 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");
+		avdtp_unregister_sep(sep->lsep);
+		g_free(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);
+		avdtp_unregister_sep(sep->lsep);
+		g_free(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 gboolean check_vendor_codec(struct a2dp_sep *sep, uint8_t *cap,
+								size_t len)
+{
+	uint8_t *capabilities;
+	size_t length;
+	a2dp_vendor_codec_t *local_codec;
+	a2dp_vendor_codec_t *remote_codec;
+
+	if (len < sizeof(a2dp_vendor_codec_t))
+		return FALSE;
+
+	remote_codec = (a2dp_vendor_codec_t *) cap;
+
+	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;
+
+	if (memcmp(remote_codec->vendor_id, local_codec->vendor_id,
+					sizeof(local_codec->vendor_id)))
+		return FALSE;
+
+	if (memcmp(remote_codec->codec_id, local_codec->codec_id,
+					sizeof(local_codec->codec_id)))
+		return FALSE;
+
+	DBG("vendor 0x%02x%02x%02x%02x codec 0x%02x%02x",
+			remote_codec->vendor_id[0], remote_codec->vendor_id[1],
+			remote_codec->vendor_id[2], remote_codec->vendor_id[3],
+			remote_codec->codec_id[0], remote_codec->codec_id[1]);
+
+	return TRUE;
+}
+
+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;
+		struct avdtp_remote_sep *rsep;
+		struct avdtp_media_codec_capability *cap;
+		struct avdtp_service_capability *service;
+
+		/* 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;
+		}
+
+		rsep = avdtp_find_remote_sep(session, sep->lsep);
+		if (rsep == NULL)
+			continue;
+
+		service = avdtp_get_codec(rsep);
+		cap = (struct avdtp_media_codec_capability *) service->data;
+
+		if (cap->media_codec_type != A2DP_CODEC_VENDOR)
+			return sep;
+
+		if (check_vendor_codec(sep, cap->data,
+					service->length - sizeof(*cap)))
+			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);
+}
+
+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;
+	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;
+	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;
+	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);
+				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;
+	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/profiles/audio/a2dp.h b/bluez/profiles/audio/a2dp.h
new file mode 100644
index 0000000..ef3be5c
--- /dev/null
+++ b/bluez/profiles/audio/a2dp.h
@@ -0,0 +1,89 @@
+/*
+ *
+ *  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_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_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);
diff --git a/bluez/profiles/audio/avctp.c b/bluez/profiles/audio/avctp.c
new file mode 100644
index 0000000..74d3512
--- /dev/null
+++ b/bluez/profiles/audio/avctp.c
@@ -0,0 +1,2036 @@
+/*
+ *
+ *  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 <bluetooth/bluetooth.h>
+#include <bluetooth/sdp.h>
+#include <bluetooth/l2cap.h>
+
+#include <glib.h>
+
+#include "btio/btio.h"
+#include "lib/uuid.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 {
+	uint8_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 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) {
+			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 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)
+{
+	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, 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);
+
+	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);
+	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;
+	}
+
+	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);
+
+	/* 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);
+
+	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);
+		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);
+		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);
+}
+
+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);
+		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);
+	}
+}
+
+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);
+		return;
+	}
+
+	avctp_set_state(session, AVCTP_STATE_CONNECTING);
+	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);
+}
+
+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);
+		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);
+
+	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);
+		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);
+
+	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);
+}
+
+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/profiles/audio/avctp.h b/bluez/profiles/audio/avctp.h
new file mode 100644
index 0000000..05fceb4
--- /dev/null
+++ b/bluez/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,
+				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/profiles/audio/avdtp.c b/bluez/profiles/audio/avdtp.c
new file mode 100644
index 0000000..8a7d1c0
--- /dev/null
+++ b/bluez/profiles/audio/avdtp.c
@@ -0,0 +1,3871 @@
+/*
+ *
+ *  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 <bluetooth/bluetooth.h>
+#include <bluetooth/sdp.h>
+#include <bluetooth/sdp_lib.h>
+
+#include <glib.h>
+
+#include "src/log.h"
+
+#include "btio/btio.h"
+#include "lib/uuid.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
+
+#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_server {
+	struct btd_adapter *adapter;
+	GIOChannel *io;
+	GSList *seps;
+	GSList *sessions;
+};
+
+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 avdtp_server *server;
+};
+
+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 avdtp_server *server;
+	struct btd_device *device;
+
+	avdtp_session_state_t state;
+
+	guint auth_id;
+
+	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 *servers = NULL;
+
+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 struct avdtp_server *find_server(GSList *list, struct btd_adapter *a)
+{
+	for (; list; list = list->next) {
+		struct avdtp_server *server = list->data;
+
+		if (server->adapter == a)
+			return server;
+	}
+
+	return NULL;
+}
+
+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; l = l->next) {
+		struct avdtp_state_callback *cb = l->data;
+
+		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);
+
+	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 int avdtp_cancel_authorization(struct avdtp *session)
+{
+	int err;
+
+	if (session->state != AVDTP_SESSION_STATE_CONNECTING)
+		return 0;
+
+	if (session->auth_id == 0)
+		return 0;
+
+	err = btd_cancel_authorization(session->auth_id);
+	if (err < 0)
+		return err;
+
+	session->auth_id = 0;
+
+	return 0;
+}
+
+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);
+
+	g_free(session);
+}
+
+static void connection_lost(struct avdtp *session, int err)
+{
+	struct avdtp_server *server = session->server;
+	char address[18];
+
+	ba2str(device_get_address(session->device), address);
+	DBG("Disconnected from %s", address);
+
+	if (err != EACCES)
+		avdtp_cancel_authorization(session);
+
+	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;
+
+	server->sessions = g_slist_remove(server->sessions, session);
+	btd_device_unref(session->device);
+	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 struct avdtp_local_sep *find_local_sep_by_seid(struct avdtp_server *server,
+							uint8_t seid)
+{
+	GSList *l;
+
+	for (l = server->seps; l != NULL; l = g_slist_next(l)) {
+		struct avdtp_local_sep *sep = l->data;
+
+		if (sep->info.seid == seid)
+			return sep;
+	}
+
+	return NULL;
+}
+
+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 (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 gboolean avdtp_discover_cmd(struct avdtp *session, uint8_t transaction,
+							void *buf, int size)
+{
+	GSList *l;
+	unsigned int rsp_size, sep_count, i;
+	struct seid_info *seps;
+	gboolean ret;
+
+	sep_count = g_slist_length(session->server->seps);
+
+	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);
+
+	for (l = session->server->seps, i = 0; l != NULL; l = l->next, i++) {
+		struct avdtp_local_sep *sep = l->data;
+
+		memcpy(&seps[i], &sep->info, sizeof(struct seid_info));
+	}
+
+	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->server, 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->server, 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->server, 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->server, 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->server,
+					req->first_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->server, 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->server,
+					req->first_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->server, 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->server, 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 struct avdtp *find_session(GSList *list, struct btd_device *device)
+{
+	for (; list != NULL; list = g_slist_next(list)) {
+		struct avdtp *s = list->data;
+
+		if (s->device == device)
+			return s;
+	}
+
+	return NULL;
+}
+
+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 struct avdtp *avdtp_get_internal(struct btd_device *device)
+{
+	struct avdtp_server *server;
+	struct avdtp *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 avdtp, 1);
+
+	session->server = server;
+	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->version = get_version(session);
+
+	server->sessions = g_slist_append(server->sessions, session);
+
+	return session;
+}
+
+struct avdtp *avdtp_get(struct btd_device *device)
+{
+	struct avdtp *session;
+
+	session = avdtp_get_internal(device);
+
+	if (!session)
+		return NULL;
+
+	return avdtp_ref(session);
+}
+
+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);
+}
+
+static void auth_cb(DBusError *derr, void *user_data)
+{
+	struct avdtp *session = user_data;
+	GError *err = NULL;
+
+	if (derr && dbus_error_is_set(derr)) {
+		error("Access denied: %s", derr->message);
+		connection_lost(session, EACCES);
+		return;
+	}
+
+	if (!bt_io_accept(session->io, avdtp_connect_cb, session, NULL,
+								&err)) {
+		error("bt_io_accept: %s", err->message);
+		connection_lost(session, EACCES);
+		g_error_free(err);
+		return;
+	}
+
+	/* This is so that avdtp_connect_cb will know to do the right thing
+	 * with respect to the disconnect timer */
+	session->stream_setup = TRUE;
+}
+
+static void avdtp_confirm_cb(GIOChannel *chan, gpointer data)
+{
+	struct avdtp *session;
+	char address[18];
+	bdaddr_t src, dst;
+	GError *err = NULL;
+	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_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;
+
+	session = avdtp_get_internal(device);
+	if (!session)
+		goto drop;
+
+	/* This state (ie, session is already *connecting*) happens when the
+	 * device initiates a connect (really a config'd L2CAP channel) even
+	 * though there is a connect we initiated in progress. In sink.c &
+	 * source.c, this state is referred to as XCASE connect:connect.
+	 * Abort the device's channel in favor of our own.
+	 */
+	if (session->state == AVDTP_SESSION_STATE_CONNECTING) {
+		DBG("connect already in progress (XCASE connect:connect)");
+		goto drop;
+	}
+
+	if (session->pending_open && session->pending_open->open_acp) {
+		if (!bt_io_accept(chan, avdtp_connect_cb, session, NULL, NULL))
+			goto drop;
+		return;
+	}
+
+	if (session->io) {
+		error("Refusing unexpected connect from %s", address);
+		goto drop;
+	}
+
+	btd_device_add_uuid(device, ADVANCED_AUDIO_UUID);
+
+	session->io = g_io_channel_ref(chan);
+	avdtp_set_state(session, AVDTP_SESSION_STATE_CONNECTING);
+
+	session->io_id = g_io_add_watch(chan, G_IO_ERR | G_IO_HUP | G_IO_NVAL,
+					(GIOFunc) session_cb, session);
+
+	session->auth_id = btd_request_authorization(&src, &dst,
+							ADVANCED_AUDIO_UUID,
+							auth_cb, session);
+	if (session->auth_id == 0) {
+		avdtp_set_state(session, AVDTP_SESSION_STATE_DISCONNECTED);
+		goto drop;
+	}
+
+	return;
+
+drop:
+	g_io_channel_shutdown(chan, TRUE, NULL);
+}
+
+static GIOChannel *l2cap_connect(struct avdtp *session)
+{
+	GError *err = NULL;
+	GIOChannel *io;
+	const bdaddr_t *src;
+
+	src = btd_adapter_get_address(session->server->adapter);
+
+	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_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 (!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));
+}
+
+static GIOChannel *avdtp_server_socket(const bdaddr_t *src, gboolean master)
+{
+	GError *err = NULL;
+	GIOChannel *io;
+
+	io = bt_io_listen(NULL, avdtp_confirm_cb,
+				NULL, NULL, &err,
+				BT_IO_OPT_SOURCE_BDADDR, src,
+				BT_IO_OPT_PSM, AVDTP_PSM,
+				BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_MEDIUM,
+				BT_IO_OPT_MASTER, master,
+				BT_IO_OPT_INVALID);
+	if (!io) {
+		error("%s", err->message);
+		g_error_free(err);
+	}
+
+	return io;
+}
+
+static struct avdtp_server *avdtp_server_init(struct btd_adapter *adapter)
+{
+	struct avdtp_server *server;
+
+	server = g_new0(struct avdtp_server, 1);
+
+	server->io = avdtp_server_socket(btd_adapter_get_address(adapter),
+									TRUE);
+	if (!server->io) {
+		g_free(server);
+		return NULL;
+	}
+
+	server->adapter = btd_adapter_ref(adapter);
+
+	servers = g_slist_append(servers, server);
+
+	return server;
+}
+
+struct avdtp_local_sep *avdtp_register_sep(struct btd_adapter *adapter,
+						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_server *server;
+	struct avdtp_local_sep *sep;
+
+	server = find_server(servers, adapter);
+	if (!server) {
+		server = avdtp_server_init(adapter);
+		if (!server)
+			return NULL;
+	}
+
+	if (g_slist_length(server->seps) > MAX_SEID)
+		return NULL;
+
+	sep = g_new0(struct avdtp_local_sep, 1);
+
+	sep->state = AVDTP_STATE_IDLE;
+	sep->info.seid = g_slist_length(server->seps) + 1;
+	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->server = server;
+	sep->delay_reporting = TRUE;
+
+	DBG("SEP %p registered: type:%d codec:%d seid:%d", sep,
+			sep->info.type, sep->codec, sep->info.seid);
+	server->seps = g_slist_append(server->seps, sep);
+
+	return sep;
+}
+
+static void avdtp_server_destroy(struct avdtp_server *server)
+{
+	g_slist_free_full(server->sessions, avdtp_free);
+
+	servers = g_slist_remove(servers, server);
+
+	g_io_channel_shutdown(server->io, TRUE, NULL);
+	g_io_channel_unref(server->io);
+	btd_adapter_unref(server->adapter);
+	g_free(server);
+}
+
+int avdtp_unregister_sep(struct avdtp_local_sep *sep)
+{
+	struct avdtp_server *server;
+
+	if (!sep)
+		return -EINVAL;
+
+	server = sep->server;
+	server->seps = g_slist_remove(server->seps, sep);
+
+	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);
+
+	g_free(sep);
+
+	if (server->seps)
+		return 0;
+
+	avdtp_server_destroy(server);
+
+	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 session->server->adapter;
+}
+
+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/profiles/audio/avdtp.h b/bluez/profiles/audio/avdtp.h
new file mode 100644
index 0000000..390c154
--- /dev/null
+++ b/bluez/profiles/audio/avdtp.h
@@ -0,0 +1,295 @@
+/*
+ *
+ *  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_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 (*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);
+
+struct avdtp *avdtp_get(struct btd_device *device);
+
+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_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 btd_adapter *adapter,
+						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 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);
diff --git a/bluez/profiles/audio/avrcp.c b/bluez/profiles/audio/avrcp.c
new file mode 100644
index 0000000..da2a746
--- /dev/null
+++ b/bluez/profiles/audio/avrcp.c
@@ -0,0 +1,3967 @@
+/*
+ *
+ *  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 <bluetooth/bluetooth.h>
+#include <bluetooth/sdp.h>
+#include <bluetooth/sdp_lib.h>
+
+#include <glib.h>
+#include <dbus/dbus.h>
+#include <gdbus/gdbus.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/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_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
+
+/* 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
+
+#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 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;
+};
+
+struct avrcp_player {
+	struct avrcp_server *server;
+	GSList *sessions;
+	uint16_t id;
+	uint8_t scope;
+	uint64_t uid;
+	uint16_t uid_counter;
+	bool browsed;
+	uint8_t *features;
+	char *path;
+
+	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;
+
+/* 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 avrcp_ver = 0x0105, 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_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[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 avrcp_ver = 0x0104, 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_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);
+
+	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;
+	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;
+	pdu->params[0] = id;
+
+	DBG("id=%u", 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;
+	default:
+		error("Unknown event %u", id);
+		return;
+	}
+
+	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],
+					AVC_CTYPE_CHANGED, 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 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;
+		}
+
+		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)
+{
+	struct avrcp_player *player = session->controller->player;
+	uint16_t len = ntohs(pdu->params_len);
+	uint8_t volume;
+
+	if (len != 1)
+		goto err;
+
+	if (!player)
+		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 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 },
+		{ },
+};
+
+/* 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 struct browsing_pdu_handler {
+	uint8_t pdu_id;
+	void (*func) (struct avrcp *session, struct avrcp_browsing_header *pdu,
+							uint8_t transaction);
+} browsing_handlers[] = {
+		{ },
+};
+
+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;
+	uint32_t items, total;
+	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);
+	total = p->end - p->start;
+	if (items < total) {
+		avrcp_list_items(session, p->start + items + 1, 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;
+	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_STATUS,
+					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_STATUS,
+					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 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,
+};
+
+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)
+		session->controller->player = 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);
+
+	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;
+
+		session->controller->players = g_slist_remove(
+						session->controller->players,
+						player);
+	}
+
+	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;
+	}
+
+	if (g_slist_find(removed, session->controller->player))
+		session->controller->player = NULL;
+
+	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;
+	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_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]);
+	session->controller->player = 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_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;
+	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 (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_SETTINGS_CHANGED:
+		case AVRCP_EVENT_ADDRESSED_PLAYER_CHANGED:
+		case AVRCP_EVENT_UIDS_CHANGED:
+		case AVRCP_EVENT_AVAILABLE_PLAYERS_CHANGED:
+		case AVRCP_EVENT_VOLUME_CHANGED:
+			avrcp_register_notification(session, event);
+			break;
+		}
+	}
+
+	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);
+	if (service != NULL)
+		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;
+
+	/* 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);
+
+	if (controller->version >= 0x0104)
+		session->supported_events |= (1 << AVRCP_EVENT_VOLUME_CHANGED);
+
+	service = btd_device_get_service(session->dev, AVRCP_TARGET_UUID);
+	if (service != NULL)
+		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;
+	struct btd_service *service;
+
+	DBG("%p", controller);
+
+	g_slist_free_full(controller->players, player_destroy);
+
+	service = btd_device_get_service(session->dev, AVRCP_TARGET_UUID);
+
+	if (session->control_id == 0)
+		btd_service_connecting_complete(service, -EIO);
+	else
+		btd_service_disconnecting_complete(service, 0);
+
+	g_free(controller);
+}
+
+static void target_destroy(struct avrcp *session)
+{
+	struct avrcp_data *target = session->target;
+	struct avrcp_player *player = target->player;
+	struct btd_service *service;
+
+	DBG("%p", target);
+
+	if (player != NULL)
+		player->sessions = g_slist_remove(player->sessions, session);
+
+	service = btd_device_get_service(session->dev, AVRCP_REMOTE_UUID);
+
+	if (session->control_id == 0)
+		btd_service_connecting_complete(service, -EIO);
+	else
+		btd_service_disconnecting_complete(service, 0);
+
+	g_free(target);
+}
+
+static void session_destroy(struct avrcp *session)
+{
+	struct avrcp_server *server = session->server;
+
+	server->sessions = g_slist_remove(server->sessions, session);
+
+	session_abort_pending_pdu(session);
+
+	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, 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);
+
+		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;
+	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;
+
+	server = find_server(servers, adapter);
+	if (!server)
+		return NULL;
+
+	player = g_new0(struct avrcp_player, 1);
+	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);
+		}
+	}
+
+	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);
+	}
+
+	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;
+}
+
+int avrcp_set_volume(struct btd_device *dev, uint8_t volume)
+{
+	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 || session->target == NULL)
+		return -ENOTCONN;
+
+	if (session->target->version < 0x0104)
+		return -ENOTSUP;
+
+	memset(buf, 0, sizeof(buf));
+
+	set_company_id(pdu->company_id, IEEEID_BTSIG);
+
+	DBG("volume=%u", volume);
+
+	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/profiles/audio/avrcp.h b/bluez/profiles/audio/avrcp.h
new file mode 100644
index 0000000..6ac5294
--- /dev/null
+++ b/bluez/profiles/audio/avrcp.h
@@ -0,0 +1,117 @@
+/*
+ *
+ *  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_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);
+	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);
+
+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/profiles/audio/control.c b/bluez/profiles/audio/control.c
new file mode 100644
index 0000000..ab94a57
--- /dev/null
+++ b/bluez/profiles/audio/control.c
@@ -0,0 +1,347 @@
+/*
+ *
+ *  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 <bluetooth/bluetooth.h>
+#include <bluetooth/sdp.h>
+#include <bluetooth/sdp_lib.h>
+
+#include <glib.h>
+#include <dbus/dbus.h>
+#include <gdbus/gdbus.h>
+
+#include "lib/uuid.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"
+
+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;
+};
+
+static void state_changed(struct btd_device *dev, avctp_state_t old_state,
+				avctp_state_t new_state, 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;
+
+		g_dbus_emit_property_changed(conn, path,
+					AUDIO_CONTROL_INTERFACE, "Connected");
+
+		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;
+	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 const GDBusMethodTable control_methods[] = {
+	{ GDBUS_METHOD("Play", NULL, NULL, control_play) },
+	{ GDBUS_METHOD("Pause", NULL, NULL, control_pause) },
+	{ GDBUS_METHOD("Stop", NULL, NULL, control_stop) },
+	{ GDBUS_METHOD("Next", NULL, NULL, control_next) },
+	{ GDBUS_METHOD("Previous", NULL, NULL, control_previous) },
+	{ GDBUS_METHOD("VolumeUp", NULL, NULL, control_volume_up) },
+	{ GDBUS_METHOD("VolumeDown", NULL, NULL, control_volume_down) },
+	{ GDBUS_METHOD("FastForward", NULL, NULL, control_fast_forward) },
+	{ GDBUS_METHOD("Rewind", NULL, NULL, control_rewind) },
+	{ }
+};
+
+static const GDBusPropertyTable control_properties[] = {
+	{ "Connected", "b", control_property_get_connected },
+	{ }
+};
+
+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;
+}
+
+gboolean control_is_active(struct btd_service *service)
+{
+	struct control *control = btd_service_get_user_data(service);
+
+	if (control && control->session)
+		return TRUE;
+
+	return FALSE;
+}
diff --git a/bluez/profiles/audio/control.h b/bluez/profiles/audio/control.h
new file mode 100644
index 0000000..da8f16c
--- /dev/null
+++ b/bluez/profiles/audio/control.h
@@ -0,0 +1,35 @@
+/*
+ *
+ *  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);
+gboolean control_is_active(struct btd_service *service);
+
+int control_connect(struct btd_service *service);
+int control_disconnect(struct btd_service *service);
diff --git a/bluez/profiles/audio/media.c b/bluez/profiles/audio/media.c
new file mode 100644
index 0000000..b71196a
--- /dev/null
+++ b/bluez/profiles/audio/media.c
@@ -0,0 +1,1900 @@
+/*
+ *
+ *  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 <gdbus/gdbus.h>
+
+#include "lib/uuid.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 "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;
+};
+
+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)) {
+			if (request->cb)
+				request->cb(endpoint, NULL, size,
+							request->user_data);
+			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;
+
+	data->cb(data->setup, ret ? TRUE : 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);
+}
+
+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 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";
+
+	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;
+
+	DBG("%s = %s", key, value);
+
+	if (!g_hash_table_lookup(mp->settings, key))
+		return -EINVAL;
+
+	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,
+	.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_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);
+
+	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_EXPERIMENTAL_METHOD("RegisterPlayer",
+		GDBUS_ARGS({ "player", "o" }, { "properties", "a{sv}" }),
+		NULL, register_player) },
+	{ GDBUS_EXPERIMENTAL_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/profiles/audio/media.h b/bluez/profiles/audio/media.h
new file mode 100644
index 0000000..dd630d4
--- /dev/null
+++ b/bluez/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/profiles/audio/player.c b/bluez/profiles/audio/player.c
new file mode 100644
index 0000000..c76352e
--- /dev/null
+++ b/bluez/profiles/audio/player.c
@@ -0,0 +1,1890 @@
+/*
+ *
+ *  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;
+
+	DBG("%s", mp->status);
+
+	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;
+
+	DBG("%s %s", property->name, value);
+
+	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;
+
+	DBG("%s", mp->name);
+
+	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;
+
+	DBG("%s", mp->type);
+
+	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;
+
+	DBG("%s", mp->subtype);
+
+	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->scope != NULL;
+}
+
+static gboolean get_browsable(const GDBusPropertyTable *property,
+					DBusMessageIter *iter, void *data)
+{
+	struct media_player *mp = data;
+	dbus_bool_t value;
+
+	if (mp->scope == NULL)
+		return FALSE;
+
+	DBG("%s", mp->browsable ? "true" : "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->scope != 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;
+
+	DBG("%s", mp->searchable ? "true" : "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;
+}
+
+static const GDBusMethodTable media_player_methods[] = {
+	{ GDBUS_EXPERIMENTAL_METHOD("Play", NULL, NULL, media_player_play) },
+	{ GDBUS_EXPERIMENTAL_METHOD("Pause", NULL, NULL, media_player_pause) },
+	{ GDBUS_EXPERIMENTAL_METHOD("Stop", NULL, NULL, media_player_stop) },
+	{ GDBUS_EXPERIMENTAL_METHOD("Next", NULL, NULL, media_player_next) },
+	{ GDBUS_EXPERIMENTAL_METHOD("Previous", NULL, NULL,
+						media_player_previous) },
+	{ GDBUS_EXPERIMENTAL_METHOD("FastForward", NULL, NULL,
+						media_player_fast_forward) },
+	{ GDBUS_EXPERIMENTAL_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,
+					G_DBUS_PROPERTY_FLAG_EXPERIMENTAL },
+	{ "Type", "s", get_type, NULL, type_exists,
+					G_DBUS_PROPERTY_FLAG_EXPERIMENTAL },
+	{ "Subtype", "s", get_subtype, NULL, subtype_exists,
+					G_DBUS_PROPERTY_FLAG_EXPERIMENTAL },
+	{ "Position", "u", get_position, NULL, NULL,
+					G_DBUS_PROPERTY_FLAG_EXPERIMENTAL },
+	{ "Status", "s", get_status, NULL, status_exists,
+					G_DBUS_PROPERTY_FLAG_EXPERIMENTAL },
+	{ "Equalizer", "s", get_setting, set_setting, setting_exists,
+					G_DBUS_PROPERTY_FLAG_EXPERIMENTAL },
+	{ "Repeat", "s", get_setting, set_setting, setting_exists,
+					G_DBUS_PROPERTY_FLAG_EXPERIMENTAL },
+	{ "Shuffle", "s", get_setting, set_setting, setting_exists,
+					G_DBUS_PROPERTY_FLAG_EXPERIMENTAL },
+	{ "Scan", "s", get_setting, set_setting, setting_exists,
+					G_DBUS_PROPERTY_FLAG_EXPERIMENTAL },
+	{ "Track", "a{sv}", get_track, NULL, track_exists,
+					G_DBUS_PROPERTY_FLAG_EXPERIMENTAL },
+	{ "Device", "o", get_device, NULL, NULL,
+					G_DBUS_PROPERTY_FLAG_EXPERIMENTAL },
+	{ "Browsable", "b", get_browsable, NULL, browsable_exists,
+					G_DBUS_PROPERTY_FLAG_EXPERIMENTAL },
+	{ "Searchable", "b", get_searchable, NULL, searchable_exists,
+					G_DBUS_PROPERTY_FLAG_EXPERIMENTAL },
+	{ "Playlist", "o", get_playlist, NULL, playlist_exists,
+					G_DBUS_PROPERTY_FLAG_EXPERIMENTAL },
+	{ }
+};
+
+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)
+{
+	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;
+
+	g_dbus_emit_property_changed(btd_get_dbus_connection(), mp->path,
+				MEDIA_FOLDER_INTERFACE, "Name");
+	g_dbus_emit_property_changed(btd_get_dbus_connection(), mp->path,
+				MEDIA_FOLDER_INTERFACE, "NumberOfItems");
+}
+
+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);
+	}
+
+	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_EXPERIMENTAL_ASYNC_METHOD("Search",
+			GDBUS_ARGS({ "string", "s" }, { "filter", "a{sv}" }),
+			GDBUS_ARGS({ "folder", "o" }),
+			media_folder_search) },
+	{ GDBUS_EXPERIMENTAL_ASYNC_METHOD("ListItems",
+			GDBUS_ARGS({ "filter", "a{sv}" }),
+			GDBUS_ARGS({ "items", "a{oa{sv}}" }),
+			media_folder_list_items) },
+	{ GDBUS_EXPERIMENTAL_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;
+}
+
+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;
+
+	DBG("%s", item->player->path);
+
+	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;
+
+	DBG("%s", item->name);
+
+	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);
+
+	DBG("%s", string);
+
+	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;
+
+	DBG("%s", item->playable ? "true" : "false");
+
+	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;
+
+	DBG("%s", string);
+
+	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_EXPERIMENTAL_METHOD("Play", NULL, NULL,
+					media_item_play) },
+	{ GDBUS_EXPERIMENTAL_METHOD("AddtoNowPlaying", NULL, NULL,
+					media_item_add_to_nowplaying) },
+	{ }
+};
+
+static const GDBusPropertyTable media_item_properties[] = {
+	{ "Player", "o", get_player, NULL, NULL,
+					G_DBUS_PROPERTY_FLAG_EXPERIMENTAL },
+	{ "Name", "s", get_item_name, NULL, item_name_exists,
+					G_DBUS_PROPERTY_FLAG_EXPERIMENTAL },
+	{ "Type", "s", get_item_type, NULL, NULL,
+					G_DBUS_PROPERTY_FLAG_EXPERIMENTAL },
+	{ "FolderType", "s", get_folder_type, NULL, folder_type_exists,
+					G_DBUS_PROPERTY_FLAG_EXPERIMENTAL },
+	{ "Playable", "b", get_playable, NULL, NULL,
+					G_DBUS_PROPERTY_FLAG_EXPERIMENTAL },
+	{ "Metadata", "a{sv}", get_metadata, NULL, metadata_exists,
+					G_DBUS_PROPERTY_FLAG_EXPERIMENTAL },
+	{ }
+};
+
+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/profiles/audio/player.h b/bluez/profiles/audio/player.h
new file mode 100644
index 0000000..ac2a3da
--- /dev/null
+++ b/bluez/profiles/audio/player.h
@@ -0,0 +1,110 @@
+/*
+ *
+ *  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);
+};
+
+struct media_player *media_player_controller_create(const char *path,
+								uint16_t id);
+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_set_callbacks(struct media_player *mp,
+				const struct media_player_callback *cbs,
+				void *user_data);
diff --git a/bluez/profiles/audio/sink.c b/bluez/profiles/audio/sink.c
new file mode 100644
index 0000000..d16af23
--- /dev/null
+++ b/bluez/profiles/audio/sink.c
@@ -0,0 +1,451 @@
+/*
+ *
+ *  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 <bluetooth/bluetooth.h>
+#include <bluetooth/sdp.h>
+
+#include <glib.h>
+#include <dbus/dbus.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 "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;
+
+	if (err) {
+		avdtp_unref(sink->session);
+		sink->session = NULL;
+		if (avdtp_error_category(err) == AVDTP_ERRNO
+				&& avdtp_error_posix_errno(err) != EHOSTDOWN) {
+			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;
+
+	if (avdtp_discover(sink->session, discovery_complete, sink) < 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 = 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->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_connecting_complete(sink->service, -ECANCELED);
+
+		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/profiles/audio/sink.h b/bluez/profiles/audio/sink.h
new file mode 100644
index 0000000..93c62a2
--- /dev/null
+++ b/bluez/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/profiles/audio/source.c b/bluez/profiles/audio/source.c
new file mode 100644
index 0000000..843b3e8
--- /dev/null
+++ b/bluez/profiles/audio/source.c
@@ -0,0 +1,443 @@
+/*
+ *
+ *  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 <bluetooth/bluetooth.h>
+#include <bluetooth/sdp.h>
+
+#include <glib.h>
+#include <dbus/dbus.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 "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;
+
+	if (err) {
+		avdtp_unref(source->session);
+		source->session = NULL;
+		if (avdtp_error_category(err) == AVDTP_ERRNO
+				&& avdtp_error_posix_errno(err) != EHOSTDOWN) {
+			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;
+
+	if (avdtp_discover(source->session, discovery_complete, source) < 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 = 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->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_connecting_complete(source->service, -ECANCELED);
+
+		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/profiles/audio/source.h b/bluez/profiles/audio/source.h
new file mode 100644
index 0000000..a014c68
--- /dev/null
+++ b/bluez/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/profiles/audio/transport.c b/bluez/profiles/audio/transport.c
new file mode 100644
index 0000000..c82fbb5
--- /dev/null
+++ b/bluez/profiles/audio/transport.c
@@ -0,0 +1,954 @@
+/*
+ *
+ *  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 <gdbus/gdbus.h>
+
+#include "lib/uuid.h"
+#include "src/adapter.h"
+#include "src/device.h"
+#include "src/dbus-common.h"
+
+#include "src/log.h"
+#include "src/error.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 = 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;
+
+	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;
+	}
+
+	if (a2dp->volume != volume)
+		avrcp_set_volume(transport->device, volume);
+
+	a2dp->volume = volume;
+
+	g_dbus_pending_property_success(id);
+}
+
+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;
+	avrcp_set_volume(transport->device, a2dp->volume);
+	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/profiles/audio/transport.h b/bluez/profiles/audio/transport.h
new file mode 100644
index 0000000..505ad5c
--- /dev/null
+++ b/bluez/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/profiles/cups/cups.h b/bluez/profiles/cups/cups.h
new file mode 100644
index 0000000..f4e0c01
--- /dev/null
+++ b/bluez/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/profiles/cups/hcrp.c b/bluez/profiles/cups/hcrp.c
new file mode 100644
index 0000000..a93dda0
--- /dev/null
+++ b/bluez/profiles/cups/hcrp.c
@@ -0,0 +1,368 @@
+/*
+ *
+ *  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 <bluetooth/bluetooth.h>
+#include <bluetooth/l2cap.h>
+#include <bluetooth/sdp.h>
+#include <bluetooth/sdp_lib.h>
+
+#include <netinet/in.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/profiles/cups/main.c b/bluez/profiles/cups/main.c
new file mode 100644
index 0000000..2079812
--- /dev/null
+++ b/bluez/profiles/cups/main.c
@@ -0,0 +1,883 @@
+/*
+ *
+ *  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 <bluetooth/bluetooth.h>
+#include <bluetooth/sdp.h>
+#include <bluetooth/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, *adapter_reply;
+	DBusMessageIter iter;
+	char *object_path = NULL;
+	char *id;
+
+	adapter_reply = NULL;
+
+	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);
+
+	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;
+	}
+
+	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/profiles/cups/sdp.c b/bluez/profiles/cups/sdp.c
new file mode 100644
index 0000000..c7f17a4
--- /dev/null
+++ b/bluez/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 <bluetooth/bluetooth.h>
+#include <bluetooth/sdp.h>
+#include <bluetooth/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/profiles/cups/spp.c b/bluez/profiles/cups/spp.c
new file mode 100644
index 0000000..d906ed2
--- /dev/null
+++ b/bluez/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 <bluetooth/bluetooth.h>
+#include <bluetooth/rfcomm.h>
+#include <bluetooth/sdp.h>
+#include <bluetooth/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/profiles/cyclingspeed/cyclingspeed.c b/bluez/profiles/cyclingspeed/cyclingspeed.c
new file mode 100644
index 0000000..2f80bca
--- /dev/null
+++ b/bluez/profiles/cyclingspeed/cyclingspeed.c
@@ -0,0 +1,1282 @@
+/*
+ *
+ *  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 <gdbus/gdbus.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/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, const guint8 *pdu,
+					guint16 len, gpointer user_data)
+{
+	struct characteristic *ch = user_data;
+	struct att_data_list *list = NULL;
+	uint8_t format;
+	int i;
+
+	if (status != 0) {
+		error("Discover %s descriptors failed: %s", ch->uuid,
+							att_ecode2str(status));
+		goto done;
+	}
+
+	list = dec_find_info_resp(pdu, len, &format);
+	if (list == NULL)
+		goto done;
+
+	if (format != ATT_FIND_INFO_RESP_FMT_16BIT)
+		goto done;
+
+	for (i = 0; i < list->num; i++) {
+		uint8_t *value;
+		uint16_t handle, uuid;
+		uint8_t attr_val[2];
+		char *msg;
+
+		value = list->data[i];
+		handle = get_le16(value);
+		uuid = get_le16(value + 2);
+
+		if (uuid != GATT_CLIENT_CHARAC_CFG_UUID)
+			continue;
+
+		if (g_strcmp0(ch->uuid, CSC_MEASUREMENT_UUID) == 0) {
+			ch->csc->measurement_ccc_handle = 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 {
+			break;
+		}
+
+		gatt_write_char(ch->csc->attrib, handle, attr_val,
+					sizeof(attr_val), char_write_cb, msg);
+
+		/* We only want CCC, can break here */
+		break;
+	}
+
+done:
+	if (list)
+		att_data_list_free(list);
+	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;
+
+	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));
+
+	gatt_discover_char_desc(csc->attrib, start, end, 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/profiles/deviceinfo/deviceinfo.c b/bluez/profiles/deviceinfo/deviceinfo.c
new file mode 100644
index 0000000..208598a
--- /dev/null
+++ b/bluez/profiles/deviceinfo/deviceinfo.c
@@ -0,0 +1,206 @@
+/*
+ *
+ *  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 "lib/uuid.h"
+#include "src/plugin.h"
+#include "src/adapter.h"
+#include "src/device.h"
+#include "src/profile.h"
+#include "src/service.h"
+#include "src/shared/util.h"
+#include "attrib/gattrib.h"
+#include "src/attio.h"
+#include "attrib/att.h"
+#include "attrib/gatt.h"
+#include "src/log.h"
+
+#define PNP_ID_SIZE	7
+
+struct deviceinfo {
+	struct btd_device	*dev;		/* Device reference */
+	GAttrib			*attrib;	/* GATT connection */
+	guint			attioid;	/* Att watcher id */
+	struct att_range	*svc_range;	/* DeviceInfo range */
+	GSList			*chars;		/* Characteristics */
+};
+
+struct characteristic {
+	struct gatt_char	attr;	/* Characteristic */
+	struct deviceinfo	*d;	/* deviceinfo where the char belongs */
+};
+
+static void deviceinfo_driver_remove(struct btd_service *service)
+{
+	struct deviceinfo *d = btd_service_get_user_data(service);
+
+	if (d->attioid > 0)
+		btd_device_remove_attio_callback(d->dev, d->attioid);
+
+	if (d->attrib != NULL)
+		g_attrib_unref(d->attrib);
+
+	g_slist_free_full(d->chars, g_free);
+
+	btd_device_unref(d->dev);
+	g_free(d->svc_range);
+	g_free(d);
+}
+
+static void read_pnpid_cb(guint8 status, const guint8 *pdu, guint16 len,
+							gpointer user_data)
+{
+	struct characteristic *ch = user_data;
+	uint8_t value[PNP_ID_SIZE];
+	ssize_t vlen;
+
+	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;
+	}
+
+	btd_device_set_pnpid(ch->d->dev, value[0], get_le16(&value[1]),
+				get_le16(&value[3]), get_le16(&value[5]));
+}
+
+static void process_deviceinfo_char(struct characteristic *ch)
+{
+	if (g_strcmp0(ch->attr.uuid, PNPID_UUID) == 0)
+		gatt_read_char(ch->d->attrib, ch->attr.value_handle,
+							read_pnpid_cb, ch);
+}
+
+static void configure_deviceinfo_cb(uint8_t status, GSList *characteristics,
+								void *user_data)
+{
+	struct deviceinfo *d = user_data;
+	GSList *l;
+
+	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;
+		struct characteristic *ch;
+
+		ch = g_new0(struct characteristic, 1);
+		ch->attr.handle = c->handle;
+		ch->attr.properties = c->properties;
+		ch->attr.value_handle = c->value_handle;
+		memcpy(ch->attr.uuid, c->uuid, MAX_LEN_UUID_STR + 1);
+		ch->d = d;
+
+		d->chars = g_slist_append(d->chars, ch);
+
+		process_deviceinfo_char(ch);
+	}
+}
+static void attio_connected_cb(GAttrib *attrib, gpointer user_data)
+{
+	struct deviceinfo *d = user_data;
+
+	d->attrib = g_attrib_ref(attrib);
+
+	gatt_discover_char(d->attrib, d->svc_range->start, d->svc_range->end,
+					NULL, configure_deviceinfo_cb, d);
+}
+
+static void attio_disconnected_cb(gpointer user_data)
+{
+	struct deviceinfo *d = user_data;
+
+	g_attrib_unref(d->attrib);
+	d->attrib = NULL;
+}
+
+static int deviceinfo_register(struct btd_service *service,
+						struct gatt_primary *prim)
+{
+	struct btd_device *device = btd_service_get_device(service);
+	struct deviceinfo *d;
+
+	d = g_new0(struct deviceinfo, 1);
+	d->dev = btd_device_ref(device);
+	d->svc_range = g_new0(struct att_range, 1);
+	d->svc_range->start = prim->range.start;
+	d->svc_range->end = prim->range.end;
+
+	btd_service_set_user_data(service, d);
+
+	d->attioid = btd_device_add_attio_callback(device, attio_connected_cb,
+						attio_disconnected_cb, d);
+	return 0;
+}
+
+static int deviceinfo_driver_probe(struct btd_service *service)
+{
+	struct btd_device *device = btd_service_get_device(service);
+	struct gatt_primary *prim;
+
+	prim = btd_device_get_primary(device, DEVICE_INFORMATION_UUID);
+	if (prim == NULL)
+		return -EINVAL;
+
+	return deviceinfo_register(service, prim);
+}
+
+static struct btd_profile deviceinfo_profile = {
+	.name		= "deviceinfo",
+	.remote_uuid	= DEVICE_INFORMATION_UUID,
+	.device_probe	= deviceinfo_driver_probe,
+	.device_remove	= deviceinfo_driver_remove
+};
+
+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/profiles/gatt/gas.c b/bluez/profiles/gatt/gas.c
new file mode 100644
index 0000000..7a9d34e
--- /dev/null
+++ b/bluez/profiles/gatt/gas.c
@@ -0,0 +1,466 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  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 <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <errno.h>
+
+#include <glib.h>
+
+#include "btio/btio.h"
+#include "lib/uuid.h"
+#include "src/plugin.h"
+#include "src/adapter.h"
+#include "src/device.h"
+#include "src/profile.h"
+#include "src/service.h"
+#include "src/shared/util.h"
+#include "attrib/att.h"
+#include "attrib/gattrib.h"
+#include "src/attio.h"
+#include "attrib/gatt.h"
+#include "src/log.h"
+#include "src/textfile.h"
+
+/* Generic Attribute/Access Service */
+struct gas {
+	struct btd_device *device;
+	struct att_range gap;	/* GAP Primary service range */
+	struct att_range gatt;	/* GATT Primary service range */
+	GAttrib *attrib;
+	guint attioid;
+	guint changed_ind;
+	uint16_t changed_handle;
+	uint16_t mtu;
+};
+
+static GSList *devices = NULL;
+
+static void gas_free(struct gas *gas)
+{
+	if (gas->attioid)
+		btd_device_remove_attio_callback(gas->device, gas->attioid);
+
+	g_attrib_unref(gas->attrib);
+	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 void write_ctp_handle(struct btd_device *device, uint16_t uuid,
+					uint16_t handle)
+{
+	char *filename, group[6], value[7];
+	GKeyFile *key_file;
+	char *data;
+	gsize length = 0;
+
+	filename = btd_device_get_storage_path(device, "gatt");
+	if (!filename) {
+		warn("Unable to get gatt storage path for device");
+		return;
+	}
+
+	key_file = g_key_file_new();
+	g_key_file_load_from_file(key_file, filename, 0, NULL);
+
+	snprintf(group, sizeof(group), "%hu", uuid);
+	snprintf(value, sizeof(value), "0x%4.4X", 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_free(filename);
+	g_key_file_free(key_file);
+}
+
+static int read_ctp_handle(struct btd_device *device, uint16_t uuid,
+					uint16_t *value)
+{
+	char *filename, group[6];
+	GKeyFile *key_file;
+	char *str;
+	int err = 0;
+
+	filename = btd_device_get_storage_path(device, "gatt");
+	if (!filename) {
+		warn("Unable to get gatt storage path for device");
+		return -ENOENT;
+	}
+
+	snprintf(group, sizeof(group), "%hu", uuid);
+
+	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, group, "Value", NULL);
+	if (str == NULL || sscanf(str, "%hx", value) != 1)
+		err = -ENOENT;
+
+	g_free(str);
+	g_free(filename);
+	g_key_file_free(key_file);
+
+	return err;
+}
+
+static void gap_appearance_cb(guint8 status, const guint8 *pdu, guint16 plen,
+							gpointer user_data)
+{
+	struct gas *gas = user_data;
+	struct att_data_list *list =  NULL;
+	uint16_t app;
+	uint8_t *atval;
+
+	if (status != 0) {
+		error("Read characteristics by UUID failed: %s",
+				att_ecode2str(status));
+		return;
+	}
+
+	list = dec_read_by_type_resp(pdu, plen);
+	if (list == NULL)
+		return;
+
+	if (list->len != 4) {
+		error("GAP Appearance value: invalid data");
+		goto done;
+	}
+
+	atval = list->data[0] + 2; /* skip handle value */
+	app = get_le16(atval);
+
+	DBG("GAP Appearance: 0x%04x", app);
+
+	device_set_appearance(gas->device, app);
+
+done:
+	att_data_list_free(list);
+}
+
+static void indication_cb(const uint8_t *pdu, uint16_t len, gpointer user_data)
+{
+	uint8_t bdaddr_type;
+	struct gas *gas = user_data;
+	uint16_t start, end, olen;
+	size_t plen;
+	uint8_t *opdu;
+
+	if (len < 7) { /* 1-byte opcode + 2-byte handle + 4 range */
+		error("Malformed ATT notification");
+		return;
+	}
+
+	start = get_le16(&pdu[3]);
+	end = get_le16(&pdu[5]);
+
+	DBG("Service Changed start: 0x%04X end: 0x%04X", start, end);
+
+	/* Confirming indication received */
+	opdu = g_attrib_get_buffer(gas->attrib, &plen);
+	olen = enc_confirmation(opdu, plen);
+	g_attrib_send(gas->attrib, 0, opdu, olen, NULL, NULL, NULL);
+
+	bdaddr_type = btd_device_get_bdaddr_type(gas->device);
+	if (!device_is_bonded(gas->device, bdaddr_type)) {
+		DBG("Ignoring Service Changed: device is not bonded");
+		return;
+	}
+
+	btd_device_gatt_set_service_changed(gas->device, start, end);
+}
+
+static void ccc_written_cb(guint8 status, const guint8 *pdu, guint16 plen,
+							gpointer user_data)
+{
+	struct gas *gas = user_data;
+
+	if (status) {
+		error("Write Service Changed CCC failed: %s",
+						att_ecode2str(status));
+		return;
+	}
+
+	DBG("Service Changed indications enabled");
+
+	gas->changed_ind = g_attrib_register(gas->attrib, ATT_OP_HANDLE_IND,
+						gas->changed_handle,
+						indication_cb, gas, NULL);
+
+	write_ctp_handle(gas->device, GATT_CHARAC_SERVICE_CHANGED,
+					gas->changed_handle);
+}
+
+static void write_ccc(GAttrib *attrib, uint16_t handle, gpointer user_data)
+{
+	uint8_t value[2];
+
+	put_le16(GATT_CLIENT_CHARAC_CFG_IND_BIT, value);
+	gatt_write_char(attrib, handle, value, sizeof(value), ccc_written_cb,
+								user_data);
+}
+
+static void gatt_descriptors_cb(guint8 status, const guint8 *pdu, guint16 len,
+							gpointer user_data)
+{
+	struct gas *gas = user_data;
+	struct att_data_list *list;
+	int i;
+	uint8_t format;
+
+	if (status) {
+		error("Discover all GATT characteristic descriptors: %s",
+							att_ecode2str(status));
+		return;
+	}
+
+	list = dec_find_info_resp(pdu, len, &format);
+	if (list == NULL)
+		return;
+
+	if (format != ATT_FIND_INFO_RESP_FMT_16BIT)
+		goto done;
+
+	for (i = 0; i < list->num; i++) {
+		uint16_t uuid16, ccc;
+		uint8_t *value;
+
+		value = list->data[i];
+		ccc = get_le16(value);
+		uuid16 = get_le16(&value[2]);
+		DBG("CCC: 0x%04x UUID: 0x%04x", ccc, uuid16);
+		write_ccc(gas->attrib, ccc, user_data);
+	}
+
+done:
+	att_data_list_free(list);
+}
+
+static void gatt_characteristic_cb(uint8_t status, GSList *characteristics,
+								void *user_data)
+{
+	struct gas *gas = user_data;
+	struct gatt_char *chr;
+	uint16_t start, end;
+
+	if (status) {
+		error("Discover Service Changed handle: %s", att_ecode2str(status));
+		return;
+	}
+
+	chr = characteristics->data;
+
+	start = chr->value_handle + 1;
+	end = gas->gatt.end;
+
+	if (start > end) {
+		error("Inconsistent database: Service Changed CCC missing");
+		return;
+	}
+
+	gas->changed_handle = chr->value_handle;
+	gatt_discover_char_desc(gas->attrib, start, end, gatt_descriptors_cb,
+									gas);
+}
+
+static void exchange_mtu_cb(guint8 status, const guint8 *pdu, guint16 plen,
+							gpointer user_data)
+{
+	struct gas *gas = user_data;
+	uint16_t rmtu;
+
+	if (status) {
+		error("MTU exchange: %s", att_ecode2str(status));
+		return;
+	}
+
+	if (!dec_mtu_resp(pdu, plen, &rmtu)) {
+		error("MTU exchange: protocol error");
+		return;
+	}
+
+	gas->mtu = MIN(rmtu, gas->mtu);
+	if (g_attrib_set_mtu(gas->attrib, gas->mtu))
+		DBG("MTU exchange succeeded: %d", gas->mtu);
+	else
+		DBG("MTU exchange failed");
+}
+
+static void attio_connected_cb(GAttrib *attrib, gpointer user_data)
+{
+	struct gas *gas = user_data;
+	GIOChannel *io;
+	GError *gerr = NULL;
+	uint16_t cid, imtu;
+	uint16_t app;
+
+	gas->attrib = g_attrib_ref(attrib);
+	io = g_attrib_get_channel(attrib);
+
+	if (bt_io_get(io, &gerr, BT_IO_OPT_IMTU, &imtu,
+				BT_IO_OPT_CID, &cid, BT_IO_OPT_INVALID) &&
+							cid == ATT_CID) {
+		gatt_exchange_mtu(gas->attrib, imtu, exchange_mtu_cb, gas);
+		gas->mtu = imtu;
+		DBG("MTU Exchange: Requesting %d", imtu);
+	}
+
+	if (device_get_appearance(gas->device, &app) < 0) {
+		bt_uuid_t uuid;
+
+		bt_uuid16_create(&uuid, GATT_CHARAC_APPEARANCE);
+
+		gatt_read_char_by_uuid(gas->attrib, gas->gap.start,
+						gas->gap.end, &uuid,
+						gap_appearance_cb, gas);
+	}
+
+	/* TODO: Read other GAP characteristics - See Core spec page 1739 */
+
+	/*
+	 * When re-connecting <<Service Changed>> handle and characteristic
+	 * value doesn't need to read again: known information from the
+	 * previous interaction.
+	 */
+	if (gas->changed_handle == 0) {
+		bt_uuid_t uuid;
+
+		bt_uuid16_create(&uuid, GATT_CHARAC_SERVICE_CHANGED);
+
+		gatt_discover_char(gas->attrib, gas->gatt.start, gas->gatt.end,
+					&uuid, gatt_characteristic_cb, gas);
+	}
+}
+
+static void attio_disconnected_cb(gpointer user_data)
+{
+	struct gas *gas = user_data;
+
+	g_attrib_unregister(gas->attrib, gas->changed_ind);
+	gas->changed_ind = 0;
+
+	g_attrib_unref(gas->attrib);
+	gas->attrib = NULL;
+}
+
+static int gas_register(struct btd_device *device, struct att_range *gap,
+						struct att_range *gatt)
+{
+	struct gas *gas;
+
+	gas = g_new0(struct gas, 1);
+	gas->gap.start = gap->start;
+	gas->gap.end = gap->end;
+	gas->gatt.start = gatt->start;
+	gas->gatt.end = gatt->end;
+
+	gas->device = btd_device_ref(device);
+
+	devices = g_slist_append(devices, gas);
+
+	gas->attioid = btd_device_add_attio_callback(device,
+						attio_connected_cb,
+						attio_disconnected_cb, gas);
+
+	read_ctp_handle(gas->device, GATT_CHARAC_SERVICE_CHANGED,
+					&gas->changed_handle);
+
+	return 0;
+}
+
+static void gas_unregister(struct btd_device *device)
+{
+	struct gas *gas;
+	GSList *l;
+
+	l = g_slist_find_custom(devices, device, cmp_device);
+	if (l == NULL)
+		return;
+
+	gas = l->data;
+	devices = g_slist_remove(devices, gas);
+	gas_free(gas);
+}
+
+static int gatt_driver_probe(struct btd_service *service)
+{
+	struct btd_device *device = btd_service_get_device(service);
+	struct gatt_primary *gap, *gatt;
+
+	gap = btd_device_get_primary(device, GAP_UUID);
+	gatt = btd_device_get_primary(device, GATT_UUID);
+
+	if (gap == NULL || gatt == NULL) {
+		error("GAP and GATT are mandatory");
+		return -EINVAL;
+	}
+
+	return gas_register(device, &gap->range, &gatt->range);
+}
+
+static void gatt_driver_remove(struct btd_service *service)
+{
+	struct btd_device *device = btd_service_get_device(service);
+
+	gas_unregister(device);
+}
+
+static struct btd_profile gatt_profile = {
+	.name		= "gap-gatt-profile",
+	.remote_uuid	= GATT_UUID,
+	.device_probe	= gatt_driver_probe,
+	.device_remove	= gatt_driver_remove
+};
+
+static int gatt_init(void)
+{
+	btd_profile_register(&gatt_profile);
+
+	return 0;
+}
+
+static void gatt_exit(void)
+{
+	btd_profile_unregister(&gatt_profile);
+}
+
+BLUETOOTH_PLUGIN_DEFINE(gatt, VERSION, BLUETOOTH_PLUGIN_PRIORITY_DEFAULT,
+					gatt_init, gatt_exit)
diff --git a/bluez/profiles/health/hdp.c b/bluez/profiles/health/hdp.c
new file mode 100644
index 0000000..48dad52
--- /dev/null
+++ b/bluez/profiles/health/hdp.c
@@ -0,0 +1,2243 @@
+/*
+ *
+ *  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 <bluetooth/bluetooth.h>
+#include <bluetooth/l2cap.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 "mcap_lib.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 */
+	uint			tid;		/* echo timeout */
+};
+
+static struct hdp_channel *hdp_channel_ref(struct hdp_channel *chan)
+{
+	if (chan == NULL)
+		return NULL;
+
+	chan->ref++;
+
+	DBG("health_channel_ref(%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("health_channel_unref(%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("hdp_create_data_ref(%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("hdp_create_data_unref(%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)
+		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 (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("hdp_mcap_mdl_connected_cb");
+	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("hdp_mcap_mdl_closed_cb");
+
+	/* 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("hdp_mcap_mdl_deleted_cb");
+	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("hdp_mcap_mdl_aborted_cb");
+	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;
+		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;
+	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 != NULL) {
+		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("health_device_ref(%p): ref=%d", hdp_dev, hdp_dev->ref);
+
+	return hdp_dev;
+}
+
+void health_device_unref(struct hdp_device *hdp_dev)
+{
+	hdp_dev->ref--;
+
+	DBG("health_device_unref(%p): ref=%d", hdp_dev, hdp_dev->ref);
+
+	if (hdp_dev->ref > 0)
+		return;
+
+	free_health_device(hdp_dev);
+}
diff --git a/bluez/profiles/health/hdp.h b/bluez/profiles/health/hdp.h
new file mode 100644
index 0000000..6e78b09
--- /dev/null
+++ b/bluez/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/profiles/health/hdp_main.c b/bluez/profiles/health/hdp_main.c
new file mode 100644
index 0000000..6dc9acf
--- /dev/null
+++ b/bluez/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/profiles/health/hdp_manager.c b/bluez/profiles/health/hdp_manager.c
new file mode 100644
index 0000000..1882043
--- /dev/null
+++ b/bluez/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 <bluetooth/sdp.h>
+#include <bluetooth/sdp_lib.h>
+
+#include "btio/btio.h"
+#include "lib/uuid.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/profiles/health/hdp_manager.h b/bluez/profiles/health/hdp_manager.h
new file mode 100644
index 0000000..1cab4d0
--- /dev/null
+++ b/bluez/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/profiles/health/hdp_types.h b/bluez/profiles/health/hdp_types.h
new file mode 100644
index 0000000..b34b5e0
--- /dev/null
+++ b/bluez/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/profiles/health/hdp_util.c b/bluez/profiles/health/hdp_util.c
new file mode 100644
index 0000000..58770b5
--- /dev/null
+++ b/bluez/profiles/health/hdp_util.c
@@ -0,0 +1,1212 @@
+/*
+ *
+ *  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 <gdbus/gdbus.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/sdp.h>
+#include <bluetooth/sdp_lib.h>
+
+#include "src/adapter.h"
+#include "src/device.h"
+
+#include "src/sdpd.h"
+#include "src/sdp-client.h"
+#include "src/uuid-helper.h"
+
+#include "lib/uuid.h"
+#include "btio/btio.h"
+
+#include "src/log.h"
+#include "src/dbus-common.h"
+
+#include "mcap.h"
+#include "mcap_lib.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;
+	}
+
+	if (sdp_set_access_protos(sdp_record, access_proto_list) < 0) {
+		ret = FALSE;
+		goto end;
+	}
+	ret = TRUE;
+
+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;
+	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;
+	}
+
+	if (sdp_set_add_access_protos(sdp_record, access_proto_list) < 0)
+		ret = FALSE;
+	else
+		ret = TRUE;
+
+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 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);
+	return FALSE;
+}
+
+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_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;
+	}
+
+	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/profiles/health/hdp_util.h b/bluez/profiles/health/hdp_util.h
new file mode 100644
index 0000000..35e1196
--- /dev/null
+++ b/bluez/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/profiles/health/mcap.c b/bluez/profiles/health/mcap.c
new file mode 100644
index 0000000..102ec85
--- /dev/null
+++ b/bluez/profiles/health/mcap.c
@@ -0,0 +1,2192 @@
+/*
+ *
+ *  MCAP for 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 <netinet/in.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <unistd.h>
+
+#include <glib.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/l2cap.h>
+
+#include "btio/btio.h"
+#include "src/log.h"
+#include "src/error.h"
+
+#include "mcap.h"
+#include "mcap_lib.h"
+#include "mcap_internal.h"
+
+#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 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 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;
+		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;
+
+	shutdown_mdl(mdl);
+	if (notify)
+		mdl->mcl->cb->mdl_deleted(mdl, mdl->mcl->cb->user_data);
+
+	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 = FALSE;
+		g_slist_foreach(mcl->mdls, mcap_del_mdl, &notify);
+		g_slist_free(mcl->mdls);
+		mcl->mdls = NULL;
+		mcl->state = MCL_CONNECTED;
+		/* NULL mdl means ALL_MDLS */
+		mcl->cb->mdl_deleted(NULL, mcl->cb->user_data);
+		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);
+}
diff --git a/bluez/profiles/health/mcap.h b/bluez/profiles/health/mcap.h
new file mode 100644
index 0000000..1129e69
--- /dev/null
+++ b/bluez/profiles/health/mcap.h
@@ -0,0 +1,164 @@
+/*
+ *
+ *  MCAP for BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2010 GSyC/LibreSoft, Universidad Rey Juan Carlos.
+ *  Copyright (C) 2010 Signove
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public 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 __MCAP_H
+#define __MCAP_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#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)
+
+/*
+ * 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;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __MCAP_H */
diff --git a/bluez/profiles/health/mcap_internal.h b/bluez/profiles/health/mcap_internal.h
new file mode 100644
index 0000000..7191b23
--- /dev/null
+++ b/bluez/profiles/health/mcap_internal.h
@@ -0,0 +1,137 @@
+/*
+ *
+ *  MCAP for 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 __MCAP_INTERNAL_H
+#define __MCAP_INTERNAL_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+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_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_csp;
+struct mcap_mdl_op_cb;
+
+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 */
+};
+
+#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 */
+
+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;
+};
+
+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);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __MCAP_INTERNAL_H */
diff --git a/bluez/profiles/health/mcap_lib.h b/bluez/profiles/health/mcap_lib.h
new file mode 100644
index 0000000..603ccc0
--- /dev/null
+++ b/bluez/profiles/health/mcap_lib.h
@@ -0,0 +1,224 @@
+/*
+ *
+ *  MCAP for 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 __MCAP_LIB_H
+#define __MCAP_LIB_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+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;
+
+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);
+
+/************ 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);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __MCAP_LIB_H */
diff --git a/bluez/profiles/health/mcap_sync.c b/bluez/profiles/health/mcap_sync.c
new file mode 100644
index 0000000..cc89d47
--- /dev/null
+++ b/bluez/profiles/health/mcap_sync.c
@@ -0,0 +1,1009 @@
+/*
+ *
+ *  MCAP for BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2010 GSyC/LibreSoft, Universidad Rey Juan Carlos.
+ *  Copyright (C) 2010 Signove
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to 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 <netinet/in.h>
+#include <time.h>
+#include <stdlib.h>
+#include <sys/ioctl.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/l2cap.h>
+
+#include "btio/btio.h"
+#include "src/adapter.h"
+#include "src/log.h"
+
+#include "mcap.h"
+#include "mcap_lib.h"
+#include "mcap_internal.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
+
+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;
+};
+
+static gboolean csp_caps_initialized = FALSE;
+struct csp_caps _caps;
+
+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 * 1000000 + tv->tv_nsec / 1000;
+}
+
+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)
+{
+	int which = 1;
+	struct btd_adapter *adapter;
+
+	adapter = adapter_find(&mcl->mi->src);
+	if (!adapter)
+		return FALSE;
+
+	if (btd_adapter_read_clock(adapter, &mcl->addr, which, 1000,
+						btclock, btaccuracy) < 0)
+		return FALSE;
+
+	return TRUE;
+}
+
+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
+		clock_gettime(CLK, &now);
+
+	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;
+
+	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) {
+
+		clock_gettime(CLK, &t0);
+
+		if (!read_btclock(mcl, btclock, &btres))
+			continue;
+
+		clock_gettime(CLK, base_time);
+
+		/* Tries to detect preemption between clock_gettime
+		 * and read_btclock by measuring transaction time
+		 */
+		latency = time_us(base_time) - time_us(&t0);
+	}
+
+	*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;
+
+	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;
+
+	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);
+
+	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/profiles/heartrate/heartrate.c b/bluez/profiles/heartrate/heartrate.c
new file mode 100644
index 0000000..1dcbdf4
--- /dev/null
+++ b/bluez/profiles/heartrate/heartrate.c
@@ -0,0 +1,887 @@
+/*
+ *
+ *  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 <gdbus/gdbus.h>
+
+#include "lib/uuid.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(guint8 status, const guint8 *pdu,
+						guint16 len, gpointer user_data)
+{
+	struct heartrate *hr = user_data;
+	struct att_data_list *list;
+	uint8_t format;
+	int i;
+
+	if (status != 0) {
+		error("Discover Heart Rate Measurement descriptors failed: %s",
+							att_ecode2str(status));
+		return;
+	}
+
+	list = dec_find_info_resp(pdu, len, &format);
+	if (list == NULL)
+		return;
+
+	if (format != ATT_FIND_INFO_RESP_FMT_16BIT)
+		goto done;
+
+	for (i = 0; i < list->num; i++) {
+		uint8_t *value;
+		uint16_t handle, uuid;
+		char *msg;
+		uint8_t attr_val[2];
+
+		value = list->data[i];
+		handle = get_le16(value);
+		uuid = get_le16(value + 2);
+
+		if (uuid != GATT_CLIENT_CHARAC_CFG_UUID)
+			continue;
+
+		hr->measurement_ccc_handle = 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, handle, attr_val,
+					sizeof(attr_val), char_write_cb, msg);
+
+		break;
+	}
+
+done:
+	att_data_list_free(list);
+}
+
+static void discover_measurement_ccc(struct heartrate *hr,
+				struct gatt_char *c, struct gatt_char *c_next)
+{
+	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 != hr->svc_range->end) {
+		end = hr->svc_range->end;
+	} else {
+		return;
+	}
+
+	gatt_discover_char_desc(hr->attrib, start, end, 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/profiles/iap/main.c b/bluez/profiles/iap/main.c
new file mode 100644
index 0000000..0e8f43f
--- /dev/null
+++ b/bluez/profiles/iap/main.c
@@ -0,0 +1,465 @@
+/*
+ *
+ *  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.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/profiles/input/device.c b/bluez/profiles/input/device.c
new file mode 100644
index 0000000..a2329c6
--- /dev/null
+++ b/bluez/profiles/input/device.c
@@ -0,0 +1,985 @@
+/*
+ *
+ *  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 <stdbool.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/hidp.h>
+#include <bluetooth/sdp.h>
+#include <bluetooth/sdp_lib.h>
+
+#include <gdbus/gdbus.h>
+
+#include "src/log.h"
+
+#include "btio/btio.h"
+#include "lib/uuid.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 "device.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;
+	guint			dc_id;
+	bool			disable_sdp;
+	enum reconnect_mode_t	reconnect_mode;
+	guint			reconnect_timer;
+	uint32_t		reconnect_attempt;
+};
+
+static int idle_timeout = 0;
+
+void input_set_idle_timeout(int timeout)
+{
+	idle_timeout = timeout;
+}
+
+static void input_device_enter_reconnect_mode(struct input_device *idev);
+
+static void input_device_free(struct input_device *idev)
+{
+	if (idev->dc_id)
+		device_remove_disconnect_watch(idev->device, idev->dc_id);
+
+	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);
+
+	g_free(idev);
+}
+
+static gboolean intr_watch_cb(GIOChannel *chan, GIOCondition cond, gpointer data)
+{
+	struct input_device *idev = data;
+	char address[18];
+
+	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);
+
+	device_remove_disconnect_watch(idev->device, idev->dc_id);
+	idev->dc_id = 0;
+
+	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 gboolean ctrl_watch_cb(GIOChannel *chan, GIOCondition cond, gpointer data)
+{
+	struct input_device *idev = data;
+	char address[18];
+
+	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;
+}
+
+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 gboolean encrypt_notify(GIOChannel *io, GIOCondition condition,
+								gpointer data)
+{
+	struct input_device *idev = data;
+	int err;
+
+	DBG("");
+
+	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 + 1];
+	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);
+	filename[PATH_MAX] = '\0';
+	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;
+	}
+
+	err = ioctl_connadd(req);
+
+cleanup:
+	g_free(req->rd_data);
+	g_free(req);
+
+	return err;
+}
+
+static int 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)
+		return 0;
+
+	memset(&ci, 0, sizeof(ci));
+	bacpy(&ci.bdaddr, &idev->dst);
+	if (ioctl(ctl, HIDPGETCONNINFO, &ci) < 0) {
+		close(ctl);
+		return 0;
+	}
+
+	close(ctl);
+
+	if (ci.state != BT_CONNECTED)
+		return 0;
+	else
+		return 1;
+}
+
+static int connection_disconnect(struct input_device *idev, uint32_t flags)
+{
+	struct hidp_conndel_req req;
+	struct hidp_conninfo ci;
+	int ctl, err = 0;
+
+	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);
+
+	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)) {
+		err = -ENOTCONN;
+		goto fail;
+	}
+
+	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);
+		goto fail;
+	}
+
+fail:
+	close(ctl);
+
+	return err;
+}
+
+static void disconnect_cb(struct btd_device *device, gboolean removal,
+				void *user_data)
+{
+	struct input_device *idev = user_data;
+	int flags;
+
+	info("Input: disconnect %s", idev->path);
+
+	flags = removal ? (1 << HIDP_VIRTUAL_CABLE_UNPLUG) : 0;
+
+	connection_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;
+
+	idev->dc_id = device_add_disconnect_watch(idev->device, disconnect_cb,
+							idev, NULL);
+
+	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;
+	int err;
+
+	if (conn_err) {
+		err = -EIO;
+		goto failed;
+	}
+
+	err = input_device_connected(idev);
+	if (err < 0)
+		goto failed;
+
+	idev->intr_watch = g_io_add_watch(idev->intr_io,
+					G_IO_HUP | G_IO_ERR | G_IO_NVAL,
+					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;
+	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;
+
+	idev->ctrl_watch = g_io_add_watch(idev->ctrl_io,
+					G_IO_HUP | G_IO_ERR | G_IO_NVAL,
+					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;
+
+	DBG("");
+
+	idev = btd_service_get_user_data(service);
+
+	err = connection_disconnect(idev, 0);
+	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 (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)
+		goto error;
+
+	return 0;
+
+error:
+	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);
+
+	DBG("idev %p psm %d", idev, psm);
+
+	if (!idev)
+		return -ENOENT;
+
+	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,
+					G_IO_HUP | G_IO_ERR | G_IO_NVAL,
+					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,
+					G_IO_HUP | G_IO_ERR | G_IO_NVAL,
+					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/profiles/input/device.h b/bluez/profiles/input/device.h
new file mode 100644
index 0000000..da2149c
--- /dev/null
+++ b/bluez/profiles/input/device.h
@@ -0,0 +1,41 @@
+/*
+ *
+ *  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);
+
+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/profiles/input/hog.c b/bluez/profiles/input/hog.c
new file mode 100644
index 0000000..a11e04e
--- /dev/null
+++ b/bluez/profiles/input/hog.c
@@ -0,0 +1,950 @@
+/*
+ *
+ *  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 "uhid_copy.h"
+
+#include <bluetooth/bluetooth.h>
+
+#include <glib.h>
+
+#include "src/log.h"
+
+#include "lib/uuid.h"
+#include "src/adapter.h"
+#include "src/device.h"
+#include "src/profile.h"
+#include "src/service.h"
+#include "src/shared/util.h"
+
+#include "src/plugin.h"
+
+#include "suspend.h"
+#include "attrib/att.h"
+#include "attrib/gattrib.h"
+#include "src/attio.h"
+#include "attrib/gatt.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 UHID_DEVICE_FILE	"/dev/uhid"
+
+#define HOG_REPORT_MAP_MAX_SIZE        512
+#define HID_INFO_SIZE			4
+
+struct hog_device {
+	uint16_t		id;
+	struct btd_device	*device;
+	GAttrib			*attrib;
+	guint			attioid;
+	struct gatt_primary	*hog_primary;
+	GSList			*reports;
+	int			uhid_fd;
+	gboolean		has_report_id;
+	guint			uhid_watch_id;
+	uint16_t		bcdhid;
+	uint8_t			bcountrycode;
+	uint16_t		proto_mode_handle;
+	uint16_t		ctrlpt_handle;
+	uint8_t			flags;
+};
+
+struct report {
+	uint8_t			id;
+	uint8_t			type;
+	guint			notifyid;
+	struct gatt_char	*decl;
+	struct hog_device	*hogdev;
+};
+
+struct disc_desc_cb_data {
+	uint16_t end;
+	gpointer data;
+};
+
+static gboolean suspend_supported = FALSE;
+static GSList *devices = NULL;
+
+static void report_value_cb(const uint8_t *pdu, uint16_t len,
+							gpointer user_data)
+{
+	struct report *report = user_data;
+	struct hog_device *hogdev = report->hogdev;
+	struct uhid_event ev;
+	uint16_t report_size = len - 3;
+	uint8_t *buf;
+
+	if (len < 3) { /* 1-byte opcode + 2-byte handle */
+		error("Malformed ATT notification");
+		return;
+	}
+
+	memset(&ev, 0, sizeof(ev));
+	ev.type = UHID_INPUT;
+	ev.u.input.size = MIN(report_size, UHID_DATA_MAX);
+
+	buf = ev.u.input.data;
+	if (hogdev->has_report_id) {
+		*buf = report->id;
+		buf++;
+		ev.u.input.size++;
+	}
+
+	memcpy(buf, &pdu[3], MIN(report_size, UHID_DATA_MAX));
+
+	if (write(hogdev->uhid_fd, &ev, sizeof(ev)) < 0)
+		error("uHID write failed: %s", strerror(errno));
+	else
+		DBG("Report from HoG device 0x%04X written to uHID fd %d",
+						hogdev->id, hogdev->uhid_fd);
+}
+
+static void report_ccc_written_cb(guint8 status, const guint8 *pdu,
+					guint16 plen, gpointer user_data)
+{
+	struct report *report = user_data;
+	struct hog_device *hogdev = report->hogdev;
+
+	if (status != 0) {
+		error("Write report characteristic descriptor failed: %s",
+							att_ecode2str(status));
+		return;
+	}
+
+	report->notifyid = g_attrib_register(hogdev->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(uint16_t handle, gpointer user_data)
+{
+	struct report *report = user_data;
+	struct hog_device *hogdev = report->hogdev;
+	uint8_t value[] = { 0x01, 0x00 };
+
+	gatt_write_char(hogdev->attrib, handle, value, sizeof(value),
+					report_ccc_written_cb, report);
+}
+
+static void report_reference_cb(guint8 status, const guint8 *pdu,
+					guint16 plen, gpointer user_data)
+{
+	struct report *report = user_data;
+
+	if (status != 0) {
+		error("Read Report Reference descriptor failed: %s",
+							att_ecode2str(status));
+		return;
+	}
+
+	if (plen != 3) {
+		error("Malformed ATT read response");
+		return;
+	}
+
+	report->id = pdu[1];
+	report->type = pdu[2];
+	DBG("Report ID: 0x%02x Report type: 0x%02x", pdu[1], pdu[2]);
+}
+
+static void external_report_reference_cb(guint8 status, const guint8 *pdu,
+					guint16 plen, gpointer user_data);
+
+
+static void discover_descriptor_cb(guint8 status, const guint8 *pdu,
+					guint16 len, gpointer user_data)
+{
+	struct disc_desc_cb_data *ddcb_data = user_data;
+	struct report *report;
+	struct hog_device *hogdev;
+	struct att_data_list *list = NULL;
+	GAttrib *attrib = NULL;
+	uint8_t format;
+	uint16_t handle = 0xffff;
+	uint16_t end = ddcb_data->end;
+	int i;
+
+	if (status == ATT_ECODE_ATTR_NOT_FOUND) {
+		DBG("Discover all characteristic descriptors finished");
+		goto done;
+	}
+
+	if (status != 0) {
+		error("Discover all characteristic descriptors failed: %s",
+							att_ecode2str(status));
+		goto done;
+	}
+
+	list = dec_find_info_resp(pdu, len, &format);
+	if (list == NULL)
+		return;
+
+	if (format != ATT_FIND_INFO_RESP_FMT_16BIT)
+		goto done;
+
+	for (i = 0; i < list->num; i++) {
+		uint16_t uuid16;
+		uint8_t *value;
+
+		value = list->data[i];
+		handle = get_le16(value);
+		uuid16 = get_le16(&value[2]);
+
+		switch (uuid16) {
+		case GATT_CLIENT_CHARAC_CFG_UUID:
+			report = ddcb_data->data;
+			attrib = report->hogdev->attrib;
+			write_ccc(handle, report);
+			break;
+		case GATT_REPORT_REFERENCE:
+			report = ddcb_data->data;
+			attrib = report->hogdev->attrib;
+			gatt_read_char(attrib, handle,
+						report_reference_cb, report);
+			break;
+		case GATT_EXTERNAL_REPORT_REFERENCE:
+			hogdev = ddcb_data->data;
+			attrib = hogdev->attrib;
+			gatt_read_char(attrib, handle,
+					external_report_reference_cb, hogdev);
+			break;
+		}
+	}
+
+done:
+	att_data_list_free(list);
+
+	if (handle != 0xffff && handle < end)
+		gatt_discover_char_desc(attrib, handle + 1, end,
+					discover_descriptor_cb, ddcb_data);
+	else
+		g_free(ddcb_data);
+}
+
+static void discover_descriptor(GAttrib *attrib, uint16_t start, uint16_t end,
+							gpointer user_data)
+{
+	struct disc_desc_cb_data *ddcb_data;
+
+	if (start > end)
+		return;
+
+	ddcb_data = g_new0(struct disc_desc_cb_data, 1);
+	ddcb_data->end = end;
+	ddcb_data->data = user_data;
+
+	gatt_discover_char_desc(attrib, start, end, discover_descriptor_cb,
+								ddcb_data);
+}
+
+static void external_service_char_cb(uint8_t status, GSList *chars,
+								void *user_data)
+{
+	struct hog_device *hogdev = user_data;
+	struct gatt_primary *prim = hogdev->hog_primary;
+	struct report *report;
+	GSList *l;
+
+	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 = g_new0(struct report, 1);
+		report->hogdev = hogdev;
+		report->decl = g_memdup(chr, sizeof(*chr));
+		hogdev->reports = g_slist_append(hogdev->reports, report);
+		start = chr->value_handle + 1;
+		end = (next ? next->handle - 1 : prim->range.end);
+		discover_descriptor(hogdev->attrib, start, end, report);
+	}
+}
+
+static void external_report_reference_cb(guint8 status, const guint8 *pdu,
+					guint16 plen, gpointer user_data)
+{
+	struct hog_device *hogdev = user_data;
+	uint16_t uuid16;
+	bt_uuid_t uuid;
+
+	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);
+	bt_uuid16_create(&uuid, uuid16);
+	gatt_discover_char(hogdev->attrib, 0x00, 0xff, &uuid,
+					external_service_char_cb, hogdev);
+}
+
+static void report_map_read_cb(guint8 status, const guint8 *pdu, guint16 plen,
+							gpointer user_data)
+{
+	struct hog_device *hogdev = user_data;
+	struct btd_adapter *adapter = device_get_adapter(hogdev->device);
+	uint8_t value[HOG_REPORT_MAP_MAX_SIZE];
+	struct uhid_event ev;
+	uint16_t vendor_src, vendor, product, version;
+	ssize_t vlen;
+	int i;
+
+	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; i++) {
+		switch (value[i]) {
+		case 0x85:
+		case 0x86:
+		case 0x87:
+			hogdev->has_report_id = TRUE;
+		}
+
+		if (i % 2 == 0) {
+			if (i + 1 == vlen)
+				DBG("\t %02x", value[i]);
+			else
+				DBG("\t %02x %02x", value[i], value[i + 1]);
+		}
+	}
+
+	vendor_src = btd_device_get_vendor_src(hogdev->device);
+	vendor = btd_device_get_vendor(hogdev->device);
+	product = btd_device_get_product(hogdev->device);
+	version = btd_device_get_version(hogdev->device);
+	DBG("DIS information: vendor_src=0x%X, vendor=0x%X, product=0x%X, "
+			"version=0x%X",	vendor_src, vendor, product, version);
+
+	/* create uHID device */
+	memset(&ev, 0, sizeof(ev));
+	ev.type = UHID_CREATE;
+	if (device_name_known(hogdev->device))
+		device_get_name(hogdev->device, (char *) ev.u.create.name,
+						sizeof(ev.u.create.name));
+	else
+		strcpy((char *) ev.u.create.name, "bluez-hog-device");
+	ba2str(btd_adapter_get_address(adapter), (char *) ev.u.create.phys);
+	ba2str(device_get_address(hogdev->device), (char *) ev.u.create.uniq);
+	ev.u.create.vendor = vendor;
+	ev.u.create.product = product;
+	ev.u.create.version = version;
+	ev.u.create.country = hogdev->bcountrycode;
+	ev.u.create.bus = BUS_BLUETOOTH;
+	ev.u.create.rd_data = value;
+	ev.u.create.rd_size = vlen;
+
+	if (write(hogdev->uhid_fd, &ev, sizeof(ev)) < 0)
+		error("Failed to create uHID device: %s", strerror(errno));
+}
+
+static void info_read_cb(guint8 status, const guint8 *pdu, guint16 plen,
+							gpointer user_data)
+{
+	struct hog_device *hogdev = user_data;
+	uint8_t value[HID_INFO_SIZE];
+	ssize_t vlen;
+
+	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;
+	}
+
+	hogdev->bcdhid = get_le16(&value[0]);
+	hogdev->bcountrycode = value[2];
+	hogdev->flags = value[3];
+
+	DBG("bcdHID: 0x%04X bCountryCode: 0x%02X Flags: 0x%02X",
+			hogdev->bcdhid, hogdev->bcountrycode, hogdev->flags);
+}
+
+static void proto_mode_read_cb(guint8 status, const guint8 *pdu, guint16 plen,
+							gpointer user_data)
+{
+	struct hog_device *hogdev = user_data;
+	uint8_t value;
+	ssize_t vlen;
+
+	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 device 0x%04X is operating in Boot Procotol Mode",
+								hogdev->id);
+
+		gatt_write_cmd(hogdev->attrib, hogdev->proto_mode_handle, &nval,
+						sizeof(nval), NULL, NULL);
+	} else if (value == HOG_PROTO_MODE_REPORT)
+		DBG("HoG device 0x%04X is operating in Report Protocol Mode",
+								hogdev->id);
+}
+
+static void char_discovered_cb(uint8_t status, GSList *chars, void *user_data)
+{
+	struct hog_device *hogdev = user_data;
+	struct gatt_primary *prim = hogdev->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;
+
+	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 : prim->range.end);
+
+		if (bt_uuid_cmp(&uuid, &report_uuid) == 0) {
+			report = g_new0(struct report, 1);
+			report->hogdev = hogdev;
+			report->decl = g_memdup(chr, sizeof(*chr));
+			hogdev->reports = g_slist_append(hogdev->reports,
+								report);
+			discover_descriptor(hogdev->attrib, start, end, report);
+		} else if (bt_uuid_cmp(&uuid, &report_map_uuid) == 0) {
+			gatt_read_char(hogdev->attrib, chr->value_handle,
+						report_map_read_cb, hogdev);
+			discover_descriptor(hogdev->attrib, start, end, hogdev);
+		} 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)
+			hogdev->ctrlpt_handle = chr->value_handle;
+	}
+
+	if (proto_mode_handle) {
+		hogdev->proto_mode_handle = proto_mode_handle;
+		gatt_read_char(hogdev->attrib, proto_mode_handle,
+						proto_mode_read_cb, hogdev);
+	}
+
+	if (info_handle)
+		gatt_read_char(hogdev->attrib, info_handle, info_read_cb,
+									hogdev);
+}
+
+static void output_written_cb(guint8 status, const guint8 *pdu,
+					guint16 plen, gpointer user_data)
+{
+	if (status != 0) {
+		error("Write output report failed: %s", att_ecode2str(status));
+		return;
+	}
+}
+
+static int report_type_cmp(gconstpointer a, gconstpointer b)
+{
+	const struct report *report = a;
+	uint8_t type = GPOINTER_TO_UINT(b);
+
+	return report->type - type;
+}
+
+static void forward_report(struct hog_device *hogdev,
+						struct uhid_event *ev)
+{
+	struct report *report;
+	GSList *l;
+	void *data;
+	int size;
+	guint type;
+
+	if (hogdev->has_report_id) {
+		data = ev->u.output.data + 1;
+		size = ev->u.output.size - 1;
+	} else {
+		data = ev->u.output.data;
+		size = ev->u.output.size;
+	}
+
+	switch (ev->type) {
+	case UHID_OUTPUT:
+		type = HOG_REPORT_TYPE_OUTPUT;
+		break;
+	case UHID_FEATURE:
+		type = HOG_REPORT_TYPE_FEATURE;
+		break;
+	default:
+		return;
+	}
+
+	l = g_slist_find_custom(hogdev->reports, GUINT_TO_POINTER(type),
+							report_type_cmp);
+	if (!l)
+		return;
+
+	report = l->data;
+
+	DBG("Sending report type %d to device 0x%04X handle 0x%X", type,
+				hogdev->id, report->decl->value_handle);
+
+	if (hogdev->attrib == NULL)
+		return;
+
+	if (report->decl->properties & GATT_CHR_PROP_WRITE)
+		gatt_write_char(hogdev->attrib, report->decl->value_handle,
+				data, size, output_written_cb, hogdev);
+	else if (report->decl->properties & GATT_CHR_PROP_WRITE_WITHOUT_RESP)
+		gatt_write_cmd(hogdev->attrib, report->decl->value_handle,
+						data, size, NULL, NULL);
+}
+
+static gboolean uhid_event_cb(GIOChannel *io, GIOCondition cond,
+							gpointer user_data)
+{
+	struct hog_device *hogdev = user_data;
+	struct uhid_event ev;
+	ssize_t bread;
+	int fd;
+
+	if (cond & (G_IO_ERR | G_IO_NVAL))
+		goto failed;
+
+	fd = g_io_channel_unix_get_fd(io);
+	memset(&ev, 0, sizeof(ev));
+
+	bread = read(fd, &ev, sizeof(ev));
+	if (bread < 0) {
+		int err = -errno;
+		DBG("uhid-dev read: %s(%d)", strerror(-err), -err);
+		goto failed;
+	}
+
+	DBG("uHID event type %d received", ev.type);
+
+	switch (ev.type) {
+	case UHID_START:
+	case UHID_STOP:
+		/* These are called to start and stop the underlying hardware.
+		 * For HoG we open the channels before creating the device so
+		 * the hardware is always ready. No need to handle these.
+		 * Note that these are also called when the kernel switches
+		 * between device-drivers loaded on the HID device. But we can
+		 * simply keep the hardware alive during transitions and it
+		 * works just fine.
+		 * The kernel never destroys a device itself! Only an explicit
+		 * UHID_DESTROY request can remove a device. */
+		break;
+	case UHID_OPEN:
+	case UHID_CLOSE:
+		/* OPEN/CLOSE are sent whenever user-space opens any interface
+		 * provided by the kernel HID device. Whenever the open-count
+		 * is non-zero we must be ready for I/O. As long as it is zero,
+		 * we can decide to drop all I/O and put the device
+		 * asleep This is optional, though. Moreover, some
+		 * special device drivers are buggy in that regard, so
+		 * maybe we just keep I/O always awake like HIDP in the
+		 * kernel does. */
+		break;
+	case UHID_OUTPUT:
+	case UHID_FEATURE:
+		forward_report(hogdev, &ev);
+		break;
+	case UHID_OUTPUT_EV:
+		/* This is only sent by kernels prior to linux-3.11. It
+		 * requires us to parse HID-descriptors in user-space to
+		 * properly handle it. This is redundant as the kernel
+		 * does it already. That's why newer kernels assemble
+		 * the output-reports and send it to us via UHID_OUTPUT.
+		 * We never implemented this, so we rely on users to use
+		 * recent-enough kernels if they want this feature. No reason
+		 * to implement this for older kernels. */
+		DBG("Unsupported uHID output event: type %d code %d value %d",
+			ev.u.output_ev.type, ev.u.output_ev.code,
+			ev.u.output_ev.value);
+		break;
+	default:
+		warn("unexpected uHID event");
+		break;
+	}
+
+	return TRUE;
+
+failed:
+	hogdev->uhid_watch_id = 0;
+	return FALSE;
+}
+
+static void attio_connected_cb(GAttrib *attrib, gpointer user_data)
+{
+	struct hog_device *hogdev = user_data;
+	struct gatt_primary *prim = hogdev->hog_primary;
+	GSList *l;
+
+	DBG("HoG connected");
+
+	hogdev->attrib = g_attrib_ref(attrib);
+
+	if (hogdev->reports == NULL) {
+		gatt_discover_char(hogdev->attrib, prim->range.start,
+						prim->range.end, NULL,
+						char_discovered_cb, hogdev);
+		return;
+	}
+
+	for (l = hogdev->reports; l; l = l->next) {
+		struct report *r = l->data;
+
+		r->notifyid = g_attrib_register(hogdev->attrib,
+					ATT_OP_HANDLE_NOTIFY,
+					r->decl->value_handle,
+					report_value_cb, r, NULL);
+	}
+}
+
+static void attio_disconnected_cb(gpointer user_data)
+{
+	struct hog_device *hogdev = user_data;
+	GSList *l;
+
+	DBG("HoG disconnected");
+
+	for (l = hogdev->reports; l; l = l->next) {
+		struct report *r = l->data;
+
+		g_attrib_unregister(hogdev->attrib, r->notifyid);
+	}
+
+	g_attrib_unref(hogdev->attrib);
+	hogdev->attrib = NULL;
+}
+
+static struct hog_device *hog_new_device(struct btd_device *device,
+								uint16_t id)
+{
+	struct hog_device *hogdev;
+
+	hogdev = g_try_new0(struct hog_device, 1);
+	if (!hogdev)
+		return NULL;
+
+	hogdev->id = id;
+	hogdev->device = btd_device_ref(device);
+
+	return hogdev;
+}
+
+static void report_free(void *data)
+{
+	struct report *report = data;
+	struct hog_device *hogdev = report->hogdev;
+
+	if (hogdev->attrib)
+		g_attrib_unregister(hogdev->attrib, report->notifyid);
+
+	g_free(report->decl);
+	g_free(report);
+}
+
+static void hog_free_device(struct hog_device *hogdev)
+{
+	btd_device_unref(hogdev->device);
+	g_slist_free_full(hogdev->reports, report_free);
+	g_attrib_unref(hogdev->attrib);
+	g_free(hogdev->hog_primary);
+	g_free(hogdev);
+}
+
+static struct hog_device *hog_register_device(struct btd_device *device,
+						struct gatt_primary *prim)
+{
+	struct hog_device *hogdev;
+	GIOCondition cond = G_IO_IN | G_IO_ERR | G_IO_NVAL;
+	GIOChannel *io;
+
+	hogdev = hog_new_device(device, prim->range.start);
+	if (!hogdev)
+		return NULL;
+
+	hogdev->uhid_fd = open(UHID_DEVICE_FILE, O_RDWR | O_CLOEXEC);
+	if (hogdev->uhid_fd < 0) {
+		error("Failed to open uHID device: %s(%d)", strerror(errno),
+									errno);
+		hog_free_device(hogdev);
+		return NULL;
+	}
+
+	io = g_io_channel_unix_new(hogdev->uhid_fd);
+	g_io_channel_set_encoding(io, NULL, NULL);
+	hogdev->uhid_watch_id = g_io_add_watch(io, cond, uhid_event_cb,
+								hogdev);
+	g_io_channel_unref(io);
+
+	hogdev->hog_primary = g_memdup(prim, sizeof(*prim));
+
+	hogdev->attioid = btd_device_add_attio_callback(device,
+							attio_connected_cb,
+							attio_disconnected_cb,
+							hogdev);
+
+	return hogdev;
+}
+
+static int hog_unregister_device(struct hog_device *hogdev)
+{
+	struct uhid_event ev;
+
+	btd_device_remove_attio_callback(hogdev->device, hogdev->attioid);
+
+	if (hogdev->uhid_watch_id) {
+		g_source_remove(hogdev->uhid_watch_id);
+		hogdev->uhid_watch_id = 0;
+	}
+
+	memset(&ev, 0, sizeof(ev));
+	ev.type = UHID_DESTROY;
+	if (write(hogdev->uhid_fd, &ev, sizeof(ev)) < 0)
+		error("Failed to destroy uHID device: %s", strerror(errno));
+
+	close(hogdev->uhid_fd);
+	hogdev->uhid_fd = -1;
+
+	hog_free_device(hogdev);
+
+	return 0;
+}
+
+static int set_control_point(struct hog_device *hogdev, gboolean suspend)
+{
+	uint8_t value = suspend ? 0x00 : 0x01;
+
+	if (hogdev->attrib == NULL)
+		return -ENOTCONN;
+
+	DBG("0x%4X HID Control Point: %s", hogdev->id, suspend ?
+						"Suspend" : "Exit Suspend");
+
+	if (hogdev->ctrlpt_handle == 0)
+		return -ENOTSUP;
+
+	gatt_write_cmd(hogdev->attrib, hogdev->ctrlpt_handle, &value,
+					sizeof(value), NULL, NULL);
+
+	return 0;
+}
+
+static void set_suspend(gpointer data, gpointer user_data)
+{
+	struct hog_device *hogdev = data;
+	gboolean suspend = GPOINTER_TO_INT(user_data);
+
+	set_control_point(hogdev, suspend);
+}
+
+static void suspend_callback(void)
+{
+	gboolean suspend = TRUE;
+
+	DBG("Suspending ...");
+
+	g_slist_foreach(devices, set_suspend, GINT_TO_POINTER(suspend));
+}
+
+static void resume_callback(void)
+{
+	gboolean suspend = FALSE;
+
+	DBG("Resuming ...");
+
+	g_slist_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 *hogdev;
+
+		if (strcmp(prim->uuid, HOG_UUID) != 0)
+			continue;
+
+		hogdev = hog_register_device(device, prim);
+		if (hogdev == NULL)
+			continue;
+
+		devices = g_slist_append(devices, hogdev);
+	}
+
+	return 0;
+}
+
+static void remove_device(gpointer a, gpointer b)
+{
+	struct hog_device *hogdev = a;
+	struct btd_device *device = b;
+
+	if (hogdev->device != device)
+		return;
+
+	devices = g_slist_remove(devices, hogdev);
+	hog_unregister_device(hogdev);
+}
+
+static void hog_remove(struct btd_service *service)
+{
+	struct btd_device *device = btd_service_get_device(service);
+	const char *path = device_get_path(device);
+
+	DBG("path %s", path);
+
+	g_slist_foreach(devices, remove_device, device);
+}
+
+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/profiles/input/input.conf b/bluez/profiles/input/input.conf
new file mode 100644
index 0000000..abfb64f
--- /dev/null
+++ b/bluez/profiles/input/input.conf
@@ -0,0 +1,9 @@
+# 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
diff --git a/bluez/profiles/input/manager.c b/bluez/profiles/input/manager.c
new file mode 100644
index 0000000..6ef83f4
--- /dev/null
+++ b/bluez/profiles/input/manager.c
@@ -0,0 +1,125 @@
+/*
+ *
+ *  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 <bluetooth/bluetooth.h>
+#include <bluetooth/sdp.h>
+#include <bluetooth/sdp_lib.h>
+
+#include "src/log.h"
+#include "src/plugin.h"
+
+#include "lib/uuid.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;
+
+		idle_timeout = g_key_file_get_integer(config, "General",
+						"IdleTimeout", &err);
+		if (err) {
+			DBG("input.conf: %s", err->message);
+			g_error_free(err);
+		}
+
+		input_set_idle_timeout(idle_timeout * 60);
+	}
+
+	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/profiles/input/server.c b/bluez/profiles/input/server.c
new file mode 100644
index 0000000..772a605
--- /dev/null
+++ b/bluez/profiles/input/server.c
@@ -0,0 +1,342 @@
+/*
+ *
+ *  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 <bluetooth/bluetooth.h>
+#include <bluetooth/sdp.h>
+
+#include <glib.h>
+#include <dbus/dbus.h>
+
+#include "src/log.h"
+
+#include "src/uuid-helper.h"
+#include "btio/btio.h"
+#include "lib/uuid.h"
+#include "src/adapter.h"
+#include "src/device.h"
+#include "src/profile.h"
+
+#include "device.h"
+#include "server.h"
+
+static GSList *servers = NULL;
+struct input_server {
+	bdaddr_t src;
+	GIOChannel *ctrl;
+	GIOChannel *intr;
+	GIOChannel *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;
+
+	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;
+	bdaddr_t src, dst;
+	GError *err = NULL;
+
+	bt_io_get(server->confirm, &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);
+		goto reject;
+	}
+
+	if (derr) {
+		error("Access denied: %s", derr->message);
+		goto reject;
+	}
+
+	if (!input_device_exists(&src, &dst) && !dev_is_sixaxis(&src, &dst))
+		return;
+
+	if (!bt_io_accept(server->confirm, connect_event_cb, server,
+				NULL, &err)) {
+		error("bt_io_accept: %s", err->message);
+		g_error_free(err);
+		goto reject;
+	}
+
+	g_io_channel_unref(server->confirm);
+	server->confirm = NULL;
+
+	return;
+
+reject:
+	g_io_channel_shutdown(server->confirm, TRUE, NULL);
+	g_io_channel_unref(server->confirm);
+	server->confirm = NULL;
+	input_device_close_channels(&src, &dst);
+}
+
+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);
+		goto drop;
+	}
+
+	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_io_channel_ref(chan);
+
+	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);
+	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/profiles/input/server.h b/bluez/profiles/input/server.h
new file mode 100644
index 0000000..74159bb
--- /dev/null
+++ b/bluez/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/profiles/input/suspend-dummy.c b/bluez/profiles/input/suspend-dummy.c
new file mode 100644
index 0000000..542ae25
--- /dev/null
+++ b/bluez/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/profiles/input/suspend.h b/bluez/profiles/input/suspend.h
new file mode 100644
index 0000000..bfee3cf
--- /dev/null
+++ b/bluez/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/profiles/input/uhid_copy.h b/bluez/profiles/input/uhid_copy.h
new file mode 100644
index 0000000..381b062
--- /dev/null
+++ b/bluez/profiles/input/uhid_copy.h
@@ -0,0 +1,104 @@
+#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>
+
+enum uhid_event_type {
+	UHID_CREATE,
+	UHID_DESTROY,
+	UHID_START,
+	UHID_STOP,
+	UHID_OPEN,
+	UHID_CLOSE,
+	UHID_OUTPUT,
+	UHID_OUTPUT_EV,
+	UHID_INPUT,
+	UHID_FEATURE,
+	UHID_FEATURE_ANSWER,
+};
+
+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__));
+
+#define UHID_DATA_MAX 4096
+
+enum uhid_report_type {
+	UHID_FEATURE_REPORT,
+	UHID_OUTPUT_REPORT,
+	UHID_INPUT_REPORT,
+};
+
+struct uhid_input_req {
+	__u8 data[UHID_DATA_MAX];
+	__u16 size;
+} __attribute__((__packed__));
+
+struct uhid_output_req {
+	__u8 data[UHID_DATA_MAX];
+	__u16 size;
+	__u8 rtype;
+} __attribute__((__packed__));
+
+struct uhid_output_ev_req {
+	__u16 type;
+	__u16 code;
+	__s32 value;
+} __attribute__((__packed__));
+
+struct uhid_feature_req {
+	__u32 id;
+	__u8 rnum;
+	__u8 rtype;
+} __attribute__((__packed__));
+
+struct uhid_feature_answer_req {
+	__u32 id;
+	__u16 err;
+	__u16 size;
+	__u8 data[UHID_DATA_MAX];
+};
+
+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_feature_answer_req feature_answer;
+	} u;
+} __attribute__((__packed__));
+
+#endif /* __UHID_H_ */
diff --git a/bluez/profiles/network/bnep.c b/bluez/profiles/network/bnep.c
new file mode 100644
index 0000000..87304c5
--- /dev/null
+++ b/bluez/profiles/network/bnep.c
@@ -0,0 +1,670 @@
+/*
+ *
+ *  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 <bluetooth/bluetooth.h>
+#include <bluetooth/l2cap.h>
+#include <bluetooth/bnep.h>
+
+#include <glib.h>
+
+#include "src/log.h"
+#include "src/shared/util.h"
+#include "lib/uuid.h"
+#include "btio/btio.h"
+
+#include "bnep.h"
+
+#define CON_SETUP_RETRIES      3
+#define CON_SETUP_TO           9
+
+static int ctl;
+
+static struct {
+	const char	*name;		/* Friendly name */
+	const char	*uuid128;	/* UUID 128 */
+	uint16_t	id;		/* Service class identifier */
+} __svc[] = {
+	{ "panu",	PANU_UUID,	BNEP_SVC_PANU	},
+	{ "gn",		GN_UUID,	BNEP_SVC_GN	},
+	{ "nap",	NAP_UUID,	BNEP_SVC_NAP	},
+	{ NULL }
+};
+
+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;
+};
+
+uint16_t bnep_service_id(const char *svc)
+{
+	int i;
+	uint16_t id;
+
+	/* Friendly service name */
+	for (i = 0; __svc[i].name; i++) {
+		if (!strcasecmp(svc, __svc[i].name))
+			return __svc[i].id;
+	}
+
+	/* UUID 128 string */
+	for (i = 0; __svc[i].uuid128; i++) {
+		if (!strcasecmp(svc, __svc[i].uuid128))
+			return __svc[i].id;
+	}
+
+	/* Try convert to HEX */
+	id = strtol(svc, NULL, 16);
+	if ((id < BNEP_SVC_PANU) || (id > BNEP_SVC_GN))
+		return 0;
+
+	return id;
+}
+
+const char *bnep_uuid(uint16_t id)
+{
+	int i;
+
+	for (i = 0; __svc[i].uuid128; i++)
+		if (__svc[i].id == id)
+			return __svc[i].uuid128;
+	return NULL;
+}
+
+const char *bnep_name(uint16_t id)
+{
+	int i;
+
+	for (i = 0; __svc[i].name; i++)
+		if (__svc[i].id == id)
+			return __svc[i].name;
+	return NULL;
+}
+
+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("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)) {
+		int err = -errno;
+		error("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;
+	if (ioctl(ctl, BNEPCONNADD, &req) < 0) {
+		int err = -errno;
+		error("Failed to add device %s: %s(%d)",
+				dev, strerror(-err), -err);
+		return err;
+	}
+
+	strncpy(dev, req.device, 16);
+	return 0;
+}
+
+static int bnep_if_up(const char *devname)
+{
+	struct ifreq ifr;
+	int sk, err;
+
+	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;
+
+	err = ioctl(sk, SIOCSIFFLAGS, (void *) &ifr);
+
+	close(sk);
+
+	if (err < 0) {
+		error("Could not bring up %s", devname);
+		return err;
+	}
+
+	return 0;
+}
+
+static int bnep_if_down(const char *devname)
+{
+	struct ifreq ifr;
+	int sk, err;
+
+	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 */
+	err = ioctl(sk, SIOCSIFFLAGS, (void *) &ifr);
+
+	close(sk);
+
+	if (err < 0) {
+		error("Could not bring down %s", devname);
+		return err;
+	}
+
+	return 0;
+}
+
+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("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("IO Channel read error");
+		goto failed;
+	}
+
+	if (r == 0) {
+		error("No packet received on l2cap socket");
+		goto failed;
+	}
+
+	errno = EPROTO;
+
+	if ((size_t) r < sizeof(*rsp)) {
+		error("Packet received is not bnep type");
+		goto failed;
+	}
+
+	rsp = (void *) pkt;
+	if (rsp->type != BNEP_CONTROL) {
+		error("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)) {
+		error("bnep conn could not be added");
+		goto failed;
+	}
+
+	if (bnep_if_up(session->iface)) {
+		error("could not up %s", session->iface);
+		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("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, void *data)
+{
+	GError *gerr = NULL;
+	int err;
+
+	if (!session || !conn_cb)
+		return -EINVAL;
+
+	session->attempts = 0;
+	session->conn_cb = conn_cb;
+	session->conn_data = data;
+
+	bt_io_get(session->io, &gerr, BT_IO_OPT_DEST_BDADDR, &session->dst_addr,
+							BT_IO_OPT_INVALID);
+	if (gerr) {
+		error("%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);
+}
+
+void bnep_set_disconnect(struct bnep *session, bnep_disconnect_cb disconn_cb,
+								void *data)
+{
+	if (!session || !disconn_cb)
+		return;
+
+	if (!session->disconn_cb && !session->disconn_data) {
+		session->disconn_cb = disconn_cb;
+		session->disconn_data = data;
+	}
+}
+
+static int bnep_add_to_bridge(const char *devname, const char *bridge)
+{
+	int ifindex;
+	struct ifreq ifr;
+	int sk, err;
+
+	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;
+
+	err = ioctl(sk, SIOCBRADDIF, &ifr);
+
+	close(sk);
+
+	if (err < 0)
+		return err;
+
+	info("bridge %s: interface %s added", bridge, devname);
+
+	return 0;
+}
+
+static int bnep_del_from_bridge(const char *devname, const char *bridge)
+{
+	int ifindex;
+	struct ifreq ifr;
+	int sk, err;
+
+	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;
+
+	err = ioctl(sk, SIOCBRDELIF, &ifr);
+
+	close(sk);
+
+	if (err < 0)
+		return err;
+
+	info("bridge %s: interface %s removed", bridge, devname);
+
+	return 0;
+}
+
+int bnep_server_add(int sk, uint16_t dst, char *bridge, char *iface,
+						const bdaddr_t *addr)
+{
+	if (!bridge || !bridge || !iface || !addr)
+		return -EINVAL;
+
+	if (bnep_connadd(sk, dst, iface) < 0) {
+		error("Can't add connection to the bridge %s: %s(%d)",
+						bridge, strerror(errno), errno);
+		return -errno;
+	}
+
+	if (bnep_add_to_bridge(iface, bridge) < 0) {
+		error("Can't add %s to the bridge %s: %s(%d)",
+					iface, bridge, strerror(errno), errno);
+		bnep_conndel(addr);
+		return -errno;
+	}
+
+	if (bnep_if_up(iface) < 0) {
+		error("Can't up the interface %s: %s(%d)",
+						iface, strerror(errno), errno);
+		return -errno;
+	}
+
+	return 0;
+}
+
+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);
+}
+
+ssize_t bnep_send_ctrl_rsp(int sk, uint8_t type, uint8_t ctrl, uint16_t resp)
+{
+	struct bnep_control_rsp rsp;
+
+	rsp.type = type;
+	rsp.ctrl = ctrl;
+	rsp.resp = htons(resp);
+
+	return send(sk, &rsp, sizeof(rsp), 0);
+}
+
+uint16_t bnep_setup_chk(uint16_t dst, uint16_t src)
+{
+	/* Allowed PAN Profile scenarios */
+	switch (dst) {
+	case BNEP_SVC_NAP:
+	case BNEP_SVC_GN:
+		if (src == BNEP_SVC_PANU)
+			return 0;
+		return BNEP_CONN_INVALID_SRC;
+	case BNEP_SVC_PANU:
+		if (src == BNEP_SVC_PANU ||  src == BNEP_SVC_GN ||
+							src == BNEP_SVC_NAP)
+			return 0;
+
+		return BNEP_CONN_INVALID_SRC;
+	}
+
+	return BNEP_CONN_INVALID_DST;
+}
+
+uint16_t bnep_setup_decode(struct bnep_setup_conn_req *req, uint16_t *dst,
+								uint16_t *src)
+{
+	const uint8_t bt_base[] = { 0x00, 0x00, 0x10, 0x00, 0x80, 0x00,
+					0x00, 0x80, 0x5F, 0x9B, 0x34, 0xFB };
+	uint8_t *dest, *source;
+	uint32_t val;
+
+	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;
+	}
+
+	return BNEP_SUCCESS;
+}
diff --git a/bluez/profiles/network/bnep.h b/bluez/profiles/network/bnep.h
new file mode 100644
index 0000000..bc43d4f
--- /dev/null
+++ b/bluez/profiles/network/bnep.h
@@ -0,0 +1,51 @@
+/*
+ *
+ *  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);
+
+uint16_t bnep_service_id(const char *svc);
+const char *bnep_uuid(uint16_t id);
+const char *bnep_name(uint16_t id);
+
+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);
+int bnep_connect(struct bnep *b, bnep_connect_cb conn_cb, void *data);
+typedef void (*bnep_disconnect_cb) (void *data);
+void bnep_set_disconnect(struct bnep *session, bnep_disconnect_cb disconn_cb,
+								void *data);
+void bnep_disconnect(struct bnep *session);
+
+int bnep_server_add(int sk, uint16_t dst, char *bridge, char *iface,
+							const bdaddr_t *addr);
+void bnep_server_delete(char *bridge, char *iface, const bdaddr_t *addr);
+
+ssize_t bnep_send_ctrl_rsp(int sk, uint8_t type, uint8_t ctrl, uint16_t resp);
+uint16_t bnep_setup_chk(uint16_t dst_role, uint16_t src_role);
+uint16_t bnep_setup_decode(struct bnep_setup_conn_req *req, uint16_t *dst,
+								uint16_t *src);
diff --git a/bluez/profiles/network/connection.c b/bluez/profiles/network/connection.c
new file mode 100644
index 0000000..cc73989
--- /dev/null
+++ b/bluez/profiles/network/connection.c
@@ -0,0 +1,576 @@
+/*
+ *
+ *  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 <bluetooth/bluetooth.h>
+#include <bluetooth/bnep.h>
+#include <bluetooth/sdp.h>
+
+#include <glib.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 "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_service_id(struct btd_service *service)
+{
+	return bnep_service_id(btd_service_get_profile(service)->remote_uuid);
+}
+
+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, nc);
+	if (perr < 0) {
+		error("bnep connect(): %s (%d)", strerror(-perr), -perr);
+		goto failed;
+	}
+
+	bnep_set_disconnect(nc->session, bnep_disconn_cb, nc);
+
+	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;
+	const char *uuid;
+	uint16_t id;
+	int err;
+
+	if (dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &svc,
+						DBUS_TYPE_INVALID) == FALSE)
+		return btd_error_invalid_args(msg);
+
+	id = bnep_service_id(svc);
+	uuid = bnep_uuid(id);
+
+	if (uuid == NULL)
+		return btd_error_invalid_args(msg);
+
+	service = btd_device_get_service(peer->device, uuid);
+	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 *service)
+{
+	struct network_conn *nc = btd_service_get_user_data(service);
+	struct network_peer *peer = nc->peer;
+	uint16_t id = get_service_id(service);
+	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 *service)
+{
+	struct network_conn *nc = btd_service_get_user_data(service);
+
+	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;
+	const char *uuid;
+
+	nc = find_connection_by_state(peer->connections, CONNECTED);
+	if (nc == NULL)
+		return FALSE;
+
+	uuid = bnep_uuid(nc->id);
+
+	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 *service)
+{
+	struct btd_device *device = btd_service_get_device(service);
+	struct network_conn *conn = btd_service_get_user_data(service);
+	struct network_peer *peer = conn->peer;
+	uint16_t id = get_service_id(service);
+
+	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 *service)
+{
+	struct btd_device *device = btd_service_get_device(service);
+	struct network_peer *peer;
+	struct network_conn *nc;
+	uint16_t id = get_service_id(service);
+
+	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(service);
+	nc->state = DISCONNECTED;
+	nc->peer = peer;
+
+	btd_service_set_user_data(service, nc);
+
+	DBG("id %u registered", id);
+
+	peer->connections = g_slist_append(peer->connections, nc);
+
+	return 0;
+}
diff --git a/bluez/profiles/network/connection.h b/bluez/profiles/network/connection.h
new file mode 100644
index 0000000..4a8b43b
--- /dev/null
+++ b/bluez/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/profiles/network/manager.c b/bluez/profiles/network/manager.c
new file mode 100644
index 0000000..0fe98a0
--- /dev/null
+++ b/bluez/profiles/network/manager.c
@@ -0,0 +1,212 @@
+/*
+ *
+ *  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 <bluetooth/bluetooth.h>
+#include <bluetooth/bnep.h>
+#include <bluetooth/sdp.h>
+
+#include <glib.h>
+#include <gdbus/gdbus.h>
+
+#include "src/log.h"
+#include "src/plugin.h"
+
+#include "lib/uuid.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/profiles/network/network.conf b/bluez/profiles/network/network.conf
new file mode 100644
index 0000000..5f11639
--- /dev/null
+++ b/bluez/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/profiles/network/server.c b/bluez/profiles/network/server.c
new file mode 100644
index 0000000..3fb031f
--- /dev/null
+++ b/bluez/profiles/network/server.c
@@ -0,0 +1,747 @@
+/*
+ *
+ *  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 <bluetooth/bluetooth.h>
+#include <bluetooth/bnep.h>
+#include <bluetooth/sdp.h>
+#include <bluetooth/sdp_lib.h>
+#include <netinet/in.h>
+
+#include <glib.h>
+#include <gdbus/gdbus.h>
+
+#include "btio/btio.h"
+#include "lib/uuid.h"
+#include "src/dbus-common.h"
+#include "src/adapter.h"
+#include "src/log.h"
+#include "src/error.h"
+#include "src/sdpd.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)
+{
+	for (; list; list = list->next) {
+		struct network_server *ns = list->data;
+
+		if (strcasecmp(uuid, bnep_uuid(ns->id)) == 0)
+			return ns;
+
+		if (strcasecmp(uuid, bnep_name(ns->id)) == 0)
+			return ns;
+	}
+
+	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)
+{
+	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 src_role, dst_role, rsp = BNEP_CONN_NOT_ALLOWED;
+	int n, sk;
+
+	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);
+
+	/* Reading BNEP_SETUP_CONNECTION_REQUEST_MSG */
+	n = read(sk, packet, sizeof(packet));
+	if (n < 0) {
+		error("read(): %s(%d)", strerror(errno), errno);
+		return FALSE;
+	}
+
+	/* Highest known Control command ID
+	 * is BNEP_FILTER_MULT_ADDR_RSP = 0x06 */
+	if (req->type == BNEP_CONTROL &&
+				req->ctrl > BNEP_FILTER_MULT_ADDR_RSP) {
+		uint8_t pkt[3];
+
+		pkt[0] = BNEP_CONTROL;
+		pkt[1] = BNEP_CMD_NOT_UNDERSTOOD;
+		pkt[2] = req->ctrl;
+
+		send(sk, pkt, sizeof(pkt), 0);
+
+		return FALSE;
+	}
+
+	if (req->type != BNEP_CONTROL || req->ctrl != BNEP_SETUP_CONN_REQ)
+		return FALSE;
+
+	rsp = bnep_setup_decode(req, &dst_role, &src_role);
+	if (rsp)
+		goto reply;
+
+	rsp = bnep_setup_chk(dst_role, src_role);
+	if (rsp)
+		goto reply;
+
+	rsp = BNEP_CONN_NOT_ALLOWED;
+
+	ns = find_server(na->servers, dst_role);
+	if (!ns) {
+		error("Server unavailable: (0x%x)", dst_role);
+		goto reply;
+	}
+
+	if (!ns->record_id) {
+		error("Service record not available");
+		goto reply;
+	}
+
+	if (!ns->bridge) {
+		error("Bridge interface not configured");
+		goto reply;
+	}
+
+	strncpy(na->setup->dev, BNEP_INTERFACE, 16);
+	na->setup->dev[15] = '\0';
+
+	if (bnep_server_add(sk, dst_role, ns->bridge, na->setup->dev,
+							&na->setup->dst) < 0)
+		goto reply;
+
+	na->setup = NULL;
+
+	rsp = BNEP_SUCCESS;
+
+reply:
+	bnep_send_ctrl_rsp(sk, BNEP_CONTROL, BNEP_SETUP_CONN_RSP, rsp);
+
+	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)
+		goto drop;
+
+	if (!ns->record_id)
+		goto drop;
+
+	if (!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/profiles/network/server.h b/bluez/profiles/network/server.h
new file mode 100644
index 0000000..a76e6f7
--- /dev/null
+++ b/bluez/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/profiles/proximity/immalert.c b/bluez/profiles/proximity/immalert.c
new file mode 100644
index 0000000..3d50b8d
--- /dev/null
+++ b/bluez/profiles/proximity/immalert.c
@@ -0,0 +1,288 @@
+/*
+ *
+ *  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 <gdbus/gdbus.h>
+
+#include "lib/uuid.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/profiles/proximity/immalert.h b/bluez/profiles/proximity/immalert.h
new file mode 100644
index 0000000..1a09fa9
--- /dev/null
+++ b/bluez/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/profiles/proximity/linkloss.c b/bluez/profiles/proximity/linkloss.c
new file mode 100644
index 0000000..476803a
--- /dev/null
+++ b/bluez/profiles/proximity/linkloss.c
@@ -0,0 +1,336 @@
+/*
+ *
+ *  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 <gdbus/gdbus.h>
+
+#include "lib/uuid.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/profiles/proximity/linkloss.h b/bluez/profiles/proximity/linkloss.h
new file mode 100644
index 0000000..0447def
--- /dev/null
+++ b/bluez/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/profiles/proximity/main.c b/bluez/profiles/proximity/main.c
new file mode 100644
index 0000000..d4fe419
--- /dev/null
+++ b/bluez/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 "src/hcid.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/profiles/proximity/manager.c b/bluez/profiles/proximity/manager.c
new file mode 100644
index 0000000..3f0f63c
--- /dev/null
+++ b/bluez/profiles/proximity/manager.c
@@ -0,0 +1,192 @@
+/*
+ *
+ *  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 <gdbus/gdbus.h>
+
+#include "lib/uuid.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/profiles/proximity/manager.h b/bluez/profiles/proximity/manager.h
new file mode 100644
index 0000000..e65c31d
--- /dev/null
+++ b/bluez/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/profiles/proximity/monitor.c b/bluez/profiles/proximity/monitor.c
new file mode 100644
index 0000000..f2e0739
--- /dev/null
+++ b/bluez/profiles/proximity/monitor.c
@@ -0,0 +1,819 @@
+/*
+ *
+ *  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 <gdbus/gdbus.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <glib.h>
+
+#include <bluetooth/bluetooth.h>
+
+#include "lib/uuid.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;
+
+	btd_device_unref(monitor->device);
+	g_free(monitor->linklosslevel);
+	g_free(monitor->immediatelevel);
+	g_free(monitor->signallevel);
+	g_free(monitor);
+
+	monitors = g_slist_remove(monitors, 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/profiles/proximity/monitor.h b/bluez/profiles/proximity/monitor.h
new file mode 100644
index 0000000..d9a40c6
--- /dev/null
+++ b/bluez/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/profiles/proximity/proximity.conf b/bluez/profiles/proximity/proximity.conf
new file mode 100644
index 0000000..417610f
--- /dev/null
+++ b/bluez/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/profiles/proximity/reporter.c b/bluez/profiles/proximity/reporter.c
new file mode 100644
index 0000000..3e4c741
--- /dev/null
+++ b/bluez/profiles/proximity/reporter.c
@@ -0,0 +1,269 @@
+/*
+ *
+ *  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 <gdbus/gdbus.h>
+
+#include "src/log.h"
+
+#include "lib/uuid.h"
+#include "src/dbus-common.h"
+#include "src/error.h"
+#include "src/adapter.h"
+#include "src/device.h"
+#include "src/profile.h"
+#include "src/service.h"
+#include "src/shared/util.h"
+#include "src/hcid.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/profiles/proximity/reporter.h b/bluez/profiles/proximity/reporter.h
new file mode 100644
index 0000000..a8e1aac
--- /dev/null
+++ b/bluez/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/profiles/sap/main.c b/bluez/profiles/sap/main.c
new file mode 100644
index 0000000..ad55cd2
--- /dev/null
+++ b/bluez/profiles/sap/main.c
@@ -0,0 +1,41 @@
+/*
+ *  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/profiles/sap/manager.c b/bluez/profiles/sap/manager.c
new file mode 100644
index 0000000..5c2a0f1
--- /dev/null
+++ b/bluez/profiles/sap/manager.c
@@ -0,0 +1,69 @@
+/*
+ *  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 "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/profiles/sap/manager.h b/bluez/profiles/sap/manager.h
new file mode 100644
index 0000000..6601a03
--- /dev/null
+++ b/bluez/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/profiles/sap/sap-dummy.c b/bluez/profiles/sap/sap-dummy.c
new file mode 100644
index 0000000..854105e
--- /dev/null
+++ b/bluez/profiles/sap/sap-dummy.c
@@ -0,0 +1,374 @@
+/*
+ *  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 <gdbus/gdbus.h>
+#include <stdint.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/profiles/sap/sap-u8500.c b/bluez/profiles/sap/sap-u8500.c
new file mode 100644
index 0000000..d043029
--- /dev/null
+++ b/bluez/profiles/sap/sap-u8500.c
@@ -0,0 +1,754 @@
+/*
+ *  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 <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/profiles/sap/sap.h b/bluez/profiles/sap/sap.h
new file mode 100644
index 0000000..16c333a
--- /dev/null
+++ b/bluez/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/profiles/sap/server.c b/bluez/profiles/sap/server.c
new file mode 100644
index 0000000..20c6cab
--- /dev/null
+++ b/bluez/profiles/sap/server.c
@@ -0,0 +1,1419 @@
+/*
+ *  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 <bluetooth/sdp.h>
+#include <bluetooth/sdp_lib.h>
+
+#include "lib/uuid.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/profiles/sap/server.h b/bluez/profiles/sap/server.h
new file mode 100644
index 0000000..d7e674a
--- /dev/null
+++ b/bluez/profiles/sap/server.h
@@ -0,0 +1,24 @@
+/*
+ *  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
+ */
+
+#include <gdbus/gdbus.h>
+
+int sap_server_register(struct btd_adapter *adapter);
+void sap_server_unregister(const char *path);
diff --git a/bluez/profiles/scanparam/scan.c b/bluez/profiles/scanparam/scan.c
new file mode 100644
index 0000000..156682f
--- /dev/null
+++ b/bluez/profiles/scanparam/scan.c
@@ -0,0 +1,282 @@
+/*
+ *
+ *  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/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 "attrib/att.h"
+#include "attrib/gattrib.h"
+#include "attrib/gatt.h"
+#include "src/attio.h"
+
+#define SCAN_PARAMETERS_UUID		"00001813-0000-1000-8000-00805f9b34fb"
+
+#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;
+	GAttrib *attrib;
+	struct att_range range;
+	guint attioid;
+	uint16_t interval;
+	uint16_t window;
+	uint16_t iwhandle;
+	uint16_t refresh_handle;
+	guint refresh_cb_id;
+};
+
+static void write_scan_params(GAttrib *attrib, uint16_t handle)
+{
+	uint8_t value[4];
+
+	put_le16(SCAN_INTERVAL, &value[0]);
+	put_le16(SCAN_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 scan *scan = user_data;
+
+	DBG("Server requires refresh: %d", pdu[3]);
+
+	if (pdu[3] == SERVER_REQUIRES_REFRESH)
+		write_scan_params(scan->attrib, scan->iwhandle);
+}
+
+static void ccc_written_cb(guint8 status, const guint8 *pdu,
+					guint16 plen, gpointer user_data)
+{
+	struct scan *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 discover_descriptor_cb(guint8 status, const guint8 *pdu,
+					guint16 len, gpointer user_data)
+{
+	struct scan *scan = user_data;
+	struct att_data_list *list;
+	uint8_t *ptr;
+	uint16_t uuid16, handle;
+	uint8_t value[2];
+	uint8_t format;
+
+	list = dec_find_info_resp(pdu, len, &format);
+	if (list == NULL)
+		return;
+
+	if (format != ATT_FIND_INFO_RESP_FMT_16BIT)
+		goto done;
+
+	ptr = list->data[0];
+	handle = get_le16(ptr);
+	uuid16 = get_le16(&ptr[2]);
+
+	if (uuid16 != GATT_CLIENT_CHARAC_CFG_UUID)
+		goto done;
+
+	put_le16(GATT_CLIENT_CHARAC_CFG_NOTIF_BIT, value);
+	gatt_write_char(scan->attrib, handle, value, sizeof(value),
+						ccc_written_cb, user_data);
+done:
+	att_data_list_free(list);
+}
+
+static void refresh_discovered_cb(uint8_t status, GSList *chars,
+								void *user_data)
+{
+	struct scan *scan = user_data;
+	struct gatt_char *chr;
+	uint16_t start, end;
+
+	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->range.end;
+
+	if (start > end)
+		return;
+
+	scan->refresh_handle = chr->value_handle;
+
+	gatt_discover_char_desc(scan->attrib, start, end,
+					discover_descriptor_cb, user_data);
+}
+
+static void iwin_discovered_cb(uint8_t status, GSList *chars, void *user_data)
+{
+	struct scan *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);
+}
+
+static void attio_connected_cb(GAttrib *attrib, gpointer user_data)
+{
+	struct scan *scan = user_data;
+	bt_uuid_t iwin_uuid, refresh_uuid;
+
+	scan->attrib = g_attrib_ref(attrib);
+
+	if (scan->iwhandle) {
+		write_scan_params(scan->attrib, scan->iwhandle);
+		return;
+	}
+
+	bt_uuid16_create(&iwin_uuid, SCAN_INTERVAL_WIN_UUID);
+	bt_uuid16_create(&refresh_uuid, SCAN_REFRESH_UUID);
+
+	gatt_discover_char(scan->attrib, scan->range.start, scan->range.end,
+					&iwin_uuid, iwin_discovered_cb, scan);
+
+	gatt_discover_char(scan->attrib, scan->range.start, scan->range.end,
+				&refresh_uuid, refresh_discovered_cb, scan);
+}
+
+static void attio_disconnected_cb(gpointer user_data)
+{
+	struct scan *scan = user_data;
+
+	g_attrib_unref(scan->attrib);
+	scan->attrib = NULL;
+}
+
+static int scan_register(struct btd_service *service, struct gatt_primary *prim)
+{
+	struct btd_device *device = btd_service_get_device(service);
+	struct scan *scan;
+
+	scan = g_new0(struct scan, 1);
+	scan->device = btd_device_ref(device);
+	scan->range = prim->range;
+	scan->attioid = btd_device_add_attio_callback(device,
+							attio_connected_cb,
+							attio_disconnected_cb,
+							scan);
+
+	btd_service_set_user_data(service, scan);
+
+	return 0;
+}
+
+static void scan_param_remove(struct btd_service *service)
+{
+	struct scan *scan = btd_service_get_user_data(service);
+
+	if (scan->attrib != NULL && scan->refresh_cb_id > 0)
+		g_attrib_unregister(scan->attrib, scan->refresh_cb_id);
+
+	btd_device_remove_attio_callback(scan->device, scan->attioid);
+	btd_device_unref(scan->device);
+	g_attrib_unref(scan->attrib);
+	g_free(scan);
+}
+
+static int scan_param_probe(struct btd_service *service)
+{
+	struct btd_device *device = btd_service_get_device(service);
+	struct gatt_primary *prim;
+
+	DBG("Probing Scan Parameters");
+
+	prim = btd_device_get_primary(device, SCAN_PARAMETERS_UUID);
+	if (!prim)
+		return -EINVAL;
+
+	return scan_register(service, prim);
+}
+
+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,
+};
+
+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/profiles/thermometer/thermometer.c b/bluez/profiles/thermometer/thermometer.c
new file mode 100644
index 0000000..20575c0
--- /dev/null
+++ b/bluez/profiles/thermometer/thermometer.c
@@ -0,0 +1,1336 @@
+/*
+ *
+ *  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 <gdbus/gdbus.h>
+
+#include "lib/uuid.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, const guint8 *pdu, guint16 len,
+							gpointer user_data)
+{
+	struct characteristic *ch = user_data;
+	struct att_data_list *list = NULL;
+	uint8_t format;
+	int i;
+
+	if (status != 0) {
+		error("Discover all characteristic descriptors failed [%s]: %s",
+					ch->uuid, att_ecode2str(status));
+		goto done;
+	}
+
+	list = dec_find_info_resp(pdu, len, &format);
+	if (list == NULL)
+		goto done;
+
+	if (format != ATT_FIND_INFO_RESP_FMT_16BIT)
+		goto done;
+
+	for (i = 0; i < list->num; i++) {
+		uint8_t *value;
+		uint16_t handle, uuid;
+
+		value = list->data[i];
+		handle = get_le16(value);
+		uuid = get_le16(value + 2);
+
+		process_thermometer_desc(ch, uuid, handle);
+	}
+
+done:
+	if (list != NULL)
+		att_data_list_free(list);
+	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_char_desc(t->attrib, start, end, 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/profiles/time/server.c b/bluez/profiles/time/server.c
new file mode 100644
index 0000000..1716a5e
--- /dev/null
+++ b/bluez/profiles/time/server.c
@@ -0,0 +1,279 @@
+/*
+ *
+ *  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 <time.h>
+#include <errno.h>
+#include <stdbool.h>
+
+#include "src/adapter.h"
+#include "src/device.h"
+#include "src/profile.h"
+#include "src/plugin.h"
+
+#include "lib/uuid.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/src/adapter.c b/bluez/src/adapter.c
new file mode 100644
index 0000000..e396a3d
--- /dev/null
+++ b/bluez/src/adapter.c
@@ -0,0 +1,6782 @@
+/*
+ *
+ *  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 <bluetooth/bluetooth.h>
+#include <bluetooth/hci.h>
+#include <bluetooth/sdp.h>
+#include <bluetooth/sdp_lib.h>
+
+#include <glib.h>
+#include <dbus/dbus.h>
+#include <gdbus/gdbus.h>
+
+#include "log.h"
+#include "textfile.h"
+
+#include "lib/uuid.h"
+#include "lib/mgmt.h"
+#include "src/shared/mgmt.h"
+#include "src/shared/util.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 "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)
+
+static DBusConnection *dbus_conn = NULL;
+
+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;
+
+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 watch_client {
+	struct btd_adapter *adapter;
+	char *owner;
+	guint watch;
+};
+
+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 */
+	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 *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 */
+
+	gboolean initialized;
+
+	GSList *pin_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;
+
+	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;
+	uint8_t appearance[3];
+	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;
+
+	g_dbus_emit_property_changed(dbus_conn, adapter->path,
+						ADAPTER_INTERFACE, "Class");
+
+	appearance[0] = rp->val[0];
+	appearance[1] = rp->val[1] & 0x1f;	/* removes service class */
+	appearance[2] = rp->val[2];
+
+	attrib_gap_set(adapter, GATT_CHARAC_APPEARANCE, appearance, 2);
+}
+
+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) {
+		error("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;
+
+	error("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 + 1];
+	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);
+	filename[PATH_MAX] = '\0';
+
+	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 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_PAIRABLE) {
+		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)) {
+		error("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) {
+		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->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;
+
+	error("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 (mgmt_send(adapter->mgmt, MGMT_OP_SET_DISCOVERABLE,
+				adapter->dev_id, sizeof(cp), &cp,
+				set_mode_complete, adapter, NULL) > 0)
+		return true;
+
+	error("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_PAIRABLE, 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;
+	}
+
+	g_dbus_emit_property_changed(dbus_conn, adapter->path,
+					ADAPTER_INTERFACE, "PairableTimeout");
+
+	if (!(adapter->current_settings & MGMT_SETTING_PAIRABLE))
+		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)) {
+		error("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) {
+		error("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)) {
+		error("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;
+
+	error("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;
+
+	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) {
+		error("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)) {
+		warn("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;
+
+	error("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) {
+		error("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)) {
+		warn("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;
+
+	error("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) {
+		error("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;
+
+	error("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;
+
+	btd_device_set_temporary(device, TRUE);
+
+	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_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)) {
+		error("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 = (1 << BDADDR_LE_PUBLIC) | (1 << BDADDR_LE_RANDOM);
+
+	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;
+	}
+
+	/*
+	 * 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;
+
+	if (status != MGMT_STATUS_SUCCESS) {
+		error("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) {
+		error("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 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)) {
+		error("Wrong size of start discovery return parameters");
+		return;
+	}
+
+	if (status == MGMT_STATUS_SUCCESS) {
+		adapter->discovery_type = rp->type;
+		adapter->discovery_enable = 0x01;
+
+		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_discovery cp;
+	uint8_t new_type;
+
+	DBG("");
+
+	adapter->discovery_idle_timeout = 0;
+
+	if (adapter->current_settings & MGMT_SETTING_BREDR)
+		new_type = (1 << BDADDR_BREDR);
+	else
+		new_type = 0;
+
+	if (adapter->current_settings & MGMT_SETTING_LE)
+		new_type |= (1 << BDADDR_LE_PUBLIC) | (1 << BDADDR_LE_RANDOM);
+
+	if (adapter->discovery_enable == 0x01) {
+		/*
+		 * If there is an already running discovery and it has the
+		 * same type, then just keep it.
+		 */
+		if (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.
+		 */
+		cp.type = adapter->discovery_type;
+
+		mgmt_send(adapter->mgmt, MGMT_OP_STOP_DISCOVERY,
+					adapter->dev_id, sizeof(cp), &cp,
+					NULL, NULL, NULL);
+	}
+
+	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;
+}
+
+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)) {
+		error("Too small discovering event");
+		return;
+	}
+
+	DBG("hci%u type %u discovering %u", adapter->dev_id,
+					ev->type, ev->discovering);
+
+	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) {
+		trigger_passive_scanning(adapter);
+		return;
+	}
+
+	if (adapter->discovery_suspended)
+		return;
+
+	switch (adapter->discovery_enable) {
+	case 0x00:
+		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->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(gpointer a)
+{
+	struct btd_device *dev = a;
+
+	device_set_rssi(dev, 0);
+}
+
+static void discovery_cleanup(struct btd_adapter *adapter)
+{
+	g_slist_free_full(adapter->discovery_found, invalidate_rssi);
+	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_adapter_remove_device(adapter, dev);
+	}
+
+	return FALSE;
+}
+
+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->discovery_list = g_slist_remove(adapter->discovery_list,
+								client);
+
+	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->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)
+		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);
+}
+
+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;
+	GSList *list;
+
+	DBG("sender %s", sender);
+
+	if (!(adapter->current_settings & MGMT_SETTING_POWERED))
+		return btd_error_not_ready(msg);
+
+	/*
+	 * Every client can only start one discovery, if the client
+	 * already started a discovery then return an error.
+	 */
+	list = g_slist_find_custom(adapter->discovery_list, sender,
+						compare_sender);
+	if (list)
+		return btd_error_busy(msg);
+
+	client = g_new0(struct watch_client, 1);
+
+	client->adapter = adapter;
+	client->owner = g_strdup(sender);
+	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.
+	 */
+	trigger_start_discovery(adapter, 0);
+
+	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);
+
+	/*
+	 * As long as other discovery clients are still active, just
+	 * return success.
+	 */
+	if (adapter->discovery_list)
+		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;
+
+		error("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:
+		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_PAIRABLE:
+		opcode = MGMT_OP_SET_PAIRABLE;
+		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:
+	error("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;
+
+	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_PAIRABLE, 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_PAIRABLE, 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);
+
+	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 gboolean property_get_uuids(const GDBusPropertyTable *property,
+					DBusMessageIter *iter, void *user_data)
+{
+	struct btd_adapter *adapter = user_data;
+	DBusMessageIter entry;
+	sdp_list_t *l;
+
+	dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY,
+					DBUS_TYPE_STRING_AS_STRING, &entry);
+
+	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;
+
+		dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING,
+								&uuid);
+		free(uuid);
+	}
+
+	dbus_message_iter_close_container(iter, &entry);
+
+	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("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 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) {
+		error("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) {
+		error("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)
+		error("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;
+
+	error("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) {
+		error("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) {
+		error("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) {
+		error("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) {
+		info("Load IRKs failed: Kernel doesn't support LE Privacy");
+		return;
+	}
+
+	if (status != MGMT_STATUS_SUCCESS) {
+		error("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) {
+		error("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)
+		error("Failed to IRKs for hci%u", adapter->dev_id);
+}
+
+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 load_devices(struct btd_adapter *adapter)
+{
+	char filename[PATH_MAX + 1];
+	char srcaddr[18];
+	GSList *keys = NULL;
+	GSList *ltks = NULL;
+	GSList *irks = NULL;
+	DIR *dir;
+	struct dirent *entry;
+
+	ba2str(&adapter->bdaddr, srcaddr);
+
+	snprintf(filename, PATH_MAX, STORAGEDIR "/%s", srcaddr);
+	filename[PATH_MAX] = '\0';
+
+	dir = opendir(filename);
+	if (!dir) {
+		error("Unable to open adapter storage directory: %s", filename);
+		return;
+	}
+
+	while ((entry = readdir(dir)) != NULL) {
+		struct btd_device *device;
+		char filename[PATH_MAX + 1];
+		GKeyFile *key_file;
+		struct link_key_info *key_info;
+		GSList *list, *ltk_info;
+		struct irk_info *irk_info;
+		uint8_t bdaddr_type;
+
+		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);
+
+		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 */
+
+		list = btd_device_get_uuids(device);
+		if (list)
+			device_probe_profiles(device, list);
+
+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);
+}
+
+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) {
+		error("%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) {
+		error("%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)) {
+		error("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) {
+		error("Failed to get connections: %s (0x%02x)",
+						mgmt_errstr(status), status);
+		return;
+	}
+
+	if (length < sizeof(*rp)) {
+		error("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) {
+		error("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;
+
+	error("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_PAIRABLE)
+		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;
+}
+
+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 (g_slist_find(adapter->connect_list, device)) {
+		DBG("ignoring already added device %s",
+						device_get_path(device));
+		return 0;
+	}
+
+	if (!(adapter->supported_settings & MGMT_SETTING_LE)) {
+		error("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);
+
+	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 (!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 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 + 1];
+	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);
+	filename[PATH_MAX] = '\0';
+	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 + 1];
+	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);
+		filename[PATH_MAX] = '\0';
+
+		err = stat(filename, &st);
+		if (err || !S_ISDIR(st.st_mode))
+			return;
+	}
+
+	snprintf(filename, PATH_MAX, STORAGEDIR "/%s/%s/info",
+			converter->address, key);
+	filename[PATH_MAX] = '\0';
+
+	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 + 1];
+	struct device_converter converter;
+
+	snprintf(filename, PATH_MAX, STORAGEDIR "/%s/%s", address, file);
+	filename[PATH_MAX] = '\0';
+
+	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 + 1];
+	GKeyFile *key_file;
+	char handle_str[11];
+	char *data;
+	gsize length = 0;
+
+	snprintf(filename, PATH_MAX, STORAGEDIR "/%s/cache/%s", local, peer);
+	filename[PATH_MAX] = '\0';
+
+	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 + 1];
+	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);
+	filename[PATH_MAX] = '\0';
+
+	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;
+
+	if (!gatt_parse_record(rec, &uuid, &psm, &start, &end))
+		goto failed;
+
+	snprintf(filename, PATH_MAX, STORAGEDIR "/%s/%s/attributes", src_addr,
+								dst_addr);
+	filename[PATH_MAX] = '\0';
+
+	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 + 1];
+	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);
+	filename[PATH_MAX] = '\0';
+
+	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);
+	filename[PATH_MAX] = '\0';
+
+	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 + 1];
+	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);
+	filename[PATH_MAX] = '\0';
+
+	err = stat(filename, &st);
+	if (err || !S_ISDIR(st.st_mode))
+		return;
+
+	snprintf(filename, PATH_MAX, STORAGEDIR "/%s/%s/ccc", src_addr,
+								dst_addr);
+	filename[PATH_MAX] = '\0';
+
+	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 + 1];
+	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);
+	filename[PATH_MAX] = '\0';
+
+	err = stat(filename, &st);
+	if (err || !S_ISDIR(st.st_mode))
+		return;
+
+	snprintf(filename, PATH_MAX, STORAGEDIR "/%s/%s/gatt", src_addr,
+								dst_addr);
+	filename[PATH_MAX] = '\0';
+
+	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 + 1];
+	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);
+	filename[PATH_MAX] = '\0';
+
+	err = stat(filename, &st);
+	if (err || !S_ISDIR(st.st_mode))
+		return;
+
+	snprintf(filename, PATH_MAX, STORAGEDIR "/%s/%s/proximity", src_addr,
+									key);
+	filename[PATH_MAX] = '\0';
+
+	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 + 1];
+	char address[18];
+
+	ba2str(&adapter->bdaddr, address);
+
+	/* Convert device's name cache */
+	snprintf(filename, PATH_MAX, STORAGEDIR "/%s/names", address);
+	filename[PATH_MAX] = '\0';
+	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);
+	filename[PATH_MAX] = '\0';
+	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);
+	filename[PATH_MAX] = '\0';
+	textfile_foreach(filename, convert_sdp_entry, address);
+
+	/* Convert ccc */
+	snprintf(filename, PATH_MAX, STORAGEDIR "/%s/ccc", address);
+	filename[PATH_MAX] = '\0';
+	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);
+	filename[PATH_MAX] = '\0';
+	textfile_foreach(filename, convert_gatt_entry, address);
+
+	/* Convert proximity */
+	snprintf(filename, PATH_MAX, STORAGEDIR "/%s/proximity", address);
+	filename[PATH_MAX] = '\0';
+	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 + 1];
+	int timeout;
+	uint8_t mode;
+	char *data;
+	gsize length = 0;
+
+	ba2str(&adapter->bdaddr, address);
+	snprintf(config_path, PATH_MAX, STORAGEDIR "/%s/config", address);
+	config_path[PATH_MAX] = '\0';
+
+	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 + 1];
+	char address[18];
+	char *converted;
+
+	ba2str(&adapter->bdaddr, address);
+
+	snprintf(filename, PATH_MAX, STORAGEDIR "/%s/config", address);
+	filename[PATH_MAX] = '\0';
+	converted = textfile_get(filename, "converted");
+	if (!converted)
+		return;
+
+	free(converted);
+
+	textfile_del(filename, "converted");
+
+	snprintf(filename, PATH_MAX, STORAGEDIR "/%s/names", address);
+	filename[PATH_MAX] = '\0';
+	textfile_del(filename, "converted");
+
+	snprintf(filename, PATH_MAX, STORAGEDIR "/%s/aliases", address);
+	filename[PATH_MAX] = '\0';
+	textfile_del(filename, "converted");
+
+	snprintf(filename, PATH_MAX, STORAGEDIR "/%s/trusts", address);
+	filename[PATH_MAX] = '\0';
+	textfile_del(filename, "converted");
+
+	snprintf(filename, PATH_MAX, STORAGEDIR "/%s/blocked", address);
+	filename[PATH_MAX] = '\0';
+	textfile_del(filename, "converted");
+
+	snprintf(filename, PATH_MAX, STORAGEDIR "/%s/profiles", address);
+	filename[PATH_MAX] = '\0';
+	textfile_del(filename, "converted");
+
+	snprintf(filename, PATH_MAX, STORAGEDIR "/%s/primaries", address);
+	filename[PATH_MAX] = '\0';
+	textfile_del(filename, "converted");
+
+	snprintf(filename, PATH_MAX, STORAGEDIR "/%s/linkkeys", address);
+	filename[PATH_MAX] = '\0';
+	textfile_del(filename, "converted");
+
+	snprintf(filename, PATH_MAX, STORAGEDIR "/%s/longtermkeys", address);
+	filename[PATH_MAX] = '\0';
+	textfile_del(filename, "converted");
+
+	snprintf(filename, PATH_MAX, STORAGEDIR "/%s/classes", address);
+	filename[PATH_MAX] = '\0';
+	textfile_del(filename, "converted");
+
+	snprintf(filename, PATH_MAX, STORAGEDIR "/%s/did", address);
+	filename[PATH_MAX] = '\0';
+	textfile_del(filename, "converted");
+
+	snprintf(filename, PATH_MAX, STORAGEDIR "/%s/sdp", address);
+	filename[PATH_MAX] = '\0';
+	textfile_del(filename, "converted");
+
+	snprintf(filename, PATH_MAX, STORAGEDIR "/%s/ccc", address);
+	filename[PATH_MAX] = '\0';
+	textfile_del(filename, "converted");
+
+	snprintf(filename, PATH_MAX, STORAGEDIR "/%s/appearances", address);
+	filename[PATH_MAX] = '\0';
+	textfile_del(filename, "converted");
+
+	snprintf(filename, PATH_MAX, STORAGEDIR "/%s/gatt", address);
+	filename[PATH_MAX] = '\0';
+	textfile_del(filename, "converted");
+
+	snprintf(filename, PATH_MAX, STORAGEDIR "/%s/proximity", address);
+	filename[PATH_MAX] = '\0';
+	textfile_del(filename, "converted");
+}
+
+static void load_config(struct btd_adapter *adapter)
+{
+	GKeyFile *key_file;
+	char filename[PATH_MAX + 1];
+	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);
+	filename[PATH_MAX] = '\0';
+
+	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;
+
+	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);
+	btd_adapter_gatt_server_stop(adapter);
+
+	g_slist_free(adapter->pin_callbacks);
+	adapter->pin_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;
+
+	error("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) {
+		error("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) {
+		warn("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) {
+		error("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 update_found_devices(struct btd_adapter *adapter,
+					const bdaddr_t *bdaddr,
+					uint8_t bdaddr_type, int8_t rssi,
+					bool confirm, bool legacy,
+					const uint8_t *data, uint8_t data_len)
+{
+	struct btd_device *dev;
+	struct eir_data eir_data;
+	bool name_known, discoverable;
+	struct device_addr_type addr_type;
+	char addr[18];
+	GSList *list;
+
+	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);
+
+	bacpy(&addr_type.bdaddr, bdaddr);
+	addr_type.bdaddr_type = bdaddr_type;
+
+	list = g_slist_find_custom(adapter->devices, &addr_type,
+							device_addr_type_cmp);
+	if (!list) {
+		/*
+		 * If no client has requested discovery, then do not
+		 * create new device objects.
+		 */
+		if (!adapter->discovery_list) {
+			eir_data_free(&eir_data);
+			return;
+		}
+
+		if (discoverable)
+			dev = adapter_create_device(adapter, bdaddr,
+								bdaddr_type);
+		else
+			dev = btd_adapter_find_device(adapter, bdaddr,
+								bdaddr_type);
+
+	} else
+		dev = list->data;
+
+	if (!dev) {
+		error("Unable to create object for found device %s", addr);
+		eir_data_free(&eir_data);
+		return;
+	}
+
+	device_update_last_seen(dev, bdaddr_type);
+
+	if (bdaddr_type != BDADDR_BREDR && !(eir_data.flags & EIR_BREDR_UNSUP))
+		device_set_bredr_support(dev, true);
+
+	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;
+	}
+
+	device_set_legacy(dev, legacy);
+	device_set_rssi(dev, rssi);
+
+	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);
+
+	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:
+	/*
+	 * 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 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)) {
+		error("Too short device found event (%u bytes)", length);
+		return;
+	}
+
+	eir_len = btohs(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 = 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,
+					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)) {
+		error("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->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->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);
+
+	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 TRUE;
+
+		if (device_is_trusted(device) == TRUE) {
+			auth->cb(NULL, auth->user_data);
+			goto next;
+		}
+
+		auth->agent = agent_get(NULL);
+		if (auth->agent == NULL) {
+			warn("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->auths->length != 1)
+		return;
+
+	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;
+
+	/* Device connected? */
+	if (!g_slist_find(adapter->connections, device))
+		error("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_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);
+}
+
+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)) {
+		error("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) {
+		error("Unable to get device object for %s", addr);
+		return;
+	}
+
+	err = device_confirm_passkey(device, btohl(ev->value),
+							ev->confirm_hint);
+	if (err < 0) {
+		error("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)) {
+		error("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) {
+		error("Unable to get device object for %s", addr);
+		return;
+	}
+
+	err = device_request_passkey(device);
+	if (err < 0) {
+		error("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)) {
+		error("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) {
+		error("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)
+		error("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)) {
+		error("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) {
+		error("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) {
+				error("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) {
+		error("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;
+
+	error("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)) {
+		error("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)) {
+		error("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) {
+		error("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) {
+		error("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 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);
+
+	bonding_attempt_complete(adapter, &addr->bdaddr, addr->type,
+						MGMT_STATUS_DISCONNECTED);
+}
+
+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) {
+		warn("Disconnecting failed: already disconnected");
+	} else if (status != MGMT_STATUS_SUCCESS) {
+		error("Failed to disconnect device: %s (0x%02x)",
+						mgmt_errstr(status), status);
+		return;
+	}
+
+	if (length < sizeof(*rp)) {
+		error("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)) {
+		error("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 + 1];
+	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);
+	filename[PATH_MAX] = '\0';
+
+	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)) {
+		error("Too small new link key event");
+		return;
+	}
+
+	ba2str(&addr->bdaddr, dst);
+
+	DBG("hci%u new key for %s type %u pin_len %u", adapter->dev_id,
+					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;
+	}
+
+	device = btd_adapter_get_device(adapter, &addr->bdaddr, addr->type);
+	if (!device) {
+		error("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);
+
+		if (device_is_temporary(device))
+			btd_device_set_temporary(device, FALSE);
+	}
+
+	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 + 1];
+	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);
+	filename[PATH_MAX] = '\0';
+
+	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)) {
+		error("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) {
+		error("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);
+
+		if (device_is_temporary(device))
+			btd_device_set_temporary(device, FALSE);
+	}
+
+	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,
+				uint8_t master)
+{
+	const char *group;
+	char adapter_addr[18];
+	char device_addr[18];
+	char filename[PATH_MAX + 1];
+	GKeyFile *key_file;
+	char key_str[33];
+	gsize length = 0;
+	char *str;
+	int i;
+
+	if (master == 0x00)
+		group = "LocalSignatureKey";
+	else if (master == 0x01)
+		group = "RemoteSignatureKey";
+	else {
+		warn("Unsupported CSRK type %u", master);
+		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);
+
+	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)) {
+		error("Too small CSRK event");
+		return;
+	}
+
+	ba2str(&addr->bdaddr, dst);
+
+	DBG("hci%u new CSRK for %s master %u", adapter->dev_id, dst,
+								ev->key.master);
+
+	device = btd_adapter_get_device(adapter, &addr->bdaddr, addr->type);
+	if (!device) {
+		error("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,
+								key->master);
+
+	if (device_is_temporary(device))
+		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 + 1];
+	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);
+	filename[PATH_MAX] = '\0';
+
+	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)) {
+		error("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) {
+		error("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);
+
+	if (device_is_temporary(device))
+		btd_device_set_temporary(device, FALSE);
+}
+
+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.hash, hash, 16);
+
+	if (randomizer)
+		memcpy(cp.randomizer, 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) {
+		error("Read local OOB data failed: %s (0x%02x)",
+						mgmt_errstr(status), status);
+		hash = NULL;
+		randomizer = NULL;
+	} else if (length < sizeof(*rp)) {
+		error("Too small read local OOB data response");
+		return;
+	} else {
+		hash = rp->hash;
+		randomizer = rp->randomizer;
+	}
+
+	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 int adapter_register(struct btd_adapter *adapter)
+{
+	struct agent *agent;
+
+	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)) {
+		error("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);
+	}
+
+	btd_adapter_gatt_server_start(adapter);
+
+	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)) {
+		error("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];
+
+	if (length < sizeof(*ev)) {
+		error("Too small device connected event");
+		return;
+	}
+
+	eir_len = btohs(ev->eir_len);
+	if (length < sizeof(*ev) + eir_len) {
+		error("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) {
+		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);
+
+	if (eir_data.class != 0)
+		device_set_class(device, eir_data.class);
+
+	adapter_add_connection(adapter, device, ev->addr.type);
+
+	if (eir_data.name != NULL) {
+		device_store_cached_name(device, eir_data.name);
+		btd_device_device_set_name(device, eir_data.name);
+	}
+
+	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)) {
+		error("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)) {
+		error("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 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)) {
+		error("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) {
+		/* 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 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)) {
+		error("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) {
+		warn("No device object for unpaired device %s", addr);
+		return;
+	}
+
+	btd_device_set_temporary(device, TRUE);
+
+	if (btd_device_is_connected(device))
+		device_request_disconnect(device, NULL);
+	else
+		btd_adapter_remove_device(adapter, device);
+}
+
+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;
+	int err;
+
+	DBG("index %u status 0x%02x", adapter->dev_id, status);
+
+	if (status != MGMT_STATUS_SUCCESS) {
+		error("Failed to read info for index %u: %s (0x%02x)",
+				adapter->dev_id, mgmt_errstr(status), status);
+		goto failed;
+	}
+
+	if (length < sizeof(*rp)) {
+		error("Too small read info complete response");
+		goto failed;
+	}
+
+	if (bacmp(&rp->bdaddr, BDADDR_ANY) == 0) {
+		error("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 = btohs(rp->supported_settings);
+	adapter->current_settings = btohs(rp->current_settings);
+
+	clear_uuids(adapter);
+
+	err = adapter_register(adapter);
+	if (err < 0) {
+		error("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_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->supported_settings & MGMT_SETTING_SSP) &&
+			!(adapter->current_settings & MGMT_SETTING_SSP))
+		set_mode(adapter, MGMT_OP_SET_SSP, 0x01);
+
+	if ((adapter->supported_settings & MGMT_SETTING_LE) &&
+			!(adapter->current_settings & MGMT_SETTING_LE))
+		set_mode(adapter, MGMT_OP_SET_LE, 0x01);
+
+	set_mode(adapter, MGMT_OP_SET_PAIRABLE, 0x01);
+	set_mode(adapter, MGMT_OP_SET_CONNECTABLE, 0x01);
+
+	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) {
+		warn("Ignoring index added for an already existing adapter");
+		return;
+	}
+
+	adapter = btd_adapter_new(index);
+	if (!adapter) {
+		error("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;
+
+	error("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;
+
+	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);
+}
+
+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();
+}
diff --git a/bluez/src/adapter.h b/bluez/src/adapter.h
new file mode 100644
index 0000000..49eca11
--- /dev/null
+++ b/bluez/src/adapter.h
@@ -0,0 +1,201 @@
+/*
+ *
+ *  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 <bluetooth/bluetooth.h>
+#include <bluetooth/sdp.h>
+#include <bluetooth/hci.h>
+#include <bluetooth/hci_lib.h>
+#include <dbus/dbus.h>
+#include <glib.h>
+#include <stdbool.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);
+
+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);
+
+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);
+
+/* 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 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);
diff --git a/bluez/src/agent.c b/bluez/src/agent.c
new file mode 100644
index 0000000..4a2f606
--- /dev/null
+++ b/bluez/src/agent.c
@@ -0,0 +1,1023 @@
+/*
+ *
+ *  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 <bluetooth/bluetooth.h>
+#include <bluetooth/sdp.h>
+
+#include <glib.h>
+#include <dbus/dbus.h>
+#include <gdbus/gdbus.h>
+
+#include "log.h"
+#include "error.h"
+#include "dbus-common.h"
+#include "adapter.h"
+#include "device.h"
+#include "agent.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;
+static struct agent *default_agent = 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 void set_default_agent(struct agent *agent)
+{
+	if (default_agent == agent)
+		return;
+
+	if (agent)
+		DBG("Default agent set to %s %s", agent->owner, agent->path);
+	else
+		DBG("Default agent cleared");
+
+	default_agent = agent;
+
+	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;
+	}
+
+	if (agent == default_agent)
+		set_default_agent(NULL);
+
+	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;
+		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 (default_agent)
+		return agent_ref(default_agent);
+
+	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");
+			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);
+	}
+
+	agent_unref(agent);
+}
+
+static uint8_t parse_io_capability(const char *capability)
+{
+	if (g_str_equal(capability, ""))
+		return IO_CAPABILITY_DISPLAYYESNO;
+	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);
+
+	set_default_agent(agent);
+
+	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);
+
+	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");
+
+	set_default_agent(NULL);
+	g_hash_table_destroy(agent_list);
+}
diff --git a/bluez/src/agent.h b/bluez/src/agent.h
new file mode 100644
index 0000000..1e46920
--- /dev/null
+++ b/bluez/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/src/attio.h b/bluez/src/attio.h
new file mode 100644
index 0000000..16e2873
--- /dev/null
+++ b/bluez/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/src/attrib-server.c b/bluez/src/attrib-server.c
new file mode 100644
index 0000000..ed843d0
--- /dev/null
+++ b/bluez/src/attrib-server.c
@@ -0,0 +1,1664 @@
+/*
+ *
+ *  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 <bluetooth/bluetooth.h>
+#include <bluetooth/sdp.h>
+#include <bluetooth/sdp_lib.h>
+
+#include <gdbus/gdbus.h>
+
+#include "lib/uuid.h"
+#include "btio/btio.h"
+#include "log.h"
+#include "sdpd.h"
+#include "hcid.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 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 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 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 */
+		put_uuid_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 int channel_id_cmp(gconstpointer data, gconstpointer user_data)
+{
+	const struct gatt_channel *channel = data;
+	guint id = GPOINTER_TO_UINT(user_data);
+
+	return channel->id - id;
+}
+
+gboolean attrib_channel_detach(GAttrib *attrib, guint id)
+{
+	struct gatt_server *server;
+	struct gatt_channel *channel;
+	GError *gerr = NULL;
+	GIOChannel *io;
+	bdaddr_t src;
+	GSList *l;
+
+	io = g_attrib_get_channel(attrib);
+
+	bt_io_get(io, &gerr, BT_IO_OPT_SOURCE_BDADDR, &src, BT_IO_OPT_INVALID);
+
+	if (gerr != NULL) {
+		error("bt_io_get: %s", gerr->message);
+		g_error_free(gerr);
+		return FALSE;
+	}
+
+	server = find_gatt_server(&src);
+	if (server == NULL)
+		return FALSE;
+
+	l = g_slist_find_custom(server->clients, GUINT_TO_POINTER(id),
+								channel_id_cmp);
+	if (!l)
+		return FALSE;
+
+	channel = l->data;
+
+	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_attrib(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)
+{
+	g_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/src/attrib-server.h b/bluez/src/attrib-server.h
new file mode 100644
index 0000000..063cb66
--- /dev/null
+++ b/bluez/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/src/bluetooth.conf b/bluez/src/bluetooth.conf
new file mode 100644
index 0000000..ad8891a
--- /dev/null
+++ b/bluez/src/bluetooth.conf
@@ -0,0 +1,38 @@
+<!-- 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.freedesktop.DBus.ObjectManager"/>
+  </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/src/bluetooth.service.in b/bluez/src/bluetooth.service.in
new file mode 100644
index 0000000..35e9457
--- /dev/null
+++ b/bluez/src/bluetooth.service.in
@@ -0,0 +1,17 @@
+[Unit]
+Description=Bluetooth service
+Documentation=man:bluetoothd(8)
+
+[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/src/bluetooth.ver b/bluez/src/bluetooth.ver
new file mode 100644
index 0000000..214fa8a
--- /dev/null
+++ b/bluez/src/bluetooth.ver
@@ -0,0 +1,12 @@
+{
+	global:
+		btd_*;
+		g_dbus_*;
+		info;
+		error;
+		debug;
+		baswap;
+		ba2str;
+	local:
+		*;
+};
diff --git a/bluez/src/bluetoothd.8.in b/bluez/src/bluetoothd.8.in
new file mode 100644
index 0000000..0bf3914
--- /dev/null
+++ b/bluez/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/src/dbus-common.c b/bluez/src/dbus-common.c
new file mode 100644
index 0000000..d466a8f
--- /dev/null
+++ b/bluez/src/dbus-common.c
@@ -0,0 +1,228 @@
+/*
+ *
+ *  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_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);
+}
+
+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);
+}
+
+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/src/dbus-common.h b/bluez/src/dbus-common.h
new file mode 100644
index 0000000..f331b2f
--- /dev/null
+++ b/bluez/src/dbus-common.h
@@ -0,0 +1,34 @@
+/* *
+ *  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_entry(DBusMessageIter *dict,
+			const char *key, int type, void *val);
+
+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/src/device.c b/bluez/src/device.c
new file mode 100644
index 0000000..303e3e1
--- /dev/null
+++ b/bluez/src/device.c
@@ -0,0 +1,4904 @@
+/*
+ *
+ *  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 <bluetooth/bluetooth.h>
+#include <bluetooth/sdp.h>
+#include <bluetooth/sdp_lib.h>
+
+#include <glib.h>
+#include <dbus/dbus.h>
+#include <gdbus/gdbus.h>
+
+#include "log.h"
+
+#include "btio/btio.h"
+#include "lib/uuid.h"
+#include "lib/mgmt.h"
+#include "attrib/att.h"
+#include "hcid.h"
+#include "adapter.h"
+#include "attrib/gattrib.h"
+#include "attio.h"
+#include "device.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"
+
+#define IO_CAPABILITY_NOINPUTNOOUTPUT	0x03
+
+#define DISCONNECT_TIMER	2
+#define DISCOVERY_TIMER		1
+
+static DBusConnection *dbus_conn = NULL;
+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 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;
+	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 */
+	gboolean	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;
+	guint		attachid;		/* Attrib server attach */
+
+	struct bearer_state bredr_state;
+	struct bearer_state le_state;
+
+	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;
+
+	GIOChannel	*att_io;
+	guint		cleanup_id;
+	guint		store_id;
+};
+
+static const uint16_t uuid_list[] = {
+	L2CAP_UUID,
+	PNP_INFO_SVCLASS_ID,
+	PUBLIC_BROWSE_GROUP,
+	0
+};
+
+static int device_browse_primary(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 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 gboolean store_device_info_cb(gpointer user_data)
+{
+	struct btd_device *device = user_data;
+	GKeyFile *key_file;
+	char filename[PATH_MAX + 1];
+	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);
+	filename[PATH_MAX] = '\0';
+
+	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);
+	}
+
+	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 + 1];
+	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);
+	filename[PATH_MAX] = '\0';
+	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 attio_cleanup(struct btd_device *device)
+{
+	if (device->attachid) {
+		attrib_channel_detach(device->attrib, device->attachid);
+		device->attachid = 0;
+	}
+
+	if (device->cleanup_id) {
+		g_source_remove(device->cleanup_id);
+		device->cleanup_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;
+		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;
+
+	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;
+
+	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);
+
+	attio_cleanup(device);
+
+	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->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 *empty = "", *ptr;
+
+	ptr = device->name ?: empty;
+	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_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 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)
+		err = btd_adapter_unblock_address(device->adapter,
+					&device->bdaddr, device->bdaddr_type);
+
+	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 discover_services_req_exit(DBusConnection *conn, void *user_data)
+{
+	struct browse_req *req = user_data;
+
+	DBG("DiscoverServices 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->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);
+}
+
+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_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;
+
+		if (btd_service_connect(service) == 0)
+			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)
+		return;
+
+	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:
+	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);
+	}
+
+	g_slist_free(dev->pending);
+	dev->pending = NULL;
+
+	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 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;
+}
+
+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)
+		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_primary(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 (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)
+		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);
+
+		dev->disable_auto_connect = FALSE;
+
+		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 device_svc_resolved(struct btd_device *dev, uint8_t bdaddr_type,
+								int err)
+{
+	struct bearer_state *state = get_state(dev, bdaddr_type);
+	DBusMessage *reply;
+	struct browse_req *req = dev->browse;
+
+	DBG("%s err %d", dev->path, err);
+
+	state->svc_resolved = true;
+	dev->browse = NULL;
+
+	/* 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 (!req || !req->msg)
+		return;
+
+	if (dbus_message_is_method_call(req->msg, DEVICE_INTERFACE,
+								"Pair")) {
+		g_dbus_send_reply(dbus_conn, req->msg, DBUS_TYPE_INVALID);
+		return;
+	}
+
+	if (err) {
+		reply = btd_error_failed(req->msg, strerror(-err));
+		g_dbus_send_message(dbus_conn, reply);
+		return;
+	}
+
+	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
+		return;
+
+	dbus_message_unref(req->msg);
+	req->msg = NULL;
+
+	if (reply)
+		g_dbus_send_message(dbus_conn, reply);
+}
+
+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)
+			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");
+	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_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 },
+	{ }
+};
+
+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) {
+		dev->bredr = true;
+	} else {
+		dev->le = true;
+		dev->bdaddr_type = 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 + 1];
+	GKeyFile *key_file;
+	char *str = NULL;
+	int len;
+
+	snprintf(filename, PATH_MAX, STORAGEDIR "/%s/cache/%s", local, peer);
+	filename[PATH_MAX] = '\0';
+
+	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 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);
+	}
+
+	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 + 1];
+	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);
+	filename[PATH_MAX] = '\0';
+
+	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 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;
+
+	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);
+
+	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;
+	}
+
+	str2ba(address, &device->bdaddr);
+	device->adapter = adapter;
+
+	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, bool bredr)
+{
+	device->bredr = bredr;
+}
+
+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 + 1];
+
+	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;
+
+		snprintf(filename, PATH_MAX, "%s/%s", dirname, entry->d_name);
+		filename[PATH_MAX] = '\0';
+
+		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 + 1];
+	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);
+	filename[PATH_MAX] = '\0';
+	delete_folder_tree(filename);
+
+	snprintf(filename, PATH_MAX, STORAGEDIR "/%s/cache/%s", adapter_addr,
+			device_addr);
+	filename[PATH_MAX] = '\0';
+
+	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);
+}
+
+int device_addr_type_cmp(gconstpointer a, gconstpointer b)
+{
+	const struct btd_device *dev = a;
+	const struct device_addr_type *addr = b;
+
+	DBG("%d, %d, %p, %p", addr->bdaddr_type, dev->bdaddr_type, dev->bredr, dev->le);
+	return 0;
+
+	if (addr->bdaddr_type == BDADDR_BREDR) {
+		if (!dev->bredr)
+			return -1;
+
+		return bacmp(&dev->bdaddr, &addr->bdaddr);
+	}
+
+	if (!dev->le)
+	       return -1;
+
+	if (addr->bdaddr_type != dev->bdaddr_type)
+		return -1;
+
+	return bacmp(&dev->bdaddr, &addr->bdaddr);
+}
+
+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;
+}
+
+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;
+}
+
+struct probe_data {
+	struct btd_device *dev;
+	GSList *uuids;
+};
+
+static void dev_probe(struct btd_profile *p, void *user_data)
+{
+	struct probe_data *d = user_data;
+	struct btd_service *service;
+
+	if (p->device_probe == NULL)
+		return;
+
+	if (!device_match_profile(d->dev, p, d->uuids))
+		return;
+
+	service = service_create(d->dev, p);
+
+	if (service_probe(service) < 0) {
+		btd_service_unref(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;
+
+	if (profile->device_probe == NULL)
+		return;
+
+	if (!device_match_profile(device, profile, device->uuids))
+		return;
+
+	service = service_create(device, profile);
+
+	if (service_probe(service) < 0) {
+		btd_service_unref(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 };
+	GSList *l;
+	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:
+	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;
+
+		device->uuids = g_slist_insert_sorted(device->uuids,
+						g_strdup(l->data),
+						bt_uuid_strcmp);
+	}
+
+	g_dbus_emit_property_changed(dbus_conn, device->path,
+						DEVICE_INTERFACE, "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 + 1];
+	char att_file[PATH_MAX + 1];
+	GKeyFile *sdp_key_file = NULL;
+	GKeyFile *att_key_file = NULL;
+	char *data;
+	gsize length = 0;
+
+	ba2str(btd_adapter_get_address(device->adapter), srcaddr);
+	ba2str(&device->bdaddr, dstaddr);
+
+	if (!device->temporary) {
+		snprintf(sdp_file, PATH_MAX, STORAGEDIR "/%s/cache/%s",
+							srcaddr, dstaddr);
+		sdp_file[PATH_MAX] = '\0';
+
+		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_file[PATH_MAX] = '\0';
+
+		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_services(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 device_register_primaries(struct btd_device *device,
+						GSList *prim_list, int psm)
+{
+	device->primaries = g_slist_concat(device->primaries, 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);
+
+	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);
+	browse_request_free(req);
+}
+
+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 void store_services(struct btd_device *device)
+{
+	struct btd_adapter *adapter = device->adapter;
+	char filename[PATH_MAX + 1];
+	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);
+	filename[PATH_MAX] = '\0';
+
+	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);
+}
+
+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;
+
+	if (attio->cfunc)
+		attio->cfunc(attrib, attio->user_data);
+}
+
+static void attio_disconnected(gpointer data, gpointer user_data)
+{
+	struct attio_data *attio = data;
+
+	if (attio->dcfunc)
+		attio->dcfunc(attio->user_data);
+}
+
+static gboolean attrib_disconnected_cb(GIOChannel *io, GIOCondition cond,
+							gpointer user_data)
+{
+	struct btd_device *device = user_data;
+	int sock, err = 0;
+	socklen_t len;
+
+	if (device->browse)
+		goto done;
+
+	sock = g_io_channel_unix_get_fd(io);
+	len = sizeof(err);
+	getsockopt(sock, SOL_SOCKET, SO_ERROR, &err, &len);
+
+	DBG("%s (%d)", strerror(err), err);
+
+	g_slist_foreach(device->attios, attio_disconnected, NULL);
+
+	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);
+
+	return FALSE;
+}
+
+static void register_all_services(struct browse_req *req, GSList *services)
+{
+	struct btd_device *device = req->device;
+
+	btd_device_set_temporary(device, FALSE);
+
+	update_gatt_services(req, device->primaries, services);
+	g_slist_free_full(device->primaries, g_free);
+	device->primaries = NULL;
+
+	device_register_primaries(device, services, -1);
+
+	device_probe_profiles(device, req->profiles_added);
+
+	if (device->attios == NULL && device->attios_offline == NULL)
+		attio_cleanup(device);
+
+	g_dbus_emit_property_changed(dbus_conn, device->path,
+						DEVICE_INTERFACE, "UUIDs");
+
+	device_svc_resolved(device, device->bdaddr_type, 0);
+
+	store_services(device);
+
+	browse_request_free(req);
+}
+
+static int service_by_range_cmp(gconstpointer a, gconstpointer b)
+{
+	const struct gatt_primary *prim = a;
+	const struct att_range *range = b;
+
+	return memcmp(&prim->range, range, sizeof(*range));
+}
+
+static void send_le_browse_response(struct browse_req *req)
+{
+	struct btd_device *dev = req->device;
+	struct bearer_state *state = &dev->le_state;
+	DBusMessage *reply, *msg = req->msg;
+
+	if (!msg)
+		return;
+
+	if (!dbus_message_is_method_call(msg, DEVICE_INTERFACE, "Pair")) {
+		reply = btd_error_failed(msg, "Service discovery failed");
+		g_dbus_send_message(dbus_conn, reply);
+		return;
+	}
+
+	if (!state->paired) {
+		reply = btd_error_failed(msg, "Not paired");
+		g_dbus_send_message(dbus_conn, reply);
+		return;
+	}
+
+	if (!dev->bredr_state.paired && dev->pending_paired) {
+		g_dbus_emit_property_changed(dbus_conn, dev->path,
+						DEVICE_INTERFACE, "Paired");
+		dev->pending_paired = false;
+	}
+
+	g_dbus_send_reply(dbus_conn, msg, DBUS_TYPE_INVALID);
+}
+
+static void find_included_cb(uint8_t status, GSList *includes, void *user_data)
+{
+	struct included_search *search = user_data;
+	struct btd_device *device = search->req->device;
+	struct gatt_primary *prim;
+	GSList *l;
+
+	DBG("status %u", status);
+
+	if (device->attrib == NULL || status) {
+		struct browse_req *req = device->browse;
+
+		if (status)
+			error("Find included services failed: %s (%d)",
+					att_ecode2str(status), status);
+		else
+			error("Disconnected while doing included discovery");
+
+		if (!req)
+			goto complete;
+
+		send_le_browse_response(req);
+		device->browse = NULL;
+		browse_request_free(req);
+
+		goto complete;
+	}
+
+	if (includes == NULL)
+		goto next;
+
+	for (l = includes; l; l = l->next) {
+		struct gatt_included *incl = l->data;
+
+		if (g_slist_find_custom(search->services, &incl->range,
+						service_by_range_cmp))
+			continue;
+
+		prim = g_new0(struct gatt_primary, 1);
+		memcpy(prim->uuid, incl->uuid, sizeof(prim->uuid));
+		memcpy(&prim->range, &incl->range, sizeof(prim->range));
+
+		search->services = g_slist_append(search->services, prim);
+	}
+
+next:
+	search->current = search->current->next;
+	if (search->current == NULL) {
+		register_all_services(search->req, search->services);
+		search->services = NULL;
+		goto complete;
+	}
+
+	prim = search->current->data;
+	gatt_find_included(device->attrib, prim->range.start, prim->range.end,
+					find_included_cb, search);
+	return;
+
+complete:
+	g_slist_free_full(search->services, g_free);
+	g_free(search);
+}
+
+static void find_included_services(struct browse_req *req, GSList *services)
+{
+	struct btd_device *device = req->device;
+	struct included_search *search;
+	struct gatt_primary *prim;
+	GSList *l;
+
+	DBG("service count %u", g_slist_length(services));
+
+	if (services == NULL) {
+		DBG("No services found");
+		register_all_services(req, NULL);
+		return;
+	}
+
+	search = g_new0(struct included_search, 1);
+	search->req = req;
+
+	/* We have to completely duplicate the data in order to have a
+	 * clearly defined responsibility of freeing regardless of
+	 * failure or success. Otherwise memory leaks are inevitable.
+	 */
+	for (l = services; l; l = g_slist_next(l)) {
+		struct gatt_primary *dup;
+
+		dup = g_memdup(l->data, sizeof(struct gatt_primary));
+
+		search->services = g_slist_append(search->services, dup);
+	}
+
+	search->current = search->services;
+
+	prim = search->current->data;
+	gatt_find_included(device->attrib, prim->range.start, prim->range.end,
+					find_included_cb, search);
+}
+
+static void primary_cb(uint8_t status, GSList *services, void *user_data)
+{
+	struct browse_req *req = user_data;
+
+	DBG("status %u", status);
+
+	if (status) {
+		struct btd_device *device = req->device;
+
+		send_le_browse_response(req);
+		device->browse = NULL;
+		browse_request_free(req);
+		return;
+	}
+
+	find_included_services(req, services);
+}
+
+bool device_attach_attrib(struct btd_device *dev, GIOChannel *io)
+{
+	GAttrib *attrib;
+
+	attrib = g_attrib_new(io);
+	if (!attrib) {
+		error("Unable to create new GAttrib instance");
+		return false;
+	}
+
+	dev->attachid = attrib_channel_attach(attrib);
+	if (dev->attachid == 0) {
+		g_attrib_unref(attrib);
+		error("Attribute server attach failure!");
+		return false;
+	}
+
+	dev->attrib = attrib;
+	dev->cleanup_id = g_io_add_watch(io, G_IO_HUP,
+					attrib_disconnected_cb, 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_attrib(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)
+			device_browse_primary(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);
+	}
+}
+
+static void att_success_cb(gpointer user_data)
+{
+	struct att_callbacks *attcb = user_data;
+	struct btd_device *device = attcb->user_data;
+
+	if (device->attios == NULL)
+		return;
+
+	/*
+	 * 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(device->adapter, device);
+
+	g_slist_foreach(device->attios, attio_connected, device->attrib);
+}
+
+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->success = att_success_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;
+
+	send_le_browse_response(req);
+	device->browse = NULL;
+	browse_request_free(req);
+}
+
+static void att_browse_cb(gpointer user_data)
+{
+	struct att_callbacks *attcb = user_data;
+	struct btd_device *device = attcb->user_data;
+
+	gatt_discover_primary(device->attrib, NULL, primary_cb,
+							device->browse);
+}
+
+static int device_browse_primary(struct btd_device *device, DBusMessage *msg)
+{
+	struct btd_adapter *adapter = device->adapter;
+	struct att_callbacks *attcb;
+	struct browse_req *req;
+
+	if (device->browse)
+		return -EBUSY;
+
+	req = g_new0(struct browse_req, 1);
+	req->device = device;
+
+	device->browse = req;
+
+	if (device->attrib) {
+		gatt_discover_primary(device->attrib, NULL, primary_cb, req);
+		goto done;
+	}
+
+	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;
+	}
+
+done:
+
+	if (msg) {
+		const char *sender = dbus_message_get_sender(msg);
+
+		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,
+						sender,
+						discover_services_req_exit,
+						req, NULL);
+	}
+
+	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;
+
+	if (device->browse)
+		return -EBUSY;
+
+	req = g_new0(struct browse_req, 1);
+	req->device = device;
+	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;
+	}
+
+	device->browse = req;
+
+	if (msg) {
+		const char *sender = dbus_message_get_sender(msg);
+
+		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,
+						sender,
+						discover_services_req_exit,
+						req, NULL);
+	}
+
+	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_primary(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, gboolean temporary)
+{
+	if (!device)
+		return;
+
+	if (device->temporary == temporary)
+		return;
+
+	DBG("temporary %d", temporary);
+
+	if (temporary)
+		adapter_connect_list_remove(device->adapter, device);
+
+	device->temporary = temporary;
+}
+
+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;
+}
+
+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(struct btd_device *device, int8_t rssi)
+{
+	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 8 dBm or more */
+		if (delta < 8)
+			return;
+
+		DBG("rssi %d delta %d", rssi, delta);
+
+		device->rssi = rssi;
+	}
+
+	g_dbus_emit_property_changed(dbus_conn, device->path,
+						DEVICE_INTERFACE, "RSSI");
+}
+
+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);
+
+	device->auto_connect = enable;
+
+	/* Disabling auto connect */
+	if (enable == FALSE) {
+		adapter_connect_list_remove(device->adapter, device);
+		return;
+	}
+
+	if (device->attrib) {
+		DBG("Already connected");
+		return;
+	}
+
+	/* Enabling auto connect */
+	adapter_connect_list_add(device->adapter, device);
+}
+
+static gboolean start_discovery(gpointer user_data)
+{
+	struct btd_device *device = user_data;
+
+	if (device->bredr)
+		device_browse_sdp(device, NULL);
+	else
+		device_browse_primary(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");
+}
+
+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) {
+		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_primary(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;
+}
+
+void btd_device_gatt_set_service_changed(struct btd_device *device,
+						uint16_t start, uint16_t end)
+{
+	GSList *l;
+
+	for (l = device->primaries; l; l = g_slist_next(l)) {
+		struct gatt_primary *prim = l->data;
+
+		if (start <= prim->range.end && end >= prim->range.start)
+			prim->changed = TRUE;
+	}
+
+	device_browse_primary(device, NULL);
+}
+
+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 + 1];
+	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);
+	filename[PATH_MAX] = '\0';
+
+	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)
+{
+	if (device->tmp_records) {
+		const sdp_record_t *record;
+
+		record = find_record_in_list(device->tmp_records, uuid);
+		if (record != NULL)
+			return record;
+
+		sdp_list_free(device->tmp_records,
+					(sdp_free_func_t) sdp_record_free);
+		device->tmp_records = NULL;
+	}
+
+	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;
+
+	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);
+
+	if (device->attios != NULL || device->attios_offline != NULL)
+		return TRUE;
+
+	attio_cleanup(device);
+
+	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/src/device.h b/bluez/src/device.h
new file mode 100644
index 0000000..30e2d5d
--- /dev/null
+++ b/bluez/src/device.h
@@ -0,0 +1,151 @@
+/*
+ *
+ *  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, bool bredr);
+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);
+void btd_device_gatt_set_service_changed(struct btd_device *device,
+						uint16_t start, uint16_t end);
+bool device_attach_attrib(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_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 btd_device_set_temporary(struct btd_device *device, gboolean 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(struct btd_device *device, int8_t rssi);
+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);
+
+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);
+
+void btd_device_init(void);
+void btd_device_cleanup(void);
diff --git a/bluez/src/eir.c b/bluez/src/eir.c
new file mode 100644
index 0000000..d22ad91
--- /dev/null
+++ b/bluez/src/eir.c
@@ -0,0 +1,480 @@
+/*
+ *
+ *  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 <bluetooth/bluetooth.h>
+#include <bluetooth/hci.h>
+#include <bluetooth/sdp.h>
+
+#include "src/shared/util.h"
+#include "uuid-helper.h"
+#include "eir.h"
+
+#define EIR_OOB_MIN (2 + 6)
+
+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;
+}
+
+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);
+}
+
+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;
+		}
+
+		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/src/eir.h b/bluez/src/eir.h
new file mode 100644
index 0000000..e486fa2
--- /dev/null
+++ b/bluez/src/eir.h
@@ -0,0 +1,74 @@
+/*
+ *
+ *  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 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_GAP_APPEARANCE          0x19  /* GAP appearance */
+
+/* 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) */
+
+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;
+};
+
+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/src/error.c b/bluez/src/error.c
new file mode 100644
index 0000000..7692790
--- /dev/null
+++ b/bluez/src/error.c
@@ -0,0 +1,116 @@
+/*
+ *
+ *  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 g_dbus_create_error(msg, ERROR_INTERFACE ".InvalidArguments",
+					"Invalid arguments in method call");
+}
+
+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_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/src/error.h b/bluez/src/error.h
new file mode 100644
index 0000000..cdb8919
--- /dev/null
+++ b/bluez/src/error.h
@@ -0,0 +1,43 @@
+/*
+ *
+ *  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_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_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/src/gatt-dbus.c b/bluez/src/gatt-dbus.c
new file mode 100644
index 0000000..95d1243
--- /dev/null
+++ b/bluez/src/gatt-dbus.c
@@ -0,0 +1,479 @@
+/*
+ *
+ *  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 <stdint.h>
+#include <errno.h>
+
+#include <glib.h>
+#include <dbus/dbus.h>
+#include <gdbus/gdbus.h>
+
+#include "adapter.h"
+#include "device.h"
+#include "lib/uuid.h"
+#include "dbus-common.h"
+#include "log.h"
+
+#include "error.h"
+#include "gatt.h"
+#include "gatt-dbus.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"
+
+struct external_app {
+	char *owner;
+	char *path;
+	DBusMessage *reg;
+	GDBusClient *client;
+	GSList *proxies;
+	unsigned int watch;
+};
+
+struct proxy_write_data {
+	btd_attr_write_result_t result_cb;
+	void *user_data;
+};
+
+/*
+ * Attribute to Proxy hash table. Used to map incoming
+ * ATT operations to its external characteristic proxy.
+ */
+static GHashTable *proxy_hash;
+
+static GSList *external_apps;
+
+static int external_app_path_cmp(gconstpointer a, gconstpointer b)
+{
+	const struct external_app *eapp = a;
+	const char *path = b;
+
+	return g_strcmp0(eapp->path, path);
+}
+
+static void external_app_watch_destroy(gpointer user_data)
+{
+	struct external_app *eapp = user_data;
+
+	/* TODO: Remove from the database */
+
+	external_apps = g_slist_remove(external_apps, eapp);
+
+	g_dbus_client_unref(eapp->client);
+	if (eapp->reg)
+		dbus_message_unref(eapp->reg);
+
+	g_free(eapp->owner);
+	g_free(eapp->path);
+	g_free(eapp);
+}
+
+static int proxy_path_cmp(gconstpointer a, gconstpointer b)
+{
+	GDBusProxy *proxy1 = (GDBusProxy *) a;
+	GDBusProxy *proxy2 = (GDBusProxy *) b;
+	const char *path1 = g_dbus_proxy_get_path(proxy1);
+	const char *path2 = g_dbus_proxy_get_path(proxy2);
+
+	return g_strcmp0(path1, path2);
+}
+
+static void proxy_added(GDBusProxy *proxy, void *user_data)
+{
+	struct external_app *eapp = user_data;
+	const char *interface, *path;
+
+	interface = g_dbus_proxy_get_interface(proxy);
+	path = g_dbus_proxy_get_path(proxy);
+
+	if (!g_str_has_prefix(path, eapp->path))
+		return;
+
+	if (g_strcmp0(interface, GATT_CHR_IFACE) != 0 &&
+			g_strcmp0(interface, GATT_SERVICE_IFACE) != 0 &&
+			g_strcmp0(interface, GATT_DESCRIPTOR_IFACE) != 0)
+		return;
+
+	DBG("path %s iface %s", path, interface);
+
+	/*
+	 * Object path follows a hierarchical organization. Add the
+	 * proxies sorted by path helps the logic to register the
+	 * object path later.
+	 */
+	eapp->proxies = g_slist_insert_sorted(eapp->proxies, proxy,
+							proxy_path_cmp);
+}
+
+static void proxy_removed(GDBusProxy *proxy, void *user_data)
+{
+	struct external_app *eapp = user_data;
+	const char *interface, *path;
+
+	interface = g_dbus_proxy_get_interface(proxy);
+	path = g_dbus_proxy_get_path(proxy);
+
+	DBG("path %s iface %s", path, interface);
+
+	eapp->proxies = g_slist_remove(eapp->proxies, proxy);
+}
+
+static void proxy_read_cb(struct btd_attribute *attr,
+				btd_attr_read_result_t result, void *user_data)
+{
+	DBusMessageIter iter, array;
+	GDBusProxy *proxy;
+	uint8_t *value;
+	int len;
+
+	/*
+	 * Remote device is trying to read the informed attribute,
+	 * "Value" should be read from the proxy. GDBusProxy tracks
+	 * properties changes automatically, it is not necessary to
+	 * get the value directly from the GATT server.
+	 */
+	proxy = g_hash_table_lookup(proxy_hash, attr);
+	if (!proxy) {
+		result(-ENOENT, NULL, 0, user_data);
+		return;
+	}
+
+	if (!g_dbus_proxy_get_property(proxy, "Value", &iter)) {
+		/* Unusual situation, read property will checked earlier */
+		result(-EPERM, NULL, 0, user_data);
+		return;
+	}
+
+	if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY) {
+		DBG("External service inconsistent!");
+		result(-EPERM, NULL, 0, user_data);
+		return;
+	}
+
+	dbus_message_iter_recurse(&iter, &array);
+	dbus_message_iter_get_fixed_array(&array, &value, &len);
+
+	DBG("attribute: %p read %d bytes", attr, len);
+
+	result(0, value, len, user_data);
+}
+
+static void proxy_write_reply(const DBusError *derr, void *user_data)
+{
+	struct proxy_write_data *wdata = user_data;
+	int err;
+
+	/*
+	 * Security requirements shall be handled by the core. If external
+	 * applications returns an error, the reasons will be restricted to
+	 * invalid argument or application specific errors.
+	 */
+
+	if (!dbus_error_is_set(derr)) {
+		err = 0;
+		goto done;
+	}
+
+	DBG("Write reply: %s", derr->message);
+
+	if (dbus_error_has_name(derr, DBUS_ERROR_NO_REPLY))
+		err = -ETIMEDOUT;
+	else if (dbus_error_has_name(derr, ERROR_INTERFACE ".InvalidArguments"))
+		err = -EINVAL;
+	else
+		err = -EPROTO;
+
+done:
+	if (wdata && wdata->result_cb)
+		wdata->result_cb(err, wdata->user_data);
+}
+
+static void proxy_write_cb(struct btd_attribute *attr,
+					const uint8_t *value, size_t len,
+					btd_attr_write_result_t result,
+					void *user_data)
+{
+	GDBusProxy *proxy;
+
+	proxy = g_hash_table_lookup(proxy_hash, attr);
+	if (!proxy) {
+		result(-ENOENT, user_data);
+		return;
+	}
+
+	/*
+	 * "result" callback defines if the core wants to receive the
+	 * operation result, allowing to select ATT Write Request or Write
+	 * Command. Descriptors requires Write Request operation. For
+	 * Characteristics, the implementation will define which operations
+	 * are allowed based on the properties/flags.
+	 * TODO: Write Long Characteristics/Descriptors.
+	 */
+
+	if (result) {
+		struct proxy_write_data *wdata;
+
+		wdata = g_new0(struct proxy_write_data, 1);
+		wdata->result_cb = result;
+		wdata->user_data = user_data;
+
+		g_dbus_proxy_set_property_array(proxy, "Value", DBUS_TYPE_BYTE,
+						value, len, proxy_write_reply,
+						wdata, g_free);
+	} else {
+		/*
+		 * Caller is not interested in the Set method call result.
+		 * This flow implements the ATT Write Command scenario, where
+		 * the remote doesn't receive ATT response.
+		 */
+		g_dbus_proxy_set_property_array(proxy, "Value", DBUS_TYPE_BYTE,
+						value, len, proxy_write_reply,
+						NULL, NULL);
+	}
+
+	DBG("Server: Write attribute callback %s",
+					g_dbus_proxy_get_path(proxy));
+
+}
+
+static int register_external_service(const struct external_app *eapp,
+							GDBusProxy *proxy)
+{
+	DBusMessageIter iter;
+	const char *str, *path, *iface;
+	bt_uuid_t uuid;
+
+	path = g_dbus_proxy_get_path(proxy);
+	iface = g_dbus_proxy_get_interface(proxy);
+	if (g_strcmp0(eapp->path, path) != 0 ||
+			g_strcmp0(iface, GATT_SERVICE_IFACE) != 0)
+		return -EINVAL;
+
+	if (!g_dbus_proxy_get_property(proxy, "UUID", &iter))
+		return -EINVAL;
+
+	if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
+		return -EINVAL;
+
+	dbus_message_iter_get_basic(&iter, &str);
+
+	if (bt_string_to_uuid(&uuid, str) < 0)
+		return -EINVAL;
+
+	if (!btd_gatt_add_service(&uuid))
+		return -EINVAL;
+
+	return 0;
+}
+
+static int register_external_characteristics(GSList *proxies)
+
+{
+	GSList *list;
+
+	for (list = proxies; list; list = g_slist_next(proxies)) {
+		DBusMessageIter iter;
+		const char *str, *path;
+		bt_uuid_t uuid;
+		struct btd_attribute *attr;
+		GDBusProxy *proxy = list->data;
+
+		if (!g_dbus_proxy_get_property(proxy, "UUID", &iter))
+			return -EINVAL;
+
+		if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
+			return -EINVAL;
+
+		dbus_message_iter_get_basic(&iter, &str);
+
+		if (bt_string_to_uuid(&uuid, str) < 0)
+			return -EINVAL;
+
+		/*
+		 * TODO: Missing Flags/property
+		 * Add properties according to Core SPEC 4.1 page 2183.
+		 * Reference table 3.5: Characteristic Properties bit field.
+		 */
+
+		attr = btd_gatt_add_char(&uuid, 0x00, proxy_read_cb,
+							proxy_write_cb);
+		if (!attr)
+			return -EINVAL;
+
+		path = g_dbus_proxy_get_path(proxy);
+		DBG("Added GATT CHR: %s (%s)", path, str);
+
+		g_hash_table_insert(proxy_hash, attr, g_dbus_proxy_ref(proxy));
+	}
+
+	return 0;
+}
+
+static void client_ready(GDBusClient *client, void *user_data)
+{
+	struct external_app *eapp = user_data;
+	GDBusProxy *proxy;
+	DBusConnection *conn = btd_get_dbus_connection();
+	DBusMessage *reply;
+
+	if (!eapp->proxies)
+		goto fail;
+
+	proxy = eapp->proxies->data;
+	if (register_external_service(eapp, proxy) < 0)
+		goto fail;
+
+	if (register_external_characteristics(g_slist_next(eapp->proxies)) < 0)
+		goto fail;
+
+	DBG("Added GATT service %s", eapp->path);
+
+	reply = dbus_message_new_method_return(eapp->reg);
+	goto reply;
+
+fail:
+	error("Could not register external service: %s", eapp->path);
+
+	reply = btd_error_invalid_args(eapp->reg);
+	/* TODO: missing eapp/database cleanup */
+
+reply:
+	dbus_message_unref(eapp->reg);
+	eapp->reg = NULL;
+
+	g_dbus_send_message(conn, reply);
+}
+
+static struct external_app *new_external_app(DBusConnection *conn,
+					DBusMessage *msg, const char *path)
+{
+	struct external_app *eapp;
+	GDBusClient *client;
+	const char *sender = dbus_message_get_sender(msg);
+
+	client = g_dbus_client_new(conn, sender, "/");
+	if (!client)
+		return NULL;
+
+	eapp = g_new0(struct external_app, 1);
+
+	eapp->watch = g_dbus_add_disconnect_watch(btd_get_dbus_connection(),
+			sender, NULL, eapp, external_app_watch_destroy);
+	if (eapp->watch == 0) {
+		g_dbus_client_unref(client);
+		g_free(eapp);
+		return NULL;
+	}
+
+	eapp->owner = g_strdup(sender);
+	eapp->reg = dbus_message_ref(msg);
+	eapp->client = client;
+	eapp->path = g_strdup(path);
+
+	g_dbus_client_set_proxy_handlers(client, proxy_added, proxy_removed,
+								NULL, eapp);
+
+	g_dbus_client_set_ready_watch(client, client_ready, eapp);
+
+	return eapp;
+}
+
+static DBusMessage *register_service(DBusConnection *conn,
+					DBusMessage *msg, void *user_data)
+{
+	struct external_app *eapp;
+	DBusMessageIter iter;
+	const char *path;
+
+	if (!dbus_message_iter_init(msg, &iter))
+		return btd_error_invalid_args(msg);
+
+	if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_OBJECT_PATH)
+		return btd_error_invalid_args(msg);
+
+	dbus_message_iter_get_basic(&iter, &path);
+
+	if (g_slist_find_custom(external_apps, path, external_app_path_cmp))
+		return btd_error_already_exists(msg);
+
+	eapp = new_external_app(conn, msg, path);
+	if (!eapp)
+		return btd_error_failed(msg, "Not enough resources");
+
+	external_apps = g_slist_prepend(external_apps, eapp);
+
+	DBG("New app %p: %s", eapp, path);
+
+	return NULL;
+}
+
+static DBusMessage *unregister_service(DBusConnection *conn,
+					DBusMessage *msg, void *user_data)
+{
+	return dbus_message_new_method_return(msg);
+}
+
+static const GDBusMethodTable methods[] = {
+	{ GDBUS_EXPERIMENTAL_ASYNC_METHOD("RegisterService",
+				GDBUS_ARGS({ "service", "o"},
+						{ "options", "a{sv}"}),
+				NULL, register_service) },
+	{ GDBUS_EXPERIMENTAL_METHOD("UnregisterService",
+				GDBUS_ARGS({"service", "o"}),
+				NULL, unregister_service) },
+	{ }
+};
+
+gboolean gatt_dbus_manager_register(void)
+{
+	if (!g_dbus_register_interface(btd_get_dbus_connection(),
+				"/org/bluez", GATT_MGR_IFACE,
+				methods, NULL, NULL, NULL, NULL))
+		return FALSE;
+
+	proxy_hash = g_hash_table_new_full(g_direct_hash, g_direct_equal,
+				NULL, (GDestroyNotify) g_dbus_proxy_unref);
+
+	return TRUE;
+}
+
+void gatt_dbus_manager_unregister(void)
+{
+	/* We might not have initialized if experimental features are
+	 * not enabled.
+	 */
+	if (!proxy_hash)
+		return;
+
+	g_hash_table_destroy(proxy_hash);
+	proxy_hash = NULL;
+
+	g_dbus_unregister_interface(btd_get_dbus_connection(), "/org/bluez",
+							GATT_MGR_IFACE);
+}
diff --git a/bluez/src/gatt-dbus.h b/bluez/src/gatt-dbus.h
new file mode 100644
index 0000000..310cfa9
--- /dev/null
+++ b/bluez/src/gatt-dbus.h
@@ -0,0 +1,25 @@
+/*
+ *
+ *  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
+ *
+ */
+
+gboolean gatt_dbus_manager_register(void);
+void gatt_dbus_manager_unregister(void);
diff --git a/bluez/src/gatt.c b/bluez/src/gatt.c
new file mode 100644
index 0000000..92092d3
--- /dev/null
+++ b/bluez/src/gatt.c
@@ -0,0 +1,234 @@
+/*
+ *
+ *  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 <glib.h>
+
+#include "log.h"
+#include "lib/uuid.h"
+#include "attrib/att.h"
+#include "src/shared/util.h"
+
+#include "gatt-dbus.h"
+#include "gatt.h"
+
+/* Common GATT UUIDs */
+static const bt_uuid_t primary_uuid  = { .type = BT_UUID16,
+					.value.u16 = GATT_PRIM_SVC_UUID };
+
+static const bt_uuid_t chr_uuid = { .type = BT_UUID16,
+					.value.u16 = GATT_CHARAC_UUID };
+
+struct btd_attribute {
+	uint16_t handle;
+	bt_uuid_t type;
+	btd_attr_read_t read_cb;
+	btd_attr_write_t write_cb;
+	uint16_t value_len;
+	uint8_t value[0];
+};
+
+static GList *local_attribute_db;
+static uint16_t next_handle = 0x0001;
+
+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 if (src->type == BT_UUID32)
+		put_le32(src->value.u32, dst);
+	else
+		/* Convert from 128-bit BE to LE */
+		bswap_128(&src->value.u128, dst);
+}
+
+/*
+ * Helper function to create new attributes containing constant/static values.
+ * eg: declaration of services/characteristics, and characteristics with
+ * fixed values.
+ */
+static struct btd_attribute *new_const_attribute(const bt_uuid_t *type,
+							const uint8_t *value,
+							uint16_t len)
+{
+	struct btd_attribute *attr;
+
+	attr = malloc0(sizeof(struct btd_attribute) + len);
+	if (!attr)
+		return NULL;
+
+	attr->type = *type;
+	memcpy(&attr->value, value, len);
+	attr->value_len = len;
+
+	return attr;
+}
+
+static int local_database_add(uint16_t handle, struct btd_attribute *attr)
+{
+	attr->handle = handle;
+
+	local_attribute_db = g_list_append(local_attribute_db, attr);
+
+	return 0;
+}
+
+struct btd_attribute *btd_gatt_add_service(const bt_uuid_t *uuid)
+{
+	struct btd_attribute *attr;
+	uint16_t len = bt_uuid_len(uuid);
+	uint8_t value[len];
+
+	/*
+	 * Service DECLARATION
+	 *
+	 *   TYPE         ATTRIBUTE VALUE
+	 * +-------+---------------------------------+
+	 * |0x2800 | 0xYYYY...                       |
+	 * | (1)   | (2)                             |
+	 * +------+----------------------------------+
+	 * (1) - 2 octets: Primary/Secondary Service UUID
+	 * (2) - 2 or 16 octets: Service UUID
+	 */
+
+	/* Set attribute value */
+	put_uuid_le(uuid, value);
+
+	attr = new_const_attribute(&primary_uuid, value, len);
+	if (!attr)
+		return NULL;
+
+	if (local_database_add(next_handle, attr) < 0) {
+		free(attr);
+		return NULL;
+	}
+
+	/* TODO: missing overflow checking */
+	next_handle = next_handle + 1;
+
+	return attr;
+}
+
+struct btd_attribute *btd_gatt_add_char(const bt_uuid_t *uuid,
+						uint8_t properties,
+						btd_attr_read_t read_cb,
+						btd_attr_write_t write_cb)
+{
+	struct btd_attribute *char_decl, *char_value = NULL;
+
+	/* Attribute value length */
+	uint16_t len = 1 + 2 + bt_uuid_len(uuid);
+	uint8_t value[len];
+
+	/*
+	 * Characteristic DECLARATION
+	 *
+	 *   TYPE         ATTRIBUTE VALUE
+	 * +-------+---------------------------------+
+	 * |0x2803 | 0xXX 0xYYYY 0xZZZZ...           |
+	 * | (1)   |  (2)   (3)   (4)                |
+	 * +------+----------------------------------+
+	 * (1) - 2 octets: Characteristic declaration UUID
+	 * (2) - 1 octet : Properties
+	 * (3) - 2 octets: Handle of the characteristic Value
+	 * (4) - 2 or 16 octets: Characteristic UUID
+	 */
+
+	value[0] = properties;
+
+	/*
+	 * Since we don't know yet the characteristic value attribute
+	 * handle, we skip and set it later.
+	 */
+
+	put_uuid_le(uuid, &value[3]);
+
+	char_decl = new_const_attribute(&chr_uuid, value, len);
+	if (!char_decl)
+		goto fail;
+
+	char_value = new0(struct btd_attribute, 1);
+	if (!char_value)
+		goto fail;
+
+	if (local_database_add(next_handle, char_decl) < 0)
+		goto fail;
+
+	next_handle = next_handle + 1;
+
+	/*
+	 * Characteristic VALUE
+	 *
+	 *   TYPE         ATTRIBUTE VALUE
+	 * +----------+---------------------------------+
+	 * |0xZZZZ... | 0x...                           |
+	 * |  (1)     |  (2)                            |
+	 * +----------+---------------------------------+
+	 * (1) - 2 or 16 octets: Characteristic UUID
+	 * (2) - N octets: Value is read dynamically from the service
+	 * implementation (external entity).
+	 */
+
+	char_value->type = *uuid;
+	char_value->read_cb = read_cb;
+	char_value->write_cb = write_cb;
+
+	if (local_database_add(next_handle, char_value) < 0)
+		/* TODO: remove declaration */
+		goto fail;
+
+	next_handle = next_handle + 1;
+
+	/*
+	 * Update characteristic value handle in characteristic declaration
+	 * attribute. For local attributes, we can assume that the handle
+	 * representing the characteristic value will get the next available
+	 * handle. However, for remote attribute this assumption is not valid.
+	 */
+	put_le16(char_value->handle, &char_decl->value[1]);
+
+	return char_value;
+
+fail:
+	free(char_decl);
+	free(char_value);
+
+	return NULL;
+}
+
+void gatt_init(void)
+{
+	DBG("Starting GATT server");
+
+	gatt_dbus_manager_register();
+}
+
+void gatt_cleanup(void)
+{
+	DBG("Stopping GATT server");
+
+	gatt_dbus_manager_unregister();
+}
diff --git a/bluez/src/gatt.h b/bluez/src/gatt.h
new file mode 100644
index 0000000..6c7f57e
--- /dev/null
+++ b/bluez/src/gatt.h
@@ -0,0 +1,98 @@
+/*
+ *
+ *  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
+ *
+ */
+
+struct btd_attribute;
+
+void gatt_init(void);
+
+void gatt_cleanup(void);
+
+/*
+ * Read operation result callback. Called from the service implementation
+ * informing the core (ATT layer) the result of read operation.
+ * @err:	error in -errno format.
+ * @value:	value of the attribute read.
+ * @len:	length of value.
+ * @user_data:	user_data passed in btd_attr_read_t callback.
+ */
+typedef void (*btd_attr_read_result_t) (int err, uint8_t *value, size_t len,
+							void *user_data);
+/*
+ * Service implementation callback passed to core (ATT layer). It manages read
+ * operations received from remote devices.
+ * @attr:	reference of the attribute to be read.
+ * @result:	callback called from the service implementation informing the
+ *		value of attribute read.
+ * @user_data:	user_data passed in btd_attr_read_result_t callback.
+ */
+typedef void (*btd_attr_read_t) (struct btd_attribute *attr,
+						btd_attr_read_result_t result,
+						void *user_data);
+
+/*
+ * Write operation result callback. Called from the service implementation
+ * informing the core (ATT layer) the result of the write operation. It is used
+ * to manage Write Request operations.
+ * @err:	error in -errno format.
+ * @user_data:	user_data passed in btd_attr_write_t callback.
+ */
+typedef void (*btd_attr_write_result_t) (int err, void *user_data);
+/*
+ * Service implementation callback passed to core (ATT layer). It manages write
+ * operations received from remote devices.
+ * @attr:	reference of the attribute to be changed.
+ * @value:	new attribute value.
+ * @len:	length of value.
+ * @result:	callback called from the service implementation informing the
+ *		result of the write operation.
+ * @user_data:	user_data passed in btd_attr_write_result_t callback.
+ */
+typedef void (*btd_attr_write_t) (struct btd_attribute *attr,
+					const uint8_t *value, size_t len,
+					btd_attr_write_result_t result,
+					void *user_data);
+
+/* btd_gatt_add_service - Add a service declaration to local attribute database.
+ * @uuid:	Service UUID.
+ *
+ * Returns a reference to service declaration attribute. In case of error,
+ * NULL is returned.
+ */
+struct btd_attribute *btd_gatt_add_service(const bt_uuid_t *uuid);
+
+/*
+ * btd_gatt_add_char - Add a characteristic (declaration and value attributes)
+ * to local attribute database.
+ * @uuid:	Characteristic UUID (16-bits or 128-bits).
+ * @properties:	Characteristic properties. See Core SPEC 4.1 page 2183.
+ * @read_cb:	Callback used to provide the characteristic value.
+ * @write_cb:	Callback called to notify the implementation that a new value
+ *              is available.
+ *
+ * Returns a reference to characteristic value attribute. In case of error,
+ * NULL is returned.
+ */
+struct btd_attribute *btd_gatt_add_char(const bt_uuid_t *uuid,
+						uint8_t properties,
+						btd_attr_read_t read_cb,
+						btd_attr_write_t write_cb);
diff --git a/bluez/src/genbuiltin b/bluez/src/genbuiltin
new file mode 100755
index 0000000..8b6f047
--- /dev/null
+++ b/bluez/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/src/hcid.h b/bluez/src/hcid.h
new file mode 100644
index 0000000..ea67cc2
--- /dev/null
+++ b/bluez/src/hcid.h
@@ -0,0 +1,50 @@
+/*
+ *
+ *  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
+ *
+ */
+
+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;
+
+	uint16_t	did_source;
+	uint16_t	did_vendor;
+	uint16_t	did_product;
+	uint16_t	did_version;
+};
+
+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);
+
+void btd_exit(void);
diff --git a/bluez/src/log.c b/bluez/src/log.c
new file mode 100644
index 0000000..ca783f3
--- /dev/null
+++ b/bluez/src/log.c
@@ -0,0 +1,144 @@
+/*
+ *
+ *  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 <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 warn(const char *format, ...)
+{
+	va_list ap;
+
+	va_start(ap, format);
+
+	vsyslog(LOG_WARNING, 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 btd_debug(const char *format, ...)
+{
+	va_list ap;
+
+	va_start(ap, format);
+
+	vsyslog(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);
+
+	if (!detach)
+		option |= LOG_PERROR;
+
+	openlog("bluetoothd", option, LOG_DAEMON);
+
+	syslog(LOG_INFO, "Bluetooth daemon %s", VERSION);
+}
+
+void __btd_log_cleanup(void)
+{
+	closelog();
+
+	g_strfreev(enabled);
+}
diff --git a/bluez/src/log.h b/bluez/src/log.h
new file mode 100644
index 0000000..bf9eac2
--- /dev/null
+++ b/bluez/src/log.h
@@ -0,0 +1,59 @@
+/*
+ *
+ *  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
+ *
+ */
+
+void info(const char *format, ...) __attribute__((format(printf, 1, 2)));
+void warn(const char *format, ...) __attribute__((format(printf, 1, 2)));
+void error(const char *format, ...) __attribute__((format(printf, 1, 2)));
+
+void btd_debug(const char *format, ...) __attribute__((format(printf, 1, 2)));
+
+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(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("%s:%s() " fmt,  __FILE__, __func__ , ## arg); \
+} while (0)
diff --git a/bluez/src/main.c b/bluez/src/main.c
new file mode 100644
index 0000000..b3136fc
--- /dev/null
+++ b/bluez/src/main.c
@@ -0,0 +1,625 @@
+/*
+ *
+ *  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 <sys/signalfd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <bluetooth/bluetooth.h>
+
+#include <glib.h>
+
+#include <dbus/dbus.h>
+
+#include <gdbus/gdbus.h>
+
+#include "log.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 "gatt.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 const char * const supported_options[] = {
+	"Name",
+	"Class",
+	"DiscoverableTimeout",
+	"PairableTimeout",
+	"AutoConnectTimeout",
+	"DeviceID",
+	"ReverseServiceDiscovery",
+	"NameResolving",
+	"DebugKeys",
+};
+
+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)
+{
+	char **keys;
+	int i;
+
+	if (!config)
+		return;
+
+	keys = g_key_file_get_groups(config, NULL);
+
+	for (i = 0; keys != NULL && keys[i] != NULL; i++) {
+		if (!g_str_equal(keys[i], "General"))
+			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 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;
+}
+
+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 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 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_timeout_add_seconds(SHUTDOWN_GRACE_SECONDS,
+							quit_eventloop, NULL);
+
+			sd_notify(0, "STATUS=Powering down");
+			adapter_shutdown();
+		}
+
+		__terminated = 1;
+		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;
+	GKeyFile *config;
+	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);
+
+	event_loop = g_main_loop_new(NULL, FALSE);
+
+	signal = setup_signalfd();
+
+	__btd_log_init(option_debug, option_detach);
+
+	sd_notify(0, "STATUS=Starting up");
+
+	config = load_config(CONFIGDIR "/main.conf");
+
+	parse_config(config);
+
+	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);
+
+	gatt_init();
+
+	if (adapter_init() < 0) {
+		error("Adapter handling initialization failed");
+		exit(1);
+	}
+
+	btd_device_init();
+	btd_agent_init();
+	btd_profile_init();
+
+	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);
+
+	/* 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();
+
+	gatt_cleanup();
+
+	rfkill_exit();
+
+	stop_sdp_server();
+
+	g_main_loop_unref(event_loop);
+
+	if (config)
+		g_key_file_free(config);
+
+	disconnect_dbus();
+
+	info("Exit");
+
+	if (watchdog > 0)
+		g_source_remove(watchdog);
+
+	__btd_log_cleanup();
+
+	return 0;
+}
diff --git a/bluez/src/main.conf b/bluez/src/main.conf
new file mode 100644
index 0000000..de79d52
--- /dev/null
+++ b/bluez/src/main.conf
@@ -0,0 +1,49 @@
+[General]
+
+# Default adaper name
+# %h - substituted for hostname
+# %d - substituted for adapter id
+# Defaults to 'BlueZ'
+#Name = %h-%d
+Name = Dropcam
+
+# 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
diff --git a/bluez/src/org.bluez.service b/bluez/src/org.bluez.service
new file mode 100644
index 0000000..dd7ae8f
--- /dev/null
+++ b/bluez/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/src/oui.c b/bluez/src/oui.c
new file mode 100644
index 0000000..8422986
--- /dev/null
+++ b/bluez/src/oui.c
@@ -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
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#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/src/oui.h b/bluez/src/oui.h
new file mode 100644
index 0000000..abd0386
--- /dev/null
+++ b/bluez/src/oui.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
+ *
+ */
+
+#include <bluetooth/bluetooth.h>
+
+char *batocomp(const bdaddr_t *ba);
diff --git a/bluez/src/plugin.c b/bluez/src/plugin.c
new file mode 100644
index 0000000..edb47db
--- /dev/null
+++ b/bluez/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 <bluetooth/bluetooth.h>
+
+#include <glib.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/src/plugin.h b/bluez/src/plugin.h
new file mode 100644
index 0000000..89c7b85
--- /dev/null
+++ b/bluez/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/src/profile.c b/bluez/src/profile.c
new file mode 100644
index 0000000..f6aa970
--- /dev/null
+++ b/bluez/src/profile.c
@@ -0,0 +1,2419 @@
+/*
+ *
+ *  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 <gdbus/gdbus.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/sdp.h>
+#include <bluetooth/sdp_lib.h>
+
+#include "btio/btio.h"
+#include "lib/uuid.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 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>						\
+	</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>						\
+	</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=\"0x0200\">				\
+			<uint16 value=\"%u\" name=\"psm\"/>		\
+		</attribute>						\
+		<attribute id=\"0x0317\">				\
+			<uint32 value=\"0x0000007f\"/>			\
+		</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_str_equal(ext->owner, owner))
+			continue;
+
+		if (g_str_equal(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:
+	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)) {
+		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);
+
+	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)) {
+		btd_service_disconnecting_complete(conn->service, 0);
+		goto disconnect;
+	}
+
+	error("%s replied with an error: %s, %s", ext->name,
+						err.name, err.message);
+
+	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 (send_new_connection(ext, conn))
+		return;
+
+drop:
+	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;
+	}
+
+	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;
+	}
+
+	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);
+	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);
+			rfcomm = NULL;
+			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);
+	}
+	if (rfcomm) {
+		ext->servers = g_slist_remove(ext->servers, rfcomm);
+		ext_io_destroy(rfcomm);
+	}
+
+	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);
+		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);
+			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);
+		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:
+	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_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)
+{
+	return g_strdup_printf(PSE_RECORD, rfcomm->chan, ext->version,
+								ext->name);
+}
+
+static char *get_mas_record(struct ext_profile *ext, struct ext_io *l2cap,
+							struct ext_io *rfcomm)
+{
+	return g_strdup_printf(MAS_RECORD, rfcomm->chan, ext->version,
+								ext->name);
+}
+
+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,
+	}, {
+		.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,
+		.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	= 0x0101,
+	}, {
+		.uuid		= OBEX_MAS_UUID,
+		.name		= "Message Access",
+		.channel	= MAS_DEFAULT_CHANNEL,
+		.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;
+
+	if (ext->enable_server || ext->record || ext->get_record) {
+		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/src/profile.h b/bluez/src/profile.h
new file mode 100644
index 0000000..9aec27e
--- /dev/null
+++ b/bluez/src/profile.h
@@ -0,0 +1,74 @@
+/*
+ *
+ *  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;
+
+	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 (*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/src/rfkill.c b/bluez/src/rfkill.c
new file mode 100644
index 0000000..70588c0
--- /dev/null
+++ b/bluez/src/rfkill.c
@@ -0,0 +1,171 @@
+/*
+ *
+ *  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 "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/src/sdp-client.c b/bluez/src/sdp-client.c
new file mode 100644
index 0000000..edbdaec
--- /dev/null
+++ b/bluez/src/sdp-client.c
@@ -0,0 +1,384 @@
+/*
+ *
+ *  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 <bluetooth/bluetooth.h>
+#include <bluetooth/sdp.h>
+#include <bluetooth/sdp_lib.h>
+
+#include <glib.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;
+};
+
+static GSList *cached_sdp_sessions = NULL;
+
+static gboolean cached_session_expired(gpointer user_data)
+{
+	struct cached_sdp_session *cached = user_data;
+
+	cached_sdp_sessions = g_slist_remove(cached_sdp_sessions, cached);
+
+	sdp_close(cached->session);
+
+	g_free(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);
+
+		session = c->session;
+
+		cached_sdp_sessions = g_slist_remove(cached_sdp_sessions, c);
+		g_free(c);
+
+		return session;
+	}
+
+	return NULL;
+}
+
+static void cache_sdp_session(bdaddr_t *src, bdaddr_t *dst,
+						sdp_session_t *session)
+{
+	struct cached_sdp_session *cached;
+
+	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);
+}
+
+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/src/sdp-client.h b/bluez/src/sdp-client.h
new file mode 100644
index 0000000..9aa5a4d
--- /dev/null
+++ b/bluez/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/src/sdp-xml.c b/bluez/src/sdp-xml.c
new file mode 100644
index 0000000..6492781
--- /dev/null
+++ b/bluez/src/sdp-xml.c
@@ -0,0 +1,996 @@
+/*
+ *
+ *  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 <bluetooth/sdp.h>
+#include <bluetooth/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);
+	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);
+
+		/* 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 (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 (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 = 0;
+
+		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));
+
+			/* 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));
+			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/src/sdp-xml.h b/bluez/src/sdp-xml.h
new file mode 100644
index 0000000..d6a2f73
--- /dev/null
+++ b/bluez/src/sdp-xml.h
@@ -0,0 +1,35 @@
+/*
+ *
+ *  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
+ *
+ */
+
+
+#ifndef __SDP_XML_H
+#define __SDP_XML_H
+
+#include <bluetooth/sdp.h>
+
+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);
+
+#endif /* __SDP_XML_H */
diff --git a/bluez/src/sdpd-database.c b/bluez/src/sdpd-database.c
new file mode 100644
index 0000000..f65a526
--- /dev/null
+++ b/bluez/src/sdpd-database.c
@@ -0,0 +1,298 @@
+/*
+ *
+ *  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 <bluetooth/bluetooth.h>
+#include <bluetooth/sdp.h>
+#include <bluetooth/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));
+	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/src/sdpd-request.c b/bluez/src/sdpd-request.c
new file mode 100644
index 0000000..3e58c22
--- /dev/null
+++ b/bluez/src/sdpd-request.c
@@ -0,0 +1,1118 @@
+/*
+ *
+ *  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 <bluetooth/bluetooth.h>
+#include <bluetooth/l2cap.h>
+#include <bluetooth/sdp.h>
+#include <bluetooth/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/src/sdpd-server.c b/bluez/src/sdpd-server.c
new file mode 100644
index 0000000..b411abe
--- /dev/null
+++ b/bluez/src/sdpd-server.c
@@ -0,0 +1,274 @@
+/*
+ *
+ *  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 <sys/stat.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/l2cap.h>
+#include <bluetooth/sdp.h>
+#include <bluetooth/sdp_lib.h>
+
+#include <sys/un.h>
+
+#include <glib.h>
+
+#include "log.h"
+#include "sdpd.h"
+
+static guint l2cap_id = 0, unix_id = 0;
+
+static int l2cap_sock, unix_sock;
+
+/*
+ * 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);
+	if (len != size) {
+		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/src/sdpd-service.c b/bluez/src/sdpd-service.c
new file mode 100644
index 0000000..a90b299
--- /dev/null
+++ b/bluez/src/sdpd-service.c
@@ -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
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <errno.h>
+#include <stdlib.h>
+#include <assert.h>
+#include <sys/time.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/sdp.h>
+#include <bluetooth/sdp_lib.h>
+
+#include <glib.h>
+
+#include "src/shared/util.h"
+#include "sdpd.h"
+#include "log.h"
+
+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;
+
+/*
+ * 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();
+}
+
+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_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_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/src/sdpd.h b/bluez/src/sdpd.h
new file mode 100644
index 0000000..6de0af7
--- /dev/null
+++ b/bluez/src/sdpd.h
@@ -0,0 +1,83 @@
+/*
+ *
+ *  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
+ *
+ */
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/sdp.h>
+
+#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);
+
+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/src/service.c b/bluez/src/service.c
new file mode 100644
index 0000000..7a480d6
--- /dev/null
+++ b/bluez/src/service.c
@@ -0,0 +1,339 @@
+/*
+ *
+ *  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 <assert.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <stdbool.h>
+#include <sys/stat.h>
+#include <sys/ioctl.h>
+#include <errno.h>
+
+#include <bluetooth/bluetooth.h>
+
+#include <glib.h>
+
+#include "log.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;
+
+	assert(service->device != NULL);
+	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;
+
+	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_UNAVAILABLE, 0);
+	service->profile->device_remove(service);
+	service->device = NULL;
+	service->profile = NULL;
+	btd_service_unref(service);
+}
+
+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:
+	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)
+{
+	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_DISCONNECTED &&
+				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/src/service.h b/bluez/src/service.h
new file mode 100644
index 0000000..5230115
--- /dev/null
+++ b/bluez/src/service.h
@@ -0,0 +1,69 @@
+/*
+ *
+ *  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);
+
+/* 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/src/shared/btsnoop.c b/bluez/src/shared/btsnoop.c
new file mode 100644
index 0000000..17a872c
--- /dev/null
+++ b/bluez/src/shared/btsnoop.c
@@ -0,0 +1,467 @@
+/*
+ *
+ *  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 type;
+	uint16_t index;
+	bool aborted;
+	bool pklg_format;
+};
+
+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->type = 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)
+			goto failed;
+
+		btsnoop->type = BTSNOOP_TYPE_MONITOR;
+		btsnoop->index = 0xffff;
+		btsnoop->pklg_format = true;
+
+		/* 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 type)
+{
+	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->type = type;
+	btsnoop->index = 0xffff;
+
+	memcpy(hdr.id, btsnoop_id, sizeof(btsnoop_id));
+	hdr.version = htobe32(btsnoop_version);
+	hdr.type = htobe32(btsnoop->type);
+
+	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_type(struct btsnoop *btsnoop)
+{
+	if (!btsnoop)
+		return BTSNOOP_TYPE_INVALID;
+
+	return btsnoop->type;
+}
+
+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;
+	}
+
+	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->type) {
+	case BTSNOOP_TYPE_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_TYPE_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->type) {
+	case BTSNOOP_TYPE_SIMULATOR:
+		flags = (1 << 16) | frequency;
+		break;
+
+	default:
+		return false;
+	}
+
+	return btsnoop_write(btsnoop, tv, flags, data, size);
+}
+
+static uint16_t get_opcode_from_pklg(uint8_t type)
+{
+	switch (type) {
+	case 0x00:
+		return BTSNOOP_OPCODE_COMMAND_PKT;
+	case 0x01:
+		return BTSNOOP_OPCODE_EVENT_PKT;
+	case 0x02:
+		return BTSNOOP_OPCODE_ACL_TX_PKT;
+	case 0x03:
+		return BTSNOOP_OPCODE_ACL_RX_PKT;
+	}
+
+	return 0xffff;
+}
+
+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;
+	}
+
+	toread = be32toh(pkt.len) - 9;
+
+	ts = be64toh(pkt.ts);
+	tv->tv_sec = ts >> 32;
+	tv->tv_usec = ts & 0xffffffff;
+
+	*index = 0;
+	*opcode = get_opcode_from_pklg(pkt.type);
+
+	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);
+	flags = be32toh(pkt.flags);
+
+	ts = be64toh(pkt.ts) - 0x00E03AB44A676000ll;
+	tv->tv_sec = (ts / 1000000ll) + 946684800ll;
+	tv->tv_usec = ts % 1000000ll;
+
+	switch (btsnoop->type) {
+	case BTSNOOP_TYPE_HCI:
+		*index = 0;
+		*opcode = get_opcode_from_flags(0xff, flags);
+		break;
+
+	case BTSNOOP_TYPE_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_TYPE_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/src/shared/btsnoop.h b/bluez/src/shared/btsnoop.h
new file mode 100644
index 0000000..2c55d02
--- /dev/null
+++ b/bluez/src/shared/btsnoop.h
@@ -0,0 +1,76 @@
+/*
+ *
+ *  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_TYPE_INVALID		0
+#define BTSNOOP_TYPE_HCI		1001
+#define BTSNOOP_TYPE_UART		1002
+#define BTSNOOP_TYPE_BCSP		1003
+#define BTSNOOP_TYPE_3WIRE		1004
+#define BTSNOOP_TYPE_MONITOR		2001
+#define BTSNOOP_TYPE_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
+
+struct btsnoop_opcode_new_index {
+	uint8_t  type;
+	uint8_t  bus;
+	uint8_t  bdaddr[6];
+	char     name[8];
+} __attribute__((packed));
+
+struct btsnoop;
+
+struct btsnoop *btsnoop_open(const char *path, unsigned long flags);
+struct btsnoop *btsnoop_create(const char *path, uint32_t type);
+
+struct btsnoop *btsnoop_ref(struct btsnoop *btsnoop);
+void btsnoop_unref(struct btsnoop *btsnoop);
+
+uint32_t btsnoop_get_type(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/src/shared/crypto.c b/bluez/src/shared/crypto.c
new file mode 100644
index 0000000..cc7536a
--- /dev/null
+++ b/bluez/src/shared/crypto.c
@@ -0,0 +1,470 @@
+/*
+ *
+ *  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 PF_ALG
+#include <linux/types.h>
+
+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
+
+struct bt_crypto {
+	int ref_count;
+	int ecb_aes;
+	int urandom;
+};
+
+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;
+}
+
+struct bt_crypto *bt_crypto_new(void)
+{
+	struct bt_crypto *crypto;
+
+	crypto = new0(struct bt_crypto, 1);
+	if (!crypto)
+		return NULL;
+
+	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;
+	}
+
+	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);
+
+	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 swap128(const uint8_t src[16], uint8_t dst[16])
+{
+	int i;
+
+	for (i = 0; i < 16; i++)
+		dst[15 - i] = src[i];
+}
+
+/*
+ * 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] */
+	swap128(key, tmp);
+
+	fd = alg_new(crypto->ecb_aes, tmp, 16);
+	if (fd < 0)
+		return false;
+
+
+	/* Most significant octet of plaintextData corresponds to in[0] */
+	swap128(plaintext, in);
+
+	if (!alg_encrypt(fd, in, 16, out, 16)) {
+		close(fd);
+		return false;
+	}
+
+	/* Most significant octet of encryptedData corresponds to out[0] */
+	swap128(out, encrypted);
+
+	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);
+}
diff --git a/bluez/src/shared/crypto.h b/bluez/src/shared/crypto.h
new file mode 100644
index 0000000..cae8daa
--- /dev/null
+++ b/bluez/src/shared/crypto.h
@@ -0,0 +1,48 @@
+/*
+ *
+ *  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]);
diff --git a/bluez/src/shared/hci.c b/bluez/src/shared/hci.c
new file mode 100644
index 0000000..cf677c0
--- /dev/null
+++ b/bluez/src/shared/hci.c
@@ -0,0 +1,607 @@
+/*
+ *
+ *  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 "monitor/bt.h"
+#include "monitor/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 fd, 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;
+
+	fd = io_get_fd(hci->io);
+	if (fd < 0)
+		return;
+
+	if (writev(fd, 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;
+}
+
+struct bt_hci *bt_hci_new(int fd)
+{
+	struct bt_hci *hci;
+
+	if (fd < 0)
+		return NULL;
+
+	hci = new0(struct bt_hci, 1);
+	if (!hci)
+		return NULL;
+
+	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();
+	if (!hci->cmd_queue) {
+		io_destroy(hci->io);
+		free(hci);
+		return NULL;
+	}
+
+	hci->rsp_queue = queue_new();
+	if (!hci->rsp_queue) {
+		queue_destroy(hci->cmd_queue, NULL);
+		io_destroy(hci->io);
+		free(hci);
+		return NULL;
+	}
+
+	hci->evt_list = queue_new();
+	if (!hci->evt_list) {
+		queue_destroy(hci->rsp_queue, NULL);
+		queue_destroy(hci->cmd_queue, NULL);
+		io_destroy(hci->io);
+		free(hci);
+		return NULL;
+	}
+
+	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);
+}
+
+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 = bt_hci_new(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 = bt_hci_new(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);
+	if (!cmd)
+		return 0;
+
+	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);
+	if (!evt)
+		return 0;
+
+	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/src/shared/hci.h b/bluez/src/shared/hci.h
new file mode 100644
index 0000000..dba0f11
--- /dev/null
+++ b/bluez/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/src/shared/hciemu.c b/bluez/src/shared/hciemu.c
new file mode 100644
index 0000000..6c93005
--- /dev/null
+++ b/bluez/src/shared/hciemu.c
@@ -0,0 +1,500 @@
+/*
+ *
+ *  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 <bluetooth/bluetooth.h>
+#include <bluetooth/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 "src/shared/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 write_callback(const void *data, uint16_t len, void *user_data)
+{
+	GIOChannel *channel = user_data;
+	ssize_t written;
+	int fd;
+
+	fd = g_io_channel_unix_get_fd(channel);
+
+	written = write(fd, data, len);
+	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, write_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 < 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, write_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;
+	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);
+
+	bthost_stop(hciemu->host_stack);
+
+	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);
+}
+
+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/src/shared/hciemu.h b/bluez/src/shared/hciemu.h
new file mode 100644
index 0000000..d948867
--- /dev/null
+++ b/bluez/src/shared/hciemu.h
@@ -0,0 +1,69 @@
+/*
+ *
+ *  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,
+};
+
+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);
+
+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/src/shared/hfp.c b/bluez/src/shared/hfp.c
new file mode 100644
index 0000000..36c8c3e
--- /dev/null
+++ b/bluez/src/shared/hfp.c
@@ -0,0 +1,819 @@
+/*
+ *
+ *  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 permissive_syntax;
+	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 cmd_handler {
+	char *prefix;
+	void *user_data;
+	hfp_destroy_func_t destroy;
+	hfp_result_func_t callback;
+};
+
+struct hfp_gw_result {
+	const char *data;
+	unsigned int offset;
+};
+
+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 (strlen(handler->prefix) != strlen(prefix))
+		return false;
+
+	if (memcmp(handler->prefix, prefix, strlen(prefix)))
+		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_gw_result *result)
+{
+	while (result->data[result->offset] == ' ')
+		result->offset++;
+}
+
+static bool call_prefix_handler(struct hfp_gw *hfp, const char *data)
+{
+	struct cmd_handler *handler;
+	const char *separators = ";?=\0";
+	struct hfp_gw_result result;
+	enum hfp_gw_cmd_type type;
+	char lookup_prefix[18];
+	uint8_t pref_len = 0;
+	const char *prefix;
+	int i;
+
+	result.offset = 0;
+	result.data = data;
+
+	skip_whitespace(&result);
+
+	if (strlen(data + result.offset) < 3)
+		return false;
+
+	if (strncmp(data + result.offset, "AT", 2))
+		if (strncmp(data + result.offset, "at", 2))
+			return false;
+
+	result.offset += 2;
+	prefix = data + result.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';
+	result.offset += pref_len;
+
+	if (lookup_prefix[0] == 'D') {
+		type = HFP_GW_CMD_TYPE_SET;
+		goto done;
+	}
+
+	if (data[result.offset] == '=') {
+		result.offset++;
+		if (data[result.offset] == '?') {
+			result.offset++;
+			type = HFP_GW_CMD_TYPE_TEST;
+		} else {
+			type = HFP_GW_CMD_TYPE_SET;
+		}
+		goto done;
+	}
+
+	if (data[result.offset] == '?') {
+		result.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)
+		return false;
+
+	handler->callback(&result, type, handler->user_data);
+
+	return true;
+}
+
+static void next_field(struct hfp_gw_result *result)
+{
+	if (result->data[result->offset] == ',')
+		result->offset++;
+}
+
+bool hfp_gw_result_get_number_default(struct hfp_gw_result *result,
+						unsigned int *val,
+						unsigned int default_val)
+{
+	skip_whitespace(result);
+
+	if (result->data[result->offset] == ',') {
+		if (val)
+			*val = default_val;
+
+		result->offset++;
+		return true;
+	}
+
+	return hfp_gw_result_get_number(result, val);
+}
+
+bool hfp_gw_result_get_number(struct hfp_gw_result *result, unsigned int *val)
+{
+	unsigned int i;
+	int tmp = 0;
+
+	skip_whitespace(result);
+
+	i = result->offset;
+
+	while (result->data[i] >= '0' && result->data[i] <= '9')
+		tmp = tmp * 10 + result->data[i++] - '0';
+
+	if (i == result->offset)
+		return false;
+
+	if (val)
+		*val = tmp;
+	result->offset = i;
+
+	skip_whitespace(result);
+	next_field(result);
+
+	return true;
+}
+
+bool hfp_gw_result_open_container(struct hfp_gw_result *result)
+{
+	skip_whitespace(result);
+
+	/* The list shall be preceded by a left parenthesis "(") */
+	if (result->data[result->offset] != '(')
+		return false;
+
+	result->offset++;
+
+	return true;
+}
+
+bool hfp_gw_result_close_container(struct hfp_gw_result *result)
+{
+	skip_whitespace(result);
+
+	/* The list shall be followed by a right parenthesis (")" V250 5.7.3.1*/
+	if (result->data[result->offset] != ')')
+		return false;
+
+	result->offset++;
+
+	return true;
+}
+
+bool hfp_gw_result_get_string(struct hfp_gw_result *result, char *buf,
+								uint8_t len)
+{
+	int i = 0;
+	const char *data = result->data;
+	unsigned int offset;
+
+	skip_whitespace(result);
+
+	if (data[result->offset] != '"')
+		return false;
+
+	offset = result->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;
+
+	result->offset = offset;
+
+	skip_whitespace(result);
+	next_field(result);
+
+	return true;
+}
+
+bool hfp_gw_result_get_unquoted_string(struct hfp_gw_result *result, char *buf,
+								uint8_t len)
+{
+	const char *data = result->data;
+	unsigned int offset;
+	int i = 0;
+	char c;
+
+	skip_whitespace(result);
+
+	c = data[result->offset];
+	if (c == '"' || c == ')' || c == '(')
+		return false;
+
+	offset = result->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';
+
+	result->offset = offset;
+
+	next_field(result);
+
+	return true;
+}
+
+bool hfp_gw_result_has_next(struct hfp_gw_result *result)
+{
+	return result->data[result->offset] != '\0';
+}
+
+static void process_input(struct hfp_gw *hfp)
+{
+	char *str, *ptr;
+	size_t len, count;
+
+	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 = asprintf(&ptr, "%s%s", str, str2);
+		str = ptr;
+	} else {
+		count = ptr - str;
+		*ptr = '\0';
+	}
+
+	hfp->result_pending = true;
+
+	if (!call_prefix_handler(hfp, str)) {
+		if (hfp->command_callback)
+			hfp->command_callback(str, hfp->command_data);
+		else
+			hfp_gw_send_result(hfp, HFP_RESULT_ERROR);
+	}
+
+	len = ringbuf_drain(hfp->read_buf, count + 1);
+
+	if (str == ptr)
+		free(ptr);
+}
+
+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);
+	if (!hfp)
+		return NULL;
+
+	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 (!hfp->cmd_handlers) {
+		io_destroy(hfp->io);
+		ringbuf_free(hfp->write_buf);
+		ringbuf_free(hfp->read_buf);
+		free(hfp);
+		return NULL;
+	}
+
+	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->permissive_syntax = 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_set_permissive_syntax(struct hfp_gw *hfp, bool permissive)
+{
+	if (!hfp)
+		return false;
+
+	hfp->permissive_syntax = permissive;
+
+	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;
+	default:
+		return false;
+	}
+
+	if (ringbuf_printf(hfp->write_buf, "\r\n%s\r\n", str) < 0)
+		return false;
+
+	wakeup_writer(hfp);
+
+	hfp->result_pending = false;
+
+	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);
+
+	hfp->result_pending = false;
+
+	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);
+	if (!handler)
+		return false;
+
+	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);
+}
diff --git a/bluez/src/shared/hfp.h b/bluez/src/shared/hfp.h
new file mode 100644
index 0000000..743db65
--- /dev/null
+++ b/bluez/src/shared/hfp.h
@@ -0,0 +1,126 @@
+/*
+ *
+ *  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,
+};
+
+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_gw_result;
+
+typedef void (*hfp_result_func_t)(struct hfp_gw_result *result,
+				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_gw_result_get_number(struct hfp_gw_result *result, unsigned int *val);
+bool hfp_gw_result_get_number_default(struct hfp_gw_result *result,
+						unsigned int *val,
+						unsigned int default_val);
+bool hfp_gw_result_open_container(struct hfp_gw_result *result);
+bool hfp_gw_result_close_container(struct hfp_gw_result *result);
+bool hfp_gw_result_get_string(struct hfp_gw_result *result, char *buf,
+								uint8_t len);
+bool hfp_gw_result_get_unquoted_string(struct hfp_gw_result *result, char *buf,
+								uint8_t len);
+bool hfp_gw_result_has_next(struct hfp_gw_result *result);
diff --git a/bluez/src/shared/io-glib.c b/bluez/src/shared/io-glib.c
new file mode 100644
index 0000000..6316037
--- /dev/null
+++ b/bluez/src/shared/io-glib.c
@@ -0,0 +1,334 @@
+/*
+ *
+ *  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 <glib.h>
+
+#include "src/shared/io.h"
+
+struct io {
+	int ref_count;
+	GIOChannel *channel;
+	guint read_watch;
+	io_callback_func_t read_callback;
+	io_destroy_func_t read_destroy;
+	void *read_data;
+	guint write_watch;
+	io_callback_func_t write_callback;
+	io_destroy_func_t write_destroy;
+	void *write_data;
+	guint disconnect_watch;
+	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;
+
+	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);
+
+	io->read_watch = 0;
+	io->read_callback = NULL;
+	io->read_destroy = NULL;
+	io->read_data = NULL;
+
+	io->write_watch = 0;
+	io->write_callback = NULL;
+	io->write_destroy = NULL;
+	io->write_data = NULL;
+
+	return io_ref(io);
+}
+
+void io_destroy(struct io *io)
+{
+	if (!io)
+		return;
+
+	if (io->read_watch > 0) {
+		g_source_remove(io->read_watch);
+		io->read_watch = 0;
+	}
+
+	if (io->write_watch > 0) {
+		g_source_remove(io->write_watch);
+		io->write_watch = 0;
+	}
+
+	g_io_channel_unref(io->channel);
+	io->channel = NULL;
+
+	io_unref(io);
+}
+
+int io_get_fd(struct io *io)
+{
+	if (!io)
+		return -1;
+
+	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 void read_watch_destroy(gpointer user_data)
+{
+	struct io *io = user_data;
+
+	if (io->read_destroy)
+		io->read_destroy(io->read_data);
+
+	io->read_watch = 0;
+	io->read_callback = NULL;
+	io->read_destroy = NULL;
+	io->read_data = NULL;
+
+	io_unref(io);
+}
+
+static gboolean read_callback(GIOChannel *channel, GIOCondition cond,
+							gpointer user_data)
+{
+	struct io *io = user_data;
+	bool result;
+
+	if (cond & (G_IO_ERR | G_IO_NVAL))
+		return FALSE;
+
+	if (io->read_callback)
+		result = io->read_callback(io, io->read_data);
+	else
+		result = false;
+
+	return result ? TRUE : FALSE;
+}
+
+bool io_set_read_handler(struct io *io, io_callback_func_t callback,
+				void *user_data, io_destroy_func_t destroy)
+{
+	if (!io)
+		return false;
+
+	if (io->read_watch > 0) {
+		g_source_remove(io->read_watch);
+		io->read_watch = 0;
+	}
+
+	if (!callback)
+		goto done;
+
+	io->read_watch = g_io_add_watch_full(io->channel, G_PRIORITY_DEFAULT,
+						G_IO_IN | G_IO_ERR | G_IO_NVAL,
+						read_callback, io_ref(io),
+						read_watch_destroy);
+	if (io->read_watch == 0)
+		return false;
+
+	io->read_destroy = destroy;
+	io->read_data = user_data;
+
+done:
+	io->read_callback = callback;
+
+	return true;
+}
+
+static void write_watch_destroy(gpointer user_data)
+{
+	struct io *io = user_data;
+
+	if (io->write_destroy)
+		io->write_destroy(io->write_data);
+
+	io->write_watch = 0;
+	io->write_callback = NULL;
+	io->write_destroy = NULL;
+	io->write_data = NULL;
+
+	io_unref(io);
+}
+
+static gboolean write_callback(GIOChannel *channel, GIOCondition cond,
+							gpointer user_data)
+{
+	struct io *io = user_data;
+	bool result;
+
+	if (cond & (G_IO_ERR | G_IO_NVAL))
+		return FALSE;
+
+	if (io->write_callback)
+		result = io->write_callback(io, io->write_data);
+	else
+		result = false;
+
+	return result ? TRUE : FALSE;
+}
+
+bool io_set_write_handler(struct io *io, io_callback_func_t callback,
+				void *user_data, io_destroy_func_t destroy)
+{
+	if (!io)
+		return false;
+
+	if (io->write_watch > 0) {
+		g_source_remove(io->write_watch);
+		io->write_watch = 0;
+	}
+
+	if (!callback)
+		goto done;
+
+	io->write_watch = g_io_add_watch_full(io->channel, G_PRIORITY_DEFAULT,
+						G_IO_OUT | G_IO_ERR | G_IO_NVAL,
+						write_callback, io_ref(io),
+						write_watch_destroy);
+	if (io->write_watch == 0)
+		return false;
+
+	io->write_destroy = destroy;
+	io->write_data = user_data;
+
+done:
+	io->write_callback = callback;
+
+	return true;
+}
+
+static void disconnect_watch_destroy(gpointer user_data)
+{
+	struct io *io = user_data;
+
+	if (io->disconnect_destroy)
+		io->disconnect_destroy(io->disconnect_data);
+
+	io->disconnect_watch = 0;
+	io->disconnect_callback = NULL;
+	io->disconnect_destroy = NULL;
+	io->disconnect_data = NULL;
+
+	io_unref(io);
+}
+
+static gboolean disconnect_callback(GIOChannel *channel, GIOCondition cond,
+							gpointer user_data)
+{
+	struct io *io = user_data;
+	bool result;
+
+	if (io->disconnect_callback)
+		result = io->disconnect_callback(io, io->disconnect_data);
+	else
+		result = false;
+
+	return result ? TRUE : FALSE;
+}
+
+bool io_set_disconnect_handler(struct io *io, io_callback_func_t callback,
+				void *user_data, io_destroy_func_t destroy)
+{
+	if (!io)
+		return false;
+
+	if (io->disconnect_watch > 0) {
+		g_source_remove(io->disconnect_watch);
+		io->disconnect_watch = 0;
+	}
+
+	if (!callback)
+		goto done;
+
+	io->disconnect_watch = g_io_add_watch_full(io->channel,
+						G_PRIORITY_DEFAULT,
+						G_IO_HUP | G_IO_ERR | G_IO_NVAL,
+						disconnect_callback, io_ref(io),
+						disconnect_watch_destroy);
+	if (io->disconnect_watch == 0)
+		return false;
+
+	io->disconnect_destroy = destroy;
+	io->disconnect_data = user_data;
+
+done:
+	io->disconnect_callback = callback;
+
+	return true;
+}
+
+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/src/shared/io-mainloop.c b/bluez/src/shared/io-mainloop.c
new file mode 100644
index 0000000..3e33d88
--- /dev/null
+++ b/bluez/src/shared/io-mainloop.c
@@ -0,0 +1,304 @@
+/*
+ *
+ *  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 <sys/socket.h>
+
+#include "monitor/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;
+
+	if ((events & (EPOLLRDHUP | EPOLLHUP | EPOLLERR))) {
+		io->read_callback = NULL;
+		io->write_callback = NULL;
+
+		if (!io->disconnect_callback) {
+			mainloop_remove_fd(io->fd);
+			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);
+		}
+	}
+}
+
+struct io *io_new(int fd)
+{
+	struct io *io;
+
+	if (fd < 0)
+		return NULL;
+
+	io = new0(struct io, 1);
+	if (!io)
+		return NULL;
+
+	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 -1;
+
+	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;
+}
+
+bool io_shutdown(struct io *io)
+{
+	if (!io || io->fd < 0)
+		return false;
+
+	return shutdown(io->fd, SHUT_RDWR) == 0;
+}
diff --git a/bluez/src/shared/io.h b/bluez/src/shared/io.h
new file mode 100644
index 0000000..8897964
--- /dev/null
+++ b/bluez/src/shared/io.h
@@ -0,0 +1,45 @@
+/*
+ *
+ *  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 (*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);
+
+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/src/shared/mgmt.c b/bluez/src/shared/mgmt.c
new file mode 100644
index 0000000..ae90b89
--- /dev/null
+++ b/bluez/src/shared/mgmt.c
@@ -0,0 +1,813 @@
+/*
+ *
+ *  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;
+	bool destroyed;
+	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 can_write_data(struct io *io, void *user_data)
+{
+	struct mgmt *mgmt = user_data;
+	struct mgmt_request *request;
+	ssize_t bytes_written;
+
+	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;
+	}
+
+	bytes_written = write(mgmt->fd, request->buf, request->len);
+	if (bytes_written < 0) {
+		util_debug(mgmt->debug_callback, mgmt->debug_data,
+				"write failed: %s", strerror(errno));
+		if (request->callback)
+			request->callback(MGMT_STATUS_FAILED, 0, NULL,
+							request->user_data);
+		destroy_request(request);
+		return true;
+	}
+
+	util_debug(mgmt->debug_callback, mgmt->debug_data,
+				"[0x%04x] command 0x%04x",
+				request->index, request->opcode);
+
+	util_hexdump('<', request->buf, bytes_written,
+				mgmt->debug_callback, mgmt->debug_data);
+
+	queue_push_tail(mgmt->pending_list, request);
+
+	return false;
+}
+
+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;
+
+	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);
+	}
+
+	if (mgmt->destroyed)
+		return;
+
+	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 void read_watch_destroy(void *user_data)
+{
+	struct mgmt *mgmt = user_data;
+
+	if (mgmt->destroyed) {
+		queue_destroy(mgmt->notify_list, NULL);
+		queue_destroy(mgmt->pending_list, NULL);
+		free(mgmt);
+	}
+}
+
+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;
+
+	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;
+	}
+
+	if (mgmt->destroyed)
+		return false;
+
+	return true;
+}
+
+struct mgmt *mgmt_new(int fd)
+{
+	struct mgmt *mgmt;
+
+	if (fd < 0)
+		return NULL;
+
+	mgmt = new0(struct mgmt, 1);
+	if (!mgmt)
+		return NULL;
+
+	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();
+	if (!mgmt->request_queue) {
+		io_destroy(mgmt->io);
+		free(mgmt->buf);
+		free(mgmt);
+		return NULL;
+	}
+
+	mgmt->reply_queue = queue_new();
+	if (!mgmt->reply_queue) {
+		queue_destroy(mgmt->request_queue, NULL);
+		io_destroy(mgmt->io);
+		free(mgmt->buf);
+		free(mgmt);
+		return NULL;
+	}
+
+	mgmt->pending_list = queue_new();
+	if (!mgmt->pending_list) {
+		queue_destroy(mgmt->reply_queue, NULL);
+		queue_destroy(mgmt->request_queue, NULL);
+		io_destroy(mgmt->io);
+		free(mgmt->buf);
+		free(mgmt);
+		return NULL;
+	}
+
+	mgmt->notify_list = queue_new();
+	if (!mgmt->notify_list) {
+		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;
+	}
+
+	if (!io_set_read_handler(mgmt->io, can_read_data, mgmt,
+						read_watch_destroy)) {
+		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;
+	}
+
+	mgmt->destroyed = true;
+}
+
+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);
+	if (!request)
+		return NULL;
+
+	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_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);
+	if (!notify)
+		return 0;
+
+	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/src/shared/mgmt.h b/bluez/src/shared/mgmt.h
new file mode 100644
index 0000000..626a699
--- /dev/null
+++ b/bluez/src/shared/mgmt.h
@@ -0,0 +1,69 @@
+/*
+ *
+ *  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_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/src/shared/pcap.c b/bluez/src/shared/pcap.c
new file mode 100644
index 0000000..bd7675f
--- /dev/null
+++ b/bluez/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/src/shared/pcap.h b/bluez/src/shared/pcap.h
new file mode 100644
index 0000000..b47de62
--- /dev/null
+++ b/bluez/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/src/shared/queue.c b/bluez/src/shared/queue.c
new file mode 100644
index 0000000..8a69729
--- /dev/null
+++ b/bluez/src/shared/queue.c
@@ -0,0 +1,346 @@
+/*
+ *
+ *  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_entry {
+	void *data;
+	struct queue_entry *next;
+};
+
+struct queue {
+	struct queue_entry *head;
+	struct queue_entry *tail;
+	unsigned int entries;
+};
+
+struct queue *queue_new(void)
+{
+	struct queue *queue;
+
+	queue = new0(struct queue, 1);
+	if (!queue)
+		return NULL;
+
+	queue->head = NULL;
+	queue->tail = NULL;
+	queue->entries = 0;
+
+	return queue;
+}
+
+void queue_destroy(struct queue *queue, queue_destroy_func_t destroy)
+{
+	struct queue_entry *entry;
+
+	if (!queue)
+		return;
+
+	entry = queue->head;
+
+	while (entry) {
+		struct queue_entry *tmp = entry;
+
+		if (destroy)
+			destroy(entry->data);
+
+		entry = entry->next;
+
+		free(tmp);
+	}
+
+	free(queue);
+}
+
+bool queue_push_tail(struct queue *queue, void *data)
+{
+	struct queue_entry *entry;
+
+	if (!queue)
+		return false;
+
+	entry = new0(struct queue_entry, 1);
+	if (!entry)
+		return false;
+
+	entry->data = data;
+	entry->next = NULL;
+
+	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 = new0(struct queue_entry, 1);
+	if (!entry)
+		return false;
+
+	entry->data = data;
+	entry->next = queue->head;
+
+	queue->head = entry;
+
+	if (!queue->tail)
+		queue->tail = 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;
+
+	while (entry) {
+		struct queue_entry *tmp = entry;
+
+		entry = tmp->next;
+
+		function(tmp->data, user_data);
+	}
+}
+
+void *queue_find(struct queue *queue, queue_match_func_t function,
+							void *user_data)
+{
+	struct queue_entry *entry;
+
+	if (!queue || !function)
+		return NULL;
+
+	for (entry = queue->head; entry; entry = entry->next)
+		if (function(entry->data, user_data))
+			return entry->data;
+
+	return NULL;
+}
+
+bool queue_remove(struct queue *queue, void *data)
+{
+	struct queue_entry *entry, *prev;
+
+	if (!queue || !data)
+		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) {
+		struct queue_entry *prev = NULL;
+
+		while (entry) {
+			if (function(entry->data, user_data)) {
+				struct queue_entry *tmp = entry;
+
+				if (prev)
+					prev->next = entry->next;
+				else
+					queue->head = entry->next;
+
+				if (!entry->next)
+					queue->tail = prev;
+
+				entry = entry->next;
+
+				if (destroy)
+					destroy(tmp->data);
+
+				free(tmp);
+				count++;
+			} else {
+				prev = entry;
+				entry = entry->next;
+			}
+		}
+
+		queue->entries -= count;
+	} else {
+		while (entry) {
+			struct queue_entry *tmp = entry;
+
+			entry = entry->next;
+
+			if (destroy)
+				destroy(tmp->data);
+
+			free(tmp);
+			count++;
+		}
+
+		queue->head = NULL;
+		queue->tail = NULL;
+		queue->entries = 0;
+	}
+
+	return count;
+}
+
+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/src/shared/queue.h b/bluez/src/shared/queue.h
new file mode 100644
index 0000000..8201ff8
--- /dev/null
+++ b/bluez/src/shared/queue.h
@@ -0,0 +1,56 @@
+/*
+ *
+ *  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 *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);
+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 *a, const void *b);
+
+void *queue_find(struct queue *queue, queue_match_func_t function,
+							void *user_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);
+
+unsigned int queue_length(struct queue *queue);
+bool queue_isempty(struct queue *queue);
diff --git a/bluez/src/shared/ringbuf.c b/bluez/src/shared/ringbuf.c
new file mode 100644
index 0000000..a11d2dc
--- /dev/null
+++ b/bluez/src/shared/ringbuf.c
@@ -0,0 +1,312 @@
+/*
+ *
+ *  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);
+	if (!ringbuf)
+		return NULL;
+
+	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/src/shared/ringbuf.h b/bluez/src/shared/ringbuf.h
new file mode 100644
index 0000000..adf471a
--- /dev/null
+++ b/bluez/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/src/shared/tester.c b/bluez/src/shared/tester.c
new file mode 100644
index 0000000..56e5696
--- /dev/null
+++ b/bluez/src/shared/tester.c
@@ -0,0 +1,809 @@
+/*
+ *
+ *  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>
+
+#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("%-45s " 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;
+	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->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_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);
+	if (!test) {
+		if (destroy)
+			destroy(user_data);
+		return;
+	}
+
+	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 void 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,
+			(float) passed * 100 / (not_run + passed + failed),
+			failed, not_run);
+
+	execution_time = g_timer_elapsed(test_timer, NULL);
+	printf("Overall execution time: %.3g seconds\n", execution_time);
+
+}
+
+static gboolean teardown_callback(gpointer user_data)
+{
+	struct test_case *test = user_data;
+
+	test->stage = TEST_STAGE_TEARDOWN;
+
+	print_progress(test->name, COLOR_MAGENTA, "teardown");
+	test->teardown_func(test->test_data);
+
+	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;
+
+	test->stage = TEST_STAGE_SETUP;
+
+	tester_setup_failed();
+}
+
+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;
+
+	if (test->timeout_id > 0) {
+		g_source_remove(test->timeout_id);
+		test->timeout_id = 0;
+	}
+
+	print_progress(test->name, COLOR_RED, "setup failed");
+
+	g_idle_add(done_callback, test);
+}
+
+void tester_test_passed(void)
+{
+	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 = TEST_RESULT_PASSED;
+	print_progress(test->name, COLOR_GREEN, "test passed");
+
+	g_idle_add(teardown_callback, test);
+}
+
+void tester_test_failed(void)
+{
+	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 = TEST_RESULT_FAILED;
+	print_progress(test->name, COLOR_RED, "test failed");
+
+	g_idle_add(teardown_callback, test);
+}
+
+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);
+	if (!wait)
+		return;
+
+	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 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;
+}
+
+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;
+
+	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);
+
+	tester_summarize();
+
+	g_list_free_full(test_list, test_destroy);
+
+	return EXIT_SUCCESS;
+}
diff --git a/bluez/src/shared/tester.h b/bluez/src/shared/tester.h
new file mode 100644
index 0000000..85d5e95
--- /dev/null
+++ b/bluez/src/shared/tester.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>
+
+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)));
+
+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_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/src/shared/timeout-glib.c b/bluez/src/shared/timeout-glib.c
new file mode 100644
index 0000000..4163bce
--- /dev/null
+++ b/bluez/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/src/shared/timeout-mainloop.c b/bluez/src/shared/timeout-mainloop.c
new file mode 100644
index 0000000..77aaa63
--- /dev/null
+++ b/bluez/src/shared/timeout-mainloop.c
@@ -0,0 +1,86 @@
+/*
+ *
+ *  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 "monitor/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);
+	if (!data)
+		return 0;
+
+	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/src/shared/timeout.h b/bluez/src/shared/timeout.h
new file mode 100644
index 0000000..4930ce1
--- /dev/null
+++ b/bluez/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/src/shared/util.c b/bluez/src/shared/util.c
new file mode 100644
index 0000000..eb90ecb
--- /dev/null
+++ b/bluez/src/shared/util.c
@@ -0,0 +1,90 @@
+/*
+ *
+ *  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 "src/shared/util.h"
+
+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);
+	}
+}
diff --git a/bluez/src/shared/util.h b/bluez/src/shared/util.h
new file mode 100644
index 0000000..4bc77a1
--- /dev/null
+++ b/bluez/src/shared/util.h
@@ -0,0 +1,162 @@
+/*
+ *
+ *  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>
+
+#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)			\
+({						\
+	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(t, n) ((t*) calloc((n), sizeof(t)))
+#define newa(t, n) ((t*) alloca(sizeof(t)*(n)))
+#define malloc0(n) (calloc((n), 1))
+
+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);
+
+static inline void bswap_128(const void *src, void *dst)
+{
+	const uint8_t *s = src;
+	uint8_t *d = dst;
+	int i;
+
+	for (i = 0; i < 16; i++)
+		d[15 - i] = s[i];
+}
+
+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/src/storage.c b/bluez/src/storage.c
new file mode 100644
index 0000000..b230e1e
--- /dev/null
+++ b/bluez/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 <bluetooth/bluetooth.h>
+#include <bluetooth/sdp.h>
+#include <bluetooth/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 + 1], *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 + 1], *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 + 1], *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 + 1], *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/src/storage.h b/bluez/src/storage.h
new file mode 100644
index 0000000..1c0ad57
--- /dev/null
+++ b/bluez/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/src/systemd.c b/bluez/src/systemd.c
new file mode 100644
index 0000000..f0985d1
--- /dev/null
+++ b/bluez/src/systemd.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 <errno.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <stddef.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/src/systemd.h b/bluez/src/systemd.h
new file mode 100644
index 0000000..0ef7c82
--- /dev/null
+++ b/bluez/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/src/textfile.c b/bluez/src/textfile.c
new file mode 100644
index 0000000..7267f3a
--- /dev/null
+++ b/bluez/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/src/textfile.h b/bluez/src/textfile.h
new file mode 100644
index 0000000..f01629e
--- /dev/null
+++ b/bluez/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/src/uinput.h b/bluez/src/uinput.h
new file mode 100644
index 0000000..20e0941
--- /dev/null
+++ b/bluez/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/src/uuid-helper.c b/bluez/src/uuid-helper.c
new file mode 100644
index 0000000..bce36b0
--- /dev/null
+++ b/bluez/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 <bluetooth/bluetooth.h>
+#include <bluetooth/sdp.h>
+#include <bluetooth/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/src/uuid-helper.h b/bluez/src/uuid-helper.h
new file mode 100644
index 0000000..c0d7f9e
--- /dev/null
+++ b/bluez/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/test/bluezutils.py b/bluez/test/bluezutils.py
new file mode 100644
index 0000000..de08cbd
--- /dev/null
+++ b/bluez/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/test/dbusdef.py b/bluez/test/dbusdef.py
new file mode 100644
index 0000000..f1cd35a
--- /dev/null
+++ b/bluez/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/test/ftp-client b/bluez/test/ftp-client
new file mode 100755
index 0000000..78c32b3
--- /dev/null
+++ b/bluez/test/ftp-client
@@ -0,0 +1,178 @@
+#!/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 properties['Status'] == 'complete' or \
+				properties['Status'] == 'error':
+			if self.verbose:
+				print("Transfer %s" % properties['Status'])
+			mainloop.quit()
+			return
+
+		if properties["Transferred"] == None:
+			return
+
+		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/test/list-devices b/bluez/test/list-devices
new file mode 100755
index 0000000..0aac217
--- /dev/null
+++ b/bluez/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/test/map-client b/bluez/test/map-client
new file mode 100755
index 0000000..b9695da
--- /dev/null
+++ b/bluez/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/test/monitor-bluetooth b/bluez/test/monitor-bluetooth
new file mode 100755
index 0000000..d9b5472
--- /dev/null
+++ b/bluez/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/test/opp-client b/bluez/test/opp-client
new file mode 100755
index 0000000..62d5b84
--- /dev/null
+++ b/bluez/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/test/pbap-client b/bluez/test/pbap-client
new file mode 100755
index 0000000..51e26eb
--- /dev/null
+++ b/bluez/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"])
+
+	mainloop.run()
diff --git a/bluez/test/sap_client.py b/bluez/test/sap_client.py
new file mode 100644
index 0000000..413424c
--- /dev/null
+++ b/bluez/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/test/service-did.xml b/bluez/test/service-did.xml
new file mode 100644
index 0000000..52eb68c
--- /dev/null
+++ b/bluez/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/test/service-ftp.xml b/bluez/test/service-ftp.xml
new file mode 100644
index 0000000..1bda885
--- /dev/null
+++ b/bluez/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/test/service-opp.xml b/bluez/test/service-opp.xml
new file mode 100644
index 0000000..351b4a4
--- /dev/null
+++ b/bluez/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/test/service-record.dtd b/bluez/test/service-record.dtd
new file mode 100644
index 0000000..f53be5d
--- /dev/null
+++ b/bluez/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/test/service-spp.xml b/bluez/test/service-spp.xml
new file mode 100644
index 0000000..2b156c3
--- /dev/null
+++ b/bluez/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/test/simple-agent b/bluez/test/simple-agent
new file mode 100755
index 0000000..a69299a
--- /dev/null
+++ b/bluez/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/test/simple-endpoint b/bluez/test/simple-endpoint
new file mode 100755
index 0000000..0164cff
--- /dev/null
+++ b/bluez/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/test/simple-player b/bluez/test/simple-player
new file mode 100755
index 0000000..23e78ad
--- /dev/null
+++ b/bluez/test/simple-player
@@ -0,0 +1,155 @@
+#!/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",
+					"LoopStatus" : "None",
+					"Rate" : dbus.Double(1.0),
+					"Shuffle" : dbus.Boolean(False),
+					"Metadata" : track,
+					"Volume" : dbus.Double(1.0),
+					"Position" : dbus.UInt32(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/test/simple-service b/bluez/test/simple-service
new file mode 100755
index 0000000..02d7648
--- /dev/null
+++ b/bluez/test/simple-service
@@ -0,0 +1,128 @@
+#!/usr/bin/python
+
+from __future__ import absolute_import, print_function, unicode_literals
+
+import sys
+import time
+import dbus
+import bluezutils
+
+xml = ' \
+<?xml version="1.0" encoding="UTF-8" ?> 	\
+<record>					\
+  <attribute id="0x0001">			\
+    <sequence>					\
+      <uuid value="0x1101"/>			\
+    </sequence>					\
+  </attribute>					\
+						\
+  <attribute id="0x0002">			\
+     <uint32 value="0"/>			\
+  </attribute>					\
+						\
+  <attribute id="0x0003">			\
+    <uuid value="00001101-0000-1000-8000-00805f9b34fb"/> \
+  </attribute>					\
+						\
+  <attribute id="0x0004">			\
+    <sequence>					\
+      <sequence>				\
+        <uuid value="0x0100"/>			\
+      </sequence>				\
+      <sequence>				\
+        <uuid value="0x0003"/>			\
+        <uint8 value="23"/>			\
+      </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"/>			\
+    </sequence>					\
+  </attribute>					\
+						\
+  <attribute id="0x0007">			\
+     <uint32 value="0"/>			\
+  </attribute>					\
+						\
+  <attribute id="0x0008">			\
+     <uint8 value="0xff"/>			\
+  </attribute>					\
+						\
+  <attribute id="0x0009">			\
+    <sequence>					\
+      <sequence>				\
+        <uuid value="0x1101"/>			\
+        <uint16 value="0x0100"/>		\
+      </sequence>				\
+    </sequence>					\
+  </attribute>					\
+						\
+  <attribute id="0x000a">			\
+    <url value="http://www.bluez.org/"/>	\
+  </attribute>					\
+						\
+  <attribute id="0x000b">			\
+    <url value="http://www.bluez.org/"/>	\
+  </attribute>					\
+						\
+  <attribute id="0x000c">			\
+    <url value="http://www.bluez.org/"/>	\
+  </attribute>					\
+						\
+  <attribute id="0x0100">			\
+    <text value="Serial Port"/>			\
+  </attribute>					\
+						\
+  <attribute id="0x0101">			\
+    <text value="Serial Port Service"/>		\
+  </attribute>					\
+						\
+  <attribute id="0x0102">			\
+     <text value="BlueZ"/>			\
+  </attribute>					\
+						\
+  <attribute id="0x0200">			\
+    <sequence>					\
+      <uint16 value="0x0100"/>			\
+    </sequence>					\
+  </attribute>					\
+						\
+  <attribute id="0x0201">			\
+     <uint32 value="0"/>			\
+  </attribute>					\
+</record>					\
+'
+
+bus = dbus.SystemBus()
+
+if len(sys.argv) > 1:
+	path = bluezutils.find_adapter(sys.argv[1]).object_path
+else:
+	path = bluezutils.find_adapter().object_path
+
+service = dbus.Interface(bus.get_object("org.bluez", path),
+						"org.bluez.Service")
+
+handle = service.AddRecord(xml)
+
+print("Service record with handle 0x%04x added" % (handle))
+
+print("Press CTRL-C to remove service record")
+
+try:
+	time.sleep(1000)
+	print("Terminating session")
+except:
+	pass
+
+service.RemoveRecord(dbus.UInt32(handle))
diff --git a/bluez/test/test-adapter b/bluez/test/test-adapter
new file mode 100755
index 0000000..959a437
--- /dev/null
+++ b/bluez/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/test/test-alert b/bluez/test/test-alert
new file mode 100755
index 0000000..43b3cf3
--- /dev/null
+++ b/bluez/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/test/test-cyclingspeed b/bluez/test/test-cyclingspeed
new file mode 100755
index 0000000..393f79c
--- /dev/null
+++ b/bluez/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/test/test-device b/bluez/test/test-device
new file mode 100755
index 0000000..b490d53
--- /dev/null
+++ b/bluez/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/test/test-discovery b/bluez/test/test-discovery
new file mode 100755
index 0000000..73b8161
--- /dev/null
+++ b/bluez/test/test-discovery
@@ -0,0 +1,158 @@
+#!/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])
+
+def property_changed(name, value):
+	if (name == "Discovering" and not value):
+		mainloop.quit()
+
+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")
+
+	bus.add_signal_receiver(property_changed,
+					dbus_interface = "org.bluez.Adapter1",
+					signal_name = "PropertyChanged")
+
+	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/test/test-health b/bluez/test/test-health
new file mode 100755
index 0000000..343f29c
--- /dev/null
+++ b/bluez/test/test-health
@@ -0,0 +1,232 @@
+#!/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 = adapter.GetProperties()["Devices"]
+
+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/test/test-health-sink b/bluez/test/test-health-sink
new file mode 100755
index 0000000..52be535
--- /dev/null
+++ b/bluez/test/test-health-sink
@@ -0,0 +1,99 @@
+#!/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()
+
+hdp_manager = dbus.Interface(bus.get_object(BUS_NAME, PATH),
+						HEALTH_MANAGER_INTERFACE)
+app_path = hdp_manager.CreateApplication({"DataType": dbus.types.UInt16(4103),
+					"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 = adapter.GetProperties()["Devices"]
+
+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/test/test-heartrate b/bluez/test/test-heartrate
new file mode 100755
index 0000000..5e4e7e5
--- /dev/null
+++ b/bluez/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/test/test-hfp b/bluez/test/test-hfp
new file mode 100755
index 0000000..a806043
--- /dev/null
+++ b/bluez/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/test/test-manager b/bluez/test/test-manager
new file mode 100755
index 0000000..4f5994f
--- /dev/null
+++ b/bluez/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/test/test-nap b/bluez/test/test-nap
new file mode 100755
index 0000000..00a2585
--- /dev/null
+++ b/bluez/test/test-nap
@@ -0,0 +1,44 @@
+#!/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
+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]
+
+server.Register(service, bridge)
+
+print("Server for %s registered for %s" % (service, bridge))
+
+print("Press CTRL-C to disconnect")
+
+try:
+	time.sleep(1000)
+	print("Terminating connection")
+except:
+	pass
+
+server.Unregister(service)
diff --git a/bluez/test/test-network b/bluez/test/test-network
new file mode 100755
index 0000000..6f09486
--- /dev/null
+++ b/bluez/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/test/test-profile b/bluez/test/test-profile
new file mode 100755
index 0000000..2791580
--- /dev/null
+++ b/bluez/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/test/test-proximity b/bluez/test/test-proximity
new file mode 100755
index 0000000..66b7bc2
--- /dev/null
+++ b/bluez/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/test/test-sap-server b/bluez/test/test-sap-server
new file mode 100755
index 0000000..ff178af
--- /dev/null
+++ b/bluez/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/test/test-thermometer b/bluez/test/test-thermometer
new file mode 100755
index 0000000..7e67c23
--- /dev/null
+++ b/bluez/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/tools/3dsp.c b/bluez/tools/3dsp.c
new file mode 100644
index 0000000..68dcbb5
--- /dev/null
+++ b/bluez/tools/3dsp.c
@@ -0,0 +1,478 @@
+/*
+ *
+ *  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/mainloop.h"
+#include "monitor/bt.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 LT_ADDR
+
+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 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");
+		shutdown_device();
+		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),
+					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");
+
+	bt_hci_register(hci_dev, BT_HCI_EVT_SYNC_TRAIN_RECEIVED,
+					sync_train_received, NULL, NULL);
+
+	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 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);
+
+		bt_hci_register(hci_dev, BT_HCI_EVT_TRUNCATED_PAGE_COMPLETE,
+					truncated_page_complete, 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");
+
+	shutdown_device();
+}
+
+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_glasses(void)
+{
+	struct bt_hci_cmd_inquiry cmd;
+	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_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);
+
+	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 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);
+}
+
+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 slave_page_response_timeout(const void *data, uint8_t size,
+							void *user_data)
+{
+	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_READ_SYNC_TRAIN_PARAMS, NULL, 0,
+							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 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 start_display(void)
+{
+	struct bt_hci_cmd_set_slave_broadcast cmd;
+	uint8_t bcastdata[20] = { LT_ADDR, 0x03, 0x11, 0x23, 0x42, };
+	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_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);
+
+	bt_hci_send(hci_dev, BT_HCI_CMD_SET_SLAVE_BROADCAST_DATA,
+			bcastdata, sizeof(bcastdata), NULL, 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),
+							NULL, 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/tools/amptest.c b/bluez/tools/amptest.c
new file mode 100644
index 0000000..6192f7e
--- /dev/null
+++ b/bluez/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 <sys/poll.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/hci.h>
+#include <bluetooth/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/tools/avinfo.c b/bluez/tools/avinfo.c
new file mode 100644
index 0000000..d237742
--- /dev/null
+++ b/bluez/tools/avinfo.c
@@ -0,0 +1,586 @@
+/*
+ *
+ *  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 "profiles/audio/a2dp-codecs.h"
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/hci.h>
+#include <bluetooth/hci_lib.h>
+#include <bluetooth/l2cap.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
+
+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));
+
+static void print_vendor(a2dp_vendor_codec_t *vendor)
+{
+	printf("\tMedia Codec: Vendor Specific A2DP Codec");
+
+	printf("\n\t\tVendor ID 0x%02x%02x%02x%02x", vendor->vendor_id[0],
+		vendor->vendor_id[1], vendor->vendor_id[2],
+		vendor->vendor_id[3]);
+
+	printf("\n\t\tVendor Specific Codec ID 0x%02x%02x\n",
+			vendor->codec_id[0], vendor->codec_id[1]);
+}
+
+
+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_VENDOR:
+		print_vendor((void *) cap->data);
+		break;
+	default:
+		printf("\tMedia Codec: Unknown\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_CONTENT_PROTECTION:
+		case AVDTP_MULTIPLEXING:
+			/* FIXME: Add proper functions */
+			break;
+		case AVDTP_MEDIA_CODEC:
+			print_media_codec((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);
+		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);
+		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/tools/avtest.c b/bluez/tools/avtest.c
new file mode 100644
index 0000000..541b3cd
--- /dev/null
+++ b/bluez/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 <bluetooth/bluetooth.h>
+#include <bluetooth/hci.h>
+#include <bluetooth/hci_lib.h>
+#include <bluetooth/l2cap.h>
+#include <bluetooth/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/tools/bccmd.1 b/bluez/tools/bccmd.1
new file mode 100644
index 0000000..26c83a6
--- /dev/null
+++ b/bluez/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/tools/bccmd.c b/bluez/tools/bccmd.c
new file mode 100644
index 0000000..ff1b307
--- /dev/null
+++ b/bluez/tools/bccmd.c
@@ -0,0 +1,1229 @@
+/*
+ *
+ *  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 <bluetooth/bluetooth.h>
+#include <bluetooth/hci.h>
+#include <bluetooth/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;
+
+	handle = array[0] | (array[1] << 8);
+	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[130];
+	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 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"                 },
+	{ 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/tools/bdaddr.c b/bluez/tools/bdaddr.c
new file mode 100644
index 0000000..8356a8d
--- /dev/null
+++ b/bluez/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 <bluetooth/bluetooth.h>
+#include <bluetooth/hci.h>
+#include <bluetooth/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/tools/bluemoon.c b/bluez/tools/bluemoon.c
new file mode 100644
index 0000000..67d6d25
--- /dev/null
+++ b/bluez/tools/bluemoon.c
@@ -0,0 +1,541 @@
+/*
+ *
+ *  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/mainloop.h"
+#include "monitor/bt.h"
+#include "src/shared/util.h"
+#include "src/shared/hci.h"
+
+#define CMD_READ_VERSION	0xfc05
+struct rsp_read_version {
+	uint8_t  status;
+	uint8_t  hw_platform;
+	uint8_t  hw_variant;
+	uint8_t  hw_revision;
+	uint8_t  fw_variant;
+	uint8_t  fw_revision;
+	uint8_t  fw_build_nn;
+	uint8_t  fw_build_cw;
+	uint8_t  fw_build_yy;
+	uint8_t  fw_patch;
+} __attribute__ ((packed));
+
+#define CMD_MANUFACTURER_MODE	0xfc11
+struct cmd_manufacturer_mode {
+	uint8_t  mode_switch;
+	uint8_t  reset;
+} __attribute__ ((packed));
+
+#define CMD_WRITE_BD_DATA	0xfc2f
+struct cmd_write_bd_data {
+	uint8_t  bdaddr[6];
+	uint8_t  reserved1[6];
+	uint8_t  features[8];
+	uint8_t  le_features;
+	uint8_t  reserved2[32];
+	uint8_t  lmp_version;
+	uint8_t  reserved3[26];
+} __attribute__ ((packed));
+
+#define CMD_READ_BD_DATA	0xfc30
+struct rsp_read_bd_data {
+	uint8_t  status;
+	uint8_t  bdaddr[6];
+	uint8_t  reserved1[6];
+	uint8_t  features[8];
+	uint8_t  le_features;
+	uint8_t  reserved2[32];
+	uint8_t  lmp_version;
+	uint8_t  reserved3[26];
+} __attribute__ ((packed));
+
+#define CMD_WRITE_BD_ADDRESS	0xfc31
+struct cmd_write_bd_address {
+	uint8_t  bdaddr[6];
+} __attribute__ ((packed));
+
+#define CMD_ACT_DEACT_TRACES	0xfc43
+struct cmd_act_deact_traces {
+	uint8_t  tx_trace;
+	uint8_t  tx_arq;
+	uint8_t  rx_trace;
+} __attribute__ ((packed));
+
+static struct bt_hci *hci_dev;
+static uint16_t hci_index = 0;
+
+static bool set_bdaddr = false;
+static const char *set_bdaddr_value = NULL;
+
+static bool reset_on_exit = false;
+static bool use_manufacturer_mode = false;
+static bool get_bddata = false;
+static bool set_traces = false;
+
+static void reset_complete(const void *data, uint8_t size, void *user_data)
+{
+	uint8_t status = *((uint8_t *) data);
+
+	if (status) {
+		fprintf(stderr, "Failed to reset (0x%02x)\n", status);
+		mainloop_quit();
+		return;
+	}
+
+	mainloop_quit();
+}
+
+static void leave_manufacturer_mode_complete(const void *data, uint8_t size,
+							void *user_data)
+{
+	uint8_t status = *((uint8_t *) data);
+
+	if (status) {
+		fprintf(stderr, "Failed to leave manufacturer mode (0x%02x)\n",
+									status);
+		mainloop_quit();
+		return;
+	}
+
+	if (reset_on_exit) {
+		bt_hci_send(hci_dev, BT_HCI_CMD_RESET, NULL, 0,
+						reset_complete, NULL, NULL);
+		return;
+	}
+
+	mainloop_quit();
+}
+
+static void shutdown_device(void)
+{
+	bt_hci_flush(hci_dev);
+
+	if (use_manufacturer_mode) {
+		struct cmd_manufacturer_mode cmd;
+
+		cmd.mode_switch = 0x00;
+		cmd.reset = 0x00;
+
+		bt_hci_send(hci_dev, CMD_MANUFACTURER_MODE, &cmd, sizeof(cmd),
+				leave_manufacturer_mode_complete, NULL, NULL);
+		return;
+	}
+
+	if (reset_on_exit) {
+		bt_hci_send(hci_dev, BT_HCI_CMD_RESET, NULL, 0,
+						reset_complete, NULL, NULL);
+		return;
+	}
+
+	mainloop_quit();
+}
+
+static void write_bd_address_complete(const void *data, uint8_t size,
+							void *user_data)
+{
+	uint8_t status = *((uint8_t *) data);
+
+	if (status) {
+		fprintf(stderr, "Failed to write address (0x%02x)\n", status);
+		mainloop_quit();
+		return;
+	}
+
+	shutdown_device();
+}
+
+static void read_bd_addr_complete(const void *data, uint8_t size,
+							void *user_data)
+{
+	const struct bt_hci_rsp_read_bd_addr *rsp = data;
+	struct cmd_write_bd_address cmd;
+
+	if (rsp->status) {
+		fprintf(stderr, "Failed to read address (0x%02x)\n",
+							rsp->status);
+		mainloop_quit();
+		shutdown_device();
+		return;
+	}
+
+	if (set_bdaddr_value) {
+		fprintf(stderr, "Setting address is not supported\n");
+		mainloop_quit();
+		return;
+	}
+
+	printf("Controller Address\n");
+	printf("\tOld BD_ADDR: %2.2X:%2.2X:%2.2X:%2.2X:%2.2X:%2.2X\n",
+					rsp->bdaddr[5], rsp->bdaddr[4],
+					rsp->bdaddr[3], rsp->bdaddr[2],
+					rsp->bdaddr[1], rsp->bdaddr[0]);
+
+	memcpy(cmd.bdaddr, rsp->bdaddr, 6);
+	cmd.bdaddr[0] = (hci_index & 0xff);
+
+	printf("\tNew BD_ADDR: %2.2X:%2.2X:%2.2X:%2.2X:%2.2X:%2.2X\n",
+					cmd.bdaddr[5], cmd.bdaddr[4],
+					cmd.bdaddr[3], cmd.bdaddr[2],
+					cmd.bdaddr[1], cmd.bdaddr[0]);
+
+	bt_hci_send(hci_dev, CMD_WRITE_BD_ADDRESS, &cmd, sizeof(cmd),
+					write_bd_address_complete, NULL, NULL);
+}
+
+static void act_deact_traces_complete(const void *data, uint8_t size,
+							void *user_data)
+{
+	uint8_t status = *((uint8_t *) data);
+
+	if (status) {
+		fprintf(stderr, "Failed to activate traces (0x%02x)\n", status);
+		shutdown_device();
+		return;
+	}
+
+	shutdown_device();
+}
+
+static void act_deact_traces(void)
+{
+	struct cmd_act_deact_traces cmd;
+
+	cmd.tx_trace = 0x03;
+	cmd.tx_arq = 0x03;
+	cmd.rx_trace = 0x03;
+
+	bt_hci_send(hci_dev, CMD_ACT_DEACT_TRACES, &cmd, sizeof(cmd),
+					act_deact_traces_complete, NULL, NULL);
+}
+
+static void write_bd_data_complete(const void *data, uint8_t size,
+							void *user_data)
+{
+	uint8_t status = *((uint8_t *) data);
+
+	if (status) {
+		fprintf(stderr, "Failed to write data (0x%02x)\n", status);
+		shutdown_device();
+		return;
+	}
+
+	if (set_traces) {
+		act_deact_traces();
+		return;
+	}
+
+	shutdown_device();
+}
+
+static void read_bd_data_complete(const void *data, uint8_t size,
+							void *user_data)
+{
+	const struct rsp_read_bd_data *rsp = data;
+
+	if (rsp->status) {
+		fprintf(stderr, "Failed to read data (0x%02x)\n", rsp->status);
+		shutdown_device();
+		return;
+	}
+
+	printf("Controller Data\n");
+	printf("\tBD_ADDR: %2.2X:%2.2X:%2.2X:%2.2X:%2.2X:%2.2X\n",
+					rsp->bdaddr[5], rsp->bdaddr[4],
+					rsp->bdaddr[3], rsp->bdaddr[2],
+					rsp->bdaddr[1], rsp->bdaddr[0]);
+
+	printf("\tLMP Version: %u\n", rsp->lmp_version);
+	printf("\tLMP Features: 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",
+					rsp->features[0], rsp->features[1],
+					rsp->features[2], rsp->features[3],
+					rsp->features[4], rsp->features[5],
+					rsp->features[6], rsp->features[7]);
+	printf("\tLE Features: 0x%2.2x\n", rsp->le_features);
+
+	if (set_bdaddr) {
+		struct cmd_write_bd_data cmd;
+
+		memcpy(cmd.bdaddr, rsp->bdaddr, 6);
+		cmd.bdaddr[0] = (hci_index & 0xff);
+		cmd.lmp_version = 0x07;
+		memcpy(cmd.features, rsp->features, 8);
+		cmd.le_features = rsp->le_features;
+		cmd.le_features |= 0x1e;
+		memcpy(cmd.reserved1, rsp->reserved1, sizeof(cmd.reserved1));
+		memcpy(cmd.reserved2, rsp->reserved2, sizeof(cmd.reserved2));
+		memcpy(cmd.reserved3, rsp->reserved3, sizeof(cmd.reserved3));
+
+		bt_hci_send(hci_dev, CMD_WRITE_BD_DATA, &cmd, sizeof(cmd),
+					write_bd_data_complete, NULL, NULL);
+		return;
+	}
+
+	shutdown_device();
+}
+
+static void enter_manufacturer_mode_complete(const void *data, uint8_t size,
+							void *user_data)
+{
+	uint8_t status = *((uint8_t *) data);
+
+	if (status) {
+		fprintf(stderr, "Failed to enter manufacturer mode (0x%02x)\n",
+									status);
+		mainloop_quit();
+		return;
+	}
+
+	if (get_bddata || set_bdaddr) {
+		bt_hci_send(hci_dev, CMD_READ_BD_DATA, NULL, 0,
+					read_bd_data_complete, NULL, NULL);
+		return;
+	}
+
+	if (set_traces) {
+		act_deact_traces();
+		return;
+	}
+
+	shutdown_device();
+}
+
+static void read_version_complete(const void *data, uint8_t size,
+							void *user_data)
+{
+	const struct rsp_read_version *rsp = data;
+	const char *str;
+
+	if (rsp->status) {
+		fprintf(stderr, "Failed to read version (0x%02x)\n",
+							rsp->status);
+		mainloop_quit();
+		return;
+	}
+
+	if (use_manufacturer_mode) {
+		struct cmd_manufacturer_mode cmd;
+
+		cmd.mode_switch = 0x01;
+		cmd.reset = 0x00;
+
+		bt_hci_send(hci_dev, CMD_MANUFACTURER_MODE, &cmd, sizeof(cmd),
+				enter_manufacturer_mode_complete, NULL, NULL);
+		return;
+	}
+
+	if (set_bdaddr) {
+		bt_hci_send(hci_dev, BT_HCI_CMD_READ_BD_ADDR, NULL, 0,
+					read_bd_addr_complete, NULL, NULL);
+		return;
+	}
+
+	printf("Controller Version Information\n");
+	printf("\tHardware Platform:\t%u\n", rsp->hw_platform);
+
+	switch (rsp->hw_variant) {
+	case 0x07:
+		str = "iBT 2.0";
+		break;
+	default:
+		str = "Reserved";
+		break;
+	}
+
+	printf("\tHardware Variant:\t%s (0x%02x)\n", str, rsp->hw_variant);
+	printf("\tHardware Revision:\t%u.%u\n", rsp->hw_revision >> 4,
+						rsp->hw_revision & 0x0f);
+
+	switch (rsp->fw_variant) {
+	case 0x01:
+		str = "BT IP 4.0";
+		break;
+	case 0x06:
+		str = "iBT Bootloader";
+		break;
+	default:
+		str = "Reserved";
+		break;
+	}
+
+	printf("\tFirmware Variant:\t%s (0x%02x)\n", str, rsp->fw_variant);
+	printf("\tFirmware Revision:\t%u.%u\n", rsp->fw_revision >> 4,
+						rsp->fw_revision & 0x0f);
+	printf("\tFirmware Build Number:\t%u-%u.%u\n", rsp->fw_build_nn,
+				rsp->fw_build_cw, 2000 + rsp->fw_build_yy);
+	printf("\tFirmware Patch Number:\t%u\n", rsp->fw_patch);
+
+	mainloop_quit();
+}
+
+static void read_local_version_complete(const void *data, uint8_t size,
+							void *user_data)
+{
+	const struct bt_hci_rsp_read_local_version *rsp = data;
+	uint16_t manufacturer;
+
+	if (rsp->status) {
+		fprintf(stderr, "Failed to read local version (0x%02x)\n",
+								rsp->status);
+		mainloop_quit();
+		return;
+	}
+
+	manufacturer = le16_to_cpu(rsp->manufacturer);
+
+	if (manufacturer != 2) {
+		fprintf(stderr, "Unsupported manufacturer (%u)\n",
+							manufacturer);
+		mainloop_quit();
+		return;
+	}
+
+	bt_hci_send(hci_dev, CMD_READ_VERSION, NULL, 0,
+					read_version_complete, NULL, NULL);
+}
+
+static void signal_callback(int signum, void *user_data)
+{
+	switch (signum) {
+	case SIGINT:
+	case SIGTERM:
+		mainloop_quit();
+		break;
+	}
+}
+
+static void usage(void)
+{
+	printf("bluemoon - Bluemoon configuration utility\n"
+		"Usage:\n");
+	printf("\tbluemoon [options]\n");
+	printf("Options:\n"
+		"\t-B, --bdaddr [addr]    Set Bluetooth address\n"
+		"\t-R, --reset            Reset controller\n"
+		"\t-i, --index <num>      Use specified controller\n"
+		"\t-h, --help             Show help options\n");
+}
+
+static const struct option main_options[] = {
+	{ "bdaddr",  optional_argument, NULL, 'A' },
+	{ "bddata",  no_argument,       NULL, 'D' },
+	{ "traces",  no_argument,       NULL, 'T' },
+	{ "reset",   no_argument,       NULL, 'R' },
+	{ "index",   required_argument, NULL, 'i' },
+	{ "version", no_argument,       NULL, 'v' },
+	{ "help",    no_argument,       NULL, 'h' },
+	{ }
+};
+
+int main(int argc, char *argv[])
+{
+	const char *str;
+	sigset_t mask;
+	int exit_status;
+
+	for (;;) {
+		int opt;
+
+		opt = getopt_long(argc, argv, "A::DTRi:vh", main_options, NULL);
+		if (opt < 0)
+			break;
+
+		switch (opt) {
+		case 'A':
+			if (optarg)
+				set_bdaddr_value = optarg;
+			set_bdaddr = true;
+			break;
+		case 'D':
+			use_manufacturer_mode = true;
+			get_bddata = true;
+			break;
+		case 'T':
+			use_manufacturer_mode = true;
+			set_traces = true;
+			break;
+		case 'R':
+			reset_on_exit = 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;
+			}
+			hci_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;
+	}
+
+	mainloop_init();
+
+	sigemptyset(&mask);
+	sigaddset(&mask, SIGINT);
+	sigaddset(&mask, SIGTERM);
+
+	mainloop_set_signal(&mask, signal_callback, NULL, NULL);
+
+	printf("Bluemoon configuration utility ver %s\n", VERSION);
+
+	hci_dev = bt_hci_new_user_channel(hci_index);
+	if (!hci_dev) {
+		fprintf(stderr, "Failed to open HCI user channel\n");
+		return EXIT_FAILURE;
+	}
+
+	bt_hci_send(hci_dev, BT_HCI_CMD_READ_LOCAL_VERSION, NULL, 0,
+				read_local_version_complete, NULL, NULL);
+
+	exit_status = mainloop_run();
+
+	bt_hci_unref(hci_dev);
+
+	return exit_status;
+}
diff --git a/bluez/tools/bluetooth-player.c b/bluez/tools/bluetooth-player.c
new file mode 100644
index 0000000..f10d9be
--- /dev/null
+++ b/bluez/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.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/tools/btattach.c b/bluez/tools/btattach.c
new file mode 100644
index 0000000..a084440
--- /dev/null
+++ b/bluez/tools/btattach.c
@@ -0,0 +1,210 @@
+/*
+ *
+ *  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 <sys/poll.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/hci.h>
+#include <bluetooth/hci_lib.h>
+
+#include "hciattach.h"
+
+static int open_serial(const char *path)
+{
+	struct termios ti;
+	int fd, 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;
+	}
+
+	/* Switch TTY to raw mode */
+	memset(&ti, 0, sizeof(ti));
+	cfmakeraw(&ti);
+
+	ti.c_cflag |= (B115200 | CLOCAL | CREAD);
+
+	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;
+	}
+
+	return fd;
+}
+
+static int attach_proto(const char *path, unsigned int proto,
+						unsigned int flags)
+{
+	int fd;
+
+	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;
+	}
+
+	return fd;
+}
+
+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-h, --help             Show help options\n");
+}
+
+static const struct option main_options[] = {
+	{ "bredr",   required_argument, NULL, 'B' },
+	{ "amp",     required_argument, NULL, 'A' },
+	{ "version", no_argument,       NULL, 'v' },
+	{ "help",    no_argument,       NULL, 'h' },
+	{ }
+};
+
+int main(int argc, char *argv[])
+{
+	const char *bredr_path = NULL, *amp_path = NULL;
+	struct pollfd p[5];
+	int i, count = 0;
+
+	for (;;) {
+		int opt;
+
+		opt = getopt_long(argc, argv, "B:A:vh",
+						main_options, NULL);
+		if (opt < 0)
+			break;
+
+		switch (opt) {
+		case 'B':
+			bredr_path = optarg;
+			break;
+		case 'A':
+			amp_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 > 0) {
+		fprintf(stderr, "Invalid command line parameters\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);
+
+		fd = attach_proto(bredr_path, HCI_UART_H4, flags);
+		if (fd >= 0)
+			p[count++].fd = fd;
+	}
+
+	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);
+
+		fd = attach_proto(amp_path, HCI_UART_H4, flags);
+		if (fd >= 0)
+			p[count++].fd = fd;
+	}
+
+	if (count < 1) {
+		fprintf(stderr, "No controller attached\n");
+		return EXIT_FAILURE;
+	}
+
+	for (i = 0; i < count; i++) {
+		p[i].events = POLLERR | POLLHUP;
+		p[i].revents = 0;
+	}
+
+	while (1) {
+		if (poll(p, count, -1) < 0)
+			break;
+        }
+
+	for (i = 0; i < count; i++)
+		close(p[i].fd);
+
+	return EXIT_SUCCESS;
+}
diff --git a/bluez/tools/btinfo.c b/bluez/tools/btinfo.c
new file mode 100644
index 0000000..b838c25
--- /dev/null
+++ b/bluez/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/mainloop.h"
+#include "monitor/bt.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/tools/btiotest.c b/bluez/tools/btiotest.c
new file mode 100644
index 0000000..6a87ffd
--- /dev/null
+++ b/bluez/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 <bluetooth/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/tools/btmgmt.c b/bluez/tools/btmgmt.c
new file mode 100644
index 0000000..adb5747
--- /dev/null
+++ b/bluez/tools/btmgmt.c
@@ -0,0 +1,2244 @@
+/*
+ *  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 <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <errno.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <poll.h>
+#include <getopt.h>
+#include <stdbool.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/hci.h>
+#include <bluetooth/hci_lib.h>
+#include <bluetooth/sdp.h>
+#include <bluetooth/sdp_lib.h>
+
+#include "src/uuid-helper.h"
+#include "lib/mgmt.h"
+
+#include "monitor/mainloop.h"
+#include "src/shared/util.h"
+#include "src/shared/mgmt.h"
+
+static bool monitor = false;
+static bool discovery = false;
+static bool resolve_names = true;
+
+static int pending = 0;
+
+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)) {
+		fprintf(stderr,
+			"Too short (%u bytes) controller error event\n", len);
+		return;
+	}
+
+	if (monitor)
+		printf("hci%u error 0x%02x\n", index, ev->error_code);
+}
+
+static void index_added(uint16_t index, uint16_t len,
+				const void *param, void *user_data)
+{
+	if (monitor)
+		printf("hci%u added\n", index);
+}
+
+static void index_removed(uint16_t index, uint16_t len,
+				const void *param, void *user_data)
+{
+	if (monitor)
+		printf("hci%u removed\n", index);
+}
+
+static const char *settings_str[] = {
+				"powered",
+				"connectable",
+				"fast-connectable",
+				"discoverable",
+				"pairable",
+				"link-security",
+				"ssp",
+				"br/edr",
+				"hs",
+				"le",
+				"advertising",
+				"secure-conn",
+				"debug-keys",
+				"privacy",
+};
+
+static void print_settings(uint32_t settings)
+{
+	unsigned i;
+
+	for (i = 0; i < NELEM(settings_str); i++) {
+		if ((settings & (1 << i)) != 0)
+			printf("%s ", settings_str[i]);
+	}
+}
+
+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)) {
+		fprintf(stderr, "Too short new_settings event (%u)\n", len);
+		return;
+	}
+
+	if (monitor) {
+		printf("hci%u new_settings: ", index);
+		print_settings(get_le32(ev));
+		printf("\n");
+	}
+}
+
+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)) {
+		fprintf(stderr, "Too short (%u bytes) discovering event\n",
+									len);
+		return;
+	}
+
+	if (ev->discovering == 0 && discovery) {
+		mainloop_quit();
+		return;
+	}
+
+	if (monitor)
+		printf("hci%u type %u discovering %s\n", index,
+				ev->type, ev->discovering ? "on" : "off");
+}
+
+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;
+
+	if (len != sizeof(*ev)) {
+		fprintf(stderr, "Invalid new_link_key length (%u bytes)\n",
+									len);
+		return;
+	}
+
+	if (monitor) {
+		char addr[18];
+		ba2str(&ev->key.addr.bdaddr, addr);
+		printf("hci%u new_link_key %s type 0x%02x pin_len %d "
+				"store_hint %u\n", index, addr, ev->key.type,
+				ev->key.pin_len, ev->store_hint);
+	}
+}
+
+static const char *typestr(uint8_t type)
+{
+	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;
+
+	if (len < sizeof(*ev)) {
+		fprintf(stderr,
+			"Invalid connected event length (%u bytes)\n", len);
+		return;
+	}
+
+	eir_len = get_le16(&ev->eir_len);
+	if (len != sizeof(*ev) + eir_len) {
+		fprintf(stderr, "Invalid connected event length "
+			"(%u bytes, eir_len %u bytes)\n", len, eir_len);
+		return;
+	}
+
+	if (monitor) {
+		char addr[18];
+		ba2str(&ev->addr.bdaddr, addr);
+		printf("hci%u %s type %s connected eir_len %u\n", index, addr,
+					typestr(ev->addr.type), eir_len);
+	}
+}
+
+static void disconnected(uint16_t index, uint16_t len, const void *param,
+							void *user_data)
+{
+	const struct mgmt_ev_device_disconnected *ev = param;
+
+	if (len < sizeof(struct mgmt_addr_info)) {
+		fprintf(stderr,
+			"Invalid disconnected event length (%u bytes)\n", len);
+		return;
+	}
+
+	if (monitor) {
+		char addr[18];
+		uint8_t reason;
+
+		if (len < sizeof(*ev))
+			reason = MGMT_DEV_DISCONN_UNKNOWN;
+		else
+			reason = ev->reason;
+
+		ba2str(&ev->addr.bdaddr, addr);
+		printf("hci%u %s type %s disconnected with reason %u\n",
+				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;
+
+	if (len != sizeof(*ev)) {
+		fprintf(stderr,
+			"Invalid connect_failed event length (%u bytes)\n", len);
+		return;
+	}
+
+	if (monitor) {
+		char addr[18];
+		ba2str(&ev->addr.bdaddr, addr);
+		printf("hci%u %s type %s connect failed (status 0x%02x, %s)\n",
+				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;
+
+	if (len != sizeof(*ev)) {
+		fprintf(stderr,
+			"Invalid auth_failed event length (%u bytes)\n", len);
+		return;
+	}
+
+	if (monitor) {
+		char addr[18];
+		ba2str(&ev->addr.bdaddr, addr);
+		printf("hci%u %s auth failed with status 0x%02x (%s)\n",
+			index, addr, ev->status, mgmt_errstr(ev->status));
+	}
+}
+
+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)) {
+		fprintf(stderr,
+			"Invalid local_name_changed length (%u bytes)\n", len);
+		return;
+	}
+
+	if (monitor)
+		printf("hci%u name changed: %s\n", 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) {
+		fprintf(stderr,
+			"confirm_name failed with status 0x%02x (%s)\n",
+					status, mgmt_errstr(status));
+		return;
+	}
+
+	if (len != sizeof(*rp)) {
+		fprintf(stderr, "confirm_name rsp length %u instead of %zu\n",
+			len, sizeof(*rp));
+		return;
+	}
+
+	ba2str(&rp->addr.bdaddr, addr);
+
+	if (status != 0)
+		fprintf(stderr, "confirm_name for %s failed: 0x%02x (%s)\n",
+			addr, status, mgmt_errstr(status));
+	else
+		printf("confirm_name succeeded for %s\n", 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 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)) {
+		fprintf(stderr,
+			"Too short device_found length (%u bytes)\n", len);
+		return;
+	}
+
+	flags = btohl(ev->flags);
+
+	eir_len = get_le16(&ev->eir_len);
+	if (len != sizeof(*ev) + eir_len) {
+		fprintf(stderr, "dev_found: expected %zu bytes, got %u bytes\n",
+						sizeof(*ev) + eir_len, len);
+		return;
+	}
+
+	if (monitor || discovery) {
+		char addr[18], *name;
+
+		ba2str(&ev->addr.bdaddr, addr);
+		printf("hci%u dev_found: %s type %s rssi %d "
+			"flags 0x%04x ", index, addr,
+			typestr(ev->addr.type), ev->rssi, flags);
+
+		name = eir_get_name(ev->eir, eir_len);
+		if (name)
+			printf("name %s\n", name);
+		else
+			printf("eir_len %u\n", 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) {
+		fprintf(stderr,
+			"PIN Code reply failed with status 0x%02x (%s)\n",
+						status, mgmt_errstr(status));
+		mainloop_quit();
+		return;
+	}
+
+	printf("PIN Reply successful\n");
+}
+
+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) {
+		fprintf(stderr,
+			"PIN Neg reply failed with status 0x%02x (%s)\n",
+						status, mgmt_errstr(status));
+		mainloop_quit();
+		return;
+	}
+
+	printf("PIN Negative Reply successful\n");
+}
+
+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 request_pin(uint16_t index, uint16_t len, const void *param,
+							void *user_data)
+{
+	const struct mgmt_ev_pin_code_request *ev = param;
+	struct mgmt *mgmt = user_data;
+	char pin[18];
+	size_t pin_len;
+
+	if (len != sizeof(*ev)) {
+		fprintf(stderr,
+			"Invalid pin_code request length (%u bytes)\n", len);
+		return;
+	}
+
+	if (monitor) {
+		char addr[18];
+		ba2str(&ev->addr.bdaddr, addr);
+		printf("hci%u %s request PIN\n", index, addr);
+	}
+
+	printf("PIN Request (press enter to reject) >> ");
+	fflush(stdout);
+
+	memset(pin, 0, sizeof(pin));
+
+	if (fgets(pin, sizeof(pin), stdin) == NULL || pin[0] == '\n') {
+		mgmt_pin_neg_reply(mgmt, index, &ev->addr);
+		return;
+	}
+
+	pin_len = strlen(pin);
+	if (pin[pin_len - 1] == '\n') {
+		pin[pin_len - 1] = '\0';
+		pin_len--;
+	}
+
+	mgmt_pin_reply(mgmt, index, &ev->addr, pin, pin_len);
+}
+
+static void confirm_rsp(uint8_t status, uint16_t len, const void *param,
+							void *user_data)
+{
+	if (status != 0) {
+		fprintf(stderr,
+			"User Confirm reply failed. status 0x%02x (%s)\n",
+						status, mgmt_errstr(status));
+		mainloop_quit();
+		return;
+	}
+
+	printf("User Confirm Reply successful\n");
+}
+
+static int mgmt_confirm_reply(struct mgmt *mgmt, uint16_t index,
+							const bdaddr_t *bdaddr)
+{
+	struct mgmt_cp_user_confirm_reply cp;
+
+	memset(&cp, 0, sizeof(cp));
+	bacpy(&cp.addr.bdaddr, bdaddr);
+
+	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) {
+		fprintf(stderr,
+			"Confirm Neg reply failed. status 0x%02x (%s)\n",
+						status, mgmt_errstr(status));
+		mainloop_quit();
+		return;
+	}
+
+	printf("User Confirm Negative Reply successful\n");
+}
+
+static int mgmt_confirm_neg_reply(struct mgmt *mgmt, uint16_t index,
+							const bdaddr_t *bdaddr)
+{
+	struct mgmt_cp_user_confirm_reply cp;
+
+	memset(&cp, 0, sizeof(cp));
+	bacpy(&cp.addr.bdaddr, bdaddr);
+
+	return mgmt_reply(mgmt, MGMT_OP_USER_CONFIRM_NEG_REPLY, index,
+				sizeof(cp), &cp, confirm_neg_rsp, NULL, NULL);
+}
+
+
+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;
+	struct mgmt *mgmt = user_data;
+	char rsp[5];
+	size_t rsp_len;
+	uint32_t val;
+	char addr[18];
+
+	if (len != sizeof(*ev)) {
+		fprintf(stderr,
+			"Invalid user_confirm request length (%u)\n", len);
+		return;
+	}
+
+	ba2str(&ev->addr.bdaddr, addr);
+	val = get_le32(&ev->value);
+
+	if (monitor)
+		printf("hci%u %s User Confirm %06u hint %u\n", index, addr,
+							val, ev->confirm_hint);
+
+	if (ev->confirm_hint)
+		printf("Accept pairing with %s (yes/no) >> ", addr);
+	else
+		printf("Confirm value %06u for %s (yes/no) >> ", val, addr);
+
+	fflush(stdout);
+
+	memset(rsp, 0, sizeof(rsp));
+
+	if (fgets(rsp, sizeof(rsp), stdin) == NULL || rsp[0] == '\n') {
+		mgmt_confirm_neg_reply(mgmt, index, &ev->addr.bdaddr);
+		return;
+	}
+
+	rsp_len = strlen(rsp);
+	if (rsp[rsp_len - 1] == '\n')
+		rsp[rsp_len - 1] = '\0';
+
+	if (rsp[0] == 'y' || rsp[0] == 'Y')
+		mgmt_confirm_reply(mgmt, index, &ev->addr.bdaddr);
+	else
+		mgmt_confirm_neg_reply(mgmt, index, &ev->addr.bdaddr);
+}
+
+static void cmd_monitor(struct mgmt *mgmt, uint16_t index, int argc,
+								char **argv)
+{
+	printf("Monitoring mgmt events...\n");
+	monitor = true;
+}
+
+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) {
+		fprintf(stderr, "Reading mgmt version failed with status"
+			" 0x%02x (%s)\n", status, mgmt_errstr(status));
+		goto done;
+	}
+
+	if (len < sizeof(*rp)) {
+		fprintf(stderr, "Too small version reply (%u bytes)\n", len);
+		goto done;
+	}
+
+	printf("MGMT Version %u, revision %u\n", rp->version,
+						get_le16(&rp->revision));
+
+done:
+	mainloop_quit();
+}
+
+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) {
+		fprintf(stderr, "Unable to send read_version cmd\n");
+		exit(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) {
+		fprintf(stderr, "Reading supported commands failed with status"
+			" 0x%02x (%s)\n", status, mgmt_errstr(status));
+		goto done;
+	}
+
+	if (len < sizeof(*rp)) {
+		fprintf(stderr, "Too small commands reply (%u bytes)\n", 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) {
+		fprintf(stderr, "Too small commands reply (%u != %zu)\n",
+							len, expected_len);
+		goto done;
+	}
+
+	opcode = rp->opcodes;
+
+	printf("%u commands:\n", num_commands);
+	for (i = 0; i < num_commands; i++) {
+		uint16_t op = get_le16(opcode++);
+		printf("\t%s (0x%04x)\n", mgmt_opstr(op), op);
+	}
+
+	printf("%u events:\n", num_events);
+	for (i = 0; i < num_events; i++) {
+		uint16_t ev = get_le16(opcode++);
+		printf("\t%s (0x%04x)\n", mgmt_evstr(ev), ev);
+	}
+
+done:
+	mainloop_quit();
+}
+
+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) {
+		fprintf(stderr, "Unable to send read_commands cmd\n");
+		exit(EXIT_FAILURE);
+	}
+}
+
+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);
+	char addr[18];
+
+	pending--;
+
+	if (status != 0) {
+		fprintf(stderr,
+			"Reading hci%u info failed with status 0x%02x (%s)\n",
+					index, status, mgmt_errstr(status));
+		goto done;
+	}
+
+	if (len < sizeof(*rp)) {
+		fprintf(stderr, "Too small info reply (%u bytes)\n", len);
+		goto done;
+	}
+
+	ba2str(&rp->bdaddr, addr);
+	printf("hci%u:\taddr %s version %u manufacturer %u"
+			" class 0x%02x%02x%02x\n", index,
+			addr, rp->version, get_le16(&rp->manufacturer),
+			rp->dev_class[2], rp->dev_class[1], rp->dev_class[0]);
+
+	printf("\tsupported settings: ");
+	print_settings(get_le32(&rp->supported_settings));
+
+	printf("\n\tcurrent settings: ");
+	print_settings(get_le32(&rp->current_settings));
+
+	printf("\n\tname %s\n", rp->name);
+	printf("\tshort name %s\n", rp->short_name);
+
+	if (pending > 0)
+		return;
+
+done:
+	mainloop_quit();
+}
+
+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) {
+		fprintf(stderr,
+			"Reading index list failed with status 0x%02x (%s)\n",
+						status, mgmt_errstr(status));
+		goto done;
+	}
+
+	if (len < sizeof(*rp)) {
+		fprintf(stderr, "Too small index list reply (%u bytes)\n",
+									len);
+		goto done;
+	}
+
+	count = get_le16(&rp->num_controllers);
+
+	if (len < sizeof(*rp) + count * sizeof(uint16_t)) {
+		fprintf(stderr,
+			"Index count (%u) doesn't match reply length (%u)\n",
+								count, len);
+		goto done;
+	}
+
+	if (monitor)
+		printf("Index list with %u item%s\n",
+						count, count > 1 ? "s" : "");
+
+	if (count == 0)
+		goto done;
+
+	if (monitor && count > 0)
+		printf("\t");
+
+	for (i = 0; i < count; i++) {
+		uint16_t index;
+		void *data;
+
+		index = get_le16(&rp->index[i]);
+
+		if (monitor)
+			printf("hci%u ", index);
+
+		data = UINT_TO_PTR(index);
+
+		if (mgmt_send(mgmt, MGMT_OP_READ_INFO, index, 0, NULL,
+						info_rsp, data, NULL) == 0) {
+			fprintf(stderr, "Unable to send read_info cmd\n");
+			goto done;
+		}
+
+		pending++;
+	}
+
+	if (monitor && count > 0)
+		printf("\n");
+
+	return;
+
+done:
+	mainloop_quit();
+}
+
+static void cmd_info(struct mgmt *mgmt, uint16_t index, int argc, char **argv)
+{
+	void *data;
+
+	if (index == MGMT_INDEX_NONE) {
+		if (mgmt_send(mgmt, MGMT_OP_READ_INDEX_LIST,
+					MGMT_INDEX_NONE, 0, NULL,
+					index_rsp, mgmt, NULL) == 0) {
+			fprintf(stderr, "Unable to send index_list cmd\n");
+			exit(EXIT_FAILURE);
+		}
+
+		return;
+	}
+
+	data = UINT_TO_PTR(index);
+
+	if (mgmt_send(mgmt, MGMT_OP_READ_INFO, index, 0, NULL, info_rsp,
+							data, NULL) == 0) {
+		fprintf(stderr, "Unable to send read_info cmd\n");
+		exit(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) {
+		fprintf(stderr,
+			"%s for hci%u failed with status 0x%02x (%s)\n",
+			mgmt_opstr(op), id, status, mgmt_errstr(status));
+		goto done;
+	}
+
+	if (len < sizeof(*rp)) {
+		fprintf(stderr, "Too small %s response (%u bytes)\n",
+							mgmt_opstr(op), len);
+		goto done;
+	}
+
+	printf("hci%u %s complete, settings: ", id, mgmt_opstr(op));
+	print_settings(get_le32(rp));
+	printf("\n");
+
+done:
+	mainloop_quit();
+}
+
+static void cmd_setting(struct mgmt *mgmt, uint16_t index, uint16_t op,
+							int argc, char **argv)
+{
+	uint8_t val;
+
+	if (argc < 2) {
+		printf("Specify \"on\" or \"off\"\n");
+		exit(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) {
+		fprintf(stderr, "Unable to send %s cmd\n", mgmt_opstr(op));
+		exit(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) {
+		printf("Usage: btmgmt %s <yes/no/limited> [timeout]\n", argv[0]);
+		exit(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) {
+		fprintf(stderr, "Unable to send set_discoverable cmd\n");
+		exit(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_pairable(struct mgmt *mgmt, uint16_t index, int argc,
+								char **argv)
+{
+	cmd_setting(mgmt, index, MGMT_OP_SET_PAIRABLE, 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) {
+		printf("Specify \"on\" or \"off\" or \"only\"\n");
+		exit(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) {
+		fprintf(stderr, "Unable to send set_secure_conn cmd\n");
+		exit(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;
+	int fd;
+
+	if (argc < 2) {
+		printf("Specify \"on\" or \"off\"\n");
+		exit(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;
+
+	fd = open("/dev/urandom", O_RDONLY);
+	if (fd < 0) {
+		fprintf(stderr, "open(/dev/urandom): %s\n", strerror(errno));
+		exit(EXIT_FAILURE);
+	}
+
+	if (read(fd, cp.irk, sizeof(cp.irk)) != sizeof(cp.irk)) {
+		fprintf(stderr, "Reading from urandom failed\n");
+		close(fd);
+		exit(EXIT_FAILURE);
+	}
+
+	close(fd);
+
+	if (send_cmd(mgmt, MGMT_OP_SET_PRIVACY, index, sizeof(cp), &cp,
+							setting_rsp) == 0) {
+		fprintf(stderr, "Unable to send Set Privacy command\n");
+		exit(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) {
+		fprintf(stderr, "%s failed, status 0x%02x (%s)\n",
+				mgmt_opstr(op), status, mgmt_errstr(status));
+		goto done;
+	}
+
+	if (len != sizeof(*rp)) {
+		fprintf(stderr, "Unexpected %s len %u\n", mgmt_opstr(op), len);
+		goto done;
+	}
+
+	printf("%s succeeded. Class 0x%02x%02x%02x\n", mgmt_opstr(op),
+		rp->class_of_dev[2], rp->class_of_dev[1], rp->class_of_dev[0]);
+
+done:
+	mainloop_quit();
+}
+
+static void cmd_class(struct mgmt *mgmt, uint16_t index, int argc, char **argv)
+{
+	uint8_t class[2];
+
+	if (argc < 3) {
+		printf("Usage: btmgmt %s <major> <minor>\n", argv[0]);
+		exit(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) {
+		fprintf(stderr, "Unable to send set_dev_class cmd\n");
+		exit(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) {
+		fprintf(stderr, "Disconnect failed with status 0x%02x (%s)\n",
+						status, mgmt_errstr(status));
+		goto done;
+	}
+
+	if (len != sizeof(*rp)) {
+		fprintf(stderr, "Invalid disconnect response length (%u)\n",
+									len);
+		goto done;
+	}
+
+	ba2str(&rp->addr.bdaddr, addr);
+
+	if (status == 0)
+		printf("%s disconnected\n", addr);
+	else
+		fprintf(stderr,
+			"Disconnecting %s failed with status 0x%02x (%s)\n",
+				addr, status, mgmt_errstr(status));
+
+done:
+	mainloop_quit();
+}
+
+static void cmd_disconnect(struct mgmt *mgmt, uint16_t index, int argc,
+								char **argv)
+{
+	struct mgmt_cp_disconnect cp;
+
+	if (argc < 2) {
+		printf("Usage: btmgmt %s <address>\n", argv[0]);
+		exit(EXIT_FAILURE);
+	}
+
+	str2ba(argv[1], &cp.addr.bdaddr);
+
+	if (index == MGMT_INDEX_NONE)
+		index = 0;
+
+	if (mgmt_send(mgmt, MGMT_OP_DISCONNECT, index, sizeof(cp), &cp,
+					disconnect_rsp, NULL, NULL) == 0) {
+		fprintf(stderr, "Unable to send disconnect cmd\n");
+		exit(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)) {
+		fprintf(stderr, "Too small (%u bytes) get_connections rsp\n",
+									len);
+		goto done;
+	}
+
+	count = get_le16(&rp->conn_count);
+	if (len != sizeof(*rp) + count * sizeof(struct mgmt_addr_info)) {
+		fprintf(stderr, "Invalid get_connections length "
+					" (count=%u, len=%u)\n", count, len);
+		goto done;
+	}
+
+	for (i = 0; i < count; i++) {
+		char addr[18];
+
+		ba2str(&rp->addr[i].bdaddr, addr);
+
+		printf("%s type %s\n", addr, typestr(rp->addr[i].type));
+	}
+
+done:
+	mainloop_quit();
+}
+
+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) {
+		fprintf(stderr, "Unable to send get_connections cmd\n");
+		exit(EXIT_FAILURE);
+	}
+}
+
+static void find_rsp(uint8_t status, uint16_t len, const void *param,
+							void *user_data)
+{
+	if (status != 0) {
+		fprintf(stderr,
+			"Unable to start discovery. status 0x%02x (%s)\n",
+						status, mgmt_errstr(status));
+		mainloop_quit();
+		return;
+	}
+
+	printf("Discovery started\n");
+	discovery = true;
+}
+
+static void find_usage(void)
+{
+	printf("Usage: btmgmt find [-l|-b]>\n");
+}
+
+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;
+	int opt;
+
+	if (index == MGMT_INDEX_NONE)
+		index = 0;
+
+	type = 0;
+	hci_set_bit(BDADDR_BREDR, &type);
+	hci_set_bit(BDADDR_LE_PUBLIC, &type);
+	hci_set_bit(BDADDR_LE_RANDOM, &type);
+
+	while ((opt = getopt_long(argc, argv, "+lbh", find_options,
+								NULL)) != -1) {
+		switch (opt) {
+		case 'l':
+			hci_clear_bit(BDADDR_BREDR, &type);
+			hci_set_bit(BDADDR_LE_PUBLIC, &type);
+			hci_set_bit(BDADDR_LE_RANDOM, &type);
+			break;
+		case 'b':
+			hci_set_bit(BDADDR_BREDR, &type);
+			hci_clear_bit(BDADDR_LE_PUBLIC, &type);
+			hci_clear_bit(BDADDR_LE_RANDOM, &type);
+			break;
+		case 'h':
+		default:
+			find_usage();
+			exit(EXIT_SUCCESS);
+		}
+	}
+
+	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) {
+		fprintf(stderr, "Unable to send start_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)
+		fprintf(stderr, "Unable to set local name "
+						"with status 0x%02x (%s)\n",
+						status, mgmt_errstr(status));
+
+	mainloop_quit();
+}
+
+static void cmd_name(struct mgmt *mgmt, uint16_t index, int argc, char **argv)
+{
+	struct mgmt_cp_set_local_name cp;
+
+	if (argc < 2) {
+		printf("Usage: btmgmt %s <name> [shortname]\n", argv[0]);
+		exit(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) {
+		fprintf(stderr, "Unable to send set_name cmd\n");
+		exit(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) {
+		fprintf(stderr, "Pairing failed with status 0x%02x (%s)\n",
+						status, mgmt_errstr(status));
+		goto done;
+	}
+
+	if (len != sizeof(*rp)) {
+		fprintf(stderr, "Unexpected pair_rsp len %u\n", len);
+		goto done;
+	}
+
+	ba2str(&rp->addr.bdaddr, addr);
+
+	if (status != 0) {
+		fprintf(stderr,
+			"Pairing with %s (%s) failed. status 0x%02x (%s)\n",
+			addr, typestr(rp->addr.type), status,
+			mgmt_errstr(status));
+		goto done;
+	}
+
+	printf("Paired with %s (%s)\n", addr, typestr(rp->addr.type));
+
+done:
+	mainloop_quit();
+}
+
+static void pair_usage(void)
+{
+	printf("Usage: btmgmt pair [-c cap] [-t type] <remote address>\n");
+}
+
+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':
+		default:
+			pair_usage();
+			exit(EXIT_SUCCESS);
+		}
+	}
+
+	argc -= optind;
+	argv += optind;
+	optind = 0;
+
+	if (argc < 1) {
+		pair_usage();
+		exit(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);
+	printf("Pairing with %s (%s)\n", addr, typestr(cp.addr.type));
+
+	if (mgmt_send(mgmt, MGMT_OP_PAIR_DEVICE, index, sizeof(cp), &cp,
+						pair_rsp, NULL, NULL) == 0) {
+		fprintf(stderr, "Unable to send pair_device cmd\n");
+		exit(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) {
+		fprintf(stderr, "Cancel Pairing failed with 0x%02x (%s)\n",
+						status, mgmt_errstr(status));
+		goto done;
+	}
+
+	if (len != sizeof(*rp)) {
+		fprintf(stderr, "Unexpected cancel_pair_rsp len %u\n", len);
+		goto done;
+	}
+
+	ba2str(&rp->bdaddr, addr);
+
+	if (status != 0) {
+		fprintf(stderr,
+			"Cancel Pairing with %s (%s) failed. 0x%02x (%s)\n",
+			addr, typestr(rp->type), status,
+			mgmt_errstr(status));
+		goto done;
+	}
+
+	printf("Pairing Cancelled with %s\n", addr);
+
+done:
+	mainloop_quit();
+}
+
+static void cancel_pair_usage(void)
+{
+	printf("Usage: btmgmt cancelpair [-t type] <remote address>\n");
+}
+
+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':
+		default:
+			cancel_pair_usage();
+			exit(EXIT_SUCCESS);
+		}
+	}
+
+	argc -= optind;
+	argv += optind;
+	optind = 0;
+
+	if (argc < 1) {
+		cancel_pair_usage();
+		exit(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) {
+		fprintf(stderr, "Unable to send cancel_pair_device cmd\n");
+		exit(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) {
+		fprintf(stderr, "Unpair device failed. status 0x%02x (%s)\n",
+						status, mgmt_errstr(status));
+		goto done;
+	}
+
+	if (len != sizeof(*rp)) {
+		fprintf(stderr, "Unexpected unpair_device_rsp len %u\n", len);
+		goto done;
+	}
+
+	ba2str(&rp->addr.bdaddr, addr);
+
+	if (status != 0) {
+		fprintf(stderr,
+			"Unpairing %s failed. status 0x%02x (%s)\n",
+				addr, status, mgmt_errstr(status));
+		goto done;
+	}
+
+	printf("%s unpaired\n", addr);
+
+done:
+	mainloop_quit();
+}
+
+static void unpair_usage(void)
+{
+	printf("Usage: btmgmt unpair [-t type] <remote address>\n");
+}
+
+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':
+		default:
+			unpair_usage();
+			exit(EXIT_SUCCESS);
+		}
+	}
+
+	argc -= optind;
+	argv += optind;
+	optind = 0;
+
+	if (argc < 1) {
+		unpair_usage();
+		exit(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) {
+		fprintf(stderr, "Unable to send unpair_device cmd\n");
+		exit(EXIT_FAILURE);
+	}
+}
+
+static void keys_rsp(uint8_t status, uint16_t len, const void *param,
+							void *user_data)
+{
+	if (status != 0)
+		fprintf(stderr, "Load keys failed with status 0x%02x (%s)\n",
+						status, mgmt_errstr(status));
+	else
+		printf("Keys successfully loaded\n");
+
+	mainloop_quit();
+}
+
+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) {
+		fprintf(stderr, "Unable to send load_keys cmd\n");
+		exit(EXIT_FAILURE);
+	}
+}
+
+static void ltks_rsp(uint8_t status, uint16_t len, const void *param,
+							void *user_data)
+{
+	if (status != 0)
+		fprintf(stderr, "Load keys failed with status 0x%02x (%s)\n",
+						status, mgmt_errstr(status));
+	else
+		printf("Long term keys successfully loaded\n");
+
+	mainloop_quit();
+}
+
+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) {
+		fprintf(stderr, "Unable to send load_ltks cmd\n");
+		exit(EXIT_FAILURE);
+	}
+}
+
+static void irks_rsp(uint8_t status, uint16_t len, const void *param,
+							void *user_data)
+{
+	if (status != 0)
+		fprintf(stderr, "Load IRKs failed with status 0x%02x (%s)\n",
+						status, mgmt_errstr(status));
+	else
+		printf("Identity Resolving Keys successfully loaded\n");
+
+	mainloop_quit();
+}
+
+static void cmd_irks(struct mgmt *mgmt, uint16_t index, int argc, char **argv)
+{
+	struct mgmt_cp_load_irks cp;
+
+	if (index == MGMT_INDEX_NONE)
+		index = 0;
+
+	memset(&cp, 0, sizeof(cp));
+
+	if (mgmt_send(mgmt, MGMT_OP_LOAD_IRKS, index, sizeof(cp), &cp,
+						irks_rsp, NULL, NULL) == 0) {
+		fprintf(stderr, "Unable to send load_irks cmd\n");
+		exit(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) {
+		fprintf(stderr, "%s failed, status 0x%02x (%s)\n",
+				mgmt_opstr(op), status, mgmt_errstr(status));
+		goto done;
+	}
+
+	if (len != sizeof(*rp)) {
+		fprintf(stderr, "Unexpected %s len %u\n", mgmt_opstr(op), len);
+		goto done;
+	}
+
+	ba2str(&rp->bdaddr, addr);
+
+	if (status != 0) {
+		fprintf(stderr, "%s %s (%s) failed. status 0x%02x (%s)\n",
+				mgmt_opstr(op), addr, typestr(rp->type),
+				status, mgmt_errstr(status));
+		goto done;
+	}
+
+	printf("%s %s succeeded\n", mgmt_opstr(op), addr);
+
+done:
+	mainloop_quit();
+}
+
+static void block_usage(void)
+{
+	printf("Usage: btmgmt block [-t type] <remote address>\n");
+}
+
+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':
+		default:
+			block_usage();
+			exit(EXIT_SUCCESS);
+		}
+	}
+
+	argc -= optind;
+	argv += optind;
+	optind = 0;
+
+	if (argc < 1) {
+		block_usage();
+		exit(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) {
+		fprintf(stderr, "Unable to send block_device cmd\n");
+		exit(EXIT_FAILURE);
+	}
+}
+
+static void unblock_usage(void)
+{
+	printf("Usage: btmgmt unblock [-t type] <remote address>\n");
+}
+
+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':
+		default:
+			unblock_usage();
+			exit(EXIT_SUCCESS);
+		}
+	}
+
+	argc -= optind;
+	argv += optind;
+	optind = 0;
+
+	if (argc < 1) {
+		unblock_usage();
+		exit(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) {
+		fprintf(stderr, "Unable to send unblock_device cmd\n");
+		exit(EXIT_FAILURE);
+	}
+}
+
+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 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) {
+		printf("UUID and service hint needed\n");
+		exit(EXIT_FAILURE);
+	}
+
+	if (index == MGMT_INDEX_NONE)
+		index = 0;
+
+	if (bt_string2uuid(&uuid, argv[1]) < 0) {
+		printf("Invalid UUID: %s\n", argv[1]);
+		exit(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) {
+		fprintf(stderr, "Unable to send add_uuid cmd\n");
+		exit(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) {
+		printf("UUID needed\n");
+		exit(EXIT_FAILURE);
+	}
+
+	if (index == MGMT_INDEX_NONE)
+		index = 0;
+
+	if (bt_string2uuid(&uuid, argv[1]) < 0) {
+		printf("Invalid UUID: %s\n", argv[1]);
+		exit(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) {
+		fprintf(stderr, "Unable to send remove_uuid cmd\n");
+		exit(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;
+	const struct mgmt_rp_read_local_oob_ext_data *rp_ext = param;
+	int i;
+
+	if (status != 0) {
+		fprintf(stderr, "Read Local OOB Data failed "
+						"with status 0x%02x (%s)\n",
+						status, mgmt_errstr(status));
+		goto done;
+	}
+
+	if (len < sizeof(*rp)) {
+		fprintf(stderr, "Too small (%u bytes) read_local_oob rsp\n",
+									len);
+		goto done;
+	}
+
+	printf("Hash C from P-192: ");
+	for (i = 0; i < 16; i++)
+		printf("%02x", rp->hash[i]);
+	printf("\n");
+
+	printf("Randomizer R with P-192: ");
+	for (i = 0; i < 16; i++)
+		printf("%02x", rp->randomizer[i]);
+	printf("\n");
+
+	if (len < sizeof(*rp_ext))
+		goto done;
+
+	printf("Hash C from P-256: ");
+	for (i = 0; i < 16; i++)
+		printf("%02x", rp_ext->hash256[i]);
+	printf("\n");
+
+	printf("Randomizer R with P-256: ");
+	for (i = 0; i < 16; i++)
+		printf("%02x", rp_ext->randomizer256[i]);
+	printf("\n");
+
+done:
+	mainloop_quit();
+}
+
+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) {
+		fprintf(stderr, "Unable to send read_local_oob cmd\n");
+		exit(EXIT_FAILURE);
+	}
+}
+
+static void did_rsp(uint8_t status, uint16_t len, const void *param,
+							void *user_data)
+{
+	if (status != 0)
+		fprintf(stderr, "Set Device ID failed "
+						"with status 0x%02x (%s)\n",
+						status, mgmt_errstr(status));
+	else
+		printf("Device ID successfully set\n");
+
+	mainloop_quit();
+}
+
+static void did_usage(void)
+{
+	printf("Usage: btmgmt did <source>:<vendor>:<product>:<version>\n");
+	printf("       possible source values: bluetooth, usb\n");
+}
+
+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();
+		exit(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();
+	exit(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) {
+		fprintf(stderr, "Unable to send set_device_id cmd\n");
+		exit(EXIT_FAILURE);
+	}
+}
+
+static void static_addr_rsp(uint8_t status, uint16_t len, const void *param,
+							void *user_data)
+{
+	if (status != 0)
+		fprintf(stderr, "Set static address failed "
+						"with status 0x%02x (%s)\n",
+						status, mgmt_errstr(status));
+	else
+		printf("Static address successfully set\n");
+
+	mainloop_quit();
+}
+
+static void static_addr_usage(void)
+{
+	printf("Usage: btmgmt static-addr <address>\n");
+}
+
+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();
+		exit(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) {
+		fprintf(stderr, "Unable to send set_static_address cmd\n");
+		exit(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 struct {
+	char *cmd;
+	void (*func)(struct mgmt *mgmt, uint16_t index, int argc, char **argv);
+	char *doc;
+} command[] = {
+	{ "monitor",	cmd_monitor,	"Monitor events"		},
+	{ "version",	cmd_version,	"Get the MGMT Version"		},
+	{ "commands",	cmd_commands,	"List supported commands"	},
+	{ "info",	cmd_info,	"Show controller info"		},
+	{ "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"	},
+	{ "pairable",	cmd_pairable,	"Toggle pairable 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"	},
+	{ "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"		},
+	{ "did",	cmd_did,	"Set Device ID"			},
+	{ "static-addr",cmd_static_addr,"Set static address"		},
+	{ "debug-keys",	cmd_debug_keys,	"Toogle debug keys"		},
+	{ }
+};
+
+static void usage(void)
+{
+	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; command[i].cmd; i++)
+		printf("\t%-15s\t%s\n", command[i].cmd, command[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 }
+};
+
+int main(int argc, char *argv[])
+{
+	int opt, i;
+	uint16_t index = MGMT_INDEX_NONE;
+	struct mgmt *mgmt;
+	int exit_status;
+
+	while ((opt = getopt_long(argc, argv, "+hvi:",
+						main_options, NULL)) != -1) {
+		switch (opt) {
+		case 'i':
+			if (strlen(optarg) > 3 &&
+					strncasecmp(optarg, "hci", 3) == 0)
+				index = atoi(&optarg[4]);
+			else
+				index = atoi(optarg);
+			break;
+		case 'v':
+			monitor = true;
+			break;
+		case 'h':
+		default:
+			usage();
+			return 0;
+		}
+	}
+
+	argc -= optind;
+	argv += optind;
+	optind = 0;
+
+	if (argc < 1) {
+		usage();
+		return 0;
+	}
+
+	mainloop_init();
+
+	mgmt = mgmt_new_default();
+	if (!mgmt) {
+		fprintf(stderr, "Unable to open mgmt_socket\n");
+		return EXIT_FAILURE;
+	}
+
+	for (i = 0; command[i].cmd; i++) {
+		if (strcmp(command[i].cmd, argv[0]) != 0)
+			continue;
+
+		command[i].func(mgmt, index, argc, argv);
+		break;
+	}
+
+	if (command[i].cmd == NULL) {
+		fprintf(stderr, "Unknown command: %s\n", argv[0]);
+		mgmt_unref(mgmt);
+		return EXIT_FAILURE;
+	}
+
+	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_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);
+
+	exit_status = mainloop_run();
+
+	mgmt_cancel_all(mgmt);
+	mgmt_unregister_all(mgmt);
+	mgmt_unref(mgmt);
+
+	return exit_status;
+}
diff --git a/bluez/tools/btproxy.c b/bluez/tools/btproxy.c
new file mode 100644
index 0000000..3503148
--- /dev/null
+++ b/bluez/tools/btproxy.c
@@ -0,0 +1,713 @@
+/*
+ *
+ *  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 <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 "monitor/mainloop.h"
+#include "monitor/bt.h"
+
+#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 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;
+};
+
+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_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 (!write_packet(proxy->dev_fd, proxy->host_buf, pktlen, "D: ")) {
+		fprintf(stderr, "Write to device descriptor failed\n");
+		mainloop_remove_fd(proxy->dev_fd);
+		return;
+	}
+
+	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 (!write_packet(proxy->host_fd, proxy->dev_buf, pktlen, "H: ")) {
+		fprintf(stderr, "Write to host descriptor failed\n");
+		mainloop_remove_fd(proxy->host_fd);
+		return;
+	}
+
+	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 NULL;
+
+	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-d, --debug                 Enable debugging output\n"
+		"\t-h, --help                  Show help options\n");
+}
+
+static const struct option main_options[] = {
+	{ "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' },
+	{ "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 */
+	const char *str;
+	sigset_t mask;
+
+	for (;;) {
+		int opt;
+
+		opt = getopt_long(argc, argv, "c:l::u::p:i:dvh",
+						main_options, NULL);
+		if (opt < 0)
+			break;
+
+		switch (opt) {
+		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 '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) {
+		fprintf(stderr, "Invalid to specify TCP and Unix servers\n");
+		return EXIT_FAILURE;
+	}
+
+	if (connect_address && (unix_path || server_address)) {
+		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) {
+		int host_fd, dev_fd;
+
+		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(0x00);
+		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/tools/btsnoop.c b/bluez/tools/btsnoop.c
new file mode 100644
index 0000000..6ca62d2
--- /dev/null
+++ b/bluez/tools/btsnoop.c
@@ -0,0 +1,607 @@
+/*
+ *
+ *  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 <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 = input_pkt[select_input].size = htobe32(toread - 1);
+	written = input_pkt[select_input].len = htobe32(toread - 1);
+
+	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_ACL_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/tools/ciptool.1 b/bluez/tools/ciptool.1
new file mode 100644
index 0000000..65d903d
--- /dev/null
+++ b/bluez/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/tools/ciptool.c b/bluez/tools/ciptool.c
new file mode 100644
index 0000000..72338dc
--- /dev/null
+++ b/bluez/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 <sys/poll.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/hci.h>
+#include <bluetooth/hci_lib.h>
+#include <bluetooth/l2cap.h>
+#include <bluetooth/sdp.h>
+#include <bluetooth/sdp_lib.h>
+#include <bluetooth/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/tools/cltest.c b/bluez/tools/cltest.c
new file mode 100644
index 0000000..4ddb98a
--- /dev/null
+++ b/bluez/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 <sys/poll.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/hci.h>
+#include <bluetooth/hci_lib.h>
+#include <bluetooth/l2cap.h>
+
+#include "monitor/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/tools/csr.c b/bluez/tools/csr.c
new file mode 100644
index 0000000..b4ea1fb
--- /dev/null
+++ b/bluez/tools/csr.c
@@ -0,0 +1,2853 @@
+/*
+ *
+ *  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 <bluetooth/bluetooth.h>
+#include <bluetooth/hci.h>
+#include <bluetooth/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, "=") + 1;
+	if (!off)
+		return -EIO;
+
+	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/tools/csr.h b/bluez/tools/csr.h
new file mode 100644
index 0000000..8b94d7b
--- /dev/null
+++ b/bluez/tools/csr.h
@@ -0,0 +1,553 @@
+/*
+ *
+ *  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_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_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/tools/csr_3wire.c b/bluez/tools/csr_3wire.c
new file mode 100644
index 0000000..33fcf38
--- /dev/null
+++ b/bluez/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/tools/csr_bcsp.c b/bluez/tools/csr_bcsp.c
new file mode 100644
index 0000000..f7afe53
--- /dev/null
+++ b/bluez/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/tools/csr_h4.c b/bluez/tools/csr_h4.c
new file mode 100644
index 0000000..3371770
--- /dev/null
+++ b/bluez/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/tools/csr_hci.c b/bluez/tools/csr_hci.c
new file mode 100644
index 0000000..6bd37c3
--- /dev/null
+++ b/bluez/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 <bluetooth/bluetooth.h>
+#include <bluetooth/hci.h>
+#include <bluetooth/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/tools/csr_usb.c b/bluez/tools/csr_usb.c
new file mode 100644
index 0000000..5fb6bdc
--- /dev/null
+++ b/bluez/tools/csr_usb.c
@@ -0,0 +1,300 @@
+/*
+ *
+ *  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)
+		return -1;
+
+	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/tools/gap-tester.c b/bluez/tools/gap-tester.c
new file mode 100644
index 0000000..d788626
--- /dev/null
+++ b/bluez/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.h>
+
+#include "src/shared/tester.h"
+#include "src/shared/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/tools/gatt-service.c b/bluez/tools/gatt-service.c
new file mode 100644
index 0000000..624b835
--- /dev/null
+++ b/bluez/tools/gatt-service.c
@@ -0,0 +1,407 @@
+/*
+ *
+ *  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"
+
+/* Immediate Alert Service UUID */
+#define IAS_UUID			"00001802-0000-1000-8000-00805f9b34fb"
+#define ALERT_LEVEL_CHR_UUID		"00002a06-0000-1000-8000-00805f9b34fb"
+
+static GMainLoop *main_loop;
+static GSList *services;
+static DBusConnection *connection;
+
+struct characteristic {
+	char *uuid;
+	char *path;
+	uint8_t *value;
+	int vlen;
+};
+
+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("Get(\"Value\")\n");
+
+	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 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("Set('Value', ...)\n");
+
+	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 },
+	{ }
+};
+
+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 gboolean register_characteristic(DBusConnection *conn, const char *uuid,
+						const uint8_t *value, int vlen,
+						const char *service_path)
+{
+	struct characteristic *chr;
+	static int id = 1;
+	char *path;
+	gboolean ret = TRUE;
+
+	path = g_strdup_printf("%s/characteristic%d", service_path, id++);
+
+	chr = g_new0(struct characteristic, 1);
+
+	chr->uuid = g_strdup(uuid);
+	chr->value = g_memdup(value, vlen);
+	chr->vlen = vlen;
+	chr->path = path;
+
+	if (!g_dbus_register_interface(conn, path, GATT_CHR_IFACE,
+					NULL, NULL, chr_properties,
+					chr, chr_iface_destroy)) {
+		printf("Couldn't register characteristic interface\n");
+		ret = FALSE;
+	}
+
+	return ret;
+}
+
+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
+	 * According to the IAS SPEC, reading <<Alert level>> is not allowed.
+	 * "Value" is readable for testing purpose only.
+	 */
+	if (!register_characteristic(connection, ALERT_LEVEL_CHR_UUID, &level,
+						sizeof(level), 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/tools/hci-tester.c b/bluez/tools/hci-tester.c
new file mode 100644
index 0000000..a5dbde2
--- /dev/null
+++ b/bluez/tools/hci-tester.c
@@ -0,0 +1,673 @@
+/*
+ *
+ *  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/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;
+};
+
+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_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("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/tools/hciattach.1 b/bluez/tools/hciattach.1
new file mode 100644
index 0000000..d506034
--- /dev/null
+++ b/bluez/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/tools/hciattach.c b/bluez/tools/hciattach.c
new file mode 100644
index 0000000..0334ba2
--- /dev/null
+++ b/bluez/tools/hciattach.c
@@ -0,0 +1,1474 @@
+/*
+ *
+ *  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 <sys/time.h>
+#include <sys/poll.h>
+#include <sys/param.h>
+#include <sys/ioctl.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/hci.h>
+#include <bluetooth/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);
+}
+
+static 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 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  },
+
+	{ "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");
+		return -1;
+	}
+
+	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");
+		return -1;
+	}
+
+	/* Set initial baudrate */
+	if (set_speed(fd, &ti, u->init_speed) < 0) {
+		perror("Can't set initial baud rate");
+		return -1;
+	}
+
+	tcflush(fd, TCIOFLUSH);
+
+	if (send_break) {
+		tcsendbreak(fd, 0);
+		usleep(500000);
+	}
+
+	if (u->init && u->init(fd, u, &ti) < 0)
+		return -1;
+
+	tcflush(fd, TCIOFLUSH);
+
+	/* Set actual baudrate */
+	if (set_speed(fd, &ti, u->speed) < 0) {
+		perror("Can't set baud rate");
+		return -1;
+	}
+
+	/* Set TTY to N_HCI line discipline */
+	i = N_HCI;
+	if (ioctl(fd, TIOCSETD, &i) < 0) {
+		perror("Can't set line discipline");
+		return -1;
+	}
+
+	if (flags && ioctl(fd, HCIUARTSETFLAGS, flags) < 0) {
+		perror("Can't set UART flags");
+		return -1;
+	}
+
+	if (ioctl(fd, HCIUARTSETPROTO, u->proto) < 0) {
+		perror("Can't set device");
+		return -1;
+	}
+
+	if (u->post && u->post(fd, u, &ti) < 0)
+		return -1;
+
+	return fd;
+}
+
+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] [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/");
+			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;
+		}
+
+		/* set a new process group so that ctrl-c
+		   won't abort the detached process */
+		setpgrp();
+
+		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/tools/hciattach.h b/bluez/tools/hciattach.h
new file mode 100644
index 0000000..1b23ad7
--- /dev/null
+++ b/bluez/tools/hciattach.h
@@ -0,0 +1,59 @@
+/*
+ *
+ *  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_RAW_DEVICE	0
+#define HCI_UART_RESET_ON_INIT	1
+#define HCI_UART_CREATE_AMP	2
+
+int read_hci_event(int fd, unsigned char *buf, int size);
+int set_speed(int fd, struct termios *ti, 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);
diff --git a/bluez/tools/hciattach_ath3k.c b/bluez/tools/hciattach_ath3k.c
new file mode 100644
index 0000000..23208c6
--- /dev/null
+++ b/bluez/tools/hciattach_ath3k.c
@@ -0,0 +1,1044 @@
+/*
+ *  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 <bluetooth/bluetooth.h>
+#include <bluetooth/hci.h>
+#include <bluetooth/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;
+	}
+
+	/*
+	 * It is not necessary that Patch file be available,
+	 * continue with PS Operations if patch file is not available.
+	 */
+	if (patch_file[0] == '\0')
+		err = 0;
+
+	stream = fopen(patch_file, "r");
+	if (!stream)
+		err = 0;
+	else {
+		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/tools/hciattach_intel.c b/bluez/tools/hciattach_intel.c
new file mode 100644
index 0000000..749098e
--- /dev/null
+++ b/bluez/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 <bluetooth/bluetooth.h>
+#include <bluetooth/hci.h>
+#include <bluetooth/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/tools/hciattach_qualcomm.c b/bluez/tools/hciattach_qualcomm.c
new file mode 100644
index 0000000..0e02e1e
--- /dev/null
+++ b/bluez/tools/hciattach_qualcomm.c
@@ -0,0 +1,273 @@
+/*
+ *
+ *  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 <sys/time.h>
+#include <sys/poll.h>
+#include <sys/param.h>
+#include <sys/ioctl.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/hci.h>
+#include <bluetooth/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/tools/hciattach_st.c b/bluez/tools/hciattach_st.c
new file mode 100644
index 0000000..dbb7c47
--- /dev/null
+++ b/bluez/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 <bluetooth/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/tools/hciattach_ti.c b/bluez/tools/hciattach_ti.c
new file mode 100644
index 0000000..8322b45
--- /dev/null
+++ b/bluez/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 <bluetooth/bluetooth.h>
+#include <bluetooth/hci.h>
+#include <bluetooth/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/tools/hciattach_tialt.c b/bluez/tools/hciattach_tialt.c
new file mode 100644
index 0000000..c3caa49
--- /dev/null
+++ b/bluez/tools/hciattach_tialt.c
@@ -0,0 +1,242 @@
+/*
+ *
+ *  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 <sys/time.h>
+#include <sys/poll.h>
+#include <sys/param.h>
+#include <sys/ioctl.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/hci.h>
+#include <bluetooth/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/tools/hciconfig.1 b/bluez/tools/hciconfig.1
new file mode 100644
index 0000000..633ffa3
--- /dev/null
+++ b/bluez/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/tools/hciconfig.c b/bluez/tools/hciconfig.c
new file mode 100644
index 0000000..765d980
--- /dev/null
+++ b/bluez/tools/hciconfig.c
@@ -0,0 +1,2053 @@
+/*
+ *
+ *  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 <bluetooth/bluetooth.h>
+#include <bluetooth/hci.h>
+#include <bluetooth/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");
+		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;
+		if (hci_test_bit(HCI_RAW, &di.flags) &&
+				!bacmp(&di.bdaddr, BDADDR_ANY)) {
+			int dd = hci_open_dev(di.dev_id);
+			hci_read_bd_addr(dd, &di.bdaddr, 1000);
+			hci_close_dev(dd);
+		}
+		print_dev_info(ctl, &di);
+	}
+}
+
+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);
+	}
+
+	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;
+	}
+	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));
+	}
+}
+
+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]);
+	}
+}
+
+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);
+	}
+}
+
+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);
+	}
+}
+
+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);
+	}
+}
+
+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");
+	}
+}
+
+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");
+	}
+}
+
+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;
+	}
+	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);
+	}
+
+	if (hci_test_bit(HCI_RAW, &di.flags) &&
+			!bacmp(&di.bdaddr, BDADDR_ANY)) {
+		int dd = hci_open_dev(di.dev_id);
+		hci_read_bd_addr(dd, &di.bdaddr, 1000);
+		hci_close_dev(dd);
+	}
+
+	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/tools/hcidump.1 b/bluez/tools/hcidump.1
new file mode 100644
index 0000000..5c1441b
--- /dev/null
+++ b/bluez/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/tools/hcidump.c b/bluez/tools/hcidump.c
new file mode 100644
index 0000000..2bbc207
--- /dev/null
+++ b/bluez/tools/hcidump.c
@@ -0,0 +1,853 @@
+/*
+ *
+ *  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 <sys/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 int  permcheck = 1;
+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, *ctrl;
+	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;
+
+	ctrl = malloc(100);
+	if (!ctrl) {
+		free(buf);
+		perror("Can't allocate control buffer");
+		return -1;
+	}
+
+	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;
+	struct hci_dev_info di;
+	int sk, dd, opt;
+
+	if (permcheck && dev != HCI_DEV_NONE) {
+		dd = hci_open_dev(dev);
+		if (dd < 0) {
+			perror("Can't open device");
+			return -1;
+		}
+
+		if (hci_devinfo(dev, &di) < 0) {
+			perror("Can't get device info");
+			return -1;
+		}
+
+		opt = hci_test_bit(HCI_RAW, &di.flags);
+		if (ioctl(dd, HCISETRAW, opt) < 0) {
+			if (errno == EACCES) {
+				perror("Can't access device");
+				return -1;
+			}
+		}
+
+		hci_close_dev(dd);
+	}
+
+	/* 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");
+		return -1;
+	}
+
+	opt = 1;
+	if (setsockopt(sk, SOL_HCI, HCI_TIME_STAMP, &opt, sizeof(opt)) < 0) {
+		perror("Can't enable time stamp");
+		return -1;
+	}
+
+	/* 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");
+		return -1;
+	}
+
+	/* 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);
+		return -1;
+	}
+
+	return sk;
+}
+
+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' },
+	{ "nopermcheck",	0, 0, 'Z' },
+	{ "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:YZhv",
+				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 'Z':
+			permcheck = 0;
+			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/tools/hcieventmask.c b/bluez/tools/hcieventmask.c
new file mode 100644
index 0000000..87beac9
--- /dev/null
+++ b/bluez/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 <bluetooth/bluetooth.h>
+#include <bluetooth/hci.h>
+#include <bluetooth/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/tools/hcisecfilter.c b/bluez/tools/hcisecfilter.c
new file mode 100644
index 0000000..9ad4ce0
--- /dev/null
+++ b/bluez/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 <bluetooth/bluetooth.h>
+#include <bluetooth/hci.h>
+#include <bluetooth/hci_lib.h>
+
+int main(void)
+{
+	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/tools/hcitool.1 b/bluez/tools/hcitool.1
new file mode 100644
index 0000000..85498dc
--- /dev/null
+++ b/bluez/tools/hcitool.1
@@ -0,0 +1,209 @@
+.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).
+.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/tools/hcitool.c b/bluez/tools/hcitool.c
new file mode 100644
index 0000000..c2df8e3
--- /dev/null
+++ b/bluez/tools/hcitool.c
@@ -0,0 +1,3127 @@
+/*
+ *
+ *  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 <glib.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/hci.h>
+#include <bluetooth/hci_lib.h>
+
+#include "src/textfile.h"
+#include "src/oui.h"
+
+/* Unofficial value, might still change */
+#define LE_LINK		0x03
+
+#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 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;
+	}
+	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"
+};
+
+static char *get_device_name(const bdaddr_t *local, const bdaddr_t *peer)
+{
+	char filename[PATH_MAX + 1];
+	char local_addr[18], peer_addr[18];
+	GKeyFile *key_file;
+	char *str = NULL;
+	int len;
+
+	ba2str(local, local_addr);
+	ba2str(peer, peer_addr);
+
+	snprintf(filename, PATH_MAX, STORAGEDIR "/%s/cache/%s", local_addr,
+			peer_addr);
+	filename[PATH_MAX] = '\0';
+	key_file = g_key_file_new();
+
+	if (g_key_file_load_from_file(key_file, filename, 0, NULL)) {
+		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';
+		}
+	}
+
+	g_key_file_free(key_file);
+
+	return str;
+}
+
+/* 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' },
+	{ "refresh",	0, 0, 'r' },
+	{ "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, *tmp;
+	struct hci_version version;
+	struct hci_dev_info di;
+	struct hci_conn_info_req *cr;
+	int refresh = 0, extcls = 0, extinf = 0, extoui = 0;
+	int i, n, l, opt, dd, cc, nc;
+
+	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 'r':
+			refresh = 1;
+			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 (!refresh) {
+			memset(name, 0, sizeof(name));
+			tmp = get_device_name(&di.bdaddr, &(info+i)->bdaddr);
+			if (tmp) {
+				strncpy(name, tmp, 249);
+				free(tmp);
+				nc = 1;
+			} else
+				nc = 0;
+		} else
+			nc = 0;
+
+		if (!extcls && !extinf && !extoui) {
+			ba2str(&(info+i)->bdaddr, addr);
+
+			if (nc) {
+				printf("\t%s\t%s\n", addr, name);
+				continue;
+			}
+
+			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 (handle > 0 || !nc) {
+			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) {
+				if (!nc)
+					strcpy(name, "n/a");
+			} else {
+				for (n = 0; n < 248 && name[n]; n++) {
+					if ((unsigned char) name[i] < 32 || name[i] == 127)
+						name[i] = '.';
+				}
+
+				name[248] = '\0';
+				nc = 0;
+			}
+		}
+
+		if (strlen(name) > 0)
+			printf("Device name:\t%s%s\n", name, nc ? " [cached]" : "");
+
+		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);
+
+	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' },
+	{ "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 = 0x00;
+	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 = 1;
+
+	for_each_opt(opt, lescan_options, NULL) {
+		switch (opt) {
+		case 'p':
+			own_type = 0x01; /* Random */
+			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, 1000);
+	if (err < 0) {
+		perror("Set scan parameters failed");
+		exit(1);
+	}
+
+	err = hci_le_set_scan_enable(dd, 0x01, filter_dup, 1000);
+	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, 1000);
+	if (err < 0) {
+		perror("Disable scan failed");
+		exit(1);
+	}
+
+	hci_close_dev(dd);
+}
+
+static struct option lecc_options[] = {
+	{ "help",	0, 0, 'h' },
+	{ "random",	0, 0, 'r' },
+	{ "whitelist",	0, 0, 'w' },
+	{ 0, 0, 0, 0 }
+};
+
+static const char *lecc_help =
+	"Usage:\n"
+	"\tlecc [--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;
+
+	peer_bdaddr_type = LE_PUBLIC_ADDRESS;
+	initiator_filter = 0; /* Use peer address */
+
+	for_each_opt(opt, lecc_options, NULL) {
+		switch (opt) {
+		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);
+	own_bdaddr_type = 0x00;
+	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 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    -H, --handle <0xXXXX>  LE connection handle\n"
+	"\t    -m, --min <interval>   Range: 0x0006 to 0x0C80\n"
+	"\t    -M, --max <interval>   Range: 0x0006 to 0x0C80\n"
+	"\t    -l, --latency <range>  Slave latency. Range: 0x0000 to 0x03E8\n"
+	"\t    -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, base;
+
+	/* Aleatory valid values */
+	min = 0x0C8;
+	max = 0x0960;
+	latency = 0x0007;
+	timeout = 0x0C80;
+
+	for_each_opt(opt, lecup_options, NULL) {
+		if (optarg && strncasecmp("0x", optarg, 2) == 0)
+			base = 16;
+		else
+			base = 10;
+
+		switch (opt) {
+		case 'H':
+			handle = strtoul(optarg, NULL, base);
+			break;
+		case 'm':
+			min = strtoul(optarg, NULL, base);
+			break;
+		case 'M':
+			max = strtoul(optarg, NULL, base);
+			break;
+		case 'l':
+			latency = strtoul(optarg, NULL, base);
+			break;
+		case 't':
+			timeout = strtoul(optarg, NULL, base);
+			break;
+		default:
+			printf("%s", lecup_help);
+			return;
+		}
+	}
+
+	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"                        },
+	{ "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"                  },
+	{ "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/tools/hid2hci.1 b/bluez/tools/hid2hci.1
new file mode 100644
index 0000000..8c5d520
--- /dev/null
+++ b/bluez/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, 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/tools/hid2hci.c b/bluez/tools/hid2hci.c
new file mode 100644
index 0000000..2dbfca7
--- /dev/null
+++ b/bluez/tools/hid2hci.c
@@ -0,0 +1,396 @@
+/*
+ * 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 */
+	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,
+					char *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 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)
+{
+	char 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|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, "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;
+	}
+	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/tools/hid2hci.rules b/bluez/tools/hid2hci.rules
new file mode 100644
index 0000000..db6bb03
--- /dev/null
+++ b/bluez/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/tools/hwdb.c b/bluez/tools/hwdb.c
new file mode 100644
index 0000000..3b712e1
--- /dev/null
+++ b/bluez/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 <bluetooth/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/tools/ibeacon.c b/bluez/tools/ibeacon.c
new file mode 100644
index 0000000..28967de
--- /dev/null
+++ b/bluez/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/mainloop.h"
+#include "monitor/bt.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/tools/l2cap-tester.c b/bluez/tools/l2cap-tester.c
new file mode 100644
index 0000000..79362b2
--- /dev/null
+++ b/bluez/tools/l2cap-tester.c
@@ -0,0 +1,1409 @@
+/*
+ *
+ *  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 "src/shared/tester.h"
+#include "src/shared/mgmt.h"
+#include "src/shared/hciemu.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;
+};
+
+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;
+	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;
+};
+
+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,
+};
+
+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_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_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 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,
+};
+
+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();
+	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)
+		bthost_set_adv_enable(bthost, 0x01);
+	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_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_PAIRABLE, 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);
+	}
+
+	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 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 (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_sock(struct test_data *data, int sk, uint16_t psm,
+								uint16_t cid)
+{
+	const uint8_t *client_bdaddr;
+	struct sockaddr_l2 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.l2_family = AF_BLUETOOTH;
+	bacpy(&addr.l2_bdaddr, (void *) client_bdaddr);
+	addr.l2_psm = htobs(psm);
+	addr.l2_cid = htobs(cid);
+	if (data->hciemu_type == HCIEMU_TYPE_LE)
+		addr.l2_bdaddr_type = BDADDR_LE_PUBLIC;
+	else
+		addr.l2_bdaddr_type = BDADDR_BREDR;
+
+	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 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 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);
+	}
+
+	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 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);
+		goto failed;
+	}
+
+	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 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_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 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/tools/l2ping.1 b/bluez/tools/l2ping.1
new file mode 100644
index 0000000..4d09b05
--- /dev/null
+++ b/bluez/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/tools/l2ping.c b/bluez/tools/l2ping.c
new file mode 100644
index 0000000..29fb3d0
--- /dev/null
+++ b/bluez/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 <sys/poll.h>
+#include <sys/socket.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/hci.h>
+#include <bluetooth/hci_lib.h>
+#include <bluetooth/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/tools/l2test.c b/bluez/tools/l2test.c
new file mode 100644
index 0000000..c70bac0
--- /dev/null
+++ b/bluez/tools/l2test.c
@@ -0,0 +1,1658 @@
+/*
+ *
+ *  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 <sys/poll.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/hci.h>
+#include <bluetooth/hci_lib.h>
+#include <bluetooth/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;
+
+static 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 || cid) {
+		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 || cid)
+		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);
+		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 */
+		close(sk);
+
+		/* 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);
+
+		syslog(LOG_INFO, "Disconnect: %m");
+		exit(0);
+	}
+
+	return;
+
+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;
+		}
+		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++;
+
+		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))
+			usleep(send_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");
+}
+
+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[-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");
+}
+
+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, "rdscuwmntqxyzpb:a:"
+		"i:P:I:O:J:B:N:L:W:C:D:X:F:Q:Z:Y:H:K:V:RUGAESMT")) != 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 = strdup(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;
+
+		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/tools/magic.btsnoop b/bluez/tools/magic.btsnoop
new file mode 100644
index 0000000..ebb845d
--- /dev/null
+++ b/bluez/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/tools/mgmt-tester.c b/bluez/tools/mgmt-tester.c
new file mode 100644
index 0000000..a8b14e4
--- /dev/null
+++ b/bluez/tools/mgmt-tester.c
@@ -0,0 +1,4012 @@
+/*
+ *
+ *  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 <glib.h>
+
+#include "lib/bluetooth.h"
+#include "lib/mgmt.h"
+
+#include "monitor/bt.h"
+#include "emulator/bthost.h"
+
+#include "src/shared/util.h"
+#include "src/shared/tester.h"
+#include "src/shared/mgmt.h"
+#include "src/shared/hciemu.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;
+	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: 0x%02x", 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: 0x%02x", 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;
+
+	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;
+	}
+
+	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;
+	}
+
+	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: 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();
+	}
+}
+
+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(name, data, setup, func) \
+	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 = 0x06; \
+		user->expected_manufacturer = 0x003f; \
+		user->expected_supported_settings = 0x00003fff; \
+		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 = 0x06; \
+		user->expected_manufacturer = 0x003f; \
+		user->expected_supported_settings = 0x00003611; \
+		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;
+	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;
+	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;
+	bool reject_ssp;
+	bool client_reject_ssp;
+};
+
+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 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 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 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_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 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_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_pairable_on_param[] = { 0x01 };
+static const char set_pairable_invalid_param[] = { 0x02 };
+static const char set_pairable_garbage_param[] = { 0x01, 0x00 };
+static const char set_pairable_settings_param[] = { 0x90, 0x00, 0x00, 0x00 };
+
+static const struct generic_data set_pairable_on_success_test = {
+	.send_opcode = MGMT_OP_SET_PAIRABLE,
+	.send_param = set_pairable_on_param,
+	.send_len = sizeof(set_pairable_on_param),
+	.expect_status = MGMT_STATUS_SUCCESS,
+	.expect_param = set_pairable_settings_param,
+	.expect_len = sizeof(set_pairable_settings_param),
+	.expect_settings_set = MGMT_SETTING_PAIRABLE,
+};
+
+static const struct generic_data set_pairable_on_invalid_param_test_1 = {
+	.send_opcode = MGMT_OP_SET_PAIRABLE,
+	.expect_status = MGMT_STATUS_INVALID_PARAMS,
+};
+
+static const struct generic_data set_pairable_on_invalid_param_test_2 = {
+	.send_opcode = MGMT_OP_SET_PAIRABLE,
+	.send_param = set_pairable_invalid_param,
+	.send_len = sizeof(set_pairable_invalid_param),
+	.expect_status = MGMT_STATUS_INVALID_PARAMS,
+};
+
+static const struct generic_data set_pairable_on_invalid_param_test_3 = {
+	.send_opcode = MGMT_OP_SET_PAIRABLE,
+	.send_param = set_pairable_garbage_param,
+	.send_len = sizeof(set_pairable_garbage_param),
+	.expect_status = MGMT_STATUS_INVALID_PARAMS,
+};
+
+static const struct generic_data set_pairable_on_invalid_index_test = {
+	.send_index_none = true,
+	.send_opcode = MGMT_OP_SET_PAIRABLE,
+	.send_param = set_pairable_on_param,
+	.send_len = sizeof(set_pairable_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 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_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, 0x01 };
+
+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 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,
+};
+
+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,
+};
+
+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,
+};
+
+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 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 char stop_discovery_inq_param[] = { 0x33, 0x8b, 0x9e, 0x08, 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 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 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 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 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);
+	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();
+	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 uint16_t settings_powered_pairable[] = { MGMT_OP_SET_PAIRABLE,
+						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_pairable,
+	.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_pairable_linksec[] = { MGMT_OP_SET_PAIRABLE,
+							MGMT_OP_SET_POWERED,
+							MGMT_OP_SET_LINK_SECURITY,
+							0 };
+
+static const struct generic_data pair_device_success_test_2 = {
+	.setup_settings = settings_powered_pairable_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 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_reject_test_1 = {
+	.setup_settings = settings_powered_pairable,
+	.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_pairable,
+	.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_pairable_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_pairable_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_pairable_ssp[] = {	MGMT_OP_SET_PAIRABLE,
+							MGMT_OP_SET_SSP,
+							MGMT_OP_SET_POWERED,
+							0 };
+
+static const struct generic_data pair_device_ssp_test_1 = {
+	.setup_settings = settings_powered_pairable_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 struct generic_data pair_device_ssp_test_2 = {
+	.setup_settings = settings_powered_pairable_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_ssp_reject_1 = {
+	.setup_settings = settings_powered_pairable_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 */
+	.reject_ssp = true,
+};
+
+static const struct generic_data pair_device_ssp_reject_2 = {
+	.setup_settings = settings_powered_pairable_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_ssp = true,
+};
+
+static const struct generic_data pair_device_ssp_nonpairable_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_AUTH_FAILED,
+	.expect_func = pair_device_expect_param_func,
+	.expect_alt_ev = MGMT_EV_AUTH_FAILED,
+	.expect_alt_ev_len = 8,
+	.io_cap = 0x01, /* DisplayYesNo */
+	.client_io_cap = 0x01, /* DisplayYesNo */
+};
+
+static uint16_t settings_powered_connectable_pairable[] = {
+						MGMT_OP_SET_PAIRABLE,
+						MGMT_OP_SET_CONNECTABLE,
+						MGMT_OP_SET_POWERED, 0 };
+
+static const struct generic_data pairing_acceptor_legacy_1 = {
+	.setup_settings = settings_powered_connectable_pairable,
+	.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_pairable,
+	.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 uint16_t settings_powered_connectable_pairable_linksec[] = {
+						MGMT_OP_SET_PAIRABLE,
+						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_pairable_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_pairable_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_pairable_ssp[] = {
+						MGMT_OP_SET_PAIRABLE,
+						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_pairable_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 */
+};
+
+static const struct generic_data pairing_acceptor_ssp_2 = {
+	.setup_settings = settings_powered_connectable_pairable_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 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 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,
+};
+
+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 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 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 status 0x%02x", 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 status 0x%02x", 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_ssp_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 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");
+	tester_setup_complete();
+}
+
+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;
+
+	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_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;
+
+	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 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);
+
+	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_reject_ssp)
+		bthost_set_reject_user_confirm(bthost, true);
+
+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 *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_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 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;
+
+	if (length != test->expect_alt_ev_len) {
+		tester_warn("Invalid length %s event",
+					mgmt_evstr(test->expect_alt_ev));
+		tester_test_failed();
+		return;
+	}
+
+	tester_print("New %s event received", mgmt_evstr(test->expect_alt_ev));
+
+	if (test->expect_alt_ev_param &&
+			memcmp(param, test->expect_alt_ev_param,
+						test->expect_alt_ev_len) != 0)
+		return;
+
+	tester_print("Unregistering %s notification",
+					mgmt_evstr(test->expect_alt_ev));
+
+	mgmt_unregister(data->mgmt_alt, data->mgmt_alt_ev_id);
+
+	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("Command 0x%04x finished with status 0x%02x",
+						test->send_opcode, status);
+
+	if (status != test->expect_status) {
+		tester_test_failed();
+		return;
+	}
+
+	if (test->expect_func)
+		expect_param = test->expect_func(&expect_len);
+
+	if (length != expect_len) {
+		tester_test_failed();
+		return;
+	}
+
+	if (expect_param && expect_len > 0 &&
+				memcmp(param, expect_param, length)) {
+		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 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);
+	}
+
+	tester_print("Sending command 0x%04x", test->send_opcode);
+
+	if (test->send_func)
+		send_param = test->send_func(&send_len);
+
+	mgmt_send(data->mgmt, test->send_opcode, index, send_len, send_param,
+					command_generic_callback, NULL, NULL);
+	test_add_condition(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);
+}
+
+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("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_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_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_le("Set fast connectable on - Not Supported 1",
+				&set_fast_conn_on_not_supported_test_1,
+				NULL, test_command_generic);
+
+	test_bredrle("Set pairable on - Success",
+				&set_pairable_on_success_test,
+				NULL, test_command_generic);
+	test_bredrle("Set pairable on - Invalid parameters 1",
+				&set_pairable_on_invalid_param_test_1,
+				NULL, test_command_generic);
+	test_bredrle("Set pairable on - Invalid parameters 2",
+				&set_pairable_on_invalid_param_test_2,
+				NULL, test_command_generic);
+	test_bredrle("Set pairable on - Invalid parameters 3",
+				&set_pairable_on_invalid_param_test_3,
+				NULL, test_command_generic);
+	test_bredrle("Set pairable on - Invalid index",
+				&set_pairable_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("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("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("Pair Device - Not Powered 1",
+				&pair_device_not_powered_test_1,
+				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 - Legacy Success 1",
+				&pair_device_success_test_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 Confirm Success 1",
+				&pair_device_ssp_test_2,
+				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-pairable 1",
+				&pair_device_ssp_nonpairable_1,
+				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 - 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_ssp_acceptor,
+				test_pairing_acceptor);
+	test_bredrle("Pairing Acceptor - SSP 2",
+				&pairing_acceptor_ssp_2, setup_ssp_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_bredrle("Set Static Address - Success",
+				&set_static_addr_success_test,
+				NULL, test_command_generic);
+	test_bredrle("Set Static Address - Failure",
+				&set_static_addr_failure_test,
+				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);
+
+	return tester_run();
+}
diff --git a/bluez/tools/mpris-player.c b/bluez/tools/mpris-player.c
new file mode 100644
index 0000000..c94330c
--- /dev/null
+++ b/bluez/tools/mpris-player.c
@@ -0,0 +1,2593 @@
+/*
+ *
+ *  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_NOT_YET_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 * 1000;
+
+	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 * 1000;
+
+	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 * 1000;
+
+	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/tools/obex-client-tool.c b/bluez/tools/obex-client-tool.c
new file mode 100644
index 0000000..4716a8c
--- /dev/null
+++ b/bluez/tools/obex-client-tool.c
@@ -0,0 +1,467 @@
+/*
+ *
+ *  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);
+		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)
+		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/tools/obex-server-tool.c b/bluez/tools/obex-server-tool.c
new file mode 100644
index 0000000..3fed6dc
--- /dev/null
+++ b/bluez/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/tools/obexctl.c b/bluez/tools/obexctl.c
new file mode 100644
index 0000000..fc051d7
--- /dev/null
+++ b/bluez/tools/obexctl.c
@@ -0,0 +1,2481 @@
+/*
+ *
+ *  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 <readline/readline.h>
+#include <readline/history.h>
+#include <glib.h>
+#include <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: %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 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 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;
+	int ctype;
+
+	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 ((ctype = 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_ls(GDBusProxy *proxy, int argc, char *argv[])
+{
+	if (argc > 1) {
+		pbap_search(proxy, argc, argv);
+		return;
+	}
+
+	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 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" },
+	{ "cd",           "<path>",   cmd_cd, "Change current folder" },
+	{ "ls",           NULL,       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)
+{
+	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("  %s %-*s %s\n", cmd_table[i].cmd,
+					(int)(25 - strlen(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 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/tools/parser/amp.c b/bluez/tools/parser/amp.c
new file mode 100644
index 0000000..158ca4a
--- /dev/null
+++ b/bluez/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/tools/parser/att.c b/bluez/tools/parser/att.c
new file mode 100644
index 0000000..5d9bea4
--- /dev/null
+++ b/bluez/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 = get_u8(frm);
+	uint16_t handle = btohs(htons(get_u16(frm)));
+	uint8_t err = 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(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(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(get_u16(frm)));
+	uint16_t end = btohs(htons(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] = 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 = get_u8(frm);
+
+	p_indent(level, frm);
+
+	if (fmt == 0x01) {
+		printf("format: uuid-16\n");
+
+		while (frm->len > 0) {
+			uint16_t handle = btohs(htons(get_u16(frm)));
+			uint16_t uuid = btohs(htons(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(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(get_u16(frm)));
+	uint16_t end = btohs(htons(get_u16(frm)));
+	uint16_t uuid = btohs(htons(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", 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(get_u16(frm)));
+		uint16_t end = btohs(htons(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(get_u16(frm)));
+	uint16_t end = btohs(htons(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(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 = get_u8(frm);
+
+	p_indent(level, frm);
+	printf("length: %d\n", length);
+
+	while (frm->len > 0) {
+		uint16_t handle = btohs(htons(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 ", get_u8(frm));
+		}
+		printf("\n");
+	}
+}
+
+static void att_read_req_dump(int level, struct frame *frm)
+{
+	uint16_t handle = btohs(htons(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(get_u16(frm)));
+	uint16_t offset = btohs(htons(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", 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(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", get_u8(frm));
+	printf("\n");
+}
+
+static void att_read_by_group_resp_dump(int level, struct frame *frm)
+{
+	uint8_t length = get_u8(frm);
+
+	while (frm->len > 0) {
+		uint16_t attr_handle = btohs(htons(get_u16(frm)));
+		uint16_t end_grp_handle = btohs(htons(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", get_u8(frm));
+			remaining--;
+		}
+		printf("\n");
+	}
+}
+
+static void att_write_req_dump(int level, struct frame *frm)
+{
+	uint16_t handle = btohs(htons(get_u16(frm)));
+
+	p_indent(level, frm);
+	printf("handle 0x%4.4x value ", handle);
+
+	while (frm->len > 0)
+		printf(" 0x%2.2x", get_u8(frm));
+	printf("\n");
+}
+
+static void att_signed_write_dump(int level, struct frame *frm)
+{
+	uint16_t handle = btohs(htons(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", get_u8(frm));
+	printf("\n");
+
+	p_indent(level, frm);
+	printf("auth signature ");
+	while (frm->len > 0)
+		printf(" 0x%2.2x", get_u8(frm));
+	printf("\n");
+}
+
+static void att_prep_write_dump(int level, struct frame *frm)
+{
+	uint16_t handle = btohs(htons(get_u16(frm)));
+	uint16_t val_offset = btohs(htons(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", get_u8(frm));
+	printf("\n");
+}
+
+static void att_exec_write_req_dump(int level, struct frame *frm)
+{
+	uint8_t flags = 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(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 ", get_u8(frm));
+	printf("\n");
+}
+
+void att_dump(int level, struct frame *frm)
+{
+	uint8_t op;
+
+	op = 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/tools/parser/avctp.c b/bluez/tools/parser/avctp.c
new file mode 100644
index 0000000..58b181d
--- /dev/null
+++ b/bluez/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 = get_u8(frm);
+	pid = 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/tools/parser/avdtp.c b/bluez/tools/parser/avdtp.c
new file mode 100644
index 0000000..5a2ee55
--- /dev/null
+++ b/bluez/tools/parser/avdtp.c
@@ -0,0 +1,520 @@
+/*
+ *
+ *  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 *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 = 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 = 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 = get_u8(frm);
+	int_seid = 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 = get_u8(frm);
+		len = get_u8(frm);
+
+		if (cat == 7) {
+			uint8_t type, codec, tmp;
+
+			type  = get_u8(frm);
+			codec = get_u8(frm);
+
+			printf("%s - %s\n", cat2str(cat), codec2str(type, codec));
+
+			switch (codec) {
+			case 0:
+				tmp = 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 = 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 = get_u8(frm);
+				p_indent(level + 1, frm);
+				printf("Bitpool Range %d-%d\n", tmp, get_u8(frm));
+				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 = get_u8(frm);
+			type = 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 = 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 = 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 = get_u8(frm);
+		delay = 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 = get_u8(frm);
+
+		nsp = (hdr & 0x0c) == 0x04 ? get_u8(frm) : 0;
+		sid = hdr & 0x08 ? 0x00 : 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  = get_u8(frm);
+		type = get_u8(frm);
+		seqn = get_u16(frm);
+		time = get_u32(frm);
+		ssrc = 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/tools/parser/avrcp.c b/bluez/tools/parser/avrcp.c
new file mode 100644
index 0000000..7c320ea
--- /dev/null
+++ b/bluez/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 = 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 = get_u8(frm);
+	printf("CapabilityID: 0x%02x (%s)\n", cap, cap2str(cap));
+
+	if (len == 1)
+		return;
+
+	p_indent(level, frm);
+
+	count = 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", get_u8(frm));
+			printf("\n");
+		}
+		break;
+	case 0x3:
+		for (; count > 0; count--) {
+			uint8_t event;
+
+			p_indent(level, frm);
+
+			event = 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 = get_u8(frm);
+	printf("AttributeCount: 0x%02x\n", num);
+
+	for (; num > 0; num--) {
+		uint8_t attr;
+
+		p_indent(level, frm);
+
+		attr = 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 = get_u8(frm);
+	printf("AttributeID: 0x%02x (%s)\n", attr, attr2str(attr));
+
+	return;
+
+response:
+	num = get_u8(frm);
+	printf("ValueCount: 0x%02x\n", num);
+
+	for (; num > 0; num--) {
+		uint8_t value;
+
+		p_indent(level, frm);
+
+		value = 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 = get_u8(frm);
+	printf("AttributeCount: 0x%02x\n", num);
+
+	for (; num > 0; num--) {
+		uint8_t attr;
+
+		p_indent(level, frm);
+
+		attr = get_u8(frm);
+		printf("AttributeID: 0x%02x (%s)\n", attr, attr2str(attr));
+	}
+
+	return;
+
+response:
+	num = get_u8(frm);
+	printf("ValueCount: 0x%02x\n", num);
+
+	for (; num > 0; num--) {
+		uint8_t attr, value;
+
+		p_indent(level, frm);
+
+		attr = get_u8(frm);
+		printf("AttributeID: 0x%02x (%s)\n", attr, attr2str(attr));
+
+		p_indent(level, frm);
+
+		value = 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 = get_u8(frm);
+	printf("AttributeCount: 0x%02x\n", num);
+
+	for (; num > 0; num--) {
+		uint8_t attr, value;
+
+		p_indent(level, frm);
+
+		attr = get_u8(frm);
+		printf("AttributeID: 0x%02x (%s)\n", attr, attr2str(attr));
+
+		p_indent(level, frm);
+
+		value = 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 = 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 = 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 = get_u8(frm);
+		printf("AttributeID: 0x%02x (%s)\n", attr, attr2str(attr));
+
+		p_indent(level, frm);
+
+		charset = get_u16(frm);
+		printf("CharsetID: 0x%04x (%s)\n", charset,
+							charset2str(charset));
+
+		p_indent(level, frm);
+
+		len = get_u8(frm);
+		printf("StringLength: 0x%02x\n", len);
+
+		p_indent(level, frm);
+
+		printf("String: ");
+		for (; len > 0; len--) {
+			uint8_t c = 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 = get_u8(frm);
+	printf("AttributeID: 0x%02x (%s)\n", attr, attr2str(attr));
+
+	p_indent(level, frm);
+
+	num = get_u8(frm);
+	printf("ValueCount: 0x%02x\n", num);
+
+	for (; num > 0; num--) {
+		uint8_t value;
+
+		p_indent(level, frm);
+
+		value = get_u8(frm);
+		printf("ValueID: 0x%02x (%s)\n", value,
+						value2str(attr, value));
+	}
+
+	return;
+
+response:
+	num = 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 = get_u8(frm);
+		printf("ValueID: 0x%02x (%s)\n", value,
+						value2str(attr, value));
+
+		p_indent(level, frm);
+
+		charset = get_u16(frm);
+		printf("CharsetID: 0x%04x (%s)\n", charset,
+							charset2str(charset));
+
+		p_indent(level, frm);
+
+		len = get_u8(frm);
+		printf("StringLength: 0x%02x\n", len);
+
+		p_indent(level, frm);
+
+		printf("String: ");
+		for (; len > 0; len--) {
+			uint8_t c = 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 = get_u8(frm);
+	printf("CharsetCount: 0x%02x\n", num);
+
+	for (; num > 0; num--) {
+		uint16_t charset;
+
+		p_indent(level, frm);
+
+		charset = 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 = 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 = get_u64(frm);
+	printf("Identifier: 0x%jx (%s)\n", id, id ? "Reserved" : "PLAYING");
+
+	p_indent(level, frm);
+
+	num = get_u8(frm);
+	printf("AttributeCount: 0x%02x\n", num);
+
+	for (; num > 0; num--) {
+		uint32_t attr;
+
+		p_indent(level, frm);
+
+		attr = 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 = 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 = 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 = get_u32(frm);
+		printf("Attribute: 0x%08x (%s)\n", attr, mediattr2str(attr));
+
+		p_indent(level, frm);
+
+		charset = get_u16(frm);
+		printf("CharsetID: 0x%04x (%s)\n", charset,
+							charset2str(charset));
+
+		p_indent(level, frm);
+		attrlen = 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 = 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 = get_u32(frm);
+	printf("SongLength: 0x%08x (%u miliseconds)\n", interval, interval);
+
+	p_indent(level, frm);
+
+	interval = get_u32(frm);
+	printf("SongPosition: 0x%08x (%u miliconds)\n", interval, interval);
+
+	p_indent(level, frm);
+
+	status = 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 = get_u8(frm);
+	printf("EventID: 0x%02x (%s)\n", event, event2str(event));
+
+	p_indent(level, frm);
+
+	interval = 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 = 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 = get_u8(frm);
+		printf("PlayStatus: 0x%02x (%s)\n", status,
+						playstatus2str(status));
+		break;
+	case AVRCP_EVENT_TRACK_CHANGED:
+		id = get_u64(frm);
+		printf("Identifier: 0x%16" PRIx64 " (%" PRIu64 ")\n", id, id);
+		break;
+	case AVRCP_EVENT_PLAYBACK_POS_CHANGED:
+		interval = get_u32(frm);
+		printf("Position: 0x%08x (%u miliseconds)\n", interval,
+								interval);
+		break;
+	case AVRCP_EVENT_BATT_STATUS_CHANGED:
+		status = get_u8(frm);
+		printf("BatteryStatus: 0x%02x (%s)\n", status,
+							status2str(status));
+		break;
+	case AVRCP_EVENT_SYSTEM_STATUS_CHANGED:
+		status = 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 = get_u8(frm);
+		printf("AttributeCount: 0x%02x\n", status);
+
+		for (; status > 0; status--) {
+			uint8_t attr, value;
+
+			p_indent(level, frm);
+
+			attr = get_u8(frm);
+			printf("AttributeID: 0x%02x (%s)\n", attr,
+							attr2str(attr));
+
+			p_indent(level, frm);
+
+			value = get_u8(frm);
+			printf("ValueID: 0x%02x (%s)\n", value,
+						value2str(attr, value));
+		}
+		break;
+	case AVRCP_EVENT_VOLUME_CHANGED:
+		status = get_u8(frm) & 0x7F;
+		printf("Volume: %.2f%% (%d/127)\n", status/1.27, status);
+		break;
+	case AVRCP_EVENT_ADDRESSED_PLAYER_CHANGED:
+		uid = get_u16(frm);
+		printf("PlayerID: 0x%04x (%u)\n", uid, uid);
+
+
+		p_indent(level, frm);
+
+		uid = get_u16(frm);
+		printf("UIDCounter: 0x%04x (%u)\n", uid, uid);
+		break;
+	case AVRCP_EVENT_UIDS_CHANGED:
+		uid = 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 = 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 = 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 = 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 = 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 = get_u8(frm);
+	printf("Status: 0x%02x (%s)\n", status, error2str(status));
+
+	if (len == 1)
+		return;
+
+	p_indent(level, frm);
+
+	uids = get_u16(frm);
+	printf("UIDCounter: 0x%04x (%u)\n", uids, uids);
+
+	p_indent(level, frm);
+
+	items = get_u32(frm);
+	printf("Number of Items: 0x%08x (%u)\n", items, items);
+
+	p_indent(level, frm);
+
+	charset = get_u16(frm);
+	printf("CharsetID: 0x%04x (%s)\n", charset, charset2str(charset));
+
+	p_indent(level, frm);
+
+	folders = get_u8(frm);
+	printf("Folder Depth: 0x%02x (%u)\n", folders, folders);
+
+	for (; folders > 0; folders--) {
+		uint16_t len;
+
+		p_indent(level, frm);
+
+		len = get_u8(frm);
+		printf("Folder: ");
+		for (; len > 0; len--) {
+			uint8_t c = 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 = get_u8(frm);
+	printf("Scope: 0x%02x (%s)\n", scope, scope2str(scope));
+
+	p_indent(level, frm);
+
+	uid = get_u64(frm);
+	printf("UID: 0x%16" PRIx64 " (%" PRIu64 ")\n", uid, uid);
+
+	p_indent(level, frm);
+
+	uidcounter = get_u16(frm);
+	printf("UIDCounter: 0x%04x (%u)\n", uidcounter, uidcounter);
+
+	return;
+
+response:
+	status = 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 = get_u8(frm);
+	printf("Scope: 0x%02x (%s)\n", scope, scope2str(scope));
+
+	p_indent(level, frm);
+
+	uid = get_u64(frm);
+	printf("UID: 0x%16" PRIx64 " (%" PRIu64 ")\n", uid, uid);
+
+	p_indent(level, frm);
+
+	uidcounter = get_u16(frm);
+	printf("UIDCounter: 0x%04x (%u)\n", uidcounter, uidcounter);
+
+	return;
+
+response:
+	status = 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 = get_u8(frm);
+	pt = get_u8(frm);
+	len = 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 = get_u8(frm);
+	printf("Operation: 0x%02x (%s %s)\n", op, op2str(op),
+					op & 0x80 ? "Released" : "Pressed");
+
+	p_indent(level, frm);
+
+	len = 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 = get_u16(frm);
+	printf("PlayerID: 0x%04x (%u)\n", id, id);
+
+	p_indent(level, frm);
+
+	type = get_u8(frm);
+	printf("PlayerType: 0x%04x (%s)\n", type, playertype2str(type));
+
+	p_indent(level, frm);
+
+	subtype = get_u32(frm);
+	printf("PlayerSubtype: 0x%08x (%s)\n", subtype,
+						playersubtype2str(subtype));
+
+	p_indent(level, frm);
+
+	status = get_u8(frm);
+	printf("PlayStatus: 0x%02x (%s)\n", status, playstatus2str(status));
+
+	p_indent(level, frm);
+
+	get_u128(frm, &features[0], &features[1]);
+	printf("Features: 0x%16" PRIx64 "%16" PRIx64 "\n", features[1],
+								features[0]);
+
+	p_indent(level, frm);
+
+	charset = get_u16(frm);
+	printf("CharsetID: 0x%04x (%s)\n", charset, charset2str(charset));
+
+	p_indent(level, frm);
+
+	namelen = get_u16(frm);
+	printf("NameLength: 0x%04x (%u)\n", namelen, namelen);
+
+	p_indent(level, frm);
+
+	printf("Name: ");
+	for (; namelen > 0; namelen--) {
+		uint8_t c = 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 = get_u64(frm);
+	printf("FolderUID: 0x%16" PRIx64 " (%" PRIu64 ")\n", uid, uid);
+
+	p_indent(level, frm);
+
+	type = get_u8(frm);
+	printf("FolderType: 0x%02x (%s)\n", type, foldertype2str(type));
+
+	p_indent(level, frm);
+
+	playable = get_u8(frm);
+	printf("IsPlayable: 0x%02x (%s)\n", playable,
+					playable & 0x01 ? "True" : "False");
+
+	p_indent(level, frm);
+
+	charset = get_u16(frm);
+	printf("CharsetID: 0x%04x (%s)\n", charset, charset2str(charset));
+
+	p_indent(level, frm);
+
+	namelen = get_u16(frm);
+	printf("NameLength: 0x%04x (%u)\n", namelen, namelen);
+
+	p_indent(level, frm);
+
+	printf("Name: ");
+	for (; namelen > 0; namelen--) {
+		uint8_t c = 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 = get_u32(frm);
+		printf("AttributeID: 0x%08x (%s)\n", attr, mediattr2str(attr));
+
+		p_indent(level, frm);
+
+		charset = get_u16(frm);
+		printf("CharsetID: 0x%04x (%s)\n", charset,
+							charset2str(charset));
+
+		p_indent(level, frm);
+
+		len = get_u16(frm);
+		printf("AttributeLength: 0x%04x (%u)\n", len, len);
+
+		p_indent(level, frm);
+
+		printf("AttributeValue: ");
+		for (; len > 0; len--) {
+			uint8_t c = 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 = get_u64(frm);
+	printf("ElementUID: 0x%16" PRIx64 " (%" PRIu64 ")\n", uid, uid);
+
+	p_indent(level, frm);
+
+	type = get_u8(frm);
+	printf("ElementType: 0x%02x (%s)\n", type, elementtype2str(type));
+
+	p_indent(level, frm);
+
+	charset = get_u16(frm);
+	printf("CharsetID: 0x%04x (%s)\n", charset, charset2str(charset));
+
+	p_indent(level, frm);
+
+	namelen = get_u16(frm);
+	printf("NameLength: 0x%04x (%u)\n", namelen, namelen);
+
+	p_indent(level, frm);
+
+	printf("Name: ");
+	for (; namelen > 0; namelen--) {
+		uint8_t c = get_u8(frm);
+		printf("%1c", isprint(c) ? c : '.');
+	}
+	printf("\n");
+
+	p_indent(level, frm);
+
+	count = 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 = get_u8(frm);
+	printf("Scope: 0x%02x (%s)\n", scope, scope2str(scope));
+
+	p_indent(level, frm);
+
+	start = get_u32(frm);
+	printf("StartItem: 0x%08x (%u)\n", start, start);
+
+	p_indent(level, frm);
+
+	end = get_u32(frm);
+	printf("EndItem: 0x%08x (%u)\n", end, end);
+
+	p_indent(level, frm);
+
+	count = get_u8(frm);
+	printf("AttributeCount: 0x%02x (%u)\n", count, count);
+
+	for (; count > 0; count--) {
+		uint32_t attr;
+
+		p_indent(level, frm);
+
+		attr = get_u32(frm);
+		printf("AttributeID: 0x%08x (%s)\n", attr, mediattr2str(attr));
+	}
+
+	return;
+
+response:
+	status = get_u8(frm);
+	printf("Status: 0x%02x (%s)\n", status, error2str(status));
+
+	if (len == 1)
+		return;
+
+	p_indent(level, frm);
+
+	uid = get_u16(frm);
+	printf("UIDCounter: 0x%04x (%u)\n", uid, uid);
+
+	p_indent(level, frm);
+
+	num = 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 = get_u8(frm);
+		len = 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 = get_u16(frm);
+	printf("UIDCounter: 0x%04x (%u)\n", uidcounter, uidcounter);
+
+	p_indent(level, frm);
+
+	dir = get_u8(frm);
+	printf("Direction: 0x%02x (%s)\n", dir, dir2str(dir));
+
+	p_indent(level, frm);
+
+	uid = get_u64(frm);
+	printf("FolderUID: 0x%16" PRIx64 " (%" PRIu64 ")\n", uid, uid);
+
+	return;
+
+response:
+	status = get_u8(frm);
+	printf("Status: 0x%02x (%s)\n", status, error2str(status));
+
+	if (len == 1)
+		return;
+
+	p_indent(level, frm);
+
+	items = 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 = get_u8(frm);
+	printf("Scope: 0x%02x (%s)\n", scope, scope2str(scope));
+
+	p_indent(level, frm);
+
+	uid = get_u64(frm);
+	printf("UID: 0x%16" PRIx64 " (%" PRIu64 ")\n", uid, uid);
+
+	p_indent(level, frm);
+
+	uidcounter = get_u16(frm);
+	printf("UIDCounter: 0x%04x (%u)\n", uidcounter, uidcounter);
+
+	p_indent(level, frm);
+
+	count = get_u8(frm);
+	printf("AttributeCount: 0x%02x (%u)\n", count, count);
+
+	for (; count > 0; count--) {
+		uint32_t attr;
+
+		p_indent(level, frm);
+
+		attr = get_u32(frm);
+		printf("AttributeID: 0x%08x (%s)\n", attr, mediattr2str(attr));
+	}
+
+	return;
+
+response:
+	status = get_u8(frm);
+	printf("Status: 0x%02x (%s)\n", status, error2str(status));
+
+	if (len == 1)
+		return;
+
+	p_indent(level, frm);
+
+	count = 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 = get_u16(frm);
+	printf("CharsetID: 0x%04x (%s)\n", charset, charset2str(charset));
+
+	p_indent(level, frm);
+
+	namelen = get_u16(frm);
+	printf("Length: 0x%04x (%u)\n", namelen, namelen);
+
+	p_indent(level, frm);
+
+	printf("String: ");
+	for (; namelen > 0; namelen--) {
+		uint8_t c = get_u8(frm);
+		printf("%1c", isprint(c) ? c : '.');
+	}
+	printf("\n");
+
+	return;
+
+response:
+	status = get_u8(frm);
+	printf("Status: 0x%02x (%s)\n", status, error2str(status));
+
+	if (len == 1)
+		return;
+
+	p_indent(level, frm);
+
+	uidcounter = get_u16(frm);
+	printf("UIDCounter: 0x%04x (%u)\n", uidcounter, uidcounter);
+
+	p_indent(level, frm);
+
+	items = 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 = 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 = get_u8(frm);
+	len = 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 = get_u8(frm);
+	address = get_u8(frm);
+	opcode = 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] = 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/tools/parser/bnep.c b/bluez/tools/parser/bnep.c
new file mode 100644
index 0000000..d9e958d
--- /dev/null
+++ b/bluez/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 = 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, get_u8(frm));
+		break;
+
+	case BNEP_SETUP_CONNECTION_REQUEST_MSG:
+		uuid_size = get_u8(frm);
+		printf("Setup Req(0x%02x) size 0x%02x ", type, uuid_size);
+		switch (uuid_size) {
+		case 2:
+			uuid = get_u16(frm);
+			printf("dst 0x%x", uuid);
+			if ((s = get_uuid_name(uuid)) != 0)
+				printf("(%s)", s);
+			uuid = get_u16(frm);
+			printf(" src 0x%x", uuid);
+			if ((s = get_uuid_name(uuid)) != 0)
+				printf("(%s)", s);
+			printf("\n");
+			break;
+		case 4:
+			uuid = get_u32(frm);
+			printf("dst 0x%x", uuid);
+			if ((s = get_uuid_name(uuid)) != 0)
+				printf("(%s)", s);
+			uuid = get_u32(frm);
+			printf(" src 0x%x", uuid);
+			if ((s = get_uuid_name(uuid)) != 0)
+				printf("(%s)", s);
+			printf("\n");
+			break;
+		case 16:
+			uuid = 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 = 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, get_u16(frm));
+		break;
+
+	case BNEP_FILTER_NET_TYPE_SET_MSG:
+		length = 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 - ", get_u16(frm));
+			printf("0x%04x\n", get_u16(frm));
+		}
+		break;
+
+	case BNEP_FILTER_NET_TYPE_RESPONSE_MSG:
+		printf("Filter NetType Rsp(0x%02x) res 0x%04x\n",
+							type, get_u16(frm));
+		break;
+
+	case BNEP_FILTER_MULT_ADDR_SET_MSG:
+		length = 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, 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 = get_u8(frm);
+	uint8_t length = 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 = 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 = 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 = 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 = 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 = 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 ", get_u16(frm));
+		proto = 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/tools/parser/bpa.c b/bluez/tools/parser/bpa.c
new file mode 100644
index 0000000..207397f
--- /dev/null
+++ b/bluez/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)  (get_u8(frm))
+#define BPA_U16(frm) (btohs(htons(get_u16(frm))))
+#define BPA_U32(frm) (btohl(htonl(get_u32(frm))))
+
+void bpa_dump(int level, struct frame *frm)
+{
+	uint8_t id, status, channel;
+	uint16_t num, len;
+	uint32_t time;
+
+	id = get_u8(frm);
+	num = get_u16(frm);
+	len = BPA_U16(frm);
+
+	status  = get_u8(frm);
+	time    = get_u32(frm);
+	channel = 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/tools/parser/capi.c b/bluez/tools/parser/capi.c
new file mode 100644
index 0000000..97abc4c
--- /dev/null
+++ b/bluez/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)  (get_u8(frm))
+#define CAPI_U16(frm) (btohs(htons(get_u16(frm))))
+#define CAPI_U32(frm) (btohl(htonl(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) 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/tools/parser/cmtp.c b/bluez/tools/parser/cmtp.c
new file mode 100644
index 0000000..ac5cf84
--- /dev/null
+++ b/bluez/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 = get_u8(frm);
+		bid = (hdr & 0x3c) >> 2;
+
+		switch ((hdr & 0xc0) >> 6) {
+		case 0x01:
+			len = get_u8(frm);
+			break;
+		case 0x02:
+			len = htons(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/tools/parser/csr.c b/bluez/tools/parser/csr.c
new file mode 100644
index 0000000..1a63ae0
--- /dev/null
+++ b/bluez/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)  (get_u8(frm))
+#define CSR_U16(frm) (btohs(htons(get_u16(frm))))
+#define CSR_U32(frm) ((CSR_U16(frm) << 16) + CSR_U16(frm))
+#define CSR_S16(frm) (btohs(htons(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/tools/parser/ericsson.c b/bluez/tools/parser/ericsson.c
new file mode 100644
index 0000000..a401959
--- /dev/null
+++ b/bluez/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 = 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/tools/parser/hci.c b/bluez/tools/parser/hci.c
new file mode 100644
index 0000000..351f843
--- /dev/null
+++ b/bluez/tools/parser/hci.c
@@ -0,0 +1,4127 @@
+/*
+ *
+ *  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",
+	"Unspecified Error",
+	"Unsupported LMP 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 Interval",
+	"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 = get_u8(frm);
+
+	while (length > 0) {
+		ext_inquiry_data_dump(level, frm, data);
+
+		frm->ptr += length;
+		frm->len -= length;
+
+		data = frm->ptr;
+		length = 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(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 = 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(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 = 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 = 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 = get_u8(frm);
+	uint16_t handle, packets;
+	int i;
+
+	for (i = 0; i < num; i++) {
+		handle = btohs(htons(get_u16(frm)));
+		packets = btohs(htons(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 = 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(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 = 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 = get_u8(frm);
+	uint16_t handle = btohs(htons(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 = get_u8(frm);
+	uint8_t mode = 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 = get_u8(frm);
+	uint16_t policy = btohs(htons(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 = get_u8(frm);
+	uint8_t enable = 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 {
+		p_indent(level, frm);
+		printf("HCI Version: %s (0x%x) HCI Revision: 0x%x\n",
+					hci_vertostr(rp->hci_ver),
+					rp->hci_ver, btohs(rp->hci_rev));
+		p_indent(level, frm);
+		printf("LMP Version: %s (0x%x) LMP Subversion: 0x%x\n",
+					lmp_vertostr(rp->lmp_ver),
+					rp->lmp_ver, btohs(rp->lmp_subver));
+		p_indent(level, frm);
+		printf("Manufacturer: %s (%d)\n",
+				bt_compidtostr(manufacturer), manufacturer);
+	}
+}
+
+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 = 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 {
+		p_indent(level, frm);
+		printf("LMP Version: %s (0x%x) LMP Subversion: 0x%x\n",
+			lmp_vertostr(evt->lmp_ver), evt->lmp_ver,
+			btohs(evt->lmp_subver));
+		p_indent(level, frm);
+		printf("Manufacturer: %s (%d)\n",
+			bt_compidtostr(manufacturer), manufacturer);
+	}
+}
+
+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 = 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 = 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 = 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(get_u16(frm)));
+		uint16_t proto = btohs(htons(get_u16(frm)));
+		uint16_t type = btohs(htons(get_u16(frm)));
+		uint16_t plen = btohs(htons(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/tools/parser/hcrp.c b/bluez/tools/parser/hcrp.c
new file mode 100644
index 0000000..0a16a22
--- /dev/null
+++ b/bluez/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 = get_u16(frm);
+	tid = get_u16(frm);
+	plen = 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 = get_u16(frm);
+		printf(" status %d (%s)\n", status, status2str(status));
+	} else
+		printf("\n");
+
+	if (pid == 0x0001 && !frm->in) {
+		credits = get_u32(frm);
+		p_indent(level + 1, frm);
+		printf("credits %d\n", credits);
+	}
+
+	if (pid == 0x0002 && frm->in) {
+		credits = get_u32(frm);
+		p_indent(level + 1, frm);
+		printf("credits %d\n", credits);
+	}
+
+	raw_dump(level + 1, frm);
+}
diff --git a/bluez/tools/parser/hidp.c b/bluez/tools/parser/hidp.c
new file mode 100644
index 0000000..6039eb9
--- /dev/null
+++ b/bluez/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 = 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/tools/parser/l2cap.c b/bluez/tools/parser/l2cap.c
new file mode 100644
index 0000000..a057964
--- /dev/null
+++ b/bluez/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/tools/parser/l2cap.h b/bluez/tools/parser/l2cap.h
new file mode 100644
index 0000000..788aef0
--- /dev/null
+++ b/bluez/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/tools/parser/lmp.c b/bluez/tools/parser/lmp.c
new file mode 100644
index 0000000..c303c1b
--- /dev/null
+++ b/bluez/tools/parser/lmp.c
@@ -0,0 +1,1347 @@
+/*
+ *
+ *  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)  (get_u8(frm))
+#define LMP_U16(frm) (btohs(htons(get_u16(frm))))
+#define LMP_U32(frm) (btohl(htonl(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;
+	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;
+	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;
+	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/tools/parser/obex.c b/bluez/tools/parser/obex.c
new file mode 100644
index 0000000..66b7eff
--- /dev/null
+++ b/bluez/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 = 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 = 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 = 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 = get_u8(frm);
+			printf(" = %d\n", hv8);
+			break;
+
+		case 0xc0:	/* Four bytes */
+			if (frm->len < 4) {
+				printf("\n");
+				return;
+			}
+
+			hv32 = 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 = get_u8(frm);
+		length = 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 = get_u8(frm);
+			flags   = get_u8(frm);
+			pktlen  = 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     = get_u8(frm);
+			constants = 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/tools/parser/parser.c b/bluez/tools/parser/parser.c
new file mode 100644
index 0000000..de8dbe8
--- /dev/null
+++ b/bluez/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/tools/parser/parser.h b/bluez/tools/parser/parser.h
new file mode 100644
index 0000000..62b8ac5
--- /dev/null
+++ b/bluez/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 get_u8(struct frame *frm)
+{
+	uint8_t *u8_ptr = frm->ptr;
+	frm->ptr += 1;
+	frm->len -= 1;
+	return *u8_ptr;
+}
+
+static inline uint16_t 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 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 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 get_u128(struct frame *frm, uint64_t *l, uint64_t *h)
+{
+	*h = get_u64(frm);
+	*l = 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/tools/parser/ppp.c b/bluez/tools/parser/ppp.c
new file mode 100644
index 0000000..256e172
--- /dev/null
+++ b/bluez/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 = get_u8(frm);
+	uint8_t ctrl = 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 = get_u16(frm);
+	else
+		proto = 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/tools/parser/rfcomm.c b/bluez/tools/parser/rfcomm.c
new file mode 100644
index 0000000..acf8d66
--- /dev/null
+++ b/bluez/tools/parser/rfcomm.c
@@ -0,0 +1,350 @@
+/*
+ *
+ *  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);
+}
+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);
+		raw_dump(level, frm); 
+		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/tools/parser/rfcomm.h b/bluez/tools/parser/rfcomm.h
new file mode 100644
index 0000000..a9faa0b
--- /dev/null
+++ b/bluez/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/tools/parser/sap.c b/bluez/tools/parser/sap.c
new file mode 100644
index 0000000..0c87c36
--- /dev/null
+++ b/bluez/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 = get_u8(frm);
+		get_u8(frm);
+		len = 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", get_u16(frm));
+			break;
+		case SAP_PARAM_ID_CONN_STATUS:
+			pv8 = 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 = get_u8(frm);
+			printf("0x%02x (%s)\n", pv8, result2str(pv8));
+			break;
+		case SAP_PARAM_ID_DISCONNECT_IND:
+			pv8 = get_u8(frm);
+			printf("0x%02x (%s)\n", pv8, disctype2str(pv8));
+			break;
+		case SAP_PARAM_ID_STATUS_CHANGE:
+			pv8 = get_u8(frm);
+			printf("0x%02x (%s)\n", pv8, statuschg2str(pv8));
+			break;
+		case SAP_PARAM_ID_TRANSPORT_PROTOCOL:
+			pv8 = 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 = get_u8(frm);
+	params = get_u8(frm);
+
+	/* Skip reserved field */
+	get_u16(frm);
+
+	p_indent(level, frm);
+
+	printf("SAP: %s: params %d\n", msg2str(msg), params);
+
+	parse_parameters(level, frm);
+}
diff --git a/bluez/tools/parser/sdp.c b/bluez/tools/parser/sdp.c
new file mode 100644
index 0000000..0e28328
--- /dev/null
+++ b/bluez/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 = 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 = get_u8(frm); break;
+		case 2:
+			*n = get_u16(frm); break;
+		case 4:
+			*n = get_u32(frm); break;
+		case 8:
+			*n = 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 = get_u8(frm);
+		if (channel && de_type == SDP_DE_UINT)
+			if (*channel == 0)
+				*channel = val;
+		break;
+	case 2: /* 16-bit */
+		val = get_u16(frm);
+		if (psm && de_type == SDP_DE_UINT)
+			if (*psm == 0)
+				*psm = val;
+		break;
+	case 4: /* 32-bit */
+		val = get_u32(frm);
+		break;
+	case 8: /* 64-bit */
+		val = get_u64(frm);
+		break;
+	case 16:/* 128-bit */
+		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 = get_u16(frm);
+		s = "uuid-16";
+		break;
+	case 4: /* 32_bit UUID */
+		uuid = 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 = 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 = 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 = 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 ", 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", get_u16(frm));
+
+		/* Parse ContinuationState */
+		print_cont_state(level + 1, frm->ptr);
+		break;
+
+	case SDP_SERVICE_SEARCH_RSP:
+		/* Parse TotalServiceRecordCount */
+		total = get_u16(frm);
+
+		/* Parse CurrentServiceRecordCount */
+		count = 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", 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", get_u32(frm));
+
+		/* Parse MaximumAttributeByteCount */
+		p_indent(level + 1, frm);
+		printf("max %d\n", 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 = 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", 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 = 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/tools/parser/sdp.h b/bluez/tools/parser/sdp.h
new file mode 100644
index 0000000..ed55a23
--- /dev/null
+++ b/bluez/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/tools/parser/smp.c b/bluez/tools/parser/smp.c
new file mode 100644
index 0000000..c81b585
--- /dev/null
+++ b/bluez/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 = get_u8(frm);
+	uint8_t oob = get_u8(frm);
+	uint8_t auth = get_u8(frm);
+	uint8_t key_size = get_u8(frm);
+	uint8_t int_dist = get_u8(frm);
+	uint8_t resp_dist = 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", 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", get_u8(frm));
+	printf("\n");
+}
+
+static void smp_cmd_pairing_failed_dump(int level, struct frame *frm)
+{
+	uint8_t reason = 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", get_u8(frm));
+	printf("\n");
+}
+
+static void smp_cmd_master_ident_dump(int level, struct frame *frm)
+{
+	uint16_t ediv = btohs(htons(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", 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", get_u8(frm));
+	printf("\n");
+}
+
+static void smp_cmd_ident_addr_info_dump(int level, struct frame *frm)
+{
+	uint8_t type = 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", get_u8(frm));
+	printf("\n");
+}
+
+static void smp_cmd_security_req_dump(int level, struct frame *frm)
+{
+	uint8_t auth = 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 = 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/tools/parser/tcpip.c b/bluez/tools/parser/tcpip.c
new file mode 100644
index 0000000..6f2c3cb
--- /dev/null
+++ b/bluez/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/tools/rctest.1 b/bluez/tools/rctest.1
new file mode 100644
index 0000000..dfedbef
--- /dev/null
+++ b/bluez/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/tools/rctest.c b/bluez/tools/rctest.c
new file mode 100644
index 0000000..2c7e45b
--- /dev/null
+++ b/bluez/tools/rctest.c
@@ -0,0 +1,909 @@
+/*
+ *
+ *  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 <bluetooth/bluetooth.h>
+#include <bluetooth/hci.h>
+#include <bluetooth/hci_lib.h>
+#include <bluetooth/rfcomm.h>
+#include <bluetooth/sdp.h>
+#include <bluetooth/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 char *filename = NULL;
+static 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;
+	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;
+		sdp_list_t *protos;
+
+		if (!sdp_get_access_protos(rec, &protos)) {
+			channel = sdp_get_proto_port(protos, RFCOMM_UUID);
+			if (channel > 0)
+				break;
+		}
+	}
+
+done:
+	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);
+	}
+
+	return;
+
+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);
+		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");
+}
+
+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(recv_mode);
+			return;
+		}
+
+		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(save_mode);
+
+		close(save_fd);
+	} 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 = strdup(optarg);
+			break;
+
+		case 'O':
+			savefile = strdup(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/tools/rfcomm-tester.c b/bluez/tools/rfcomm-tester.c
new file mode 100644
index 0000000..8be6e72
--- /dev/null
+++ b/bluez/tools/rfcomm-tester.c
@@ -0,0 +1,762 @@
+/*
+ *
+ *  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/mgmt.h"
+#include "bluetooth/rfcomm.h"
+
+#include "monitor/bt.h"
+#include "emulator/bthost.h"
+
+#include "src/shared/tester.h"
+#include "src/shared/mgmt.h"
+#include "src/shared/hciemu.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 *client_data = data->test_data;
+	int sk;
+	ssize_t ret;
+	char buf[248];
+
+	sk = g_io_channel_unix_get_fd(io);
+
+	ret = read(sk, buf, client_data->data_len);
+	if (client_data->data_len != ret) {
+		tester_test_failed();
+		return false;
+	}
+
+	if (memcmp(client_data->read_data, buf, client_data->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 *client_data = data->test_data;
+	socklen_t len = sizeof(int);
+	int sk, err, 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 (client_data->expected_connect_err &&
+				err == client_data->expected_connect_err) {
+		tester_test_passed();
+		return false;
+	}
+
+	if (client_data->send_data) {
+		ssize_t ret;
+
+		ret = write(sk, client_data->send_data, client_data->data_len);
+		if (client_data->data_len != ret)
+			tester_test_failed();
+
+		return false;
+	} else if (client_data->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,
+						client_data->client_channel,
+						client_data->read_data,
+						client_data->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 *client_data = test_data->test_data;
+	ssize_t ret;
+
+	if (client_data->data_len != len) {
+		tester_test_failed();
+		return;
+	}
+
+	ret = memcmp(client_data->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 *server_data = test_data->test_data;
+	ssize_t ret;
+
+	if (server_data->data_len != len) {
+		tester_test_failed();
+		return;
+	}
+
+	ret = memcmp(server_data->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 *client_data = data->test_data;
+	struct bthost *bthost = hciemu_client_get_host(data->hciemu);
+
+	if (client_data->send_data)
+		bthost_add_rfcomm_chan_hook(bthost, handle,
+						client_data->client_channel,
+						client_hook_func, NULL);
+	else if (client_data->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 *client_data = 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, client_data->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,
+					client_data->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 *server_data = data->test_data;
+	char buf[1024];
+	ssize_t ret;
+	int sk;
+
+	sk = g_io_channel_unix_get_fd(io);
+
+	ret = read(sk, buf, server_data->data_len);
+	if (ret != server_data->data_len) {
+		tester_test_failed();
+		return false;
+	}
+
+	if (memcmp(buf, server_data->read_data, server_data->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 *server_data = 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 (server_data->send_data) {
+		ssize_t ret;
+
+		ret = write(new_sk, server_data->send_data,
+							server_data->data_len);
+		if (ret != server_data->data_len)
+			tester_test_failed();
+
+		close(new_sk);
+		return false;
+	} else if (server_data->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 *server_data = data->test_data;
+	struct bthost *bthost = hciemu_client_get_host(data->hciemu);
+
+	if (server_data->read_data) {
+		data->conn_handle = handle;
+		bthost_send_rfcomm_data(bthost, data->conn_handle,
+						server_data->client_channel,
+						server_data->read_data,
+						server_data->data_len);
+		return;
+	} else if (server_data->data_len) {
+		return;
+	}
+
+	if (server_data->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 *server_data = data->test_data;
+	struct bthost *bthost;
+
+	bthost = hciemu_client_get_host(data->hciemu);
+	bthost_add_rfcomm_chan_hook(bthost, handle,
+						server_data->client_channel,
+						server_hook_func, NULL);
+	bthost_connect_rfcomm(bthost, handle, server_data->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 *server_data = 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,
+						server_data->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/tools/rfcomm.1 b/bluez/tools/rfcomm.1
new file mode 100644
index 0000000..a108609
--- /dev/null
+++ b/bluez/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/tools/rfcomm.c b/bluez/tools/rfcomm.c
new file mode 100644
index 0000000..b5bea38
--- /dev/null
+++ b/bluez/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 <sys/poll.h>
+#include <sys/param.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <sys/wait.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/hci.h>
+#include <bluetooth/hci_lib.h>
+#include <bluetooth/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/tools/sco-tester.c b/bluez/tools/sco-tester.c
new file mode 100644
index 0000000..db45ef0
--- /dev/null
+++ b/bluez/tools/sco-tester.c
@@ -0,0 +1,605 @@
+/*
+ *
+ *  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 "src/shared/tester.h"
+#include "src/shared/mgmt.h"
+#include "src/shared/hciemu.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();
+	}
+
+	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;
+	}
+
+	voice.setting = 0;
+	len = sizeof(voice);
+	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;
+	}
+
+
+	voice.setting = 0;
+	len = sizeof(voice);
+	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;
+	}
+
+	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;
+	}
+
+	voice.setting = 0;
+	len = sizeof(voice);
+	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;
+	}
+
+	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/tools/scotest.c b/bluez/tools/scotest.c
new file mode 100644
index 0000000..e5530d9
--- /dev/null
+++ b/bluez/tools/scotest.c
@@ -0,0 +1,523 @@
+/*
+ *
+ *  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 <bluetooth/bluetooth.h>
+#include <bluetooth/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);
+				goto error;
+			}
+		}
+
+		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);
+				goto error;
+			}
+		}
+
+		handler(nsk);
+
+		syslog(LOG_INFO, "Disconnect");
+		exit(0);
+	}
+
+	return;
+
+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/tools/sdptool.1 b/bluez/tools/sdptool.1
new file mode 100644
index 0000000..ea95933
--- /dev/null
+++ b/bluez/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/tools/sdptool.c b/bluez/tools/sdptool.c
new file mode 100644
index 0000000..1600c3e
--- /dev/null
+++ b/bluez/tools/sdptool.c
@@ -0,0 +1,4290 @@
+/*
+ *
+ *  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 <bluetooth/bluetooth.h>
+#include <bluetooth/hci.h>
+#include <bluetooth/hci_lib.h>
+#include <bluetooth/sdp.h>
+#include <bluetooth/sdp_lib.h>
+
+#include <netinet/in.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 GOEP attributes. See BT assigned numbers */
+static struct attrib_def goep_attrib_names[] = {
+	{ 0x200, "GoepL2capPsm", 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 },
+};
+
+/* 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", NULL, 0 },
+	{ 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", NULL, 0 },
+	{ 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", NULL, 0 },
+	{ 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 = (void **)malloc(argc * sizeof(void *));
+	valueArray = (void **)malloc(argc * sizeof(void *));
+	allocArray = (void **)malloc(argc * sizeof(void *));
+
+	/* 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 = (uuid_t *) malloc(sizeof(uuid_t));
+			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 = (uint32_t *) malloc(sizeof(int));
+			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 */
+	for (i = 0; i < argc; i++)
+		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_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;
+
+	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);
+
+	if (sdp_device_record_register(session, &interface, &record, SDP_RECORD_PERSIST) < 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,	},
+
+	{ "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/tools/seq2bseq.c b/bluez/tools/seq2bseq.c
new file mode 100644
index 0000000..7657a57
--- /dev/null
+++ b/bluez/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] == '\r')
+		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/tools/smp-tester.c b/bluez/tools/smp-tester.c
new file mode 100644
index 0000000..e09c802
--- /dev/null
+++ b/bluez/tools/smp-tester.c
@@ -0,0 +1,692 @@
+/*
+ *
+ *  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 "src/shared/crypto.h"
+#include "src/shared/tester.h"
+#include "src/shared/mgmt.h"
+#include "src/shared/hciemu.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];
+};
+
+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;
+};
+
+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);
+}
+
+#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_LE; \
+		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, 0x08 };
+
+static const struct smp_req_rsp srv_nval_req_2[] = {
+	{ smp_nval_req_2, 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 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 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_LE, data->mgmt_index,
+				sizeof(param), param, NULL, NULL, NULL);
+	mgmt_send(data->mgmt, MGMT_OP_SET_PAIRABLE, 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 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));
+		tester_test_failed();
+		return;
+	}
+
+	tester_print("Pairing succeedded");
+	tester_test_passed();
+}
+
+static const void *get_pdu(const uint8_t *pdu)
+{
+	struct test_data *data = tester_get_data();
+	uint8_t opcode = pdu[0];
+	static uint8_t buf[17];
+
+	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];
+		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;
+	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 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;
+	const void *pdu;
+	uint8_t opcode;
+
+	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) {
+		tester_test_passed();
+		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 (!verify_random(data + 1))
+			goto failed;
+		goto next;
+	default:
+		break;
+	}
+
+	if (memcmp(req->expect, data, len) != 0) {
+		tester_warn("Unexpected SMP PDU");
+		goto failed;
+	}
+
+next:
+	if (smp->req_count == test_data->counter) {
+		tester_test_passed();
+		return;
+	}
+
+	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)
+		tester_test_passed();
+
+	return;
+
+failed:
+	tester_test_failed();
+}
+
+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);
+}
+
+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();
+	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);
+
+	memcpy(&cp.addr.bdaddr, data->ra, sizeof(data->ra));
+	cp.addr.type = BDADDR_LE_PUBLIC;
+	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();
+	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_PAIRABLE, 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);
+	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();
+	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);
+
+	bthost_hci_connect(bthost, data->ra, BDADDR_LE_PUBLIC);
+}
+
+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);
+
+	return tester_run();
+}
diff --git a/bluez/tools/ubcsp.c b/bluez/tools/ubcsp.c
new file mode 100644
index 0000000..b3f883a
--- /dev/null
+++ b/bluez/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/tools/ubcsp.h b/bluez/tools/ubcsp.h
new file mode 100644
index 0000000..6a74e9a
--- /dev/null
+++ b/bluez/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/unit/test-avctp.c b/bluez/unit/test-avctp.c
new file mode 100644
index 0000000..0759731
--- /dev/null
+++ b/bluez/unit/test-avctp.c
@@ -0,0 +1,339 @@
+/*
+ *
+ *  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/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 {
+	GMainLoop *main_loop;
+	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_malloc(sizeof(pdus));			\
+		memcpy(data.pdu_list, pdus, sizeof(pdus));		\
+		g_test_add_data_func(name, &data, function);		\
+	} while (0)
+
+static void test_debug(const char *str, void *user_data)
+{
+	const char *prefix = user_data;
+
+	g_print("%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 gboolean context_quit(gpointer user_data)
+{
+	struct context *context = user_data;
+
+	if (context->process > 0)
+		g_source_remove(context->process);
+
+	g_main_loop_quit(context->main_loop);
+
+	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);
+
+	if (g_test_verbose())
+		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);
+
+	if (g_test_verbose())
+		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];
+
+	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->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 void destroy_context(struct context *context)
+{
+	if (context->source > 0)
+		g_source_remove(context->source);
+
+	avctp_shutdown(context->session);
+
+	g_main_loop_unref(context->main_loop);
+
+	test_free(context->data);
+	g_free(context);
+}
+
+static void execute_context(struct context *context)
+{
+	g_main_loop_run(context->main_loop);
+
+	destroy_context(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)
+{
+	DBG("transaction %d code %d subunit %d operand_count %zu",
+		transaction, *code, *subunit, operand_count);
+
+	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;
+
+	DBG("code 0x%02x subunit %d operand_count %zu", code, subunit,
+								operand_count);
+
+	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_vendordep_req(context->session, AVC_CTYPE_CONTROL, 0, NULL,
+						0, handler_response, context);
+
+	execute_context(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);
+		DBG("ret %d", ret);
+		g_assert_cmpint(ret, !=, 0);
+	}
+
+	g_idle_add(send_pdu, context);
+
+	execute_context(context);
+}
+
+static void test_dummy(gconstpointer data)
+{
+	struct context *context = create_context(0x0100, data);
+
+	destroy_context(context);
+}
+
+int main(int argc, char *argv[])
+{
+	g_test_init(&argc, &argv, NULL);
+
+	if (g_test_verbose())
+		__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 g_test_run();
+}
diff --git a/bluez/unit/test-avdtp.c b/bluez/unit/test-avdtp.c
new file mode 100644
index 0000000..8fe5ce3
--- /dev/null
+++ b/bluez/unit/test-avdtp.c
@@ -0,0 +1,1334 @@
+/*
+ *
+ *  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/log.h"
+#include "android/avdtp.h"
+
+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_malloc(sizeof(pdus));			\
+		memcpy(data.pdu_list, pdus, sizeof(pdus));		\
+		g_test_add_data_func(name, &data, function);		\
+	} while (0)
+
+struct context {
+	GMainLoop *main_loop;
+	struct avdtp *session;
+	struct avdtp_local_sep *sep;
+	struct avdtp_stream *stream;
+	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;
+
+	g_print("%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 gboolean context_quit(gpointer user_data)
+{
+	struct context *context = user_data;
+
+	if (context->process > 0)
+		g_source_remove(context->process);
+
+	g_main_loop_quit(context->main_loop);
+
+	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);
+
+	if (g_test_verbose())
+		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);
+
+	if (g_test_verbose())
+		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];
+
+	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->session = avdtp_new(sv[0], imtu, omtu, 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 struct context *create_context(uint16_t version, gconstpointer data)
+{
+	return context_new(version, 672, 672, data);
+}
+
+static void execute_context(struct context *context)
+{
+	g_main_loop_run(context->main_loop);
+
+	if (context->source > 0)
+		g_source_remove(context->source);
+	avdtp_unref(context->session);
+
+	g_main_loop_unref(context->main_loop);
+
+	test_free(context->data);
+	g_free(context);
+}
+
+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/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(AVDTP_SEP_TYPE_SOURCE, AVDTP_MEDIA_TYPE_AUDIO,
+					0x00, FALSE, &sep_ind, &sep_cfm,
+					context);
+
+	g_idle_add(send_pdu, context);
+
+	execute_context(context);
+
+	avdtp_unregister_sep(sep);
+}
+
+static void test_server_1_3(gconstpointer data)
+{
+	struct context *context = create_context(0x0103, data);
+	struct avdtp_local_sep *sep;
+
+	sep = avdtp_register_sep(AVDTP_SEP_TYPE_SOURCE, AVDTP_MEDIA_TYPE_AUDIO,
+					0x00, TRUE, &sep_ind, NULL, context);
+
+	g_idle_add(send_pdu, context);
+
+	execute_context(context);
+
+	avdtp_unregister_sep(sep);
+}
+
+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(AVDTP_SEP_TYPE_SINK, AVDTP_MEDIA_TYPE_AUDIO,
+					0x00, TRUE, &sep_ind, NULL, context);
+
+	g_idle_add(send_pdu, context);
+
+	execute_context(context);
+
+	avdtp_unregister_sep(sep);
+}
+
+static void test_server_0_sep(gconstpointer data)
+{
+	struct context *context = create_context(0x0100, data);
+
+	g_idle_add(send_pdu, context);
+
+	execute_context(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(AVDTP_SEP_TYPE_SOURCE, AVDTP_MEDIA_TYPE_AUDIO,
+						0x00, TRUE, &sep_ind_frg,
+						NULL, context);
+
+	g_idle_add(send_pdu, context);
+
+	execute_context(context);
+
+	avdtp_unregister_sep(sep);
+}
+
+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(AVDTP_SEP_TYPE_SINK, AVDTP_MEDIA_TYPE_AUDIO,
+					0x00, FALSE, NULL, &sep_cfm,
+					context);
+	context->sep = sep;
+
+	avdtp_discover(context->session, discover_cb, context);
+
+	execute_context(context);
+
+	avdtp_unregister_sep(sep);
+}
+
+static void test_client_1_3(gconstpointer data)
+{
+	struct context *context = create_context(0x0103, data);
+	struct avdtp_local_sep *sep;
+
+	sep = avdtp_register_sep(AVDTP_SEP_TYPE_SINK, AVDTP_MEDIA_TYPE_AUDIO,
+					0x00, TRUE, NULL, &sep_cfm,
+					context);
+	context->sep = sep;
+
+	avdtp_discover(context->session, discover_cb, context);
+
+	execute_context(context);
+
+	avdtp_unregister_sep(sep);
+}
+
+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(AVDTP_SEP_TYPE_SINK, AVDTP_MEDIA_TYPE_AUDIO,
+					0x00, TRUE, NULL, &sep_cfm,
+					context);
+	context->sep = sep;
+
+	avdtp_discover(context->session, discover_cb, context);
+
+	execute_context(context);
+
+	avdtp_unregister_sep(sep);
+}
+
+int main(int argc, char *argv[])
+{
+	g_test_init(&argc, &argv, NULL);
+
+	if (g_test_verbose())
+		__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-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));
+	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 g_test_run();
+}
diff --git a/bluez/unit/test-avrcp.c b/bluez/unit/test-avrcp.c
new file mode 100644
index 0000000..eb00238
--- /dev/null
+++ b/bluez/unit/test-avrcp.c
@@ -0,0 +1,993 @@
+/*
+ *
+ *  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/log.h"
+#include "lib/bluetooth.h"
+
+#include "android/avctp.h"
+#include "android/avrcp-lib.h"
+
+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;
+};
+
+struct context {
+	GMainLoop *main_loop;
+	struct avrcp *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 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_malloc(sizeof(pdus));			\
+		memcpy(data.pdu_list, pdus, sizeof(pdus));		\
+		g_test_add_data_func(name, &data, function);		\
+	} while (0)
+
+static void test_debug(const char *str, void *user_data)
+{
+	const char *prefix = user_data;
+
+	g_print("%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 gboolean context_quit(gpointer user_data)
+{
+	struct context *context = user_data;
+
+	if (context->process > 0)
+		g_source_remove(context->process);
+
+	g_main_loop_quit(context->main_loop);
+
+	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);
+
+	if (g_test_verbose())
+		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++];
+
+	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);
+
+	if (g_test_verbose())
+		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];
+
+	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->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];
+	context->data = data;
+
+	return context;
+}
+
+static void destroy_context(struct context *context)
+{
+	if (context->source > 0)
+		g_source_remove(context->source);
+
+	avrcp_shutdown(context->session);
+
+	g_main_loop_unref(context->main_loop);
+
+	test_free(context->data);
+	g_free(context);
+}
+
+static void test_dummy(gconstpointer data)
+{
+	struct context *context =  create_context(0x0100, data);
+
+	destroy_context(context);
+}
+
+static void execute_context(struct context *context)
+{
+	g_main_loop_run(context->main_loop);
+
+	destroy_context(context);
+}
+
+static bool handle_play(struct avrcp *session, bool pressed, void *user_data)
+{
+	DBG("");
+
+	return true;
+}
+
+static bool handle_volume_up(struct avrcp *session, bool pressed,
+							void *user_data)
+{
+	DBG("");
+
+	return true;
+}
+
+static bool handle_channel_up(struct avrcp *session, bool pressed,
+							void *user_data)
+{
+	DBG("");
+
+	return true;
+}
+
+static bool handle_select(struct avrcp *session, bool pressed, void *user_data)
+{
+	DBG("");
+
+	return true;
+}
+
+static bool handle_vendor_uniq(struct avrcp *session, bool pressed,
+								void *user_data)
+{
+	DBG("");
+
+	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)
+{
+	DBG("");
+
+	avrcp_list_player_attributes_rsp(session, transaction, 0, NULL);
+
+	return -EAGAIN;
+}
+
+static int get_attribute_text(struct avrcp *session, uint8_t transaction,
+					uint8_t number, uint8_t *attrs,
+					void *user_data)
+{
+	const char *text[number];
+
+	DBG("");
+
+	if (number) {
+		memset(text, 0, number);
+		text[0] = "equalizer";
+	}
+
+	avrcp_get_player_attribute_text_rsp(session, transaction, number, attrs,
+									text);
+
+	return -EAGAIN;
+}
+
+static int list_values(struct avrcp *session, uint8_t transaction,
+						uint8_t attr, void *user_data)
+{
+	DBG("");
+
+	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[] = { "on" };
+
+	DBG("");
+
+	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];
+
+	DBG("");
+
+	memset(values, 0, number);
+
+	avrcp_get_current_player_value_rsp(session, transaction, number, attrs,
+									values);
+
+	return -EAGAIN;
+}
+
+static int set_value(struct avrcp *session, uint8_t transaction,
+			uint8_t number, uint8_t *attrs, void *user_data)
+{
+	DBG("");
+
+	return 0;
+}
+
+static int get_play_status(struct avrcp *session, uint8_t transaction,
+							void *user_data)
+{
+	DBG("");
+
+	avrcp_get_play_status_rsp(session, transaction, 0xaaaaaaaa, 0xbbbbbbbb,
+									0x00);
+
+	return -EAGAIN;
+}
+
+static int get_element_attributes(struct avrcp *session, uint8_t transaction,
+					uint64_t uid, uint8_t number,
+					uint32_t *attrs, void *user_data)
+{
+	DBG("");
+
+	avrcp_get_element_attrs_rsp(session, transaction, NULL, 0);
+
+	return -EAGAIN;
+}
+
+static int register_notification(struct avrcp *session, uint8_t transaction,
+					uint8_t event, uint32_t interval,
+					void *user_data)
+{
+	struct context *context = user_data;
+	uint8_t pdu[9];
+	size_t pdu_len;
+
+	DBG("");
+
+	pdu[0] = event;
+	pdu_len = 1;
+
+	switch (event) {
+	case AVRCP_EVENT_TRACK_CHANGED:
+		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(&pdu[1], 0, 8);
+		else
+			memset(&pdu[1], 0xff, 8);
+
+		pdu_len += 8;
+		break;
+	case AVRCP_EVENT_SETTINGS_CHANGED:
+		pdu[1] = 0x01;
+		pdu[2] = 0x01;
+		pdu[3] = 0x02;
+		pdu_len = 4;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	avrcp_register_notification_rsp(session, transaction, AVC_CTYPE_INTERIM,
+						pdu, pdu_len);
+
+	avrcp_register_notification_rsp(session, transaction, AVC_CTYPE_CHANGED,
+						pdu, pdu_len);
+
+	return -EAGAIN;
+}
+
+static int set_addressed(struct avrcp *session, uint8_t transaction,
+						uint16_t id, void *user_data)
+{
+	DBG("");
+
+	avrcp_set_addressed_player_rsp(session, transaction,
+							AVRCP_STATUS_SUCCESS);
+
+	return -EAGAIN;
+}
+
+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_addressed = set_addressed,
+};
+
+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);
+
+	execute_context(context);
+}
+
+static void test_client(gconstpointer data)
+{
+	struct context *context = create_context(0x0100, data);
+
+	if (g_str_equal(context->data->test_name, "/TP/MPS/BV-01-C"))
+		avrcp_set_addressed_player(context->session, 0xabcd, NULL,
+									NULL);
+
+	if (g_str_equal(context->data->test_name, "/TP/CFG/BV-01-C"))
+		avrcp_get_capabilities(context->session, CAP_EVENTS_SUPPORTED,
+								NULL, NULL);
+
+	if (g_str_equal(context->data->test_name, "/TP/PAS/BV-01-C"))
+		avrcp_list_player_attributes(context->session, NULL, NULL);
+
+	if (g_str_equal(context->data->test_name, "/TP/PAS/BV-03-C"))
+		avrcp_get_player_attribute_text(context->session, NULL, 0,
+								NULL, NULL);
+
+	if (g_str_equal(context->data->test_name, "/TP/PAS/BV-09-C")) {
+		uint8_t attributes[2] = { AVRCP_ATTRIBUTE_EQUALIZER,
+						AVRCP_ATTRIBUTE_REPEAT_MODE };
+
+		avrcp_get_current_player_value(context->session, attributes,
+						sizeof(attributes), NULL, NULL);
+	}
+
+	if (g_str_equal(context->data->test_name, "/TP/PAS/BV-11-C")) {
+		uint8_t attributes[2] = { AVRCP_ATTRIBUTE_EQUALIZER,
+						AVRCP_ATTRIBUTE_REPEAT_MODE };
+		uint8_t values[] = { 0xaa, 0xff };
+
+		avrcp_set_player_value(context->session, attributes,
+						sizeof(attributes), values,
+						NULL, NULL);
+	}
+
+	if (g_str_equal(context->data->test_name, "/TP/MDI/BV-01-C"))
+		avrcp_get_play_status(context->session, NULL, NULL);
+
+	if (g_str_equal(context->data->test_name, "/TP/MDI/BV-03-C"))
+		avrcp_get_element_attributes(context->session, NULL, NULL);
+
+	if (g_str_equal(context->data->test_name, "/TP/NFY/BV-01-C"))
+		avrcp_register_notification(context->session,
+						AVRCP_EVENT_STATUS_CHANGED, 0,
+						NULL, NULL);
+
+	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);
+
+	execute_context(context);
+}
+
+int main(int argc, char *argv[])
+{
+	g_test_init(&argc, &argv, NULL);
+
+	if (g_test_verbose())
+		__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));
+
+	/* 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, 0x00));
+
+	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-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-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, 0xaa, 0xaa, 0xaa,
+				0xaa, 0xbb, 0xbb, 0xbb, 0xbb, 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));
+
+	/* 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));
+
+	return g_test_run();
+}
diff --git a/bluez/unit/test-crc.c b/bluez/unit/test-crc.c
new file mode 100644
index 0000000..db40dc9
--- /dev/null
+++ b/bluez/unit/test-crc.c
@@ -0,0 +1,194 @@
+/*
+ *
+ *  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 <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);
+
+	if (g_test_verbose())
+		g_print("CRC: 0x%6.6x, Calculated: 0x%6.6x\n",
+						crc_value, crc);
+
+	g_assert(crc_value == crc);
+
+	rev = crc24_reverse(crc_value, test_data->packet + 4,
+						test_data->size - 7);
+
+	if (g_test_verbose())
+		g_print("Preset: 0x%6.6x, Calculated: 0x%6.6x\n",
+						crc_init, rev);
+
+	g_assert(crc_init == rev);
+}
+
+int main(int argc, char *argv[])
+{
+	g_test_init(&argc, &argv, NULL);
+
+	g_test_add_data_func("/crc/1", &crc_1, test_crc);
+	g_test_add_data_func("/crc/2", &crc_2, test_crc);
+	g_test_add_data_func("/crc/3", &crc_3, test_crc);
+	g_test_add_data_func("/crc/4", &crc_4, test_crc);
+	g_test_add_data_func("/crc/5", &crc_5, test_crc);
+	g_test_add_data_func("/crc/6", &crc_6, test_crc);
+	g_test_add_data_func("/crc/7", &crc_7, test_crc);
+	g_test_add_data_func("/crc/8", &crc_8, test_crc);
+	g_test_add_data_func("/crc/9", &crc_9, test_crc);
+
+	return g_test_run();
+}
diff --git a/bluez/unit/test-eir.c b/bluez/unit/test-eir.c
new file mode 100644
index 0000000..919a83b
--- /dev/null
+++ b/bluez/unit/test-eir.c
@@ -0,0 +1,615 @@
+/*
+ *
+ *  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 <bluetooth/bluetooth.h>
+#include <bluetooth/hci.h>
+#include <bluetooth/sdp.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(void)
+{
+	struct eir_data data;
+	unsigned char buf[HCI_MAX_EIR_LENGTH];
+
+	memset(buf, 0, sizeof(buf));
+	memset(&data, 0, sizeof(data));
+
+	eir_parse(&data, buf, HCI_MAX_EIR_LENGTH);
+	g_assert(data.services == NULL);
+	g_assert(data.name == NULL);
+
+	eir_data_free(&data);
+}
+
+static void test_parsing(gconstpointer data)
+{
+	const struct test_data *test = data;
+	struct eir_data eir;
+
+	memset(&eir, 0, sizeof(eir));
+
+	eir_parse(&eir, test->eir_data, test->eir_size);
+
+	if (g_test_verbose() == TRUE) {
+		GSList *list;
+
+		g_print("Flags: %d\n", eir.flags);
+		g_print("Name: %s\n", eir.name);
+		g_print("TX power: %d\n", eir.tx_power);
+
+		for (list = eir.services; list; list = list->next) {
+			char *uuid_str = list->data;
+			g_print("UUID: %s\n", uuid_str);
+		}
+	}
+
+	g_assert(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);
+	}
+
+	eir_data_free(&eir);
+}
+
+int main(int argc, char *argv[])
+{
+	g_test_init(&argc, &argv, NULL);
+
+	g_test_add_func("/eir/basic", test_basic);
+
+	g_test_add_data_func("/eir/macbookair", &macbookair_test, test_parsing);
+	g_test_add_data_func("/eir/iphone5", &iphone5_test, test_parsing);
+	g_test_add_data_func("/eir/ipadmini", &ipadmini_test, test_parsing);
+	g_test_add_data_func("/eir/sl400h", &gigaset_sl400h_test, test_parsing);
+	g_test_add_data_func("/eir/sl910", &gigaset_sl910_test, test_parsing);
+	g_test_add_data_func("/eir/bh907", &nokia_bh907_test, test_parsing);
+	g_test_add_data_func("/eir/fuelband", &fuelband_test, test_parsing);
+	g_test_add_data_func("/ad/bluesc", &bluesc_test, test_parsing);
+	g_test_add_data_func("/ad/wahooscale", &wahoo_scale_test, test_parsing);
+	g_test_add_data_func("/ad/mioalpha", &mio_alpha_test, test_parsing);
+	g_test_add_data_func("/ad/cookoo", &cookoo_test, test_parsing);
+	g_test_add_data_func("/ad/citizen1", &citizen_adv_test, test_parsing);
+	g_test_add_data_func("/ad/citizen2", &citizen_scan_test, test_parsing);
+
+	return g_test_run();
+}
diff --git a/bluez/unit/test-gdbus-client.c b/bluez/unit/test-gdbus-client.c
new file mode 100644
index 0000000..d0b6ce7
--- /dev/null
+++ b/bluez/unit/test-gdbus-client.c
@@ -0,0 +1,1059 @@
+/*
+ *
+ *  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.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 {
+	GMainLoop *main_loop;
+	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;
+
+	context->main_loop = g_main_loop_new(NULL, FALSE);
+	if (context->main_loop == NULL) {
+		g_free(context);
+		return NULL;
+	}
+
+	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)) {
+			if (g_test_verbose())
+				g_printerr("D-Bus setup failed: %s\n",
+								err.message);
+			dbus_error_free(&err);
+		}
+
+		g_main_loop_unref(context->main_loop);
+		g_free(context);
+		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;
+
+	if (context->timeout_source > 0)
+		g_source_remove(context->timeout_source);
+
+	g_dbus_detach_object_manager(context->dbus_conn);
+
+	dbus_connection_flush(context->dbus_conn);
+	dbus_connection_close(context->dbus_conn);
+	dbus_connection_unref(context->dbus_conn);
+
+	g_main_loop_unref(context->main_loop);
+
+	g_free(context->data);
+	g_free(context);
+}
+
+static gboolean timeout_handler(gpointer user_data)
+{
+	struct context *context = user_data;
+
+	if (g_test_verbose())
+		g_print("timeout triggered\n");
+
+	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;
+
+	if (g_test_verbose())
+		g_print("service connected\n");
+
+	g_dbus_client_unref(context->dbus_client);
+}
+
+static void disconnect_handler(DBusConnection *connection, void *user_data)
+{
+	struct context *context = user_data;
+
+	if (g_test_verbose())
+		g_print("service disconnected\n");
+
+	g_main_loop_quit(context->main_loop);
+}
+
+static void simple_client(void)
+{
+	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);
+
+	g_main_loop_run(context->main_loop);
+
+	destroy_context(context);
+}
+
+static void client_connect_disconnect(void)
+{
+	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);
+
+	g_main_loop_run(context->main_loop);
+
+	g_dbus_unregister_interface(context->dbus_conn,
+					SERVICE_PATH, SERVICE_NAME);
+
+	destroy_context(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;
+
+	if (g_test_verbose())
+		g_print("proxy %s found\n",
+					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(void)
+{
+	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);
+
+	g_main_loop_run(context->main_loop);
+
+	g_dbus_unregister_interface(context->dbus_conn,
+					SERVICE_PATH, SERVICE_NAME);
+
+	destroy_context(context);
+}
+
+static void proxy_get_string(GDBusProxy *proxy, void *user_data)
+{
+	struct context *context = user_data;
+	DBusMessageIter iter;
+	const char *string;
+
+	if (g_test_verbose())
+		g_print("proxy %s found\n",
+					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);
+
+	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(void)
+{
+	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);
+
+	g_main_loop_run(context->main_loop);
+
+	g_dbus_unregister_interface(context->dbus_conn,
+					SERVICE_PATH, SERVICE_NAME);
+
+	destroy_context(context);
+}
+
+static void proxy_get_boolean(GDBusProxy *proxy, void *user_data)
+{
+	struct context *context = user_data;
+	DBusMessageIter iter;
+	dbus_bool_t value;
+
+	if (g_test_verbose())
+		g_print("proxy %s found\n",
+					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(void)
+{
+	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);
+
+	g_main_loop_run(context->main_loop);
+
+	g_dbus_unregister_interface(context->dbus_conn,
+					SERVICE_PATH, SERVICE_NAME);
+
+	destroy_context(context);
+}
+
+static void proxy_get_array(GDBusProxy *proxy, void *user_data)
+{
+	struct context *context = user_data;
+	DBusMessageIter iter, entry;
+	const char *value1, *value2;
+
+	if (g_test_verbose())
+		g_print("proxy %s found\n",
+					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(void)
+{
+	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);
+
+	g_main_loop_run(context->main_loop);
+
+	g_dbus_unregister_interface(context->dbus_conn,
+					SERVICE_PATH, SERVICE_NAME);
+
+	destroy_context(context);
+}
+
+static void proxy_get_uint64(GDBusProxy *proxy, void *user_data)
+{
+	struct context *context = user_data;
+	DBusMessageIter iter;
+	guint64 value;
+
+	if (g_test_verbose())
+		g_print("proxy %s found\n",
+					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(void)
+{
+	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);
+
+	g_main_loop_run(context->main_loop);
+
+	g_dbus_unregister_interface(context->dbus_conn,
+					SERVICE_PATH, SERVICE_NAME);
+
+	destroy_context(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;
+
+	if (g_test_verbose())
+		g_print("proxy %s found\n",
+					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;
+
+	if (g_test_verbose())
+		g_print("property %s changed\n", 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(void)
+{
+	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);
+
+	g_main_loop_run(context->main_loop);
+
+	g_dbus_unregister_interface(context->dbus_conn,
+					SERVICE_PATH, SERVICE_NAME);
+
+	destroy_context(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;
+
+	if (g_test_verbose())
+		g_print("timeout triggered\n");
+
+	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;
+
+	if (g_test_verbose())
+		g_print("proxy %s found\n",
+					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(void)
+{
+	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);
+
+	g_main_loop_run(context->main_loop);
+
+	g_dbus_unregister_interface(context->dbus_conn,
+					SERVICE_PATH, SERVICE_NAME);
+
+	destroy_context(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;
+
+	if (g_test_verbose())
+		g_print("proxy %s found\n",
+					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(void)
+{
+	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);
+
+	g_main_loop_run(context->main_loop);
+
+	g_dbus_unregister_interface(context->dbus_conn,
+					SERVICE_PATH, SERVICE_NAME);
+
+	destroy_context(context);
+}
+
+static void proxy_removed(GDBusProxy *proxy, void *user_data)
+{
+	struct context *context = user_data;
+
+	if (g_test_verbose())
+		g_print("proxy removed\n");
+
+	g_main_loop_quit(context->main_loop);
+}
+
+static void proxy_set_removed(GDBusProxy *proxy, void *user_data)
+{
+	struct context *context = user_data;
+
+	if (g_test_verbose())
+		g_print("proxy %s found\n",
+					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(void)
+{
+	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);
+
+	g_main_loop_run(context->main_loop);
+
+	destroy_context(context);
+}
+
+static void proxy_force_disconnect(GDBusProxy *proxy, void *user_data)
+{
+	struct context *context = user_data;
+	DBusConnection *conn = context->data;
+
+	if (g_test_verbose())
+		g_print("proxy %s found\n",
+					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);
+
+	dbus_connection_flush(conn);
+	dbus_connection_close(conn);
+	context->data = NULL;
+}
+
+static void client_force_disconnect(void)
+{
+	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);
+
+	g_main_loop_run(context->main_loop);
+
+	g_dbus_unregister_interface(conn, SERVICE_PATH, SERVICE_NAME1);
+	g_dbus_detach_object_manager(conn);
+	dbus_connection_unref(conn);
+
+	destroy_context(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);
+
+	g_main_loop_quit(context->main_loop);
+}
+
+static void client_ready(void)
+{
+	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);
+
+	g_main_loop_run(context->main_loop);
+
+	destroy_context(context);
+}
+
+int main(int argc, char *argv[])
+{
+	g_test_init(&argc, &argv, NULL);
+
+	g_test_add_func("/gdbus/simple_client", simple_client);
+
+	g_test_add_func("/gdbus/client_connect_disconnect",
+						client_connect_disconnect);
+
+	g_test_add_func("/gdbus/client_get_string_property",
+						client_get_string_property);
+
+	g_test_add_func("/gdbus/client_get_boolean_property",
+						client_get_boolean_property);
+
+	g_test_add_func("/gdbus/client_get_uint64_property",
+						client_get_uint64_property);
+
+	g_test_add_func("/gdbus/client_get_array_property",
+						client_get_array_property);
+
+	g_test_add_func("/gdbus/client_get_dict_property",
+						client_get_dict_property);
+
+	g_test_add_func("/gdbus/client_set_string_property",
+						client_set_string_property);
+
+	g_test_add_func("/gdbus/client_string_changed",
+						client_string_changed);
+
+	g_test_add_func("/gdbus/client_check_order", client_check_order);
+
+	g_test_add_func("/gdbus/client_proxy_removed", client_proxy_removed);
+
+	g_test_add_func("/gdbus/client_force_disconnect",
+						client_force_disconnect);
+
+	g_test_add_func("/gdbus/client_ready", client_ready);
+
+	return g_test_run();
+}
diff --git a/bluez/unit/test-gobex-apparam.c b/bluez/unit/test-gobex-apparam.c
new file mode 100644
index 0000000..976c541
--- /dev/null
+++ b/bluez/unit/test-gobex-apparam.c
@@ -0,0 +1,427 @@
+/*
+ *
+ *  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-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/unit/test-gobex-header.c b/bluez/unit/test-gobex-header.c
new file mode 100644
index 0000000..31c49a6
--- /dev/null
+++ b/bluez/unit/test-gobex-header.c
@@ -0,0 +1,573 @@
+/*
+ *
+ *  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-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_connid",
+						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/unit/test-gobex-packet.c b/bluez/unit/test-gobex-packet.c
new file mode 100644
index 0000000..bf2035b
--- /dev/null
+++ b/bluez/unit/test-gobex-packet.c
@@ -0,0 +1,259 @@
+/*
+ *
+ *  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-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/unit/test-gobex-transfer.c b/bluez/unit/test-gobex-transfer.c
new file mode 100644
index 0000000..ffb5bdc
--- /dev/null
+++ b/bluez/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/unit/test-gobex.c b/bluez/unit/test-gobex.c
new file mode 100644
index 0000000..2834c77
--- /dev/null
+++ b/bluez/unit/test-gobex.c
@@ -0,0 +1,1245 @@
+/*
+ *
+ *  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_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_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_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/unit/test-hfp.c b/bluez/unit/test-hfp.c
new file mode 100644
index 0000000..445fcb7
--- /dev/null
+++ b/bluez/unit/test-hfp.c
@@ -0,0 +1,467 @@
+/*
+ *
+ *  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"
+
+struct context {
+	GMainLoop *main_loop;
+	guint watch_id;
+	int fd_server;
+	int fd_client;
+	struct hfp_gw *hfp;
+	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;
+};
+
+#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_malloc(sizeof(pdus));			\
+		data.result_func = result_function;			\
+		memcpy(data.pdu_list, pdus, sizeof(pdus));		\
+		g_test_add_data_func(name, &data, function);		\
+	} while (0)
+
+static void context_quit(struct context *context)
+{
+	g_main_loop_quit(context->main_loop);
+}
+
+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 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_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_gw_result *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];
+
+	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);
+
+	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,
+				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 execute_context(struct context *context)
+{
+	g_main_loop_run(context->main_loop);
+
+	g_source_remove(context->watch_id);
+
+	g_main_loop_unref(context->main_loop);
+
+	test_free(context->data);
+
+	if (context->hfp)
+		hfp_gw_unref(context->hfp);
+
+	g_free(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;
+
+	execute_context(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);
+
+	execute_context(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);
+
+	execute_context(context);
+}
+
+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_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 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);
+
+	execute_context(context);
+}
+
+static void check_ustring_1(struct hfp_gw_result *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_gw_result_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_gw_result *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_gw_result_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_gw_result *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_gw_result_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_gw_result *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_gw_result_get_string(result, str, 3));
+
+	g_assert(str[3] == 'X');
+
+	hfp_gw_send_result(context->hfp, HFP_RESULT_ERROR);
+}
+
+int main(int argc, char *argv[])
+{
+	g_test_init(&argc, &argv, NULL);
+
+	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());
+
+	return g_test_run();
+}
diff --git a/bluez/unit/test-lib.c b/bluez/unit/test-lib.c
new file mode 100644
index 0000000..ef0cffc
--- /dev/null
+++ b/bluez/unit/test-lib.c
@@ -0,0 +1,492 @@
+/*
+ *
+ *  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 "lib/sdp.h"
+#include "lib/sdp_lib.h"
+
+static void test_ntoh64(void)
+{
+	uint64_t test = 0x123456789abcdef;
+
+	g_assert(ntoh64(test) == be64toh(test));
+	g_assert(ntoh64(test) == be64_to_cpu(test));
+}
+
+static void test_hton64(void)
+{
+	uint64_t test = 0x123456789abcdef;
+
+	g_assert(hton64(test) == htobe64(test));
+	g_assert(hton64(test) == cpu_to_be64(test));
+}
+
+static void test_sdp_get_access_protos_valid(void)
+{
+	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);
+}
+
+static void test_sdp_get_access_protos_nodata(void)
+{
+	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);
+}
+
+static void test_sdp_get_access_protos_invalid_dtd1(void)
+{
+	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);
+}
+
+static void test_sdp_get_access_protos_invalid_dtd2(void)
+{
+	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);
+}
+
+static void test_sdp_get_lang_attr_valid(void)
+{
+	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);
+}
+
+static void test_sdp_get_lang_attr_nodata(void)
+{
+	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);
+}
+
+static void test_sdp_get_lang_attr_invalid_dtd(void)
+{
+	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);
+}
+
+static void test_sdp_get_profile_descs_valid(void)
+{
+	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);
+}
+
+static void test_sdp_get_profile_descs_nodata(void)
+{
+	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);
+}
+
+static void test_sdp_get_profile_descs_invalid_dtd(void)
+{
+	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);
+}
+
+static void test_sdp_get_profile_descs_workaround(void)
+{
+	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);
+}
+
+static void test_sdp_get_server_ver(void)
+{
+	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);
+}
+
+int main(int argc, char *argv[])
+{
+	g_test_init(&argc, &argv, NULL);
+
+	g_test_add_func("/lib/ntoh64", test_ntoh64);
+	g_test_add_func("/lib/hton64", test_hton64);
+
+	g_test_add_func("/lib/sdp_get_access_protos/valid",
+					test_sdp_get_access_protos_valid);
+	g_test_add_func("/lib/sdp_get_access_protos/nodata",
+					test_sdp_get_access_protos_nodata);
+	g_test_add_func("/lib/sdp_get_access_protos/invalid_dtd1",
+				test_sdp_get_access_protos_invalid_dtd1);
+	g_test_add_func("/lib/sdp_get_access_protos/invalid_dtd2",
+				test_sdp_get_access_protos_invalid_dtd2);
+
+	g_test_add_func("/lib/sdp_get_lang_attr/valid",
+						test_sdp_get_lang_attr_valid);
+	g_test_add_func("/lib/sdp_get_lang_attr/nodata",
+						test_sdp_get_lang_attr_nodata);
+	g_test_add_func("/lib/sdp_get_lang_attr/invalid_dtd",
+					test_sdp_get_lang_attr_invalid_dtd);
+
+	g_test_add_func("/lib/sdp_get_profile_descs/valid",
+					test_sdp_get_profile_descs_valid);
+	g_test_add_func("/lib/sdp_get_profile_descs/nodata",
+					test_sdp_get_profile_descs_nodata);
+	g_test_add_func("/lib/sdp_get_profile_descs/invalid_dtd",
+					test_sdp_get_profile_descs_invalid_dtd);
+	/* Test for workaround commented on sdp_get_profile_descs() */
+	g_test_add_func("/lib/sdp_get_profile_descs/workaround",
+					test_sdp_get_profile_descs_workaround);
+
+	g_test_add_func("/lib/sdp_get_server_ver", test_sdp_get_server_ver);
+
+	return g_test_run();
+}
diff --git a/bluez/unit/test-mgmt.c b/bluez/unit/test-mgmt.c
new file mode 100644
index 0000000..f9dcb00
--- /dev/null
+++ b/bluez/unit/test-mgmt.c
@@ -0,0 +1,243 @@
+/*
+ *
+ *  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;
+	struct mgmt *mgmt_client;
+	guint server_source;
+	GList *handler_list;
+};
+
+enum action {
+	ACTION_PASSED,
+	ACTION_IGNORE,
+};
+
+struct handler {
+	const void *cmd_data;
+	uint16_t cmd_size;
+	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,
+					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;
+
+		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_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, 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);
+
+	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,
+				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->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;
+};
+
+static const unsigned char read_version_command[] =
+				{ 0x01, 0x00, 0xff, 0xff, 0x00, 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),
+};
+
+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 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,
+						false, ACTION_PASSED);
+
+	mgmt_send(context->mgmt_client, test->opcode, test->index,
+					test->length, test->param,
+						NULL ,NULL, NULL);
+
+	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);
+
+	return g_test_run();
+}
diff --git a/bluez/unit/test-queue.c b/bluez/unit/test-queue.c
new file mode 100644
index 0000000..7c6d2ad
--- /dev/null
+++ b/bluez/unit/test-queue.c
@@ -0,0 +1,68 @@
+/*
+ *
+ *  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"
+
+static void test_basic(void)
+{
+	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);
+}
+
+int main(int argc, char *argv[])
+{
+	g_test_init(&argc, &argv, NULL);
+
+	g_test_add_func("/queue/basic", test_basic);
+
+	return g_test_run();
+}
diff --git a/bluez/unit/test-ringbuf.c b/bluez/unit/test-ringbuf.c
new file mode 100644
index 0000000..e28e04b
--- /dev/null
+++ b/bluez/unit/test-ringbuf.c
@@ -0,0 +1,154 @@
+/*
+ *
+ *  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"
+
+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(void)
+{
+	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;
+
+		if (g_test_verbose())
+			g_print("%zu -> size1=%zu size2=%zu size3=%zu\n",
+						i, size1, size2, size3);
+
+		g_assert(size1 == size2);
+		g_assert(size2 == size3);
+		g_assert(size3 == size1);
+	}
+}
+
+static void test_alloc(void)
+{
+	int i;
+
+	for (i = 2; i < 10000; i++) {
+		struct ringbuf *rb;
+
+		if (g_test_verbose())
+			g_print("Iteration %i\n", i);
+
+		rb = ringbuf_new(i);
+		g_assert(rb != NULL);
+
+		g_assert(ringbuf_capacity(rb) == ringbuf_avail(rb));
+
+		ringbuf_free(rb);
+	}
+}
+
+static void test_printf(void)
+{
+	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;
+
+		if (g_test_verbose())
+			g_print("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);
+}
+
+int main(int argc, char *argv[])
+{
+	g_test_init(&argc, &argv, NULL);
+
+	g_test_add_func("/ringbuf/power2", test_power2);
+	g_test_add_func("/ringbuf/alloc", test_alloc);
+	g_test_add_func("/ringbuf/printf", test_printf);
+
+	return g_test_run();
+}
diff --git a/bluez/unit/test-sdp.c b/bluez/unit/test-sdp.c
new file mode 100644
index 0000000..a89dbfc
--- /dev/null
+++ b/bluez/unit/test-sdp.c
@@ -0,0 +1,2826 @@
+/*
+ *
+ *  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/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_malloc(sizeof(pdus));			\
+		memcpy(data.pdu_list, pdus, sizeof(pdus));		\
+		g_test_add_data_func(name, &data, test_sdp);		\
+	} 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;					\
+		g_test_add_data_func("/sdp/DE/ATTR/" name, &data,	\
+						test_sdp_de_attr);	\
+	} while (0)
+
+struct context {
+	GMainLoop *main_loop;
+	guint server_source;
+	guint client_source;
+	int fd;
+	int mtu;
+	uint8_t cont_data[16];
+	uint8_t cont_size;
+	unsigned int pdu_offset;
+	const struct sdp_pdu *pdu_list;
+};
+
+static void sdp_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 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;
+	}
+
+	if (g_test_verbose() == TRUE)
+		util_hexdump('<', buf, len, sdp_debug, "SDP: ");
+
+	handle_internal_request(fd, context->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->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->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->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;
+
+	if (g_test_verbose() == TRUE)
+		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(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);
+
+	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];
+
+	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 execute_context(struct context *context)
+{
+	g_main_loop_run(context->main_loop);
+
+	sdp_svcdb_collect_all(context->fd);
+	sdp_svcdb_reset();
+
+	g_source_remove(context->server_source);
+	g_source_remove(context->client_source);
+
+	g_main_loop_unref(context->main_loop);
+
+	g_free(context);
+}
+
+static void test_sdp(gconstpointer data)
+{
+	const struct test_data *test = data;
+	struct context *context = create_context();
+
+	context->mtu = test->mtu;
+	context->pdu_list = test->pdu_list;
+
+	g_idle_add(send_pdu, context);
+
+	execute_context(context);
+
+	g_free(test->pdu_list);
+}
+
+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);
+
+	if (g_test_verbose() == TRUE)
+		g_print("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);
+}
+
+int main(int argc, char *argv[])
+{
+	g_test_init(&argc, &argv, NULL);
+
+	if (g_test_verbose())
+		__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 g_test_run();
+}
diff --git a/bluez/unit/test-textfile.c b/bluez/unit/test-textfile.c
new file mode 100644
index 0000000..d873df4
--- /dev/null
+++ b/bluez/unit/test-textfile.c
@@ -0,0 +1,277 @@
+/*
+ *
+ *  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"
+
+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(void)
+{
+	char key[18], *str;
+	int size;
+
+	size = getpagesize();
+	g_assert(size >= 4096);
+
+	if (g_test_verbose())
+		g_print("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);
+
+	if (g_test_verbose())
+		g_print("%s\n", str);
+
+	g_assert(str == NULL);
+}
+
+static void test_delete(void)
+{
+	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);
+
+	if (g_test_verbose())
+		g_print("%s\n", str);
+
+	g_free(str);
+}
+
+static void test_overwrite(void)
+{
+	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);
+
+	if (g_test_verbose())
+		g_print("%s\n", str);
+
+	g_assert(str == NULL);
+}
+
+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(void)
+{
+	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);
+
+		if (g_test_verbose())
+			g_print("%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);
+
+	if (g_test_verbose())
+		g_print("%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);
+
+	if (g_test_verbose())
+		g_print("%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);
+
+		if (g_test_verbose())
+			g_print("%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));
+}
+
+int main(int argc, char *argv[])
+{
+	g_test_init(&argc, &argv, NULL);
+
+	g_test_add_func("/textfile/pagesize", test_pagesize);
+	g_test_add_func("/textfile/delete", test_delete);
+	g_test_add_func("/textfile/overwrite", test_overwrite);
+	g_test_add_func("/textfile/multiple", test_multiple);
+
+	return g_test_run();
+}
diff --git a/bluez/unit/test-uuid.c b/bluez/unit/test-uuid.c
new file mode 100644
index 0000000..6c7e9d0
--- /dev/null
+++ b/bluez/unit/test-uuid.c
@@ -0,0 +1,228 @@
+/*
+ *
+ *  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 <bluetooth/bluetooth.h>
+
+#include "lib/uuid.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 = "00000000-0000-1000-8000-00805f9b34fb",
+	.binary = uuid_base_binary,
+	.type = BT_UUID128,
+	.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 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;
+	default:
+		return;
+        }
+}
+
+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(strcasecmp(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:
+		return;
+	}
+
+	bt_uuid_to_string(&uuid, buf, sizeof(buf));
+	g_assert(strcasecmp(buf, str) == 0);
+}
+
+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);
+}
+
+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);
+}
+
+int main(int argc, char *argv[])
+{
+	int i;
+
+	g_test_init(&argc, &argv, NULL);
+
+	g_test_add_data_func("/uuid/base", &uuid_base, test_uuid);
+	g_test_add_data_func("/uuid/base/str", &uuid_base, test_str);
+	g_test_add_data_func("/uuid/base/cmp", &uuid_base, test_cmp);
+
+	g_test_add_data_func("/uuid/sixteen1", &uuid_sixteen1, test_uuid);
+	g_test_add_data_func("/uuid/sixteen1/str", &uuid_sixteen1, test_str);
+	g_test_add_data_func("/uuid/sixteen1/cmp", &uuid_sixteen1, test_cmp);
+
+	g_test_add_data_func("/uuid/sixteen2", &uuid_sixteen2, test_uuid);
+	g_test_add_data_func("/uuid/sixteen2/str", &uuid_sixteen2, test_str);
+	g_test_add_data_func("/uuid/sixteen2/cmp", &uuid_sixteen2, test_cmp);
+
+	g_test_add_data_func("/uuid/thirtytwo1", &uuid_32_1, test_uuid);
+	g_test_add_data_func("/uuid/thirtytwo1/str", &uuid_32_1, test_str);
+	g_test_add_data_func("/uuid/thirtytwo1/cmp", &uuid_32_1, test_cmp);
+
+	g_test_add_data_func("/uuid/thirtytwo2", &uuid_32_2, test_uuid);
+	g_test_add_data_func("/uuid/thritytwo2/str", &uuid_32_2, test_str);
+	g_test_add_data_func("/uuid/thirtytwo2/cmp", &uuid_32_2, test_cmp);
+
+	for (i = 0; malformed[i]; i++) {
+		char *testpath;
+
+		testpath = g_strdup_printf("/uuid/malformed/%s", malformed[i]);
+		g_test_add_data_func(testpath, malformed[i], test_malformed);
+		g_free(testpath);
+	}
+
+	return g_test_run();
+}
diff --git a/bluez/unit/util.c b/bluez/unit/util.c
new file mode 100644
index 0000000..71fe7ca
--- /dev/null
+++ b/bluez/unit/util.c
@@ -0,0 +1,198 @@
+/*
+ *
+ *  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 "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/unit/util.h b/bluez/unit/util.h
new file mode 100644
index 0000000..96528a6
--- /dev/null
+++ b/bluez/unit/util.h
@@ -0,0 +1,59 @@
+/*
+ *
+ *  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
+ *
+ */
+
+#include <gobex/gobex.h>
+
+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);
diff --git a/build_bluez.sh b/build_bluez.sh
new file mode 100755
index 0000000..5e96cb9
--- /dev/null
+++ b/build_bluez.sh
@@ -0,0 +1,71 @@
+#!/bin/bash
+
+set -e
+
+INCLUDE_GATTOOL="N"
+CONFIGURE_GATTOOL_OPT="--disable-client"
+READLINE_INC=""
+READLINE_LIB=""
+
+
+if [[ "$#" -eq 1 && $1 == "GPLv3" ]]; then
+  INCLUDE_GATTOOL="Y"
+  CONFIGURE_GATTOOL_OPT="--enable-client"
+  READLINE_INC="-I$TOP/../readline/dist/usr/include"
+  READLINE_LIB="-L$TOP/../readline/dist/usr/lib"
+
+  echo "GPLv3 specified. Including gattool in the build."
+fi
+
+source ../../a5s_linux_sdk/ambarella/build/env/CodeSourcery.env
+PATH=$PATH:$ARM_LINUX_TOOLCHAIN_DIR/bin
+
+TOP=`pwd`
+
+rm -rf dist
+mkdir dist
+pushd bluez
+./configure --prefix=/usr --sysconfdir=/etc \
+            --localstatedir=/var \
+            --libexecdir=/lib \
+	    --disable-systemd --disable-obex --disable-cups --disable-udev --enable-experimental \
+            --host=arm-none-linux-gnueabi \
+	    --enable-library \
+            CFLAGS="-mtune=arm1136j-s -march=armv6 -Os $READLINE_INC" \
+            DBUS_CFLAGS="-I$TOP/../dbus/dist/include/dbus-1.0 -I$TOP/../dbus/dist/lib/dbus-1.0/include" \
+            GLIB_CFLAGS="-I$TOP/../glib/dist/usr/include/glib-2.0 -I$TOP/../glib/dist/usr/lib/glib-2.0/include" \
+            LDFLAGS="-Os -L$TOP/dist/usr/lib -L$TOP/../../a5s_linux_sdk/ambarella/prebuild/third-party/ncurses/lib -ltinfo $READLINE_LIB" \
+            DBUS_LIBS="-L$TOP/../dbus/dist/lib -ldbus-1" \
+            GLIB_LIBS="-L$TOP/../glib/dist/usr/lib -lglib-2.0" \
+            UDEV_CFLAGS="-I$TOP/../../a5s_linux_sdk/ambarella/prebuild/third-party/udev/include" \
+            UDEV_LIBS="-L$TOP/../../a5s_linux_sdk/ambarella/prebuild/third-party/udev/lib" \
+            --with-dbusconfdir=/etc/dbus-1 \
+            --with-dbussystembusdir=/etc/dbus-1/system.d \
+            --with-dbussessionbusdir=/etc/dbus-1/session.d \
+            $CONFIGURE_GATTOOL_OPT
+
+make install-strip DESTDIR=$TOP/dist
+
+if [[ $INCLUDE_GATTOOL == "Y" ]]; then
+  cp attrib/gatttool $TOP/dist/usr/bin/
+  arm-none-linux-gnueabi-strip $TOP/dist/usr/bin/gatttool
+fi
+
+mkdir -p $TOP/dist/etc/bluetooth
+install -v -m644 src/main.conf $TOP/dist/etc/bluetooth/main.conf
+install -v -m644 profiles/input/input.conf $TOP/dist/etc/bluetooth/input.conf
+install -v -m644 profiles/proximity/proximity.conf $TOP/dist/etc/bluetooth/proximity.conf
+install -v -m644 profiles/network/network.conf $TOP/dist/etc/bluetooth/network.conf
+popd
+
+mv $TOP/dist/etc/dbus-1/dbus-1/system.d $TOP/dist/etc/dbus-1/
+rmdir $TOP/dist/etc/dbus-1/dbus-1
+
+rm -rf fakeroot
+mkdir fakeroot
+cp -R dist/* fakeroot/
+rm -rf fakeroot/usr/share/man
+
+tar cjfv bluez.tar.bz2 fakeroot
+cp bluez.tar.bz2 ../../a5s_linux_sdk/ambarella/boards/dropcam/rootfs/
+