Project import
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..bc6fea3
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,110 @@
+#
+# Copyright (c) 2010-2011 Nest, Inc.
+# All rights reserved.
+#
+# This document is the property of Nest. It is considered
+# confidential and proprietary information.
+#
+# This document may not be reproduced or transmitted in any form,
+# in whole or in part, without the express written permission of
+# Nest.
+#
+# Description:
+# This file is the makefile for BlueZ, the official Linux
+# Bluetooth protocol stack.
+#
+
+
+include pre.mak
+
+PackageName := bluez
+
+PackageSourceDir := repo
+
+PackageBuildMakefile = $(call GenerateBuildPaths,Makefile)
+
+CleanPaths += $(PackageLicenseFile)
+
+SOURCEDIRS = $(PackageSourceDir)
+$(PackageSourceDir)_RULE_TARGET = $(BuildDirectory)/configure
+
+all: $(PackageDefaultGoal)
+
+# Generate the package license contents.
+
+$(PackageSourceDir)/LICENSE: $(BuildDirectory)/source
+
+$(PackageLicenseFile): $(PackageSourceDir)/LICENSE
+ $(copy-result)
+
+# Prepare the sources.
+
+.PHONY: source
+source: $(BuildDirectory)/source
+$(BuildDirectory)/source: | $(PackageSourceDir) $(BuildDirectory)
+ $(Verbose)$(call create-links,$(CURDIR)/$(PackageSourceDir),$(BuildDirectory))
+ $(Verbose)touch $@
+
+# Generate the package's build makefile
+
+# Configure the source for building.
+
+.PHONY: configure
+configure: $(BuildDirectory)/configure
+$(BuildDirectory)/configure: $(BuildDirectory)/source | $(PackageSourceDir) $(BuildDirectory)
+ $(Verbose)cd $(BuildDirectory) && ./bootstrap
+ $(Verbose)cd $(BuildDirectory) && ./configure \
+ CC="$(CC) $(CPPOPTFLAGS)" CXX="$(CXX) $(CPPOPTFLAGS)" AR=$(AR) NM=$(NM) RANLIB=$(RANLIB) STRIP=$(STRIP) INSTALL="$(INSTALL) $(INSTALLFLAGS)" \
+ CPPFLAGS="$(call ToolGenerateDefineArgument,FIRMWARE_DIR=\\\"/lib/firmware/\\\") \
+ $(call ToolGenerateIncludeArgument,$(LinuxIncludePath))" \
+ DBUS_CFLAGS="$(call ToolGenerateIncludeArgument,$(DbusIncludePaths))" \
+ GLIB_CFLAGS="$(call ToolGenerateIncludeArgument,$(GlibIncludePaths))" \
+ UDEV_CFLAGS="$(call ToolGenerateIncludeArgument,$(UdevIncludePaths))" \
+ DBUS_LIBS="$(call GenerateLibraryArgument,$(DbusLibraryPath))" \
+ GLIB_LIBS="$(call GenerateLibraryArgument,$(GlibLibraryPath))" \
+ UDEV_LIBS="$(call GenerateLibraryArgument,$(UdevLibraryPath))" \
+ PKG_CONFIG_PATH="$(GlibPkgConfigPath):$(DbusPkgConfigPath):$(UdevPkgConfigPath)" \
+ --build=$(HostTuple) \
+ --host=$(TargetTuple) \
+ --prefix=/usr \
+ --sysconfdir=/etc \
+ --localstatedir=/var \
+ --disable-cups \
+ --disable-monitor \
+ --disable-client \
+ --disable-systemd \
+ --disable-obex \
+ $(if $(BUILD_FEATURE_BLUEZ_LIB), --enable-library) \
+ $(NULL)
+ $(Verbose)touch $@
+
+# Build the source.
+#
+# We have to unset MAKEFLAGS since they confuse the package build otherwise.
+
+.PHONY: build
+build: $(BuildDirectory)/build
+$(BuildDirectory)/build: $(BuildDirectory)/configure
+ $(Verbose)unset MAKEFLAGS && \
+ $(MAKE) $(JOBSFLAG) -C $(BuildDirectory) \
+ all
+ $(Verbose)touch $@
+
+# Stage the build to a temporary installation area.
+#
+# We have to unset MAKEFLAGS since they confuse the package build otherwise.
+
+$(BuildDirectory)/stage: $(BuildDirectory)/build | $(ResultDirectory)
+ $(Verbose)unset MAKEFLAGS && \
+ $(MAKE) $(JOBSFLAG) -C $(BuildDirectory) \
+ DESTDIR=$(ResultDirectory) \
+ install
+ $(Verbose)touch $@
+
+stage: $(BuildDirectory)/stage
+
+clean:
+ $(Verbose)$(RM) $(RMFLAGS) -r $(BuildDirectory)
+ $(Verbose)$(RM) $(RMFLAGS) -r $(ResultDirectory)
+
+include post.mak
diff --git a/repo/.mailmap b/repo/.mailmap
new file mode 100644
index 0000000..a7e36e3
--- /dev/null
+++ b/repo/.mailmap
@@ -0,0 +1,11 @@
+Luiz Augusto von Dentz <luiz.dentz-von@nokia.com> <luiz.dentz-von@nokia.com>
+Vinicius Costa Gomes <vinicius.gomes@openbossa.org> <vinicius.gomes@openbossa.org>
+Elvis Pfützenreuter <epx@signove.com> <epx@signove.com>
+Santiago Carot-Nemesio <scarot@libresoft.es> <scarot@libresoft.es>
+José Antonio Santos Cadenas <santoscadenas@gmail.com> <santoscadenas@gmail.com>
+Waldemar Rymarkiewicz <waldemar.rymarkiewicz@tieto.com> <Waldemar.Rymarkiewicz@tieto.com>
+Alok Barsode <alokbarsode@gmail.com> <alok@greatbear.(none)>
+André Dieb Martins <andre.dieb@signove.com> <andre.dieb@signove.com>
+Tedd Ho-Jeong An <tedd.an@intel.com> <tedd.an@intel.com>
+Martin Xu <martin.xu@linux.intel.com> <martin.xu@linux.intel.com>
+Marie Janssen <jamuraa@chromium.org> <jamuraa@chromium.org>
diff --git a/repo/AUTHORS b/repo/AUTHORS
new file mode 100644
index 0000000..16fb14c
--- /dev/null
+++ b/repo/AUTHORS
@@ -0,0 +1,101 @@
+Maxim Krasnyansky <maxk@qualcomm.com>
+Marcel Holtmann <marcel@holtmann.org>
+Stephen Crane <steve.crane@rococosoft.com>
+Jean Tourrilhes <jt@hpl.hp.com>
+Jan Beutel <j.beutel@ieee.org>
+Ilguiz Latypov <ilatypov@superbt.com>
+Thomas Moser <thomas.moser@tmoser.ch>
+Nils Faerber <nils@kernelconcepts.de>
+Martin Leopold <martin@leopold.dk>
+Wolfgang Heidrich <wolfgang.heidrich@esk.fhg.de>
+Fabrizio Gennari <fabrizio.gennari@philips.com>
+Brad Midgley <bmidgley@xmission.com>
+Henryk Ploetz <henryk@ploetzli.ch>
+Philip Blundell <pb@nexus.co.uk>
+Johan Hedberg <johan.hedberg@intel.com>
+Claudio Takahasi <claudio.takahasi@indt.org.br>
+Eduardo Rocha <eduardo.rocha@indt.org.br>
+Denis Kenzior <denis.kenzior@trolltech.com>
+Frederic Dalleau <frederic.dalleau@access-company.com>
+Frederic Danis <frederic.danis@access-company.com>
+Luiz Augusto von Dentz <luiz.dentz@gmail.com>
+Fabien Chevalier <fabchevalier@free.fr>
+Ohad Ben-Cohen <ohad@bencohen.org>
+Daniel Gollub <dgollub@suse.de>
+Tom Patzig <tpatzig@suse.de>
+Kai Vehmanen <kai.vehmanen@nokia.com>
+Vinicius Gomes <vinicius.gomes@openbossa.org>
+Alok Barsode <alok.barsode@azingo.com>
+Bastien Nocera <hadess@hadess.net>
+Albert Huang <albert@csail.mit.edu>
+Glenn Durfee <gdurfee@google.com>
+David Woodhouse <david.woodhouse@intel.com>
+Christian Hoene <hoene@uni-tuebingen.de>
+Pekka Pessi <pekka.pessi@nokia.com>
+Siarhei Siamashka <siarhei.siamashka@nokia.com>
+Nick Pelly <npelly@google.com>
+Lennart Poettering <lennart@poettering.net>
+Gustavo Padovan <gustavo@padovan.org>
+Marc-Andre Lureau <marc-andre.lureau@nokia.com>
+Bea Lam <bea.lam@nokia.com>
+Zygo Blaxell <zygo.blaxell@xandros.com>
+Forrest Zhao <forrest.zhao@intel.com>
+Scott Talbot <psyc@stalbot.com>
+Ilya Rubtsov <lusyaru@gmail.com>
+Mario Limonciello <mario_limonciello@dell.com>
+Filippo Giunchedi <filippo@esaurito.net>
+Jaikumar Ganesh <jaikumar@google.com>
+Elvis Pfutzenreuter <epx@signove.com>
+Santiago Carot-Nemesio <scarot@libresoft.es>
+José Antonio Santos Cadenas <jcaden@libresoft.es>
+Francisco Alecrim <francisco.alecrim@openbossa.org>
+Daniel Orstadius <daniel.orstadius@gmail.com>
+Anderson Briglia <anderson.briglia@openbossa.org>
+Anderson Lizardo <anderson.lizardo@openbossa.org>
+Bruna Moreira <bruna.moreira@openbossa.org>
+Brian Gix <bgix@codeaurora.org>
+Andre Guedes <andre.guedes@openbossa.org>
+Sheldon Demario <sheldon.demario@openbossa.org>
+Lucas De Marchi <lucas.demarchi@profusion.mobi>
+Szymon Janc <szymon.janc@codecoup.pl>
+Syam Sidhardhan <s.syam@samsung.com>
+Paulo Alcantara <pcacjr@gmail.com>
+Jefferson Delfes <jefferson.delfes@openbossa.org>
+Andrzej Kaczmarek <andrzej.kaczmarek@codecoup.pl>
+Eder Ruiz Maria <eder.ruiz@openbossa.org>
+Mikel Astiz <mikel.astiz@bmw-carit.de>
+Chan-yeol Park <chanyeol.park@samsung.com>
+João Paulo Rechi Vita <jprvita@gmail.com>
+Larry Junior <larry.junior@openbossa.org>
+Raymond Liu <raymond.liu@intel.com>
+Radoslaw Jablonski <ext-jablonski.radoslaw@nokia.com>
+Rafal Michalski <michalski.raf@gmail.com>
+Dmitriy Paliy <dmitriy.paliy@nokia.com>
+Bartosz Szatkowski <bulislaw@linux.com>
+Lukasz Pawlik <lucas.pawlik@gmail.com>
+Slawomir Bochenski <lkslawek@gmail.com>
+Wayne Lee <waynelee@qualcomm.com>
+Ricky Yuen <ryuen@qualcomm.com>
+Takashi Sasai <sasai@sm.sony.co.jp>
+Andre Dieb Martins <andre.dieb@signove.com>
+Cristian Rodríguez <crrodriguez@opensuse.org>
+Alex Deymo <deymo@chromium.org>
+Petri Gynther <pgynther@google.com>
+Scott James Remnant <scott@netsplit.com>
+Jakub Tyszkowski <jakub.tyszkowski@tieto.com>
+Grzegorz Kołodziejczyk <grzegorz.kolodziejczyk@tieto.com>
+Marcin Krąglak <marcin.kraglak@tieto.com>
+Łukasz Rymanowski <lukasz.rymanowski@codecoup.pl>
+Jerzy Kasenberg <jerzy.kasenberg@tieto.com>
+Arman Uguray <armansito@chromium.org>
+Artem Rakhov <arakhov@chromium.org>
+Mike Ryan <mikeryan@lacklustre.net>
+David Herrmann <dh.herrmann@gmail.com>
+Jacob Siverskog <jacob@teenageengineering.com>
+Sebastian Chłąd <sebastian.chlad@tieto.com>
+Alex Gal <a.gal@miip.ca>
+Loic Poulain <loic.poulain@intel.com>
+Gowtham Anandha Babu <gowtham.ab@samsung.com>
+Bharat Panda <bharat.panda@samsung.com>
+Marie Janssen <jamuraa@chromium.org>
+Jaganath Kanakkassery <jaganath.k@samsung.com>
diff --git a/repo/COPYING b/repo/COPYING
new file mode 100644
index 0000000..6d45519
--- /dev/null
+++ b/repo/COPYING
@@ -0,0 +1,340 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+ 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.) You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+ We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+ Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ GNU GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+ 1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+ 2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) You must cause the modified files to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ b) You must cause any work that you distribute or publish, that in
+ whole or in part contains or is derived from the Program or any
+ part thereof, to be licensed as a whole at no charge to all third
+ parties under the terms of this License.
+
+ c) If the modified program normally reads commands interactively
+ when run, you must cause it, when started running for such
+ interactive use in the most ordinary way, to print or display an
+ announcement including an appropriate copyright notice and a
+ notice that there is no warranty (or else, saying that you provide
+ a warranty) and that users may redistribute the program under
+ these conditions, and telling the user how to view a copy of this
+ License. (Exception: if the Program itself is interactive but
+ does not normally print such an announcement, your work based on
+ the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+ a) Accompany it with the complete corresponding machine-readable
+ source code, which must be distributed under the terms of Sections
+ 1 and 2 above on a medium customarily used for software interchange; or,
+
+ b) Accompany it with a written offer, valid for at least three
+ years, to give any third party, for a charge no more than your
+ cost of physically performing source distribution, a complete
+ machine-readable copy of the corresponding source code, to be
+ distributed under the terms of Sections 1 and 2 above on a medium
+ customarily used for software interchange; or,
+
+ c) Accompany it with the information you received as to the offer
+ to distribute corresponding source code. (This alternative is
+ allowed only for noncommercial distribution and only if you
+ received the program in object code or executable form with such
+ an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+ 5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+ 6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+ 7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+ 9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+ 10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+ Gnomovision version 69, Copyright (C) year name of author
+ Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+ `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+ <signature of Ty Coon>, 1 April 1989
+ Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs. If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library. If this is what you want to do, use the GNU Library General
+Public License instead of this License.
diff --git a/repo/COPYING.LIB b/repo/COPYING.LIB
new file mode 100644
index 0000000..1f7c8cc
--- /dev/null
+++ b/repo/COPYING.LIB
@@ -0,0 +1,504 @@
+ GNU LESSER GENERAL PUBLIC LICENSE
+ Version 2.1, February 1999
+
+ Copyright (C) 1991, 1999 Free Software Foundation, Inc.
+ 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+[This is the first released version of the Lesser GPL. It also counts
+ as the successor of the GNU Library Public License, version 2, hence
+ the version number 2.1.]
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+Licenses are intended to guarantee your freedom to share and change
+free software--to make sure the software is free for all its users.
+
+ This license, the Lesser General Public License, applies to some
+specially designated software packages--typically libraries--of the
+Free Software Foundation and other authors who decide to use it. You
+can use it too, but we suggest you first think carefully about whether
+this license or the ordinary General Public License is the better
+strategy to use in any particular case, based on the explanations below.
+
+ When we speak of free software, we are referring to freedom of use,
+not price. Our General Public Licenses are designed to make sure that
+you have the freedom to distribute copies of free software (and charge
+for this service if you wish); that you receive source code or can get
+it if you want it; that you can change the software and use pieces of
+it in new free programs; and that you are informed that you can do
+these things.
+
+ To protect your rights, we need to make restrictions that forbid
+distributors to deny you these rights or to ask you to surrender these
+rights. These restrictions translate to certain responsibilities for
+you if you distribute copies of the library or if you modify it.
+
+ For example, if you distribute copies of the library, whether gratis
+or for a fee, you must give the recipients all the rights that we gave
+you. You must make sure that they, too, receive or can get the source
+code. If you link other code with the library, you must provide
+complete object files to the recipients, so that they can relink them
+with the library after making changes to the library and recompiling
+it. And you must show them these terms so they know their rights.
+
+ We protect your rights with a two-step method: (1) we copyright the
+library, and (2) we offer you this license, which gives you legal
+permission to copy, distribute and/or modify the library.
+
+ To protect each distributor, we want to make it very clear that
+there is no warranty for the free library. Also, if the library is
+modified by someone else and passed on, the recipients should know
+that what they have is not the original version, so that the original
+author's reputation will not be affected by problems that might be
+introduced by others.
+
+ Finally, software patents pose a constant threat to the existence of
+any free program. We wish to make sure that a company cannot
+effectively restrict the users of a free program by obtaining a
+restrictive license from a patent holder. Therefore, we insist that
+any patent license obtained for a version of the library must be
+consistent with the full freedom of use specified in this license.
+
+ Most GNU software, including some libraries, is covered by the
+ordinary GNU General Public License. This license, the GNU Lesser
+General Public License, applies to certain designated libraries, and
+is quite different from the ordinary General Public License. We use
+this license for certain libraries in order to permit linking those
+libraries into non-free programs.
+
+ When a program is linked with a library, whether statically or using
+a shared library, the combination of the two is legally speaking a
+combined work, a derivative of the original library. The ordinary
+General Public License therefore permits such linking only if the
+entire combination fits its criteria of freedom. The Lesser General
+Public License permits more lax criteria for linking other code with
+the library.
+
+ We call this license the "Lesser" General Public License because it
+does Less to protect the user's freedom than the ordinary General
+Public License. It also provides other free software developers Less
+of an advantage over competing non-free programs. These disadvantages
+are the reason we use the ordinary General Public License for many
+libraries. However, the Lesser license provides advantages in certain
+special circumstances.
+
+ For example, on rare occasions, there may be a special need to
+encourage the widest possible use of a certain library, so that it becomes
+a de-facto standard. To achieve this, non-free programs must be
+allowed to use the library. A more frequent case is that a free
+library does the same job as widely used non-free libraries. In this
+case, there is little to gain by limiting the free library to free
+software only, so we use the Lesser General Public License.
+
+ In other cases, permission to use a particular library in non-free
+programs enables a greater number of people to use a large body of
+free software. For example, permission to use the GNU C Library in
+non-free programs enables many more people to use the whole GNU
+operating system, as well as its variant, the GNU/Linux operating
+system.
+
+ Although the Lesser General Public License is Less protective of the
+users' freedom, it does ensure that the user of a program that is
+linked with the Library has the freedom and the wherewithal to run
+that program using a modified version of the Library.
+
+ The precise terms and conditions for copying, distribution and
+modification follow. Pay close attention to the difference between a
+"work based on the library" and a "work that uses the library". The
+former contains code derived from the library, whereas the latter must
+be combined with the library in order to run.
+
+ GNU LESSER GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License Agreement applies to any software library or other
+program which contains a notice placed by the copyright holder or
+other authorized party saying it may be distributed under the terms of
+this Lesser General Public License (also called "this License").
+Each licensee is addressed as "you".
+
+ A "library" means a collection of software functions and/or data
+prepared so as to be conveniently linked with application programs
+(which use some of those functions and data) to form executables.
+
+ The "Library", below, refers to any such software library or work
+which has been distributed under these terms. A "work based on the
+Library" means either the Library or any derivative work under
+copyright law: that is to say, a work containing the Library or a
+portion of it, either verbatim or with modifications and/or translated
+straightforwardly into another language. (Hereinafter, translation is
+included without limitation in the term "modification".)
+
+ "Source code" for a work means the preferred form of the work for
+making modifications to it. For a library, complete source code means
+all the source code for all modules it contains, plus any associated
+interface definition files, plus the scripts used to control compilation
+and installation of the library.
+
+ Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running a program using the Library is not restricted, and output from
+such a program is covered only if its contents constitute a work based
+on the Library (independent of the use of the Library in a tool for
+writing it). Whether that is true depends on what the Library does
+and what the program that uses the Library does.
+
+ 1. You may copy and distribute verbatim copies of the Library's
+complete source code as you receive it, in any medium, provided that
+you conspicuously and appropriately publish on each copy an
+appropriate copyright notice and disclaimer of warranty; keep intact
+all the notices that refer to this License and to the absence of any
+warranty; and distribute a copy of this License along with the
+Library.
+
+ You may charge a fee for the physical act of transferring a copy,
+and you may at your option offer warranty protection in exchange for a
+fee.
+
+ 2. You may modify your copy or copies of the Library or any portion
+of it, thus forming a work based on the Library, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) The modified work must itself be a software library.
+
+ b) You must cause the files modified to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ c) You must cause the whole of the work to be licensed at no
+ charge to all third parties under the terms of this License.
+
+ d) If a facility in the modified Library refers to a function or a
+ table of data to be supplied by an application program that uses
+ the facility, other than as an argument passed when the facility
+ is invoked, then you must make a good faith effort to ensure that,
+ in the event an application does not supply such function or
+ table, the facility still operates, and performs whatever part of
+ its purpose remains meaningful.
+
+ (For example, a function in a library to compute square roots has
+ a purpose that is entirely well-defined independent of the
+ application. Therefore, Subsection 2d requires that any
+ application-supplied function or table used by this function must
+ be optional: if the application does not supply it, the square
+ root function must still compute square roots.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Library,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Library, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote
+it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Library.
+
+In addition, mere aggregation of another work not based on the Library
+with the Library (or with a work based on the Library) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may opt to apply the terms of the ordinary GNU General Public
+License instead of this License to a given copy of the Library. To do
+this, you must alter all the notices that refer to this License, so
+that they refer to the ordinary GNU General Public License, version 2,
+instead of to this License. (If a newer version than version 2 of the
+ordinary GNU General Public License has appeared, then you can specify
+that version instead if you wish.) Do not make any other change in
+these notices.
+
+ Once this change is made in a given copy, it is irreversible for
+that copy, so the ordinary GNU General Public License applies to all
+subsequent copies and derivative works made from that copy.
+
+ This option is useful when you wish to copy part of the code of
+the Library into a program that is not a library.
+
+ 4. You may copy and distribute the Library (or a portion or
+derivative of it, under Section 2) in object code or executable form
+under the terms of Sections 1 and 2 above provided that you accompany
+it with the complete corresponding machine-readable source code, which
+must be distributed under the terms of Sections 1 and 2 above on a
+medium customarily used for software interchange.
+
+ If distribution of object code is made by offering access to copy
+from a designated place, then offering equivalent access to copy the
+source code from the same place satisfies the requirement to
+distribute the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 5. A program that contains no derivative of any portion of the
+Library, but is designed to work with the Library by being compiled or
+linked with it, is called a "work that uses the Library". Such a
+work, in isolation, is not a derivative work of the Library, and
+therefore falls outside the scope of this License.
+
+ However, linking a "work that uses the Library" with the Library
+creates an executable that is a derivative of the Library (because it
+contains portions of the Library), rather than a "work that uses the
+library". The executable is therefore covered by this License.
+Section 6 states terms for distribution of such executables.
+
+ When a "work that uses the Library" uses material from a header file
+that is part of the Library, the object code for the work may be a
+derivative work of the Library even though the source code is not.
+Whether this is true is especially significant if the work can be
+linked without the Library, or if the work is itself a library. The
+threshold for this to be true is not precisely defined by law.
+
+ If such an object file uses only numerical parameters, data
+structure layouts and accessors, and small macros and small inline
+functions (ten lines or less in length), then the use of the object
+file is unrestricted, regardless of whether it is legally a derivative
+work. (Executables containing this object code plus portions of the
+Library will still fall under Section 6.)
+
+ Otherwise, if the work is a derivative of the Library, you may
+distribute the object code for the work under the terms of Section 6.
+Any executables containing that work also fall under Section 6,
+whether or not they are linked directly with the Library itself.
+
+ 6. As an exception to the Sections above, you may also combine or
+link a "work that uses the Library" with the Library to produce a
+work containing portions of the Library, and distribute that work
+under terms of your choice, provided that the terms permit
+modification of the work for the customer's own use and reverse
+engineering for debugging such modifications.
+
+ You must give prominent notice with each copy of the work that the
+Library is used in it and that the Library and its use are covered by
+this License. You must supply a copy of this License. If the work
+during execution displays copyright notices, you must include the
+copyright notice for the Library among them, as well as a reference
+directing the user to the copy of this License. Also, you must do one
+of these things:
+
+ a) Accompany the work with the complete corresponding
+ machine-readable source code for the Library including whatever
+ changes were used in the work (which must be distributed under
+ Sections 1 and 2 above); and, if the work is an executable linked
+ with the Library, with the complete machine-readable "work that
+ uses the Library", as object code and/or source code, so that the
+ user can modify the Library and then relink to produce a modified
+ executable containing the modified Library. (It is understood
+ that the user who changes the contents of definitions files in the
+ Library will not necessarily be able to recompile the application
+ to use the modified definitions.)
+
+ b) Use a suitable shared library mechanism for linking with the
+ Library. A suitable mechanism is one that (1) uses at run time a
+ copy of the library already present on the user's computer system,
+ rather than copying library functions into the executable, and (2)
+ will operate properly with a modified version of the library, if
+ the user installs one, as long as the modified version is
+ interface-compatible with the version that the work was made with.
+
+ c) Accompany the work with a written offer, valid for at
+ least three years, to give the same user the materials
+ specified in Subsection 6a, above, for a charge no more
+ than the cost of performing this distribution.
+
+ d) If distribution of the work is made by offering access to copy
+ from a designated place, offer equivalent access to copy the above
+ specified materials from the same place.
+
+ e) Verify that the user has already received a copy of these
+ materials or that you have already sent this user a copy.
+
+ For an executable, the required form of the "work that uses the
+Library" must include any data and utility programs needed for
+reproducing the executable from it. However, as a special exception,
+the materials to be distributed need not include anything that is
+normally distributed (in either source or binary form) with the major
+components (compiler, kernel, and so on) of the operating system on
+which the executable runs, unless that component itself accompanies
+the executable.
+
+ It may happen that this requirement contradicts the license
+restrictions of other proprietary libraries that do not normally
+accompany the operating system. Such a contradiction means you cannot
+use both them and the Library together in an executable that you
+distribute.
+
+ 7. You may place library facilities that are a work based on the
+Library side-by-side in a single library together with other library
+facilities not covered by this License, and distribute such a combined
+library, provided that the separate distribution of the work based on
+the Library and of the other library facilities is otherwise
+permitted, and provided that you do these two things:
+
+ a) Accompany the combined library with a copy of the same work
+ based on the Library, uncombined with any other library
+ facilities. This must be distributed under the terms of the
+ Sections above.
+
+ b) Give prominent notice with the combined library of the fact
+ that part of it is a work based on the Library, and explaining
+ where to find the accompanying uncombined form of the same work.
+
+ 8. You may not copy, modify, sublicense, link with, or distribute
+the Library except as expressly provided under this License. Any
+attempt otherwise to copy, modify, sublicense, link with, or
+distribute the Library is void, and will automatically terminate your
+rights under this License. However, parties who have received copies,
+or rights, from you under this License will not have their licenses
+terminated so long as such parties remain in full compliance.
+
+ 9. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Library or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Library (or any work based on the
+Library), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Library or works based on it.
+
+ 10. Each time you redistribute the Library (or any work based on the
+Library), the recipient automatically receives a license from the
+original licensor to copy, distribute, link with or modify the Library
+subject to these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties with
+this License.
+
+ 11. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Library at all. For example, if a patent
+license would not permit royalty-free redistribution of the Library by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Library.
+
+If any portion of this section is held invalid or unenforceable under any
+particular circumstance, the balance of the section is intended to apply,
+and the section as a whole is intended to apply in other circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 12. If the distribution and/or use of the Library is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Library under this License may add
+an explicit geographical distribution limitation excluding those countries,
+so that distribution is permitted only in or among countries not thus
+excluded. In such case, this License incorporates the limitation as if
+written in the body of this License.
+
+ 13. The Free Software Foundation may publish revised and/or new
+versions of the Lesser General Public License from time to time.
+Such new versions will be similar in spirit to the present version,
+but may differ in detail to address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Library
+specifies a version number of this License which applies to it and
+"any later version", you have the option of following the terms and
+conditions either of that version or of any later version published by
+the Free Software Foundation. If the Library does not specify a
+license version number, you may choose any version ever published by
+the Free Software Foundation.
+
+ 14. If you wish to incorporate parts of the Library into other free
+programs whose distribution conditions are incompatible with these,
+write to the author to ask for permission. For software which is
+copyrighted by the Free Software Foundation, write to the Free
+Software Foundation; we sometimes make exceptions for this. Our
+decision will be guided by the two goals of preserving the free status
+of all derivatives of our free software and of promoting the sharing
+and reuse of software generally.
+
+ NO WARRANTY
+
+ 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
+WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
+EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
+OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
+KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
+LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
+THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+ 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
+WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
+AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
+FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
+CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
+LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
+RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
+FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
+SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Libraries
+
+ If you develop a new library, and you want it to be of the greatest
+possible use to the public, we recommend making it free software that
+everyone can redistribute and change. You can do so by permitting
+redistribution under these terms (or, alternatively, under the terms of the
+ordinary General Public License).
+
+ To apply these terms, attach the following notices to the library. It is
+safest to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least the
+"copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the library's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+Also add information on how to contact you by electronic and paper mail.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the library, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the
+ library `Frob' (a library for tweaking knobs) written by James Random Hacker.
+
+ <signature of Ty Coon>, 1 April 1990
+ Ty Coon, President of Vice
+
+That's all there is to it!
+
+
diff --git a/repo/ChangeLog b/repo/ChangeLog
new file mode 100644
index 0000000..c7a4fc0
--- /dev/null
+++ b/repo/ChangeLog
@@ -0,0 +1,2107 @@
+ver 5.37:
+ Fix issue with registering external profiles.
+ Fix issue with connecting external profiles.
+ Fix issue with GATT service changed handling.
+ Fix issue with not emitting GattServices update.
+ Convert to unified HID over GATT profile support.
+ Convert to KeyboardDisplay as default IO capability.
+ Install btattach utility by default.
+
+ver 5.36:
+ Fix issue with PBAP headers for size query.
+ Fix issue with AVRCP current player handling.
+ Fix issue with device information handling.
+ Fix issue with device disconnect handling.
+ Fix issue with duplicate connect handling.
+ Fix issue with attribute claiming for drivers.
+
+ver 5.35:
+ Fix issue with connected devices after discovery.
+ Fix issue with profile support and LTK loading.
+ Fix issue with AVRCP events for volume control.
+ Fix issue with OBEX session owner handling.
+ Fix issue with HID over GATT setup failures.
+ Fix issue with GATT notification registration.
+ Fix issue with GATT cache validation feature.
+ Add support for persistent GATT database.
+ Add support for controller enabling option.
+
+ver 5.34:
+ Fix issue with GATT profiles and auto-connect.
+ Fix issue with missing GoepL2CapPsm SDP data.
+ Fix issue with suspending AVDTP endpoints.
+ Fix issue with audio service state on disconnect.
+ Add support for AVRCP Set Addressed Player feature.
+ Add support for AVRCP Get Folder Items feature.
+ Add support for Android 5.1 HFP WBS callbacks.
+
+ver 5.33:
+ Fix issue with memory leak in GATT database.
+ Fix issue with AVDTP set configuration handling.
+ Fix issue with AVDTP discover procedure.
+ Fix issue with not emitting Paired property.
+
+ver 5.32:
+ Fix issue with OPP GET request path handling.
+ Fix issue with ATT information request errors.
+ Fix issue with advertising instance numbers.
+ Fix issue with overwriting SDP record cache.
+ Fix issue with new connections during disconnect.
+ Add support for GATT security auto-elevation.
+
+ver 5.31:
+ Fix issue with crash in networking interface.
+ Fix issue with crash when creating endless GATT loops.
+ Fix issue with memory leak when connecting services.
+ Fix issue with memory leak creating new D-Bus proxy.
+ Fix issue with profile connections from remote devices.
+ Fix issue with GATT over BR/EDR and MTU notification.
+ Fix issue with HID and dual mode remote devices.
+ Fix issue with handling A2DP vendor codec setup.
+ Fix issue with AVRCP and syncing player state.
+ Fix issue with GATT secondary discovery handling.
+ Fix issue with wrong characteristic allocation.
+ Add support for handling BNEP setup response.
+ Add support for setting GATT database security flags.
+ Add support for setting discovery filters interface.
+ Add support for user controlled advertising interface.
+ Update Android qualification documentation to PTS 6.1 release.
+
+ver 5.30:
+ Fix compilation error in C++ due to inline function.
+ Fix issue with missing storage of device information.
+ Fix issue with GATT client and gaps in service handles.
+ Fix issue with AVDTP discovery callback crashing.
+ Fix issue with AVCTP channel handling in case of conflicts.
+ Fix issue with AVRCP target and get capabilities command.
+ Add experimental support for LE advertising manager API.
+ Add support for Android 5.1 GATT MTU exchange API.
+
+ver 5.29:
+ Fix issue with AVCTP initial key repeat timeout.
+ Fix issue with Android application disconnect handling.
+ Fix issue with Android support and service notifications.
+ Fix issue with Android support and Exchange MTU Request.
+ Fix issue with Android HFP support and AT+CMER handling.
+ Fix issue with Android HFP support and SLC setup.
+ Fix issue with Android HFP support and call hold status.
+ Fix issue with Android HFP support and indicator handling.
+ Fix issue with Android HFP support and SCO/eSCO disconnection.
+ Fix issue with Android HID over GATT support and battery service.
+ Fix issue with GATT sending Exchange MTU Request for BR/EDR.
+ Fix issue with GATT notification support without CCC.
+ Fix issue with GATT object life-time after disconnects.
+ Fix issue with GATT notification handling API.
+ Add experimental support for GATT client D-Bus API.
+ Add experimental support for GATT server D-Bus API.
+ Add support for Multi Profile Specification.
+ Update Android qualification documentation to PTS 6.0 release.
+
+ver 5.28:
+ Fix issue with GATT device discovery and probing.
+ Fix issue with bearer selection for dual-mode devices.
+ Fix issue with device removal while connected.
+ Fix issue with device name setting from inquiry response.
+ Fix issue with missing termination of name characteristic.
+ Fix issue with UTF-8 length handling for device name.
+ Fix issue with AVCTP key auto release handling.
+ Fix issue with AVCTP key press repetition handling.
+ Fix issue with payload sizes and GATT notifications.
+ Fix issue with memory corruption and GATT notifications.
+ Add support for HID proxy switching and CSR 8510 A10 devices.
+ Add support for Broadcom hex2hcd conversion utility.
+
+ver 5.27:
+ Fix issue with endian handling and management interface.
+ Fix issue with pending GATT operations when disconnecting.
+ Fix issue with 128-bit UUID conversions for HID over GATT.
+ Add support for Android 5.0 SELinux policies.
+
+ver 5.26:
+ Fix issue with handling A2DP XCASE connection state.
+ Fix issue with crash and A2DP configuration failures.
+ Fix issue with crash during OBEX session shutdown.
+ Add support for version 1.2 of Phonebook Access Profile.
+ Add support for HID over GATT get and set report handling.
+ Add support for Low Energy Secure Connections feature.
+ Add support for Bluetooth 4.2 commands and events.
+ Add support for Android 5.0 Bluetooth features.
+
+ver 5.25:
+ Fix issue with SCO connection after codec negotiation.
+ Fix issue with GATT and secondary service discovery.
+ Fix issue with GATT write descriptor callback.
+ Fix issue with MAP supported features bits.
+ Add support for MAP local time and timezone offset.
+ Add support for PBAP speed-dial and favorites folders.
+ Add support for PBAP speed-dial and identifier filters.
+ Add support for controller mode configuration option.
+ Add initial support for Android Lollipop features.
+
+ver 5.24:
+ Fix issue with storing of connection parameters.
+ Add support for Phonebook Access Profile 1.2 features.
+ Add support for Message Access Profile 1.2 event reports.
+ Add support for Android Bluetooth configuration options.
+
+ver 5.23:
+ Fix issue with concurrent authorization requests.
+ Fix issue with HID report identifier mismatch.
+ Fix issue with crash when receiving uHID events.
+ Fix issue with crash and OBEX disconnect handling.
+ Fix issue with OBEX client transfers and suspend.
+ Fix issue with parsing of MAP application parameters.
+ Fix issue with devices rejecting AVRCP GetCapabilities.
+ Add support for kernel whitelist and Android Bluetooth.
+
+ver 5.22:
+ Fix issue with UHID_OUTPUT events mapping.
+ Fix issue with UHID_FEATURE events handling.
+ Fix issue with UINT32_MAX overflow and AVRCP.
+ Fix issue when dirent type DT_UNKNOWN is returned.
+ Add support for kernel whitelist filtering feature.
+ Add support for Android Bluetooth GATT over BR/EDR.
+
+ver 5.21:
+ Fix issue with SDP requests and wrong PDU size.
+ Fix issue with handling passive scanning triggers.
+ Add support for storing and loading connection parameters.
+ Add support for kernel background auto-connection feature.
+ Add support for Android Bluetooth Scan Parameters feature.
+ Add support for Android Bluetooth Device Information feature.
+ Add support for Android Bluetooth Health Device interface.
+
+ver 5.20:
+ Fix issue with LED handling of PS3 controllers.
+ Add support for Android Bluetooth GATT server interface.
+ Add support for Android Bluetooth HID over GATT feature.
+ Add support for Android Bluetooth multi-profile feature.
+ Add support for Android Bluetooth aptX audio integration.
+
+ Note: aptX codec not included
+
+ver 5.19:
+ Fix issue with OBEX Put-Delete and Create-Empty methods.
+ Fix issue with AVRCP browsable/searchable player properties.
+ Fix issue with handling multiple default agents.
+ Fix issue with handling unpair event per bearer.
+ Fix issue with HID over GATT report ID presence.
+ Add support for HID protocol handling in userspace.
+ Add support for Bluetooth reconnection policy framework.
+ Add support for Android Bluetooth SCO over HCI transport.
+ Add support for Android Bluetooth audio quality control.
+ Add support for Android Bluetooth Low Energy only mode.
+
+ver 5.18:
+ Fix issue with identifying LE single mode devices.
+ Fix issue with L2CAP and RFCOMM peer address lookup.
+ Add support for handling OBEX authentication procedure.
+ Add support for Android Bluetooth GATT client interface.
+
+ver 5.17:
+ Fix issue with not resetting OBEX SRM setup.
+ Fix issue with BR/EDR devices and auto-connect list.
+ Fix issue with bonding complete detection as peripheral.
+ Fix issue with not updating bearer timestamp of connections.
+ Fix issue with paired property for multiple bearers.
+ Add support for Android Bluetooth Handsfree interface.
+ Add support for Android Bluetooth Wideband speech.
+
+ver 5.16:
+ Fix issue with HID over GATT physical location.
+ Fix issue with HID over GATT unique identifier.
+ Fix issue with missing paired property notification.
+ Fix issue with endianess of long term key storage.
+ Add support for storing signature resolving keys.
+ Add support for Android Bluetooth AVRCP interface.
+
+ver 5.15:
+ Fix issue with LE enabling and background scanning.
+ Fix issue with HID over GATT input device name.
+ Fix issue with storage of slave long term keys.
+ Add support for handling identity resolving keys.
+ Add support for Android Bluetooth A2DP interface.
+ Add support for Android Bluetooth audio interface.
+
+ver 5.14:
+ Fix issue with marking PS3 controllers as trusted.
+ Fix issue with authorization of PS3 controllers.
+ Add support for DualShock 4 controller detection.
+ Add support for legacy pairing emulation.
+ Add support for secure simple pairing emulation.
+ Add support for automated pairing testing.
+ Add support for RFCOMM protocol testing.
+ Add support for HCI controller testing.
+
+ver 5.13:
+ Fix issue with PS3 controller detection.
+ Add support for data transfers to L2CAP testing tool.
+ Add support for delay reporting to AVDTP testing tool.
+ Add support for Android Bluetooth Core interface.
+ Add support for Android Bluetooth Socket interface.
+ Add support for Android Bluetooth HID Host interface.
+ Add support for Android Bluetooth PAN interface.
+
+ver 5.12:
+ Fix issue with missing reply to DisconnectProfile.
+ Fix issue with icon property and class of device changes.
+ Fix issue with HID devices when SDP record is not available.
+ Fix issue with handling auto-pairing of printers.
+ Fix issue with agent authorization handling.
+ Add support for PS3 controller setup and pairing.
+ Add support for LE L2CAP CoC test capabilities.
+ Add support for AVDTP qualification test cases.
+ Add support for SMP cryptographic test cases.
+
+ver 5.11:
+ Fix issue with connection attempt when not powered.
+ Fix issue with assigning player to AVRCP target role.
+ Fix issue with OBEX default cache directory.
+ Fix issue with SDP search error handling.
+ Fix issue with processing of SDP records.
+ Fix issue with HID to HCI switching utility.
+ Fix issue with mgmt end-to-end testing tool.
+ Fix issue with L2CAP end-to-end testing tool.
+ Add support for SMP end-to-end testing tool.
+ Add support for more Wii controllers.
+
+ver 5.10:
+ Fix issue with discoverable timeout handling.
+ Fix issue with MAP messages and record version.
+ Fix issue with MAP messages and status events.
+ Fix issue with MAP messages and relative folders.
+ Fix issue with MAP messages and type property signals.
+ Fix issue with transfer size for OBEX GET operations.
+ Fix issue with AVRCP service class identifier.
+ Fix issue with AVRCP tracking seeked signal.
+ Add support for OBEX command line client.
+
+ver 5.9:
+ Fix issue with network service and adapter removal.
+ Fix issue with misleading OBEX error messages.
+ Fix issue with OBEX transport reference handling.
+ Fix issue with memory leak with MAP event handler.
+ Fix issue with missing MAP property changed signal.
+ Fix issue with message type property values.
+ Fix issue with empty UUID list for devices.
+ Fix issue with profile agent cancel method.
+ Remove dependency on USB library.
+
+ver 5.8:
+ Fix issue with missing OBEX session properties.
+ Fix issue with missing SDP service refresh.
+ Fix issue with SDP attribute range check.
+ Fix issue with priority for SDP transactions.
+ Fix issue with service discovery after pairing.
+ Fix issue with race condition in service list.
+ Fix issue with input service state transition.
+ Fix issue with default authorization for profiles.
+ Fix issue with AVRCP browsing channel connections.
+ Add support for AVRCP role agnostic sessions.
+
+ver 5.7:
+ Fix issue with missing UUID discovery during pairing.
+ Fix issue with broken patch for SDP range check handling.
+ Fix issue with AVRCP usage of UID=0 for paused/stopped.
+ Add support MAP notification dispatching.
+
+ver 5.6:
+ Fix issue with incoming connections without SDP record.
+ Fix issue with canceling ongoing device connections.
+ Fix issue with handling failed connection attempts.
+ Fix issue with pending resume during A2DP open failures.
+ Fix issue with registering AVRCP unsupported notification.
+ Fix issue with listing available AVRCP target settings.
+ Fix issue with missing error for OBEX SetPath commands.
+ Fix issue with missing OBEX session command queue.
+ Fix issue with retrieving multiple MAP event reports.
+ Add support for command line player utility.
+
+ver 5.5:
+ Fix issue with race condition between SDP and properties.
+ Fix issue with handling storage of private device addresses.
+ Fix issue with NFC out-of-band pairing and power states.
+ Fix issue with short name during device update handling.
+ Fix issue with handling AVRCP without A2DP being present.
+ Add support for handling AVRCP pass-through operations.
+ Add support for automatically reconnecting HID devices.
+ Add support for automatically pairing of devices.
+
+ver 5.4:
+ Fix issue with invalid memory access and SDP service search.
+ Add support for available player changed event for controller.
+ Add support for UIDs changed event for AVRCP controller.
+ Add support for mandatory AVRCP pass-through operations.
+ Add support for Message Notification Service (MNS) server.
+ Add support for agent methods within command line client.
+
+ver 5.3:
+ Fix issue with registering invalid profiles.
+ Fix issue with inconsistent A2DP transport state.
+ Fix issue with A2DP resume while in configured state.
+ Fix issue with buffer overflow when processing SDP response.
+ Fix issue with missing range check for SDP attribute response.
+ Fix issue with missing validation of SDP data elements.
+ Fix issue with missing fallback to static hostname.
+ Fix issue with default adapter assignment.
+
+ver 5.2:
+ Fix issue with connection handling for Low Energy.
+ Fix issue with broken device discovery handling.
+ Fix issue with invalid memory access within A2DP.
+ Fix issue with handling empty path name of SetPath.
+ Fix issue with handling Message Access Profile filters.
+ Fix issue with handling network service unregistration.
+ Fix issue with not handling bogus device pairing results.
+ Fix issue with initial service discovery and profile manager.
+ Add support for AVRCP volume notifications.
+ Add support for AVRCP browsing commands.
+
+ver 5.1:
+ Fix issue with crash when removing OBEX session.
+ Fix issue with HID device disconnected from kernel.
+ Fix issue with buffer overflow when parsing HID SDP record.
+ Fix issue with SDP_TEXT_STR16 and SDP_URL_STR16 parsing.
+ Add support for integration with systemd's hostname daemon.
+ Add support for separate adapter alias property.
+ Add support for adapter and device modalias properties.
+ Add support for official BlueZ device information.
+ Add support for asynchronous management interface handling.
+ Add tool for testing management interface compliance.
+ Add tool for testing SDP qualification requirements.
+ Add tool for testing various EIR and AD data records.
+
+ver 5.0:
+ Introduce D-Bus Properties and ObjectManager interfaces.
+ Add support for generic profile interface.
+ Add support for global agent interface.
+ Add support for integrated OBEX daemon.
+ Add support for integrated hcidump utility.
+ Add support for Bluetooth tracing and monitor utility.
+ Add support for Bluetooth command line client utility.
+ Remove support for Handsfree gateway handling.
+ Remove support for GStreamer A2DP and SBC elements.
+ Disable default installation of Bluetooth library.
+
+ver 4.101:
+ Fix issue with missing BlueZ service file.
+ Fix issue with aborting A2DP setup during AVDTP start.
+ Fix issue with handling of multiple A2DP indication.
+ Fix issue with handling AVDTP abort with invalid SEID.
+ Fix issue with rejecting AVDTP abort commands.
+ Add support for handling AVDTP command collision.
+
+ver 4.100:
+ Fix issue with crashing when SCO connection fails.
+ Fix issue with HFP gateway failing on first GSM connection.
+ Fix issue with AVRCP and handling of vendor commands.
+ Fix issue with handling AVRCP subunit info command.
+ Fix issue with missing capability for AVRCP track reached end.
+ Fix issue with AVDTP signaling and GStreamer SBC NULL check.
+ Fix issue with AVDTP Reconfigure Reject message.
+ Fix issue with incorrect EIR length parsing.
+ Fix issue with SDP disconnect for HIDSDPDisable.
+ Fix issue with SDP interoperability with Mac OS X Lion.
+ Fix issue with reverse SDP discovery with some devices.
+ Fix issue with discovering state during power off operation.
+ Add support for AVRCP Volume Changed notifications.
+ Add support for AVRCP Set Absolute Volume handling.
+ Add support for display legacy PIN code agent method.
+ Add support for multiple media transports per endpoint.
+ Add support for discovering device information characteristics.
+ Add support for vendor source for Device ID setting.
+ Add support for immediate alert server.
+ Add support for link loss server.
+
+ Notes:
+ This version requires D-Bus 1.4 or later.
+ This version requires GLib 2.28 or later.
+
+ver 4.99:
+ Fix issue with missing retries for BNEP connection setup.
+ Fix issue with not showing name if first EIR has no details.
+ Fix issue with running SDP discovery for LE devices.
+ Add support for GATT using 128-bit Bluetooth UUIDs.
+ Add support for retrieving key size information.
+ Add support for storing Long Term Keys.
+ Add support for Proximity Reporter API.
+ Add support for KeyboardDisplay IO capability.
+ Add support for version 1.0 of management API.
+ Add support for monitoring interface.
+
+ver 4.98:
+ Fix issue with adapter list upon initialization failure.
+ Fix issue with missing legacy property for Low Energy.
+ Fix issue with missing EIR information handling.
+ Fix issue with device address type tracking.
+ Fix issue with alert level characteristic.
+ Fix issue with headset shutdown handling.
+ Fix issue with Wiimote address handling.
+ Add support for advanced l2test options.
+ Add support for attribute protocol and multiple adapters.
+
+ver 4.97:
+ Update support for proximity profile.
+ Fix issue with SBC audio decoding quality.
+ Fix multiple issues with HFP support.
+ Fix multiple issues with A2DP support.
+ Fix multiple issues with AVDTP support.
+ Fix multiple issues with AVRCP support.
+ Add support for AVRCP meta-data transfer.
+ Add support for Bluetooth based thermometers.
+
+ver 4.96:
+ Fix issue with race condition in AVDTP stream start.
+ Fix issue with global adapter offline switching.
+ Fix issue with pairing and No Bonding devices.
+ Add support for Nintendo Wii Remote pairing.
+
+ver 4.95:
+ Fix issue with AVCTP replies with invalid PID.
+ Fix issue with AVRCP and unknown packet types.
+ Fix issue with AVRCP not using NOT_IMPLEMENTED correctly.
+ Fix issue with AVDTP discovery if all endpoints are in use.
+ Fix issue with invalid memory writes and media support.
+ Fix issue with not removing device alias and unbonding.
+ Fix issue with device disconnects and offline mode handling.
+ Add support for setting adapter name based on machine-info.
+ Add support for systemd service configuration.
+
+ver 4.94:
+ Fix issue with invalid read of memory in various modules.
+ Fix issue with buffer overflow when sending AVDTP commands.
+ Fix issue with response to vendor dependent AVRCP commands.
+ Fix issue with headset when not able to reply with ERROR.
+ Fix issue with crash when creating a device from storage.
+ Fix issue with handling non UTF-8 devices names.
+ Add support for improved discovery procedure.
+
+ver 4.93:
+ Fix issue with property type and Health Main channel.
+ Fix issue with crash when removing devices.
+ Add support for hid2hci and udev integration.
+
+ver 4.92:
+ Fix issue with handling of A2DP suspend response.
+ Fix issue with crashing when acquiring A2DP stream.
+ Fix issue with missing check for valid SCO before shutdown.
+ Fix issue with waiting for POLLERR when disconnecting SCO.
+ Fix issue with disconnect after primary service discovery.
+ Fix issue with attribute interface registration.
+ Add support for primary services over BR/EDR.
+ Add support for flushable packets of A2DP media.
+
+ver 4.91:
+ Fix issue with LMP version string and hciconfig.
+ Fix issue with missing discovery signal when scanning.
+ Fix issue with wrong state and canceling name resolving.
+ Fix issue with missing check during adapter initialization.
+ Fix issue with missing protocol not supported error and A2DP.
+ Fix issue with crash during driver unregistering and A2DP.
+ Fix issue with crash when receiving AVDTP close command.
+ Fix issue with remote SEP handling when A2DP codec changes.
+ Fix issue with SCO hangup handling and state changes.
+ Fix issue with security level and MCAP instances.
+ Fix issue with memory leak and HDP data channels.
+ Add support for discover characteristics by UUID to gatttool.
+ Add initial support for Out-of-Band association model.
+ Add initial support for SIM Access Profile.
+
+ver 4.90:
+ Fix issue with setting of global mode property.
+ Fix issue with handling of RequestSession responses.
+ Fix issue with TP_BNEP_CTRL_BV_01_C qualification test.
+ Fix issue with too short AVDTP request timeout.
+ Add support for SIM Access Profile manager.
+ Add support for new UUID utility functions.
+ Add support for attribute server notifications.
+ Add support for client characteristic configuration.
+ Update support for interactive GATT utility.
+
+ver 4.89:
+ Fix issue with name resolving when discovery is suspended.
+ Fix issue with parsing flags of advertising report.
+ Fix issue with SEP handling if interface is disabled.
+ Fix issue with device object creation on disconnect event.
+ Fix issue with indicators whenever the driver is initialized.
+ Fix issue with call indicator when parsing call info reply.
+ Fix issue with crash and allowed GATT MTU was too large.
+ Add support for SDP record of Primary GATT services.
+ Add support for interactive mode for GATT utility.
+
+ver 4.88:
+ Fix issue with HID channel reference count handling.
+ Fix issue with daemon exit on badly formatted AT+VTS.
+ Fix issue with crash while parsing of endpoint properties.
+ Fix issue with possible crash on AVDTP Suspend request timeout.
+ Fix issue with stopping inquiry before adapter is initialized.
+ Fix issue with creating device object when connection fails.
+ Fix issue with sending HCIDEVUP when adapter is already up.
+ Fix issue with handling bonding IO channel closing.
+ Fix agent cancellation in security mode 3 situations.
+ Update pairing code to support management interface.
+
+ver 4.87:
+ Fix issue with initialization when adapter is already up.
+ Fix issue with attribute server MTU and incoming connections.
+ Fix issue with duplicate characteristics after discovery.
+
+ver 4.86:
+ Revert wrong fix for SDP PDU size error response.
+ Fix various memory leaks in A2DP and AVDTP support.
+ Add Routing property to MediaTransport interface
+ Add proper tracking mechanism to NREC status.
+ Add READ_BLOB_REQUEST support to attribute server.
+
+ver 4.85:
+ Fix issue with event mask setting for older adapters.
+ Fix issue with device creation and pairing failures.
+ Add support for telephony support via oFono.
+ Add support for characteristic security level.
+ Update support for service registration.
+
+ver 4.84:
+ Fix issue with wrong parameters and device found signals.
+ Fix issue with leaking EIR data if RSSI does not change.
+ Fix issue with adapter initialization state.
+ Fix issue with closing of SDP server sockets.
+
+ver 4.83:
+ Fix issue with already connected HFP/HSP endpoints.
+ Fix missing reply when create device is canceled.
+ Fix memory leak within the attribute server.
+ Fix memory leak with unused extended inquiry name.
+ Fix setting paired state when device->authr is false.
+ Fix clearing authentication request for renewed keys.
+ Add support for storing link keys in runtime memory.
+ Update support for primary service discovery.
+
+ver 4.82:
+ Fix crash with mmap of files with multiples of page size.
+ Fix HFP response and hold (AT+BTRH) command response.
+ Fix device creation error response when powered off.
+ Fix device removal when connecting/browsing fails.
+ Add initial attribute permission implementation.
+ Add AVDTP SRC stream send buffer size verification.
+ Add support for setting link policy based on features.
+
+ver 4.81:
+ Fix issue with telephony driver initialization.
+ Fix issue with adapter services list initialization.
+ Fix crash after simultaneous authentication requests.
+ Add support for primary service search on device creation.
+
+ver 4.80:
+ Fix legacy link key storing for some buggy adapters.
+ Fix invalid memory access when EIR field length is zero.
+ Fix adapter initialization to wait for kernel HCI commands.
+ Fix initialization of adapters which are already up.
+ Fix possible race condition when initializing adapters.
+ Fix possible crashes when attempting to connect AVDTP.
+ Fix not aborting sink stream configuration on disconnect.
+ Fix not indicating disconnected state when connecting to AVDTP.
+ Fix not dropping AVDTP session when canceling stream setup.
+ Fix AVDTP abort not being send when the state is idle.
+ Fix regression with Low Energy and interleave discovery.
+ Add a new configuration option to disable Low Energy support.
+ Add iwmmxt optimization for SBC for ARM PXA series CPUs.
+ Update support for GATT Primary Service Discovery.
+ Update MCAP and HDP support.
+
+ver 4.79:
+ Fix issue with adapter initialization race condition.
+ Update new Bluetooth Management interface support.
+
+ver 4.78:
+ Fix various issues with AVDTP timer handling.
+ Fix various issues with handling of mode changes.
+ Fix issue with audio disconnect watch in connecting state.
+ Fix issue with handling call waiting indicators in telephony.
+ Fix issue with handling UUID parameter and RegisterEndpoint.
+ Add initial support for Bluetooth Management interface.
+ Add support for Application property to HealthChannel.
+
+ver 4.77:
+ Fix issue with device name and accessing already freed memory.
+ Fix issue with handling CHLD=0 command for handsfree.
+ Fix issue with manager properties and no adapters.
+ Fix issue with properties and broken service records.
+ Fix issue with A2DP playback and sample rate changes.
+ Update MCAP and HDP support.
+
+ver 4.76:
+ Fix issue in telephony driver with hanging up held call.
+ Fix issue in telephony driver with notifications when on hold.
+ Fix issue with blocking on setconf confirmation callback.
+ Fix issue with not always signaling new streams as sinks.
+ Fix issue with errors in case of endpoint request timeout.
+ Fix issue with HFP/HSP microphone and speaker gain values.
+ Add source if the device attempt to configure local sink stream.
+ Add PSM option for GATT/ATT over BR/EDR on gatttool.
+ Add support for GATT/ATT Attribute Write Request.
+ Update MCAP and HDP support.
+
+ver 4.75:
+ Fix use of uninitialized variable on legacy pairing.
+ Fix mismatch of attribute protocol opcode.
+
+ver 4.74:
+ Fix regression for Legacy Pairing.
+ Fix wrong PSM value for attribute protocol.
+ Fix issue with RSSI field in advertising reports.
+ Add support for Add BR/EDR and LE interleaved discovery.
+ Add support for GATT write characteristic value option.
+ Add support for specifying download address for AR300x.
+
+ver 4.73:
+ Fix problem with EIR data when setting the name.
+ Fix reading local name from command complete event.
+ Fix registering local endpoints with disabled socket interface.
+ Add support for more HCI operations using ops infrastructure.
+ Add support for GATT characteristic hierarchy.
+ Add support for GATT indications.
+
+ver 4.72:
+ Fix memory leak while connecting BTIO channels.
+ Fix crash with GStreamer plugin if SBC is not supported.
+ Fix issue with GATT server stop sending notifications.
+ Fix issue with GATT and dealing with the minimum MTU size.
+ Fix issue with file descriptor leak in GATT client.
+ Add support for UUID 128-bit handling in attribute client.
+ Add support for encoders/decoders for MTU Exchange.
+ Add support for the MTU Exchange procedure to the server.
+ Add support for a per channel MTU to the ATT server.
+ Add support for Characteristic interface.
+ Add support for new Media API and framework.
+ Add initial support for HDP plugin.
+
+ver 4.71:
+ Fix compilation when SBC support in not enabled.
+ Fix crash with RequestSession and application disconnects.
+ Fix memory leak and possible crash when removing audio device.
+ Fix issue with closing stream of locked sep when reconfiguring.
+ Fix issue where discovery could interfere with bonding.
+ Fix issue with Connected status when PS3 BD remote connects.
+ Fix issue with lifetime of fake input devices.
+ Add support for compile time option of oui.txt path.
+ Add support for printing IEEE1284 device ID for CUPS.
+ Add plugin for setting adapter class via DMI.
+ Add more features for attribute protocol and profile.
+ Add initial support for MCAP.
+
+ver 4.70:
+ Fix incoming call indication handling when in WAITING state.
+ Fix various SDP related qualification test case issues.
+ Fix logic to write EIR when SDP records are changed.
+ Fix UTF-8 validity check for remote names in EIR.
+ Add support for UUID-128 extended inquiry response.
+ Add service UUIDs from EIR to the DeviceFound signal.
+ Add fast connectable feature for Handsfree profile.
+ Add HCI command and event definitions for AMP support.
+ Add firmware download support for Qualcommh devices.
+ Add host level support for Atheros AR300x device.
+ Add initial support of ATT and GATT for basic rate.
+
+ver 4.69:
+ Fix issue with calling g_option_context_free() twice.
+ Fix inconsistencies with initial LE commands and events.
+ Add support for telephony ClearLastNumber method.
+ Add support for network server interface.
+
+ver 4.68:
+ Fix initialization of adapters in RAW mode.
+ Fix signal strength for HFP in Maemo's telephony support.
+ Add support for following the radio state via Maemo's MCE.
+ Add initial set of LE commands and events definitions.
+ Add mode option for L2CAP sockets to the BtIO API.
+
+ver 4.67:
+ Fix issue with authentication reply when bonding already completed.
+ Fix issue with not canceling authentication when bonding fails.
+ Fix issue with changed combination keys and temporary storage.
+ Fix issue with sdp_get_supp_feat library function.
+ Fix issue with missing unblock on device removal.
+ Fix issue with not waiting for mode change completion.
+ Add ARMv6 optimized version of analysis filter for SBC encoder.
+
+ver 4.66:
+ Fix regression with full debug enabling via SIGUSR2.
+ Fix redundant speaker/microphone gains being sent.
+ Fix not emitting PropertyChanged for SpeakerGain/MicrophoneGain.
+ Fix issue with storage usage when a record is not found in memory.
+ Fix issue with DiscoverServices not retrieving any records.
+ Fix audio profile disconnection order to match whitepaper.
+ Fix auto-accept confirmation when local agent has NoInputNoOutput.
+ Fix remote just-works SSP when MITM protection is required.
+ Fix performing dedicated bonding without MITM requirement.
+ Add support for storing debug link keys in runtime memory.
+
+ver 4.65:
+ Fix issues with general bonding being default setting now.
+ Fix driver removal upon device removal.
+ Add new "Blocked" property to device objects.
+ Add hciconfig support for blacklisting.
+ Add support for dynamic debug feature.
+
+ver 4.64:
+ Fix invalid memory access in headset_get_nrec function.
+ Fix issue with disconnect event on higher protocol layers.
+ Fix issue with list parsing in sdp_set_supp_features function.
+ Fix device object reference counting for SDP browse requests.
+ Add missing memory checks whenever memory is allocated for SDP.
+ Add support for exporting local services via D-Bus.
+ Add more L2CAP Enhanced Retransmission test options.
+
+ver 4.63:
+ Fix avdtp_abort not canceling pending requests.
+ Fix stale connection when abort gets rejected.
+
+ver 4.62:
+ Fix accidental symbol breakage with inquiry transmit power.
+ Fix using invalid data from previous headset connection.
+ Fix double free on AVDTP Abort response.
+ Fix possible crash while verifying AVDTP version.
+ Fix missing inuse flag when AVDTP stream is configured.
+ Add support for Bluetooth controller types.
+
+ver 4.61:
+ Fix issues with Read Inquiry Response Transmit Power Level.
+ Fix possible invalid read when removing a temporary device.
+ Fix mode restoration when remember_powered is false.
+ Fix conference call releasing in telephony-maemo.
+ Fix segmentation fault with authorization during headset disconnects.
+ Add support for handling unanswered AVDTP request on disconnect.
+ Add support for handling Inquiry Response Transmit Power Level.
+ Add support for caching of remote host features.
+ Add preliminary voice dialing support for HSP.
+
+ver 4.60:
+ Fix voice mailbox number reading from SIM.
+ Fix some races with D-Bus mainloop integration.
+ Add helpers for D-Bus signal watches.
+
+ver 4.59:
+ Add values for Bluetooth 4.0 specification.
+ Add SDP functions for HDP support.
+ Add test scripts for input and audio.
+ Fix missing close on BtIO create_io function.
+ Fix sending incorrect AVDTP commands after timeout occurs.
+ Fix timer removal when device disconnects unexpectedly.
+ Fix Extended Inquiry Response record for Device ID.
+
+ver 4.58:
+ Fix crash when adapter agent exists during authentication.
+ Fix CK-20W quirks for play and pause events.
+
+ver 4.57:
+ Fix unloading of drivers for uninitialized adapters.
+ Fix debug message to use requested and not opened SEID.
+ Fix codec selection for GStreamer plugin.
+ Fix deleting of SDP records during service updates.
+ Fix deleting of SDP records when a device is removed.
+ Fix handling when the SDP record is modified on remote device.
+ Fix potential buffer overflow by using snprintf instead of sprintf.
+ Fix const declarations for some storage function parameters.
+
+ver 4.56:
+ Add missing values from Bluetooth 3.0 specification.
+ Add proper tracking of device paired status.
+ Fix tracking of devices without permanently stored link key.
+ Fix issue with link key removal after connection failures.
+ Fix legacy pairing information based on remote host features.
+ Fix off-by-one issue with AVDTP capability parsing.
+ Fix AVRCP, AVCTP, AVDTP, A2DP and HFP version numbers.
+ Fix agent canceling before calling agent_destroy.
+ Fix service record parsing with an empty UUID list.
+ Fix various SDP related memory leaks.
+
+ver 4.55:
+ Add support for POSIX capabilities dropping.
+ Add special quirk for the Nokia CK-20W car kit.
+ Fix error code handling for AVDTP SetConfiguration response.
+ Fix updating out of range list when RSSI hasn't changed.
+ Fix various memory leaks and unnecessary error checks.
+
+ver 4.54:
+ Add introspection interface to output of introspection calls.
+ Fix stream handling when media transport disconnects prematurely.
+ Fix command timeout handling when there's no stream.
+ Fix headset_suspend_stream behavior for invalid states
+ Fix issue with AVDTP ABORTING state transition.
+ Fix issue with AVDTP suspend while closing.
+
+ver 4.53:
+ Fix issue with telephony connection state notifications.
+ Fix AVDTP stream leak for invalid media transport config.
+ Fix audio connection authorization handling with timeouts.
+ Fix race condition in authorizing audio connections.
+ Fix device authorized setting for AVRCP-only connections.
+ Fix duplicate attempts from device to connect signal channel.
+
+ver 4.52:
+ Add AVCTP support to test utility.
+ Fix AVDTP Abort when transport closes before response.
+ Fix authorization when the audio profiles are slow to connect.
+ Fix potential AVDTP reference leaks.
+
+ver 4.51:
+ Add utility for basic AVDTP testing.
+ Add support for configuring L2CAP FCS option.
+ Fix discovery mode for CUPS 1.4.x and later.
+ Fix global state tracking of audio service.
+ Fix last issues with the new build system.
+
+ver 4.50:
+ Fix issue with missing manual pages in distribution.
+ Fix issue with the configuration and state directories.
+ Fix issue with creating include directory.
+ Fix dependencies of include file generation.
+
+ver 4.49:
+ Add simple test program for basic GAP testing.
+ Add support for confirmation requests to agent example.
+ Add support for full non-recursive build.
+ Add five millisecond delay for Simple Pairing auto-accept.
+ Fix Class of Device setting when InitiallyPowered=false.
+
+ver 4.48:
+ Add library function for comparing UUID values.
+ Add support for creating all plugins as builtins.
+ Add support for async handling of service class changes.
+ Add support for source interface to audio IPC.
+ Fix device name settings when device is off or down.
+ Fix issue with enabled SCO server when not necessary.
+ Fix missing D-Bus access policy for CUPS backend.
+ Fix discovery results of CUPS backend.
+ Fix initialization handling of Maemo telephony.
+
+ver 4.47:
+ Add support for RFKILL unblock handling.
+ Add support for serial proxy configurations.
+ Add support for caching service class updates.
+ Fix issues with updating SDP service records.
+ Fix usage of limited discoverable mode.
+ Remove deprecated methods and signals for AudioSource.
+
+ver 4.46:
+ Add support for A2DP sink role.
+ Fix clearing svc_cache before the adapter is up.
+ Fix various pointer after free usages.
+ Fix various memory leaks.
+
+ver 4.45:
+ Fix UDEV_DATADIR fallback if pkg-config fails.
+ Fix adapter cleanup and setup prototypes.
+ Fix double-free with out-of-range devices.
+ Fix inband ring setting to be per-headset.
+ Fix handling of Maemo CSD startup.
+
+ver 4.44:
+ Add some missing manual pages.
+ Fix missing number prefix when installing udev rules.
+ Fix program prefix used in Bluetooth udev rules.
+ Fix three-way calling indicator order.
+ Fix downgrade/upgrade of callheld indicator.
+ Fix +CIEV sending when indicator value changes.
+ Fix signal handling for Maemo telephony driver.
+ Fix parsing issues with messages from Maemo CSD.
+ Fix issue with duplicate active calls.
+
+ver 4.43:
+ Add support for udev based on-demand startup.
+ Fix verbose error reporting of CUPS backend.
+ Fix various string length issues.
+ Fix issues with Maemo telephony driver.
+ Fix another device setup and temporary flag issue.
+ Fix and update example agent implementation.
+
+ver 4.42:
+ Add TI WL1271 to Texas Instruments chip list.
+ Add special udev mode to bluetoothd.
+ Fix regression when there is no agent registered.
+ Fix error return when bonding socket hang up.
+ Fix SCO server socket for HFP handsfree role.
+ Fix shutdown on SCO socket before closing.
+ Fix shutdown on A2DP audio stream channel before closing.
+ Fix issue with asserting on AVDTP reference count bugs.
+ Fix authorization denied issue with certain headsets.
+ Fix AVRCP UNITINFO and SUBUNIT INFO responses.
+ Fix discovery cancel issues in case SDP discovery fails.
+
+ver 4.41:
+ Fix pairing even if the ACL gets dropped before successful SDP.
+ Fix regression which caused device to be removed after pairing.
+ Fix HSP record fetching when remote device doesn't support it.
+ Fix SDP discovery canceling when clearing hs->pending.
+ Fix headset never connecting on the first attempt.
+ Fix headset state tracking if bt_search_service() fails.
+ Fix maximum headset connection count check.
+ Fix AVDTP Discover timeout handling.
+ Fix also UI_SET_KEYBIT for the new pause and play key codes.
+
+ver 4.40:
+ Add telephony driver for oFono telephony stack.
+ Add support for Dell specific HID proxy switching.
+ Add support for running hid2hci from udev.
+ Add mapping for AVRCP Play and Pause to dedicated key codes.
+ Fix AVRCP keycodes to better match existing X keymap support.
+ Fix various quoting issues within telephony support.
+ Fix memory allocation issue when generating PDUs for SDP.
+ Fix race condition on device removal.
+ Fix non-cancelable issue with CreateDevice method.
+ Fix non-working CancelDiscovery method call.
+
+ver 4.39:
+ Add workaround for dealing with unknown inquiry complete.
+ Fix discovering when using software scheduler.
+ Fix wrong NoInputNoOutput IO capability string.
+ Fix race condition with agent during pairing.
+ Fix agent cancellation for security mode 3 acceptor failure.
+ Fix temporary flag removal when device creation fails.
+ Fix hciattach to use ppoll instead of poll.
+ Fix service class update when adapter is down.
+ Fix service classes race condition during startup.
+ Fix release of audio client before freeing the device.
+
+ver 4.38:
+ Add support for builtin plugins.
+ Add framework for adapter operations.
+ Add constants for Enhanced Retransmission modes.
+ Fix HCI socket leak in device_remove_bonding.
+ Fix various format string issues.
+ Fix crashes with various free functions.
+ Fix issues with Headset and A2DP drivers to load again.
+ Fix sending AVRCP button released passthrough messages
+ Fix bug which prevent input devices to work after restart.
+ Fix issue with interpretation of UUID-128 as channel.
+
+ver 4.37:
+ Add version value for Bluetooth 3.0 devices.
+ Add additional L2CAP extended feature mask bits.
+ Add support for loading plugins in priority order.
+ Add support for more detailed usage of disconnect watches.
+ Add support for AVRCP volume control.
+ Add saturated clipping of SBC decoder output to 16-bit.
+ Fix potentially infinite recursion of adapter_up.
+ Fix SCO handling in the case of an incoming call.
+ Fix input service to use confirm callback.
+ Fix cleanup of temporary device entries from storage.
+
+ver 4.36:
+ Add proper tracking of AVCTP connect attempts.
+ Add support to channel pattern in Serial interface.
+ Fix A2DP sink crash if removing device while connecting.
+ Fix error handling if HFP indicators aren't initialized.
+ Fix segfault while handling an incoming SCO connection.
+ Fix Serial.Disconnect to abort connection attempt.
+
+ver 4.35:
+ Add support for Handsfree profile headset role.
+ Add additional checks for open SEIDs from clients.
+ Fix device removal while audio IPC client is connected.
+ Fix device removal when an authorization request is pending.
+ Fix incoming AVDTP connect while authorization in progress.
+ Fix disconnection timers for audio support.
+ Fix various potential NULL pointer deferences.
+ Fix callheld indicator value for multiple calls.
+ Fix voice number type usage.
+ Fix GDBus watch handling.
+
+ver 4.34:
+ Add support for version checks of plugins.
+ Add support for class property on adapter interface.
+ Add support for second SDP attempt after connection reset.
+ Add support for more detailed audio states.
+ Add support for HFP+A2DP auto connection feature.
+ Add support for new and improved audio IPC.
+ Add program for testing audio IPC interface.
+ Fix various AVDTP qualification related issues.
+ Fix broken SDP AttributeIdList parsing.
+ Fix invalid memory access of SDP URL handling.
+ Fix local class of device race conditions.
+ Fix issue with periodic inquiry on startup.
+ Fix missing temporary devices in some situations.
+ Fix SBC alignment issue for encoding with four subbands.
+
+ver 4.33:
+ Add Paired property to the DeviceFound signals.
+ Add support for Headset profile 1.2 version.
+ Fix broken network configuration when IPv6 is disabled.
+ Fix network regression that caused disconnection.
+ Fix SDP truncation of strings with NULL values.
+ Fix service discovery handling of CUPS helper.
+
+ver 4.32:
+ Fix broken SDP record handling.
+ Fix SDP data buffer parsing.
+ Fix more SDP memory leaks.
+ Fix read scan enable calls.
+ Fix A2DP stream handling.
+
+ver 4.31:
+ Add support for new BtIO helper library.
+ Fix AVDTP session close issue.
+ Fix SDP memory leaks.
+ Fix various uninitialized memory issues.
+ Fix duplicate signal emissions.
+ Fix property changes request handling.
+ Fix class of device storage handling.
+
+ver 4.30:
+ Add CID field to L2CAP socket address structure.
+ Fix reset of authentication requirements after bonding.
+ Fix storing of link keys when using dedicated bonding.
+ Fix storing of pre-Bluetooth 2.1 link keys.
+ Fix resetting trust settings on every reboot.
+ Fix handling of local name changes.
+ Fix memory leaks in hciconfig and hcitool
+
+ver 4.29:
+ Use AVRCP version 1.0 for now.
+ Decrease AVDTP idle timeout to one second.
+ Delay AVRCP connection when remote device connects A2DP.
+ Add workaround for AVDTP stream setup with broken headsets.
+ Add missing three-way calling feature bit for Handsfree.
+ Fix handsfree callheld indicator updating.
+ Fix parsing of all AT commands within the buffer.
+ Fix authentication replies when disconnected.
+ Fix handling of debug combination keys.
+ Fix handling of changed combination keys.
+ Fix handling of link keys when using no bonding.
+ Fix handling of invalid/unknown authentication requirements.
+ Fix closing of L2CAP raw socket used for dedicated bonding.
+
+ver 4.28:
+ Add AVDTP signal fragmentation support.
+ Add more SBC performance optimizations.
+ Add more SBC audio quality improvements.
+ Use native byte order for audio plugins.
+ Set the adapter alias only after checking the EIR data.
+ Fix auto-disconnect issue with explicit A2DP connections.
+ Fix invalid memory access of ALSA plugin.
+ Fix compilation with -Wsign-compare.
+
+ver 4.27:
+ Add more SBC optimization (MMX and ARM NEON).
+ Add BT_SECURITY and BT_DEFER_SETUP definitions.
+ Add support for deferred connection setup.
+ Add support for fragmentation of data packets.
+ Add option to trigger dedicated bonding.
+ Follow MITM requirements from remote device.
+ Require MITM for dedicated bonding if capabilities allow it.
+ Fix IO capabilities for non-pairing and pairing cases.
+ Fix no-bonding connections in non-bondable mode.
+ Fix new pairing detection with SSP.
+ Fix bonding with pre-2.1 devices and newer kernels.
+ Fix LIAC setting while toggling Pairable property.
+ Fix device creation for incoming security mode 3 connects.
+ Fix crash within A2DP with bogus pointer.
+ Fix issue with sdp_copy_record() function.
+ Fix crash with extract_des() if sdp_uuid_extract() fails.
+
+ver 4.26:
+ Use of constant shift in SBC quantization code.
+ Add possibility to analyze 4 blocks at once in encoder.
+ Fix correct handling of frame sizes in the encoder.
+ Fix for big endian problems in SBC codec.
+ Fix audio client socket to always be non-blocking.
+ Update telephony support for Maemo.
+
+ver 4.25:
+ Fix receiving data over the audio control socket.
+ Fix subbands selection for joint-stereo in SBC encoder.
+ Add new SBC analysis filter function.
+
+ver 4.24:
+ Fix signal emissions when removing adapters.
+ Fix missing adapter signals on exit.
+ Add support for bringing adapters down on exit.
+ Add support for RememberPowered option.
+ Add support for verbose compiler warnings.
+ Add more options to SBC encoder.
+
+ver 4.23:
+ Update audio IPC for better codec handling.
+ Fix bitstream optimization for SBC encoder.
+ Fix length header values of IPC messages.
+ Fix multiple coding style violations.
+ Fix FindDevice to handle temporary devices.
+ Add configuration option for DeviceID.
+ Add support for InitiallyPowered option.
+ Add missing signals for manager properties.
+ Add telephony support for Maemo.
+
+ver 4.22:
+ Add deny statements to D-Bus access policy.
+ Add support for LegacyPairing property.
+ Add support for global properties.
+ Add more commands to telephony testing script.
+ Add sender checks for serial and network interfaces.
+ Remove deprecated methods and signals from input interface.
+ Remove deprecated methods and signals from network interface.
+ Remove OffMode option and always use device down.
+
+ver 4.21:
+ Fix adapter initialization logic.
+ Fix adapter setup and start security manager early.
+ Fix usage issue with first_init variable.
+
+ver 4.20:
+ Cleanup session handling.
+ Cleanup mode setting handling.
+ Fix issue with concurrent audio clients.
+ Fix issue with HFP/HSP suspending.
+ Fix AT result code syntax handling.
+ Add Handsfree support for AT+NREC.
+ Add PairableTimeout adapter property.
+
+ver 4.19:
+ Fix installation of manual pages for old daemons.
+ Fix D-Bus signal emmissions for CreateDevice.
+ Fix issues with UUID probing.
+ Fix +BSRF syntax issue.
+ Add Pairable adapter property.
+ Add sdp_copy_record() library function.
+
+ver 4.18:
+ Fix release before close issue with RFCOMM TTYs.
+ Fix Connected property on input interface.
+ Fix DeviceFound signals during initial name resolving.
+ Fix service discovery handling.
+ Fix duplicate UUID detection.
+ Fix SBC gain mismatch and decoding handling.
+ Add more options to SBC encoder and decoder.
+ Add special any adapter object for service interface.
+ Add variable prefix to adapter and device object paths.
+
+ver 4.17:
+ Fix SBC encoder not writing last frame.
+ Fix missing timer for A2DP suspend.
+ Add more supported devices to hid2hci utility.
+ Add additional functionality to Handsfree support.
+
+ver 4.16:
+ Fix wrong parameter usage of watch callbacks.
+ Fix parameters for callback upon path removal.
+ Fix unloading of adapter drivers.
+
+ver 4.15:
+ Fix various A2DP state machine issues.
+ Fix some issues with the Handsfree error reporting.
+ Fix format string warnings with recent GCC versions.
+ Remove dependency on GModule.
+
+ver 4.14:
+ Fix types of property arrays.
+ Fix potential crash with input devices.
+ Fix PS3 BD remote input event generation.
+ Allow dynamic adapter driver registration.
+ Update udev rules.
+
+ver 4.13:
+ Fix service discovery and UUID handling.
+ Fix bonding issues with Simple Pairing.
+ Fix file descriptor misuse of SCO connections.
+ Fix various memory leaks in the device handling.
+ Fix AVCTP disconnect handling.
+ Fix GStreamer modes for MP3 encoding.
+ Add operator selection to Handsfree support.
+
+ver 4.12:
+ Fix crash with missing icon value.
+ Fix error checks of HAL plugin.
+ Fix SCO server socket cleanup on exit.
+ Fix memory leaks from DBusPendingCall.
+ Fix handling of pending authorization requests.
+ Fix missing protocol UUIDs in record pattern.
+
+ver 4.11:
+ Change SCO server socket into a generic one.
+ Add test script for dummy telephony plugin.
+ Fix uninitialized reply of multiple GetProperties methods.
+
+ver 4.10:
+ Fix memory leaks with HAL messages.
+ Add more advanced handsfree features.
+ Add properties to audio, input and network interfaces.
+ Stop device discovery timer on device removal.
+
+ver 4.9:
+ Fix signals for Powered and Discoverable properties.
+ Fix handling of Alias and Icon properties.
+ Fix duplicate entries for service UUIDs.
+
+ver 4.8:
+ Fix retrieving of formfactor value.
+ Fix retrieving of local and remote extended features.
+ Fix potential NULL pointer dereference during pairing.
+ Fix crash with browsing due to a remotely initated pairing.
+
+ver 4.7:
+ Fix pairing and service discovery logic.
+ Fix crashes during suspend and resume.
+ Fix race condition within devdown mode.
+ Add RequestSession and ReleaseSession methods.
+ Add Powered and Discoverable properties.
+ Add Devices property and deprecate ListDevices.
+ Add workaround for a broken carkit from Nokia.
+
+ver 4.6:
+ Fix Device ID record handling.
+ Fix service browsing and storage.
+ Fix authentication and encryption for input devices.
+ Fix adapter name initialization.
+
+ver 4.5:
+ Fix initialization issue with new adapters.
+ Send HID authentication request without blocking.
+ Hide the verbose SDP debug behind SDP_DEBUG.
+ Add extra UUIDs for service discovery.
+ Add SCO server socket listener.
+ Add authorization support to service plugin.
+
+ver 4.4:
+ Add temporary fix for the CUPS compile issue.
+ Add service-api.txt to distribution.
+ Mention the variable prefix of an object path
+
+ver 4.3:
+ Add dummy driver for telephony support.
+ Add support for discovery sessions.
+ Add service plugin for external services.
+ Various cleanups.
+
+ver 4.2:
+ Avoid memory copies in A2DP write routine.
+ Fix broken logic with Simple Pairing check and old kernels.
+ Allow non-bondable and outgoing SDP without agent.
+ Only remove the bonding for non-temporary devices.
+ Cleanup various unnecessary includes.
+ Make more unexported functions static.
+ Add basic infrastructure for gtk-doc support.
+
+ver 4.1:
+ Add 30 seconds timeout to BNEP connection setup phase.
+ Avoid memory copies in A2DP write routine for ALSA.
+ Make sure to include compat/sdp.h in the distribution.
+
+ver 4.0:
+ Initial public release.
+
+ver 3.36:
+ Add init routines for TI BRF chips.
+ Add extra attributes to the serial port record.
+ Add example record for headset audio gateway record.
+ Use Handsfree version 0x0105 for the gateway role.
+ Fix SDP record registration with specific record handles.
+ Fix BCSP sent/receive handling.
+ Fix various includes for cross-compilation.
+ Allow link mode settings for outgoing connections.
+ Allow bonding during periodic inquiry.
+
+ver 3.35:
+ Add two additional company identifiers.
+ Add UUID-128 support for service discovery.
+ Fix usage of friendly names for service discovery.
+ Fix authorization when experiemental is disabled.
+ Fix uninitialized variable in passkey request handling.
+ Enable output of timestamps for l2test and rctest.
+
+ver 3.34:
+ Replace various SDP functions with safe versions.
+ Add additional length validation for incoming SDP packets.
+ Use safe function versions for SDP client handling.
+ Fix issue with RemoveDevice during discovery procedure.
+ Fix collect for non-persistent service records.
+
+ver 3.33:
+ Add functions for reading and writing the link policy settings.
+ Add definition for authentication requirements.
+ Add support for handling Simple Pairing.
+ Add Simple Pairing support to Agent interface.
+ Add ReleaseMode method to Adapter interface.
+ Add DiscoverServices method to Device interface.
+ Remove obsolete code and cleanup the repository.
+ Move over to use the libgdbus API.
+ Enable PIE by default if supported.
+
+ver 3.32:
+ Add OCF constants for synchronous flow control enabling.
+ Add support for switching HID proxy devices from Dell.
+ Add more Bluetooth client/server helper functions.
+ Add support for input service idle timeout option.
+ Fix BNEP reconnection handling.
+ Fix return value for snd_pcm_hw_params() calls.
+ Use upper-case addresses for object paths.
+ Remove HAL support helpers.
+ Remove inotify support.
+ Remove service daemon activation handling.
+ Remove uneeded D-Bus API extension.
+
+ver 3.31:
+ Create device object for all pairing cases.
+ Convert authorization to internal function calls.
+ Add initial support for Headset Audio Gateway role.
+ Add generic Bluetooth helper functions for GLib.
+ Fix endiannes handling of connection handles.
+ Don't optimize when debug is enabled.
+
+ver 3.30:
+ Convert audio service into a plugin.
+ Convert input service into a plugin.
+ Convert serial service into a plugin.
+ Convert network service into a plugin.
+ Emit old device signals when a property is changed.
+ Fix missing DiscoverDevices and CancelDiscovery methods.
+ Add another company identifier.
+ Add basic support for Bluetooth sessions.
+ Add avinfo utility for AVDTP/A2DP classification.
+ Remove build option for deprecated sdpd binary.
+
+ver 3.29:
+ Introduce new D-Bus based API.
+ Add more SBC optimizations.
+ Add support for PS3 remote devices.
+ Fix alignment trap in SDP server.
+ Fix memory leak in sdp_get_uuidseq_attr function.
+
+ver 3.28:
+ Add support for MCAP UUIDs.
+ Add support for role switch for audio service.
+ Add disconnect timer for audio service.
+ Add disconnect detection to ALSA plugin.
+ Add more SBC optimizations.
+ Fix alignment issue of SDP server.
+ Remove support for SDP parsing via expat.
+
+ver 3.27:
+ Update uinput.h with extra key definitions.
+ Add support for input connect/disconnect callbacks.
+ Add ifdefs around some baud rate definitions.
+ Add another company identifier.
+ Add proper HFP service level connection handling.
+ Add basic headset automatic disconnect support.
+ Add support for new SBC API.
+ Fix SBC decoder noise at high bitpools.
+ Use 32-bit multipliers for further SBC optimization.
+ Check for RFCOMM connection state in SCO connect callback.
+ Make use of parameters selected in ALSA plugin.
+
+ver 3.26:
+ Fix compilation issues with UCHAR_MAX, USHRT_MAX and UINT_MAX.
+ Improve handling of different audio transports.
+ Enable services by default and keep old daemons disabled.
+
+ver 3.25:
+ Add limited support for Handsfree profile.
+ Add limited support for MPEG12/MP3 codec.
+ Add basic support for UNITINFO and SUBUNITINFO.
+ Add more SBC optimizations.
+ Fix external service (un)registration.
+ Allow GetInfo and GetAddress to fail.
+
+ver 3.24:
+ Add definitions for MDP.
+ Add TCP connection support for serial proxy.
+ Add fix for Logitech HID proxy switching.
+ Add missing macros, MIN, MAX, ABS and CLAMP.
+ Add more SBC encoder optimizations.
+ Add initial mechanism to handle headset commands.
+ Fix connecting to handsfree profile headsets.
+ Use proper function for checking signal name.
+
+ver 3.23:
+ Fix remote name request handling bug.
+ Fix key search function to honor the mmap area size.
+ Fix Avahi integration of network service.
+ Add new plugin communication for audio service.
+ Enable basic AVRCP support by default.
+ More optimizations to the SBC library.
+ Create common error definitions.
+
+ver 3.22:
+ Add missing include file from audio service.
+ Add SBC conformance test utility.
+ Add basic uinput support for AVRCP.
+ Fix L2CAP socket leak in audio service.
+ Fix buffer usage in GStreamer plugin.
+ Fix remote name request event handling.
+
+ver 3.21:
+ Add constant for Bluetooth socket options level.
+ Add initial AVRCP support.
+ Add A2DP sink support to GStreamer plugin.
+ Fix interoperability with A2DP suspend.
+ Fix sign error in 8-subband encoder.
+ Fix handling of service classes length size.
+ Store Extended Inquiry Response data information.
+ Publish device id information through EIR.
+ Support higher baud rates for Ericcson based chips.
+
+ver 3.20:
+ Fix GStreamer plugin file type detection.
+ Fix potential infinite loop in inotify support.
+ Fix D-Bus signatures for dict handling.
+ Fix issues with service activation.
+ Fix SDP failure handling of audio service.
+ Fix various memory leaks in input service.
+ Add secure device creation method to input service.
+ Add service information methods to serial service.
+ Add config file support to network service.
+ Add scripting capability to network service.
+ Add special on-mode handling.
+ Add optimization for SBC encoder.
+ Add tweaks for D-Bus 1.1.x libraries.
+ Add support for inquiry transmit power level.
+
+ver 3.19:
+ Limit range of bitpool announced while in ACP side.
+ Use poll instead of usleep to wait for worker thread.
+ Use default event mask from the specification.
+ Add L2CAP mode constants.
+ Add HID proxy support for Logitech diNovo Edge dongle.
+ Add refresh option to re-request device names.
+ Show correct connection link type.
+
+ver 3.18:
+ Don't allocate memory for the Bluetooth base UUID.
+ Implement proper locking for headsets.
+ Fix various A2DP SEP locking issues.
+ Fix and cleanup audio stream handling.
+ Fix stream starting if suspend request is pending.
+ Fix A2DP and AVDTP endianess problems.
+ Add network timeout and retransmission support.
+ Add more detailed decoding of EIR elements.
+
+ver 3.17:
+ Fix supported commands bit calculation.
+ Fix crashes in audio and network services.
+ Check PAN source and destination roles.
+ Only export the needed symbols for the plugins.
+
+ver 3.16:
+ Update company identifier list.
+ Add support for headsets with SCO audio over HCI.
+ Add support for auto-create through ALSA plugin.
+ Add support for ALSA plugin parameters.
+ Add GStreamer plugin with SBC decoder and encoder.
+ Fix network service NAP, GN and PANU servers.
+ Set EIR information from SDP database.
+
+ver 3.15:
+ Add A2DP support to the audio service.
+ Add proxy support to the serial service.
+ Extract main service class for later use.
+ Set service classes value from SDP database.
+
+ver 3.14:
+ Add missing signals for the adapter interface.
+ Add definitions and functions for Simple Pairing.
+ Add basic commands for Simple Pairing.
+ Add correct Simple Pairing and EIR interaction.
+ Add missing properties for remote information.
+ Add EPoX endian quirk to the input service.
+ Fix HID descriptor import and storage functions.
+ Fix handling of adapters in raw mode.
+ Fix remote device listing methods.
+
+ver 3.13:
+ Fix some issues with the headset support.
+ Fix concurrent pending connection attempts.
+ Fix usage of devname instead of netdev.
+ Add identifier for Nokia SyncML records.
+ Add command for reading the CSR chip revision.
+ Add generic CSR radio test support.
+ Update HCI command table.
+
+ver 3.12:
+ Add missing HCI command text descriptions
+ Add missing HCI commands structures.
+ Add missing HCI event structures.
+ Add common bachk() function.
+ Add support for limited discovery mode.
+ Add support for setting of event mask.
+ Add GetRemoteServiceIdentifiers method.
+ Add skeleton for local D-Bus server.
+ Add headset gain control methods.
+ Fix various headset implementation issues.
+ Fix various serial port service issues.
+ Fix various input service issues.
+ Let CUPS plugin discover printers in range.
+ Improve the BCM2035 UART init routine.
+ Ignore connection events for non-ACL links.
+
+ver 3.11:
+ Update API documentation.
+ Minimize SDP root records and browse groups.
+ Use same decoder for text and URL strings.
+ Fix URL data size handling.
+ Fix SDP pattern extraction for XML.
+ Fix network connection persistent state.
+ Add network connection helper methods.
+ Add initial version of serial port support.
+ Add class of device tracking.
+
+ver 3.10.1:
+ Add option to disable installation of manual pages.
+ Fix input service encryption setup.
+ Fix serial service methods.
+ Fix network service connection handling.
+ Provide a simple init script.
+
+ver 3.10:
+ Add initial version of network service.
+ Add initial version of serial service.
+ Add initial version of input service.
+ Add initial version of audio service.
+ Add authorization framework.
+ Add integer based SBC library.
+ Add version code for Bluetooth 2.1 specification.
+ Add ESCO_LINK connection type constant.
+ Export sdp_uuid32_to_uuid128() function.
+
+ver 3.9:
+ Add RemoteDeviceDisconnectRequested signal.
+ Add updated service framework.
+ Add embedded GLib library.
+ Add support for using system GLib library.
+ Create internal SDP server library.
+
+ver 3.8:
+ Sort discovered devices list based on their RSSI.
+ Send DiscoverableTimeoutChanged signal.
+ Fix local and remote name validity checking.
+ Add ListRemoteDevices and ListRecentRemoteDevices methods.
+ Add basic integration of confirmation concept.
+ Add support for service record description via XML.
+ Add support for external commands to the RFCOMM utility.
+ Add experimental service and authorization API.
+ Add functions for registering binary records.
+
+ver 3.7:
+ Fix class of device handling.
+ Fix error replies with pairing and security mode 3.
+ Fix disconnect method for RFCOMM connections.
+ Add match pattern for service searches.
+ Add support for prioritized watches.
+ Add additional PDU length checks.
+ Fix CSRC value for partial responses.
+
+ver 3.6.1:
+ Fix IO channel race conditions.
+ Fix pairing issues on big endian systems.
+ Fix pairing issues with page timeout errors.
+ Fix pairing state for security mode 3 requests.
+ Switch to user as default security manager mode.
+
+ver 3.6:
+ Update D-Bus based RFCOMM interface support.
+ Use L2CAP raw sockets for HCI connection creation.
+ Add periodic discovery support to the D-Bus interface.
+ Add initial support for device names via EIR.
+ Add proper UTF-8 validation of device names.
+ Add support for the J-Three keyboard.
+ Fix issues with the asynchronous API for SDP.
+
+ver 3.5:
+ Fix and cleanup watch functionality.
+ Add support for periodic inquiry mode.
+ Add support for asynchronous SDP requests.
+ Add more request owner tracking.
+ Add asynchronous API for SDP.
+ Document pageto and discovto options.
+
+ver 3.4:
+ Improve error reporting for failed HCI commands.
+ Improve handling of CancelBonding.
+ Fixed bonding reply message when disconnected.
+ Fix UUID128 string lookup handling.
+ Fix malloc() versus bt_malloc() usage.
+
+ver 3.3:
+ Don't change inquiry mode for Bluetooth 1.1 adapters.
+ Add udev rules for Bluetooth serial PCMCIA cards.
+ Add Cancel and Release methods for passkey agents.
+ Add GetRemoteClass method.
+ Convert to using ppoll() and pselect().
+ Initialize allocated memory to zero.
+ Remove bcm203x firmware loader.
+ Remove kernel specific timeouts.
+ Add additional private data field for SDP sessions.
+ Add host controller to host flow control defines.
+ Add host number of completed packets defines.
+ Initialize various memory to zero before usage.
+
+ver 3.2:
+ Only check for the low-level D-Bus library.
+ Update possible device minor classes.
+ Fix timeout for pending reply.
+ Add more Inquiry with RSSI quirks.
+ Sleep only 100 msecs for device detection.
+ Don't send BondingCreated on link key renewal.
+ Allow storing of all UTF-8 remote device names.
+ Create storage filenames with a generic function.
+ Fix handling of SDP strings.
+ Add adapter type for SDIO cards.
+ Add features bit for link supervision timeout.
+
+ver 3.1:
+ Add missing placeholders for feature bits.
+ Fix handling of raw mode devices.
+ Fix busy loop in UUID extraction routine.
+ Remove inquiry mode setting.
+ Remove auth and encrypt settings.
+
+ver 3.0:
+ Implement the new BlueZ D-Bus API.
+ Fix broken behavior with EVT_CMD_STATUS.
+ Add features bit for pause encryption.
+ Add additional EIR error code.
+ Add more company identifiers.
+ Add another Phonebook Access identifier.
+ Update sniff subrating data structures.
+
+ver 2.25:
+ Use %jx instead of %llx for uint64_t and int64_t.
+ Allow null-terminated text strings.
+ Add UUID for N-Gage games.
+ Add UUID for Apple Macintosh Attributes.
+ Add Apple attributes and iSync records.
+ Add definitions for Apple Agent.
+ Add support for the Handsfree Audio Gateway service.
+ Add support for choosing a specific record handle.
+ Add support for dialup/telephone connections.
+ Add definitions for Apple Agent.
+ Add support for record handle on service registration.
+
+ver 2.24:
+ Fix display of SDP text and data strings.
+ Add support for device scan property.
+ Add support for additional access protocols.
+ Update the D-Bus policy configuration file.
+
+ver 2.23:
+ Update the new D-Bus interface.
+ Make dfutool ready for big endian architectures.
+ Add support for AVRCP specific service records.
+ Add support for writing complex BCCMD commands.
+ Add the new BCCMD interface utility.
+ Add MicroBCSP implementation from CSR.
+ Add constants and definitions for sniff subrating.
+ Add support for allocation of binary text elements.
+ Add HCI emulation tool.
+ Add fake HID support for old EPoX presenters.
+ Reject connections from unknown HID devices.
+ Fix service discovery deadlocks with Samsung D600 phones.
+
+ver 2.22:
+ Remove D-Bus 0.23 support.
+ Add initial version of the new D-Bus interface.
+ Add support for extended inquiry response commands.
+ Add support for the Logitech diNovo Media Desktop Laser.
+ Add compile time buffer checks (FORTIFY SOURCE).
+ Decode reserved LMP feature bits.
+ Fix errno overwrite problems.
+ Fix profile descriptor problem with Samsung phones.
+
+ver 2.21:
+ Move create_dirs() and create_file() into the textfile library.
+ Let textfile_put() also replace the last key value pair.
+ Fix memory leaks with textfile_get() usage.
+ Fix infinite loops and false positive matches.
+ Don't retrieve stored link keys for RAW devices.
+ Document the putkey and delkey commands.
+ Show supported commands also in clear text.
+ Support volatile changes of the BD_ADDR for CSR chips.
+ Add support for identification of supported commands.
+ Add missing OCF declarations for the security filter.
+ Add two new company identifiers.
+
+ver 2.20:
+ Add UUIDs for video distribution profile.
+ Add UUIDs for phonebook access profile.
+ Add attribute identifier for supported repositories.
+ Add definitions for extended inquiry response.
+ Add functions for extended inquiry response.
+ Add support for extended inquiry response.
+ Add support for HotSync service record.
+ Add support for ActiveSync service record.
+ Add ActiveSync networking support.
+ Fix D-Bus crashes with new API versions.
+
+ver 2.19:
+ Fix the GCC 4.0 warnings.
+ Fix the routing for dealing with raw devices.
+ Fix off by one memory allocation error.
+ Fix security problem with escape characters in device name.
+ Add per device service record functions.
+ Send D-Bus signals for inquiry results and remote name resolves.
+ Add support for device specific SDP records.
+
+ver 2.18:
+ Support D-Bus 0.23 and 0.33 API versions.
+ Support reading of complex BCCMD values.
+ Support minimum and maximum encryption key length.
+ Add support for reading and writing the inquiry scan type.
+ Add definitions for connection accept timeout and scan enable.
+ Add support for inquiry scan type.
+ Add tool for the CSR BCCMD interface.
+ Add first draft of the Audio/Video control utility.
+ Add disconnect timer support for the A2DP ALSA plugin.
+ Make SBC parameters configurable.
+ Replace non-printable characters in device names.
+ Remove hci_vhci.h header file.
+ Remove hci_uart.h header file.
+
+ver 2.17:
+ Set the storage directory through ${localstatedir}.
+ Add the textfile library for ASCII based file access.
+ Add support for return link keys event.
+ Add support for voice setting configuration.
+ Add support for page scan timeout configuration.
+ Add support for storing and deleting of stored link keys.
+ Add support for searching for services with UUID-128.
+ Add support for retrieving all possible service records.
+ Add support for a raw mode view of service records.
+ Add support for HID information caching in hidd.
+ Add support for authentication in pand and dund.
+ Add support for changing BD_ADDR of CSR chips.
+ Add pskey utility for changing CSR persistent storage values.
+ Add the firmware upgrade utility.
+ Add connection caching for the A2DP ALSA plugin.
+ Add functions for stored link keys.
+ Add definitions for PIN type and unit key.
+ Add SDP_WAIT_ON_CLOSE flag for sdp_connect().
+ Include stdio.h in bluetooth.h header file.
+ Include sys/socket.h in the header files.
+
+ver 2.16:
+ Store link keys in ASCII based file format.
+ Support device name caching.
+ Support zero length data sizes in l2test.
+ Change default l2ping data size to 44 bytes.
+ Hide the server record and the public browse group root.
+ Read BD_ADDR if not set and if it is a raw device.
+ Add SDP language attributes.
+ Add support for browsing the L2CAP group.
+ Add support for stored pin codes for outgoing connections.
+ Add support for local commands and extended features.
+ Add support for reading CSR panic and fault codes.
+ Add config option for setting the inquiry mode.
+ Add OUI decoding support.
+ Use unlimited inquiry responses as default.
+ Use cached device names for PIN request.
+ Use the clock offset when getting the remote names.
+ Add function for reading local supported commands.
+ Add function for reading local extended features.
+ Add function for reading remote extended features.
+ Add function for getting the remote name with a clock offset.
+ Add function for extracting the OUI from a BD_ADDR.
+ Add inquiry info structure with RSSI and page scan mode.
+ Fix buffer allocation for features to string conversion.
+ Support inquiry with unlimited number of responses.
+
+ver 2.15:
+ Enable the RFCOMM service level security.
+ Add deprecated functions for reading the name.
+ Add command for reading the clock offset.
+ Add command for reading the clock.
+ Add function for reading the clock.
+ Add function for reading the local Bluetooth address.
+ Add function for reading the local supported features.
+ Don't configure raw devices.
+ Don't set inquiry scan or page scan on raw devices.
+ Don't show extended information for raw devices.
+ Support L2CAP signal sizes bigger than 2048 bytes.
+ Cleanup of the socket handling code of the test programs.
+ Use better way for unaligned access.
+ Remove sdp_internal.h and its usage.
+
+ver 2.14:
+ Make use of additional connection information.
+ Use library function for reading the RSSI.
+ Use library function for reading the link quality.
+ Use library function for reading the transmit power level.
+ Use library functions for the link supervision timeout.
+ Add tool for changing the device address.
+ Add function for reading the RSSI.
+ Add function for reading the link quality.
+ Add function for reading the transmit power level.
+ Add functions for the link supervision timeout.
+ Remove deprecated functions.
+ Update AM_PATH_BLUEZ macro.
+
+ver 2.13:
+ Use file permission 0600 for the link key file.
+ Add support for HID attribute descriptions.
+ Add support for Device ID attributes.
+ Add Device ID and HID attribute definitions.
+ Update the UUID constants and its translations.
+ Update L2CAP socket option definitions.
+ Update connection information definitions.
+ Various whitespace cleanups.
+
+ver 2.12:
+ Inherit the device specific options from the default.
+ Use --device for selecting the source device.
+ Add --nosdp option for devices with resource limitation.
+ Add support and parameter option for secure mode.
+ Add a lot of build ids and hardware revisions.
+ Add service classes and profile ids for WAP.
+ Add simple AM_PATH_BLUEZ macro.
+ Update UUID translation tables.
+ Correct kernel interface for CMTP and HIDP support.
+
+ver 2.11:
+ Initial support for the kernel security manager.
+ Various cleanups to avoid inclusion of kernel headers.
+ Fix output when the CUPS backend is called without arguments.
+ Fix problems with a 64 bit userland.
+ Use Bluetooth library functions if available.
+ Use standard numbering scheme of SDP record handles.
+ Use bit zero for vendor packets in the filter type bitmask.
+ Add SIM Access types for service discovery.
+ Add more audio/video profile translations.
+ Add another company identifier.
+ Add the missing HCI error codes.
+ Add RFCOMM socket options.
+ Add definition for the SECURE link mode.
+ Add functions for reading and writing the inquiry mode.
+ Add functions for AFH related settings and information.
+ Add version identifier for the Bluetooth 2.0 specification.
+ Add a master option to the hidd.
+ Add support for changing the link key of a connection.
+ Add support for requesting encryption on keyboards.
+ Add support for revision information of Digianswer devices.
+ Add support for the Zoom, IBM and TDK PCMCIA cards.
+ Add checks for the OpenOBEX and the ALSA libraries.
+ Add experimental mRouter support.
+
+ver 2.10:
+ Use a define for the configuration directory.
+ Fix string initialization for flags translation.
+ Fix and extend the unaligned access macros.
+ Make compiling with debug information optional.
+ Don't override CFLAGS from configure.
+ Check for usb_get_busses() and usb_interrupt_read().
+ Add optional support for compiling with PIE.
+ Make installation of the init scripts optional.
+ Make compiling with debug information optional.
+ Don't override CFLAGS from configure.
+
+ver 2.9:
+ Retry SDP connect if busy in the CUPS backend.
+ Use packet type and allow role switch in hcitool.
+ Use the functions from the USB library for hid2hci.
+ Add Broadcom firmware loader.
+ Add EPoX endian quirk for buggy keyboards.
+ Add L2CAP info type and info result definitions.
+ Add value for L2CAP_CONF_RFC_MODE.
+ Change RSSI value to signed instead of unsigned.
+ Allow UUID32 values as protocol identifiers.
+ Update the autoconf/automake scripts.
+
+ver 2.8:
+ Use LIBS and LDADD instead of LDFLAGS.
+ Use HIDP subclass field for HID boot protocol.
+ Set olen before calling getsockopt() in pand.
+ Restore signals for dev-up script.
+ Add PID file support for pand.
+ Add size parameter to expand_name() in hcid.
+ Add support for audio source and audio sink SDP records.
+ Add support for HID virtual cable unplug.
+ Add support for AmbiCom BT2000C card.
+ Add defines and UUID's for audio/video profiles.
+ Add AVDTP protocol identifier.
+ Add HIDP subclass field.
+ Add PKGConfig support.
+ Fix the event code of inquiry with RSSI.
+ Remove dummy SDP library.
+
+ver 2.7:
+ Fix display of decoded LMP features.
+ Update company identifiers.
+ Add AFH related types.
+ Add first bits from EDR prototyping specification.
+ Add support for inquiry with RSSI.
+ Add HCRP related SDP functions.
+ Add HIDP header file.
+ Add support for getting the AFH channel map.
+ Add support for AFH mode.
+ Add support for inquiry mode.
+ Add Bluetooth backend for CUPS.
+ Add the hid2hci utility.
+ Add the hidd utility.
+ Add the pand utility.
+ Add the dund utility.
+ More endian bug fixes.
+ Give udev some time to create the RFCOMM device nodes.
+ Release the TTY if no device node is found.
+ New startup script for the Bluetooth subsystem.
+ Update to the autoconf stuff.
+
+ver 2.6:
+ Change default prefix to /usr.
+ Add manpages for hcid and hcid.conf.
+ Add the sdpd server daemon.
+ Add the sdptool utility.
+ Add the ciptool utility.
+ Add new company identifiers.
+ Add BNEP and CMTP header files.
+ Add the SDP library.
+ Use R2 for default value of pscan_rep_mode.
+
+ver 2.5:
+ Add decoding of Bluetooth 1.2 features.
+ Add link manager version parameter for Bluetooth 1.2.
+ Add new company identifiers.
+ Add D-Bus support for PIN request.
+ Support for transmit power level.
+ Support for park, sniff and hold mode.
+ Support for role switch.
+ Support for reading the clock offset.
+ Support for requesting authentication.
+ Support for setting connection encryption.
+ Show revision information for Broadcom devices.
+ Replace unprintable characters in device name.
+ Use R1 for default value of pscan_rep_mode.
+ Fix some 64-bit problems.
+ Fix some endian problems.
+ Report an error on PIN helper failure.
+ Update bluepin script for GTK2.
+
+ver 2.4:
+ Increase number of inquiry responses.
+ Support for transmit power level.
+ Display all 8 bytes of the features.
+ Add support for reading and writing of IAC.
+ Correct decoding class of device.
+ Use Ericsson revision command for ST Microelectronics devices.
+ Display AVM firmware version with 'revision' command.
+ New code for CSR specific revision information.
+ Support for ST Microelectronics specific initialization.
+ Support for 3Com card version 3.0.
+ Support for TDK, IBM and Socket cards.
+ Support for initial baud rate.
+ Update man pages.
+ Fixes for some memory leaks.
+
+ver 2.3:
+ Added const qualifiers to appropriate function arguments.
+ Minor fixes.
+ CSR firmware version is now displayed by 'revision' command.
+ Voice command is working properly on big endian machines.
+ Added support for Texas Bluetooth modules.
+ Added support for high UART baud rates on Ericsson modules.
+ BCSP initialization fixes.
+ Support for role switch command (hcitool).
+ RFCOMM config file parser fixes.
+ Update man pages.
+ Removed GLib dependency.
+
+ver 2.2:
+ Updated RFCOMM header file.
+ Additional HCI command and event defines.
+ Support for voice settings (hciconfig).
+ Minor hcitool fixes.
+ Improved configure script.
+ Added Headset testing tool.
+ Updated man pages.
+ RPM package.
+
+ver 2.1.1:
+ Resurrect hci_remote_name.
+
+ver 2.1:
+ Added hci_{read, write}_class_of_dev().
+ Added hci_{read, write}_current_iac_lap().
+ Added hci_write_local_name().
+ Added RFCOMM header file.
+ Minor fixes.
+ Improved BCSP initialization (hciattach).
+ Support for displaying link quality (hcitool).
+ Support for changing link supervision timeout (hcitool).
+ New RFCOMM TTY configuration tool (rfcomm).
+ Minor fixes and updates.
+
+ver 2.0:
+ Additional company IDs.
+ BCSP initialization (hciattach).
+ Minor hciconfig fixes.
+
+ver 2.0-pr13:
+ Support for multiple pairing modes.
+ Link key database handling fixes.
+
+ver 2.0-pre12:
+ Removed max link key limit. Keys never expire.
+ Link key database is always updated. Reread PIN on SIGHUP (hcid).
+ Bluetooth script starts SDPd, if installed.
+ Other minor fixes.
+
+ver 2.0-pre11:
+ Improved link key management and more verbose logging (hcid).
+ Fixed scan command (hcitool).
+
+ver 2.0-pre10:
+ Fix hci_inquiry function to return errors and accept user buffers.
+ New functions hci_devba, hci_devid, hci_for_each_dev and hci_get_route.
+ Additional company IDs.
+ Makefile and other minor fixes.
+ Support for reading RSSI, remote name and changing
+ connection type (hcitool).
+ Device initialization fixes (hcid).
+ Other minor fixes and improvements.
+ Build environment cleanup and fixes.
+
+ver 2.0-pre9:
+ Improved bluepin. Working X authentication.
+ Improved hcitool. New flexible cmd syntax, additional commands.
+ Human readable display of the device features.
+ LMP features to string translation support.
+ Additional HCI command and event defines.
+ Extended hci_filter API.
+
+ver 2.0-pre8:
+ Additional HCI ioctls and defines.
+ All strings and buffers are allocated dynamically.
+ ba2str, str2ba automatically swap bdaddress.
+ Additional hciconfig commands. Support for ACL and SCO MTU ioctls.
+ Support for Inventel and COM1 UART based devices.
+ Minor hcitool fixes.
+ Improved l2test. New L2CAP test modes.
+ Minor fixes and cleanup.
+
+ver 2.0-pre7:
+ Bluetooth libraries and header files is now a separate package.
+ New build environment uses automake and libtool.
+ Massive header files cleanup.
+ Bluetooth utilities is now a separate package.
+ New build environment uses automake.
+ Moved all config files and security data to /etc/bluetooth.
+ Various cleanups.
+
+ver 2.0-pre6:
+ API cleanup and additions.
+ Improved hcitool.
+ l2test minor output fixes.
+ hciattach opt to display list of supported devices.
+
+ver 2.0-pre4:
+ HCI filter enhancements.
+
+ver 2.0-pre3:
+ Cleanup.
+
+ver 2.0-pre2:
+ Additional HCI library functions.
+ Improved CSR baud rate initialization.
+ PCMCIA scripts fixes and enhancements.
+ Documentation update.
+
+ver 2.0-pre1:
+ New UART initialization utility.
+ Hot plugging support for UART based PCMCIA devices.
+ SCO testing utility.
+ New authentication utility (bluepin).
+ Minor fixes and improvements.
diff --git a/repo/HACKING b/repo/HACKING
new file mode 100644
index 0000000..a8fb403
--- /dev/null
+++ b/repo/HACKING
@@ -0,0 +1,130 @@
+Hacking on BlueZ
+****************
+
+Build tools requirements
+========================
+
+When building and testing directly from the repository it is important to
+have at least automake version 1.10 or later installed. All modern
+distributions should default to the latest version, but it seems that
+Debian's default is still an earlier version:
+
+ Check version
+ # dpkg -l '*automake*'
+
+ Install new version
+ # apt-get install automake1.10
+ # update-alternatives --config automake
+
+
+Working with the source code repository
+=======================================
+
+The repository contains two extra scripts that accomplish the bootstrap
+process. One is called "bootstrap" which is the basic scripts that uses the
+autotools scripts to create the needed files for building and installing.
+It makes sure to call the right programs depending on the usage of shared or
+static libraries or translations etc.
+
+The second program is called "bootstrap-configure". This program will make
+sure to properly clean the repository, call the "bootstrap" script and then
+call configure with proper settings for development. It will use the best
+options and pass them over to configure. These options normally include
+the enabling the maintainer mode and the debugging features.
+
+So while in a normal source project the call "./configure ..." is used to
+configure the project with its settings like prefix and extra options. In
+case of bare repositories call "./bootstrap-configure" and it will bootstrap
+the repository and calls configure with all the correct options to make
+development easier.
+
+In case of preparing for a release with "make distcheck", don't use
+bootstrap-configure since it could export development specific settings.
+
+So the normal steps to checkout, build and install such a repository is
+like this:
+
+ Checkout repository
+ # git clone git://git.kernel.org/pub/scm/bluetooth/bluez.git
+ # cd bluez
+
+ Configure and build
+ # ./bootstrap-configure
+ # make
+
+ Configure and build with cgcc (Sparse)
+ # ./bootstrap-configure CC=cgcc
+ # make
+
+ Run unit tests
+ # make check
+
+ Check installation
+ # make install DESTDIR=$PWD/x
+ # find x
+ # rm -rf x
+
+ Check distribution
+ # make distcheck
+
+ Final installation
+ # sudo make install
+
+ Remove autogenerated files
+ # make maintainer-clean
+
+
+Running from within the source code repository
+==============================================
+
+When using "./configure --enable-maintainer-mode" the automake scripts will
+use the plugins directly from within the repository. This removes the need
+to use "make install" when testing "bluetoothd". The "bootstrap-configure"
+automatically includes this option.
+
+ Copy configuration file which specifies the required security policies
+ # sudo cp ./src/bluetooth.conf /etc/dbus-1/system.d/
+
+ Run daemon in foreground with debugging
+ # sudo ./src/bluetoothd -n -d
+
+ Run daemon with valgrind
+ # sudo valgrind --trace-children=yes --track-origins=yes --track-fds=yes \
+ --show-possibly-lost=no --leak-check=full --suppressions=./tools/valgrind.supp \
+ ./src/bluetoothd -n -d
+
+For production installations or distribution packaging it is important that
+the "--enable-maintainer-mode" option is NOT used.
+
+Note multiple arguments to -d can be specified, colon, comma or space
+separated. The arguments are relative source code filenames for which
+debugging output should be enabled; output shell-style globs are
+accepted (e.g.: 'plugins/*:src/main.c').
+
+Submitting patches
+==================
+
+If you fixed a bug or you want to add support for something, patches are
+welcome! In order to ease the inclusion of your patch, it's important to follow
+some rules, otherwise it will likely be rejected by maintainers.
+
+BlueZ rules for submitting patches follow most of the rules used by Linux kernel
+(https://www.kernel.org/doc/Documentation/SubmittingPatches) with some remarks:
+
+1) Do *not* add "Signed-off-by" lines in your commit messages. BlueZ does not
+use them, so including them is actually an error.
+
+2) Be sure to follow the coding style rules of BlueZ. They are listed in
+doc/coding-style.txt.
+
+3) Split your patch according to the top-level directories. E.g.: if you added
+a feature that touches files under 'include/', 'src/' and 'drivers/'
+directories, split in three separated patches, taking care not to
+break compilation.
+
+4) Bug fixes should be sent first as they take priority over new features.
+
+5) The commit message should follow 50/72 formatting which means the header
+should be limited to 50 characters and the description should be wrapped at 72
+characters except if it contains quoted information from debug tools like
+backtraces, compiler errors, etc.
diff --git a/repo/INSTALL b/repo/INSTALL
new file mode 100644
index 0000000..56b077d
--- /dev/null
+++ b/repo/INSTALL
@@ -0,0 +1,236 @@
+Installation Instructions
+*************************
+
+Copyright (C) 1994, 1995, 1996, 1999, 2000, 2001, 2002, 2004, 2005 Free
+Software Foundation, Inc.
+
+This file is free documentation; the Free Software Foundation gives
+unlimited permission to copy, distribute and modify it.
+
+Basic Installation
+==================
+
+These are generic installation instructions.
+
+ The `configure' shell script attempts to guess correct values for
+various system-dependent variables used during compilation. It uses
+those values to create a `Makefile' in each directory of the package.
+It may also create one or more `.h' files containing system-dependent
+definitions. Finally, it creates a shell script `config.status' that
+you can run in the future to recreate the current configuration, and a
+file `config.log' containing compiler output (useful mainly for
+debugging `configure').
+
+ It can also use an optional file (typically called `config.cache'
+and enabled with `--cache-file=config.cache' or simply `-C') that saves
+the results of its tests to speed up reconfiguring. (Caching is
+disabled by default to prevent problems with accidental use of stale
+cache files.)
+
+ If you need to do unusual things to compile the package, please try
+to figure out how `configure' could check whether to do them, and mail
+diffs or instructions to the address given in the `README' so they can
+be considered for the next release. If you are using the cache, and at
+some point `config.cache' contains results you don't want to keep, you
+may remove or edit it.
+
+ The file `configure.ac' (or `configure.in') is used to create
+`configure' by a program called `autoconf'. You only need
+`configure.ac' if you want to change it or regenerate `configure' using
+a newer version of `autoconf'.
+
+The simplest way to compile this package is:
+
+ 1. `cd' to the directory containing the package's source code and type
+ `./configure' to configure the package for your system. If you're
+ using `csh' on an old version of System V, you might need to type
+ `sh ./configure' instead to prevent `csh' from trying to execute
+ `configure' itself.
+
+ Running `configure' takes awhile. While running, it prints some
+ messages telling which features it is checking for.
+
+ 2. Type `make' to compile the package.
+
+ 3. Optionally, type `make check' to run any self-tests that come with
+ the package.
+
+ 4. Type `make install' to install the programs and any data files and
+ documentation.
+
+ 5. You can remove the program binaries and object files from the
+ source code directory by typing `make clean'. To also remove the
+ files that `configure' created (so you can compile the package for
+ a different kind of computer), type `make distclean'. There is
+ also a `make maintainer-clean' target, but that is intended mainly
+ for the package's developers. If you use it, you may have to get
+ all sorts of other programs in order to regenerate files that came
+ with the distribution.
+
+Compilers and Options
+=====================
+
+Some systems require unusual options for compilation or linking that the
+`configure' script does not know about. Run `./configure --help' for
+details on some of the pertinent environment variables.
+
+ You can give `configure' initial values for configuration parameters
+by setting variables in the command line or in the environment. Here
+is an example:
+
+ ./configure CC=c89 CFLAGS=-O2 LIBS=-lposix
+
+ *Note Defining Variables::, for more details.
+
+Compiling For Multiple Architectures
+====================================
+
+You can compile the package for more than one kind of computer at the
+same time, by placing the object files for each architecture in their
+own directory. To do this, you must use a version of `make' that
+supports the `VPATH' variable, such as GNU `make'. `cd' to the
+directory where you want the object files and executables to go and run
+the `configure' script. `configure' automatically checks for the
+source code in the directory that `configure' is in and in `..'.
+
+ If you have to use a `make' that does not support the `VPATH'
+variable, you have to compile the package for one architecture at a
+time in the source code directory. After you have installed the
+package for one architecture, use `make distclean' before reconfiguring
+for another architecture.
+
+Installation Names
+==================
+
+By default, `make install' will install the package's files in
+`/usr/local/bin', `/usr/local/man', etc. You can specify an
+installation prefix other than `/usr/local' by giving `configure' the
+option `--prefix=PREFIX'.
+
+ You can specify separate installation prefixes for
+architecture-specific files and architecture-independent files. If you
+give `configure' the option `--exec-prefix=PREFIX', the package will
+use PREFIX as the prefix for installing programs and libraries.
+Documentation and other data files will still use the regular prefix.
+
+ In addition, if you use an unusual directory layout you can give
+options like `--bindir=DIR' to specify different values for particular
+kinds of files. Run `configure --help' for a list of the directories
+you can set and what kinds of files go in them.
+
+ If the package supports it, you can cause programs to be installed
+with an extra prefix or suffix on their names by giving `configure' the
+option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'.
+
+Optional Features
+=================
+
+Some packages pay attention to `--enable-FEATURE' options to
+`configure', where FEATURE indicates an optional part of the package.
+They may also pay attention to `--with-PACKAGE' options, where PACKAGE
+is something like `gnu-as' or `x' (for the X Window System). The
+`README' should mention any `--enable-' and `--with-' options that the
+package recognizes.
+
+ For packages that use the X Window System, `configure' can usually
+find the X include and library files automatically, but if it doesn't,
+you can use the `configure' options `--x-includes=DIR' and
+`--x-libraries=DIR' to specify their locations.
+
+Specifying the System Type
+==========================
+
+There may be some features `configure' cannot figure out automatically,
+but needs to determine by the type of machine the package will run on.
+Usually, assuming the package is built to be run on the _same_
+architectures, `configure' can figure that out, but if it prints a
+message saying it cannot guess the machine type, give it the
+`--build=TYPE' option. TYPE can either be a short name for the system
+type, such as `sun4', or a canonical name which has the form:
+
+ CPU-COMPANY-SYSTEM
+
+where SYSTEM can have one of these forms:
+
+ OS KERNEL-OS
+
+ See the file `config.sub' for the possible values of each field. If
+`config.sub' isn't included in this package, then this package doesn't
+need to know the machine type.
+
+ If you are _building_ compiler tools for cross-compiling, you should
+use the `--target=TYPE' option to select the type of system they will
+produce code for.
+
+ If you want to _use_ a cross compiler, that generates code for a
+platform different from the build platform, you should specify the
+"host" platform (i.e., that on which the generated programs will
+eventually be run) with `--host=TYPE'.
+
+Sharing Defaults
+================
+
+If you want to set default values for `configure' scripts to share, you
+can create a site shell script called `config.site' that gives default
+values for variables like `CC', `cache_file', and `prefix'.
+`configure' looks for `PREFIX/share/config.site' if it exists, then
+`PREFIX/etc/config.site' if it exists. Or, you can set the
+`CONFIG_SITE' environment variable to the location of the site script.
+A warning: not all `configure' scripts look for a site script.
+
+Defining Variables
+==================
+
+Variables not defined in a site shell script can be set in the
+environment passed to `configure'. However, some packages may run
+configure again during the build, and the customized values of these
+variables may be lost. In order to avoid this problem, you should set
+them in the `configure' command line, using `VAR=value'. For example:
+
+ ./configure CC=/usr/local2/bin/gcc
+
+causes the specified `gcc' to be used as the C compiler (unless it is
+overridden in the site shell script). Here is a another example:
+
+ /bin/bash ./configure CONFIG_SHELL=/bin/bash
+
+Here the `CONFIG_SHELL=/bin/bash' operand causes subsequent
+configuration-related scripts to be executed by `/bin/bash'.
+
+`configure' Invocation
+======================
+
+`configure' recognizes the following options to control how it operates.
+
+`--help'
+`-h'
+ Print a summary of the options to `configure', and exit.
+
+`--version'
+`-V'
+ Print the version of Autoconf used to generate the `configure'
+ script, and exit.
+
+`--cache-file=FILE'
+ Enable the cache: use and save the results of the tests in FILE,
+ traditionally `config.cache'. FILE defaults to `/dev/null' to
+ disable caching.
+
+`--config-cache'
+`-C'
+ Alias for `--cache-file=config.cache'.
+
+`--quiet'
+`--silent'
+`-q'
+ Do not print messages saying which checks are being made. To
+ suppress all normal output, redirect it to `/dev/null' (any error
+ messages will still be shown).
+
+`--srcdir=DIR'
+ Look for the package's source code in directory DIR. Usually
+ `configure' can determine that directory automatically.
+
+`configure' also accepts some other, not widely useful, options. Run
+`configure --help' for more details.
+
diff --git a/repo/Makefile.am b/repo/Makefile.am
new file mode 100644
index 0000000..3e1d8d0
--- /dev/null
+++ b/repo/Makefile.am
@@ -0,0 +1,505 @@
+
+AM_MAKEFLAGS = --no-print-directory
+
+lib_LTLIBRARIES =
+
+noinst_LIBRARIES =
+
+noinst_LTLIBRARIES =
+
+bin_PROGRAMS =
+
+noinst_PROGRAMS =
+
+dist_man_MANS =
+
+dist_noinst_MANS =
+
+CLEANFILES =
+
+EXTRA_DIST =
+
+libexecdir = @libexecdir@/bluetooth
+
+libexec_PROGRAMS =
+
+includedir = @includedir@/bluetooth
+
+include_HEADERS =
+
+AM_CFLAGS = $(WARNING_CFLAGS) $(MISC_CFLAGS)
+AM_LDFLAGS = $(MISC_LDFLAGS)
+
+if DATAFILES
+dbusdir = @DBUS_CONFDIR@/dbus-1/system.d
+dbus_DATA = src/bluetooth.conf
+
+confdir = $(sysconfdir)/bluetooth
+conf_DATA =
+
+statedir = $(localstatedir)/lib/bluetooth
+state_DATA =
+endif
+
+if SYSTEMD
+systemdsystemunitdir = @SYSTEMD_SYSTEMUNITDIR@
+systemdsystemunit_DATA = src/bluetooth.service
+
+dbussystembusdir = @DBUS_SYSTEMBUSDIR@
+dbussystembus_DATA = src/org.bluez.service
+endif
+
+EXTRA_DIST += src/bluetooth.service.in src/org.bluez.service
+
+plugindir = $(libdir)/bluetooth/plugins
+
+if MAINTAINER_MODE
+build_plugindir = $(abs_top_srcdir)/plugins/.libs
+else
+build_plugindir = $(plugindir)
+endif
+
+
+plugin_LTLIBRARIES =
+
+lib_sources = lib/bluetooth.c lib/hci.c lib/sdp.c
+lib_headers = lib/bluetooth.h lib/hci.h lib/hci_lib.h \
+ lib/sco.h lib/l2cap.h lib/sdp.h lib/sdp_lib.h \
+ lib/rfcomm.h lib/bnep.h lib/cmtp.h lib/hidp.h
+
+extra_headers = lib/mgmt.h lib/uuid.h lib/a2mp.h lib/amp.h
+extra_sources = lib/uuid.c
+
+local_headers = $(foreach file,$(lib_headers), lib/bluetooth/$(notdir $(file)))
+
+BUILT_SOURCES = $(local_headers) src/builtin.h
+
+if LIBRARY
+include_HEADERS += $(lib_headers)
+
+lib_LTLIBRARIES += lib/libbluetooth.la
+
+lib_libbluetooth_la_SOURCES = $(lib_headers) $(lib_sources)
+lib_libbluetooth_la_LDFLAGS = $(AM_LDFLAGS) -version-info 21:10:18
+lib_libbluetooth_la_DEPENDENCIES = $(local_headers)
+endif
+
+noinst_LTLIBRARIES += lib/libbluetooth-internal.la
+
+lib_libbluetooth_internal_la_SOURCES = $(lib_headers) $(lib_sources) \
+ $(extra_headers) $(extra_sources)
+
+noinst_LTLIBRARIES += gdbus/libgdbus-internal.la
+
+gdbus_libgdbus_internal_la_SOURCES = gdbus/gdbus.h \
+ gdbus/mainloop.c gdbus/watch.c \
+ gdbus/object.c gdbus/client.c gdbus/polkit.c
+
+noinst_LTLIBRARIES += src/libshared-glib.la src/libshared-mainloop.la
+
+shared_sources = src/shared/io.h src/shared/timeout.h \
+ src/shared/queue.h src/shared/queue.c \
+ src/shared/util.h src/shared/util.c \
+ src/shared/mgmt.h src/shared/mgmt.c \
+ src/shared/crypto.h src/shared/crypto.c \
+ src/shared/ecc.h src/shared/ecc.c \
+ src/shared/ringbuf.h src/shared/ringbuf.c \
+ src/shared/tester.h src/shared/tester.c \
+ src/shared/hci.h src/shared/hci.c \
+ src/shared/hci-crypto.h src/shared/hci-crypto.c \
+ src/shared/hfp.h src/shared/hfp.c \
+ src/shared/uhid.h src/shared/uhid.c \
+ src/shared/pcap.h src/shared/pcap.c \
+ src/shared/btsnoop.h src/shared/btsnoop.c \
+ src/shared/ad.h src/shared/ad.c \
+ src/shared/att-types.h \
+ src/shared/att.h src/shared/att.c \
+ src/shared/gatt-helpers.h src/shared/gatt-helpers.c \
+ src/shared/gatt-client.h src/shared/gatt-client.c \
+ src/shared/gatt-server.h src/shared/gatt-server.c \
+ src/shared/gatt-db.h src/shared/gatt-db.c \
+ src/shared/gap.h src/shared/gap.c
+
+src_libshared_glib_la_SOURCES = $(shared_sources) \
+ src/shared/io-glib.c \
+ src/shared/timeout-glib.c
+
+src_libshared_mainloop_la_SOURCES = $(shared_sources) \
+ src/shared/io-mainloop.c \
+ src/shared/timeout-mainloop.c \
+ src/shared/mainloop.h src/shared/mainloop.c
+
+attrib_sources = attrib/att.h attrib/att-database.h attrib/att.c \
+ attrib/gatt.h attrib/gatt.c \
+ attrib/gattrib.h attrib/gattrib.c \
+ attrib/gatt-service.h attrib/gatt-service.c
+
+btio_sources = btio/btio.h btio/btio.c
+
+gobex_sources = gobex/gobex.h gobex/gobex.c \
+ gobex/gobex-defs.h gobex/gobex-defs.c \
+ gobex/gobex-packet.c gobex/gobex-packet.h \
+ gobex/gobex-header.c gobex/gobex-header.h \
+ gobex/gobex-transfer.c gobex/gobex-debug.h \
+ gobex/gobex-apparam.c gobex/gobex-apparam.h
+
+builtin_modules =
+builtin_sources =
+builtin_nodist =
+
+include Makefile.plugins
+
+if MAINTAINER_MODE
+plugin_LTLIBRARIES += plugins/external-dummy.la
+plugins_external_dummy_la_SOURCES = plugins/external-dummy.c
+plugins_external_dummy_la_LDFLAGS = $(AM_LDFLAGS) -module -avoid-version \
+ -no-undefined
+plugins_external_dummy_la_CFLAGS = $(AM_CFLAGS) -fvisibility=hidden
+endif
+
+libexec_PROGRAMS += src/bluetoothd
+
+src_bluetoothd_SOURCES = $(builtin_sources) \
+ $(attrib_sources) $(btio_sources) \
+ src/bluetooth.ver \
+ src/main.c src/log.h src/log.c \
+ src/backtrace.h src/backtrace.c \
+ src/systemd.h src/systemd.c \
+ src/rfkill.c src/hcid.h src/sdpd.h \
+ src/sdpd-server.c src/sdpd-request.c \
+ src/sdpd-service.c src/sdpd-database.c \
+ src/attrib-server.h src/attrib-server.c \
+ src/gatt-database.h src/gatt-database.c \
+ src/sdp-xml.h src/sdp-xml.c \
+ src/sdp-client.h src/sdp-client.c \
+ src/textfile.h src/textfile.c \
+ src/uuid-helper.h src/uuid-helper.c \
+ src/uinput.h \
+ src/plugin.h src/plugin.c \
+ src/storage.h src/storage.c \
+ src/advertising.h src/advertising.c \
+ src/agent.h src/agent.c \
+ src/error.h src/error.c \
+ src/adapter.h src/adapter.c \
+ src/profile.h src/profile.c \
+ src/service.h src/service.c \
+ src/gatt-client.h src/gatt-client.c \
+ src/device.h src/device.c src/attio.h \
+ src/dbus-common.c src/dbus-common.h \
+ src/eir.h src/eir.c
+src_bluetoothd_LDADD = lib/libbluetooth-internal.la \
+ gdbus/libgdbus-internal.la \
+ src/libshared-glib.la \
+ @BACKTRACE_LIBS@ @GLIB_LIBS@ @DBUS_LIBS@ -ldl -lrt
+src_bluetoothd_LDFLAGS = $(AM_LDFLAGS) -Wl,--export-dynamic \
+ -Wl,--version-script=$(srcdir)/src/bluetooth.ver
+
+src_bluetoothd_DEPENDENCIES = lib/libbluetooth-internal.la \
+ gdbus/libgdbus-internal.la \
+ src/libshared-glib.la \
+ src/bluetooth.service
+
+src_bluetoothd_CFLAGS = $(AM_CFLAGS) -DBLUETOOTH_PLUGIN_BUILTIN \
+ -DPLUGINDIR=\""$(build_plugindir)"\"
+src_bluetoothd_SHORTNAME = bluetoothd
+
+builtin_files = src/builtin.h $(builtin_nodist)
+
+nodist_src_bluetoothd_SOURCES = $(builtin_files)
+
+CLEANFILES += $(builtin_files) src/bluetooth.service
+
+man_MANS = src/bluetoothd.8
+
+EXTRA_DIST += src/genbuiltin src/bluetooth.conf \
+ src/main.conf profiles/network/network.conf \
+ profiles/input/input.conf profiles/proximity/proximity.conf
+
+test_scripts =
+unit_tests =
+
+include Makefile.tools
+include Makefile.obexd
+include android/Makefile.am
+
+if HID2HCI
+rulesdir = @UDEV_DIR@/rules.d
+
+rules_DATA = tools/97-hid2hci.rules
+
+CLEANFILES += $(rules_DATA)
+endif
+
+EXTRA_DIST += tools/hid2hci.rules
+
+if TEST
+testdir = $(pkglibdir)/test
+test_SCRIPTS = $(test_scripts)
+endif
+
+EXTRA_DIST += $(test_scripts)
+
+EXTRA_DIST += doc/assigned-numbers.txt doc/supported-features.txt \
+ doc/test-coverage.txt \
+ doc/test-runner.txt \
+ doc/settings-storage.txt
+
+EXTRA_DIST += doc/mgmt-api.txt \
+ doc/adapter-api.txt doc/device-api.txt \
+ doc/agent-api.txt doc/profile-api.txt \
+ doc/network-api.txt doc/media-api.txt \
+ doc/health-api.txt doc/sap-api.txt \
+ doc/input-api.txt
+
+EXTRA_DIST += doc/alert-api.txt \
+ doc/proximity-api.txt doc/heartrate-api.txt \
+ doc/thermometer-api.txt doc/cyclingspeed-api.txt \
+ doc/gatt-api.txt doc/advertising-api.txt
+
+EXTRA_DIST += doc/obex-api.txt doc/obex-agent-api.txt
+
+EXTRA_DIST += doc/pics-opp.txt doc/pixit-opp.txt \
+ doc/pts-opp.txt
+
+EXTRA_DIST += tools/magic.btsnoop
+
+AM_CFLAGS += @DBUS_CFLAGS@ @GLIB_CFLAGS@ @UDEV_CFLAGS@
+
+AM_CPPFLAGS = -I$(builddir)/lib
+
+
+unit_tests += unit/test-eir
+
+unit_test_eir_SOURCES = unit/test-eir.c src/eir.c src/uuid-helper.c
+unit_test_eir_LDADD = src/libshared-glib.la lib/libbluetooth-internal.la \
+ @GLIB_LIBS@
+
+unit_tests += unit/test-uuid
+
+unit_test_uuid_SOURCES = unit/test-uuid.c
+unit_test_uuid_LDADD = src/libshared-glib.la lib/libbluetooth-internal.la \
+ @GLIB_LIBS@
+
+unit_tests += unit/test-textfile
+
+unit_test_textfile_SOURCES = unit/test-textfile.c src/textfile.h src/textfile.c
+unit_test_textfile_LDADD = src/libshared-glib.la @GLIB_LIBS@
+
+unit_tests += unit/test-crc
+
+unit_test_crc_SOURCES = unit/test-crc.c monitor/crc.h monitor/crc.c
+unit_test_crc_LDADD = src/libshared-glib.la @GLIB_LIBS@
+
+unit_tests += unit/test-crypto
+
+unit_test_crypto_SOURCES = unit/test-crypto.c
+unit_test_crypto_LDADD = src/libshared-glib.la @GLIB_LIBS@
+
+unit_tests += unit/test-ecc
+
+unit_test_ecc_SOURCES = unit/test-ecc.c
+unit_test_ecc_LDADD = src/libshared-glib.la @GLIB_LIBS@
+
+unit_tests += unit/test-ringbuf unit/test-queue
+
+unit_test_ringbuf_SOURCES = unit/test-ringbuf.c
+unit_test_ringbuf_LDADD = src/libshared-glib.la @GLIB_LIBS@
+
+unit_test_queue_SOURCES = unit/test-queue.c
+unit_test_queue_LDADD = src/libshared-glib.la @GLIB_LIBS@
+
+unit_tests += unit/test-mgmt
+
+unit_test_mgmt_SOURCES = unit/test-mgmt.c
+unit_test_mgmt_LDADD = src/libshared-glib.la @GLIB_LIBS@
+
+unit_tests += unit/test-uhid
+
+unit_test_uhid_SOURCES = unit/test-uhid.c
+unit_test_uhid_LDADD = src/libshared-glib.la @GLIB_LIBS@
+
+unit_tests += unit/test-sdp
+
+unit_test_sdp_SOURCES = unit/test-sdp.c \
+ src/sdpd.h src/sdpd-database.c \
+ src/log.h src/log.c \
+ src/sdpd-service.c src/sdpd-request.c
+unit_test_sdp_LDADD = lib/libbluetooth-internal.la \
+ src/libshared-glib.la @GLIB_LIBS@
+
+unit_tests += unit/test-avdtp
+
+unit_test_avdtp_SOURCES = unit/test-avdtp.c \
+ src/log.h src/log.c \
+ android/avdtp.c android/avdtp.h
+unit_test_avdtp_LDADD = src/libshared-glib.la @GLIB_LIBS@
+
+unit_tests += unit/test-avctp
+
+unit_test_avctp_SOURCES = unit/test-avctp.c \
+ src/log.h src/log.c \
+ android/avctp.c android/avctp.h
+unit_test_avctp_LDADD = src/libshared-glib.la @GLIB_LIBS@
+
+unit_tests += unit/test-avrcp
+
+unit_test_avrcp_SOURCES = unit/test-avrcp.c \
+ src/log.h src/log.c \
+ android/avctp.c android/avctp.h \
+ android/avrcp-lib.c android/avrcp-lib.h
+unit_test_avrcp_LDADD = lib/libbluetooth-internal.la \
+ src/libshared-glib.la @GLIB_LIBS@
+
+unit_tests += unit/test-hfp
+
+unit_test_hfp_SOURCES = unit/test-hfp.c
+unit_test_hfp_LDADD = src/libshared-glib.la @GLIB_LIBS@
+
+unit_tests += unit/test-gdbus-client
+
+unit_test_gdbus_client_SOURCES = unit/test-gdbus-client.c
+unit_test_gdbus_client_LDADD = gdbus/libgdbus-internal.la \
+ src/libshared-glib.la @GLIB_LIBS@ @DBUS_LIBS@
+
+unit_tests += unit/test-gobex-header unit/test-gobex-packet unit/test-gobex \
+ unit/test-gobex-transfer unit/test-gobex-apparam
+
+unit_test_gobex_SOURCES = $(gobex_sources) unit/util.c unit/util.h \
+ unit/test-gobex.c
+unit_test_gobex_LDADD = @GLIB_LIBS@
+
+unit_test_gobex_packet_SOURCES = $(gobex_sources) unit/util.c unit/util.h \
+ unit/test-gobex-packet.c
+unit_test_gobex_packet_LDADD = @GLIB_LIBS@
+
+unit_test_gobex_header_SOURCES = $(gobex_sources) unit/util.c unit/util.h \
+ unit/test-gobex-header.c
+unit_test_gobex_header_LDADD = @GLIB_LIBS@
+
+unit_test_gobex_transfer_SOURCES = $(gobex_sources) unit/util.c unit/util.h \
+ unit/test-gobex-transfer.c
+unit_test_gobex_transfer_LDADD = @GLIB_LIBS@
+
+unit_test_gobex_apparam_SOURCES = $(gobex_sources) unit/util.c unit/util.h \
+ unit/test-gobex-apparam.c
+unit_test_gobex_apparam_LDADD = @GLIB_LIBS@
+
+unit_tests += unit/test-lib
+
+unit_test_lib_SOURCES = unit/test-lib.c
+unit_test_lib_LDADD = src/libshared-glib.la \
+ lib/libbluetooth-internal.la @GLIB_LIBS@
+
+unit_tests += unit/test-gatt
+
+unit_test_gatt_SOURCES = unit/test-gatt.c
+unit_test_gatt_LDADD = src/libshared-glib.la \
+ lib/libbluetooth-internal.la @GLIB_LIBS@
+
+unit_tests += unit/test-hog
+
+unit_test_hog_SOURCES = unit/test-hog.c \
+ $(btio_sources) \
+ profiles/input/hog-lib.h profiles/input/hog-lib.c \
+ profiles/scanparam/scpp.h profiles/scanparam/scpp.c \
+ profiles/battery/bas.h profiles/battery/bas.c \
+ profiles/deviceinfo/dis.h profiles/deviceinfo/dis.c \
+ src/log.h src/log.c \
+ attrib/att.h attrib/att.c \
+ attrib/gatt.h attrib/gatt.c \
+ attrib/gattrib.h attrib/gattrib.c
+unit_test_hog_LDADD = src/libshared-glib.la \
+ lib/libbluetooth-internal.la @GLIB_LIBS@
+
+unit_tests += unit/test-gattrib
+
+unit_test_gattrib_SOURCES = unit/test-gattrib.c attrib/gattrib.c $(btio_sources) src/log.h src/log.c
+unit_test_gattrib_LDADD = lib/libbluetooth-internal.la \
+ src/libshared-glib.la \
+ @GLIB_LIBS@ @DBUS_LIBS@ -ldl -lrt
+
+if MAINTAINER_MODE
+noinst_PROGRAMS += $(unit_tests)
+endif
+
+TESTS = $(unit_tests)
+AM_TESTS_ENVIRONMENT = MALLOC_CHECK_=3 MALLOC_PERTURB_=69
+
+if DBUS_RUN_SESSION
+AM_TESTS_ENVIRONMENT += dbus-run-session --
+endif
+
+if VALGRIND
+LOG_COMPILER = valgrind --error-exitcode=1 --num-callers=30
+LOG_FLAGS = --trace-children=yes --leak-check=full --show-reachable=no \
+ --suppressions=$(srcdir)/tools/valgrind.supp --quiet
+endif
+
+pkgconfigdir = $(libdir)/pkgconfig
+
+if LIBRARY
+pkgconfig_DATA = lib/bluez.pc
+endif
+
+manual_pages = doc/btmon.1
+
+if MANPAGES
+dist_noinst_MANS += $(manual_pages)
+endif
+
+EXTRA_DIST += $(manual_pages:.1=.txt)
+
+DISTCHECK_CONFIGURE_FLAGS = --disable-datafiles --enable-library \
+ --enable-manpages --enable-android \
+ --disable-systemd --disable-udev
+
+DISTCLEANFILES = $(pkgconfig_DATA) $(unit_tests) $(manual_pages)
+
+MAINTAINERCLEANFILES = Makefile.in \
+ aclocal.m4 configure config.h.in config.sub config.guess \
+ ltmain.sh depcomp compile missing install-sh mkinstalldirs test-driver
+
+SED_PROCESS = $(AM_V_GEN)$(MKDIR_P) $(dir $@) && \
+ $(SED) -e 's,@libexecdir\@,$(libexecdir),g' \
+ < $< > $@
+
+%.service: %.service.in Makefile
+ $(SED_PROCESS)
+
+%.1: %.txt
+ $(AM_V_GEN)a2x --doctype manpage --format manpage $(srcdir)/$<
+
+src/builtin.h: src/genbuiltin $(builtin_sources)
+ $(AM_V_GEN)$(srcdir)/src/genbuiltin $(builtin_modules) > $@
+
+tools/%.rules:
+ $(AM_V_GEN)cp $(srcdir)/$(subst 97-,,$@) $@
+
+$(lib_libbluetooth_la_OBJECTS): $(local_headers)
+
+lib/bluetooth/%.h: lib/%.h
+ $(AM_V_at)$(MKDIR_P) lib/bluetooth
+ $(AM_V_GEN)$(LN_S) -f "$(abs_top_builddir)"/$< $@
+
+if COVERAGE
+clean-coverage:
+ @lcov --directory $(top_builddir) --zerocounters
+ $(RM) -r coverage $(top_builddir)/coverage.info
+
+coverage: check
+ @lcov --compat-libtool --directory $(top_builddir) --capture \
+ --output-file $(top_builddir)/coverage.info
+ $(AM_V_at)$(MKDIR_P) coverage
+ @genhtml -o coverage/ $(top_builddir)/coverage.info
+
+clean-local: clean-coverage
+ -find $(top_builddir) -name "*.gcno" -delete
+ -find $(top_builddir) -name "*.gcda" -delete
+ $(RM) -r lib/bluetooth
+
+else
+clean-local:
+ -find $(top_builddir) -name "*.gcno" -delete
+ -find $(top_builddir) -name "*.gcda" -delete
+ $(RM) -r lib/bluetooth
+endif
diff --git a/repo/Makefile.obexd b/repo/Makefile.obexd
new file mode 100644
index 0000000..2e33cbc
--- /dev/null
+++ b/repo/Makefile.obexd
@@ -0,0 +1,110 @@
+
+if SYSTEMD
+systemduserunitdir = @SYSTEMD_USERUNITDIR@
+systemduserunit_DATA = obexd/src/obex.service
+
+dbussessionbusdir = @DBUS_SESSIONBUSDIR@
+dbussessionbus_DATA = obexd/src/org.bluez.obex.service
+endif
+
+EXTRA_DIST += obexd/src/obex.service.in obexd/src/org.bluez.obex.service
+
+obex_plugindir = $(libdir)/obex/plugins
+
+obexd_builtin_modules =
+obexd_builtin_sources =
+obexd_builtin_nodist =
+
+obexd_builtin_modules += filesystem
+obexd_builtin_sources += obexd/plugins/filesystem.c obexd/plugins/filesystem.h
+
+obexd_builtin_modules += bluetooth
+obexd_builtin_sources += obexd/plugins/bluetooth.c
+
+if EXPERIMENTAL
+obexd_builtin_modules += pcsuite
+obexd_builtin_sources += obexd/plugins/pcsuite.c
+endif
+
+obexd_builtin_modules += opp
+obexd_builtin_sources += obexd/plugins/opp.c
+
+obexd_builtin_modules += ftp
+obexd_builtin_sources += obexd/plugins/ftp.c obexd/plugins/ftp.h
+
+if OBEX
+obexd_builtin_modules += irmc
+obexd_builtin_sources += obexd/plugins/irmc.c
+
+obexd_builtin_modules += pbap
+obexd_builtin_sources += obexd/plugins/pbap.c \
+ obexd/plugins/vcard.h obexd/plugins/vcard.c \
+ obexd/plugins/phonebook.h \
+ obexd/plugins/phonebook-dummy.c
+endif
+
+obexd_builtin_modules += mas
+obexd_builtin_sources += obexd/plugins/mas.c obexd/src/map_ap.h \
+ obexd/plugins/messages.h \
+ obexd/plugins/messages-dummy.c
+
+obexd_builtin_modules += mns
+obexd_builtin_sources += obexd/client/mns.c obexd/src/map_ap.h \
+ obexd/client/map-event.h
+
+libexec_PROGRAMS += obexd/src/obexd
+
+obexd_src_obexd_SOURCES = $(btio_sources) $(gobex_sources) \
+ $(obexd_builtin_sources) \
+ obexd/src/main.c obexd/src/obexd.h \
+ obexd/src/plugin.h obexd/src/plugin.c \
+ obexd/src/log.h obexd/src/log.c \
+ obexd/src/manager.h obexd/src/manager.c \
+ obexd/src/obex.h obexd/src/obex.c obexd/src/obex-priv.h \
+ obexd/src/mimetype.h obexd/src/mimetype.c \
+ obexd/src/service.h obexd/src/service.c \
+ obexd/src/transport.h obexd/src/transport.c \
+ obexd/src/server.h obexd/src/server.c \
+ obexd/client/manager.h obexd/client/manager.c \
+ obexd/client/session.h obexd/client/session.c \
+ obexd/client/bluetooth.h obexd/client/bluetooth.c \
+ obexd/client/sync.h obexd/client/sync.c \
+ obexd/client/pbap.h obexd/client/pbap.c \
+ obexd/client/ftp.h obexd/client/ftp.c \
+ obexd/client/opp.h obexd/client/opp.c \
+ obexd/client/map.h obexd/client/map.c \
+ obexd/client/map-event.h obexd/client/map-event.c \
+ obexd/client/transfer.h obexd/client/transfer.c \
+ obexd/client/transport.h obexd/client/transport.c \
+ obexd/client/dbus.h obexd/client/dbus.c \
+ obexd/client/driver.h obexd/client/driver.c \
+ obexd/src/map_ap.h
+obexd_src_obexd_LDADD = lib/libbluetooth-internal.la \
+ gdbus/libgdbus-internal.la \
+ @ICAL_LIBS@ @DBUS_LIBS@ @GLIB_LIBS@ -ldl
+
+obexd_src_obexd_LDFLAGS = -Wl,--export-dynamic
+
+obexd_src_obexd_CFLAGS = $(AM_CFLAGS) @GLIB_CFLAGS@ @DBUS_CFLAGS@ \
+ @ICAL_CFLAGS@ -DOBEX_PLUGIN_BUILTIN \
+ -DPLUGINDIR=\""$(obex_plugindir)"\" \
+ -fPIC -D_FILE_OFFSET_BITS=64
+
+obexd_src_obexd_CPPFLAGS = -I$(builddir)/lib -I$(builddir)/obexd/src
+
+obexd_src_obexd_SHORTNAME = obexd
+
+obexd_builtin_files = obexd/src/builtin.h $(obexd_builtin_nodist)
+
+nodist_obexd_src_obexd_SOURCES = $(obexd_builtin_files)
+
+BUILT_SOURCES += obexd/src/builtin.h
+
+obexd/src/plugin.$(OBJEXT): obexd/src/builtin.h
+
+obexd/src/builtin.h: obexd/src/genbuiltin $(obexd_builtin_sources)
+ $(AM_V_GEN)$(srcdir)/obexd/src/genbuiltin $(obexd_builtin_modules) > $@
+
+CLEANFILES += obexd/src/builtin.h $(builtin_files) obexd/src/obex.service
+
+EXTRA_DIST += obexd/src/genbuiltin
diff --git a/repo/Makefile.plugins b/repo/Makefile.plugins
new file mode 100644
index 0000000..f85b642
--- /dev/null
+++ b/repo/Makefile.plugins
@@ -0,0 +1,125 @@
+
+builtin_modules += hostname
+builtin_sources += plugins/hostname.c
+
+builtin_modules += wiimote
+builtin_sources += plugins/wiimote.c
+
+builtin_modules += autopair
+builtin_sources += plugins/autopair.c
+
+builtin_modules += policy
+builtin_sources += plugins/policy.c
+
+if MAINTAINER_MODE
+builtin_modules += gatt_example
+builtin_sources += plugins/gatt-example.c
+endif
+
+if EXPERIMENTAL
+builtin_modules += neard
+builtin_sources += plugins/neard.c
+
+builtin_modules += sap
+builtin_sources += profiles/sap/main.c profiles/sap/manager.h \
+ profiles/sap/manager.c profiles/sap/server.h \
+ profiles/sap/server.c profiles/sap/sap.h \
+ profiles/sap/sap-dummy.c
+
+noinst_LIBRARIES += profiles/sap/libsap.a
+profiles_sap_libsap_a_SOURCES = profiles/sap/sap.h profiles/sap/sap-u8500.c
+endif
+
+builtin_modules += a2dp
+builtin_sources += profiles/audio/source.h profiles/audio/source.c \
+ profiles/audio/sink.h profiles/audio/sink.c \
+ profiles/audio/a2dp.h profiles/audio/a2dp.c \
+ profiles/audio/avdtp.h profiles/audio/avdtp.c \
+ profiles/audio/media.h profiles/audio/media.c \
+ profiles/audio/transport.h profiles/audio/transport.c \
+ profiles/audio/a2dp-codecs.h
+
+builtin_modules += avrcp
+builtin_sources += profiles/audio/control.h profiles/audio/control.c \
+ profiles/audio/avctp.h profiles/audio/avctp.c \
+ profiles/audio/avrcp.h profiles/audio/avrcp.c \
+ profiles/audio/player.h profiles/audio/player.c
+
+builtin_modules += network
+builtin_sources += profiles/network/manager.c \
+ profiles/network/bnep.h profiles/network/bnep.c \
+ profiles/network/server.h profiles/network/server.c \
+ profiles/network/connection.h \
+ profiles/network/connection.c
+
+builtin_modules += input
+builtin_sources += profiles/input/manager.c \
+ profiles/input/server.h profiles/input/server.c \
+ profiles/input/device.h profiles/input/device.c \
+ profiles/input/hidp_defs.h
+
+builtin_modules += hog
+builtin_sources += profiles/input/hog.c profiles/input/uhid_copy.h \
+ profiles/input/hog-lib.c profiles/input/hog-lib.h \
+ profiles/deviceinfo/dis.c profiles/deviceinfo/dis.h \
+ profiles/battery/bas.c profiles/battery/bas.h \
+ profiles/scanparam/scpp.c profiles/scanparam/scpp.h \
+ profiles/input/suspend.h profiles/input/suspend-none.c
+
+EXTRA_DIST += profiles/input/suspend-dummy.c
+
+if EXPERIMENTAL
+builtin_modules += health
+builtin_sources += profiles/health/mcap.h profiles/health/mcap.c \
+ profiles/health/hdp_main.c profiles/health/hdp_types.h \
+ profiles/health/hdp_manager.h \
+ profiles/health/hdp_manager.c \
+ profiles/health/hdp.h profiles/health/hdp.c \
+ profiles/health/hdp_util.h profiles/health/hdp_util.c
+endif
+
+builtin_modules += gap
+builtin_sources += profiles/gap/gas.c
+
+builtin_modules += scanparam
+builtin_sources += profiles/scanparam/scan.c
+
+builtin_modules += deviceinfo
+builtin_sources += profiles/deviceinfo/deviceinfo.c
+
+if EXPERIMENTAL
+builtin_modules += alert
+builtin_sources += profiles/alert/server.c
+
+builtin_modules += time
+builtin_sources += profiles/time/server.c
+
+builtin_modules += proximity
+builtin_sources += profiles/proximity/main.c profiles/proximity/manager.h \
+ profiles/proximity/manager.c \
+ profiles/proximity/monitor.h \
+ profiles/proximity/monitor.c \
+ profiles/proximity/reporter.h \
+ profiles/proximity/reporter.c \
+ profiles/proximity/linkloss.h \
+ profiles/proximity/linkloss.c \
+ profiles/proximity/immalert.h \
+ profiles/proximity/immalert.c
+
+builtin_modules += thermometer
+builtin_sources += profiles/thermometer/thermometer.c
+
+builtin_modules += heartrate
+builtin_sources += profiles/heartrate/heartrate.c
+
+builtin_modules += cyclingspeed
+builtin_sources += profiles/cyclingspeed/cyclingspeed.c
+endif
+
+if SIXAXIS
+plugin_LTLIBRARIES += plugins/sixaxis.la
+plugins_sixaxis_la_SOURCES = plugins/sixaxis.c
+plugins_sixaxis_la_LDFLAGS = $(AM_LDFLAGS) -module -avoid-version \
+ -no-undefined @UDEV_LIBS@
+plugins_sixaxis_la_CFLAGS = $(AM_CFLAGS) -fvisibility=hidden @UDEV_CFLAGS@
+endif
diff --git a/repo/Makefile.tools b/repo/Makefile.tools
new file mode 100644
index 0000000..e79b53b
--- /dev/null
+++ b/repo/Makefile.tools
@@ -0,0 +1,421 @@
+
+if CLIENT
+bin_PROGRAMS += client/bluetoothctl
+
+client_bluetoothctl_SOURCES = client/main.c \
+ client/display.h client/display.c \
+ client/agent.h client/agent.c \
+ client/gatt.h client/gatt.c \
+ monitor/uuid.h monitor/uuid.c
+client_bluetoothctl_LDADD = gdbus/libgdbus-internal.la @GLIB_LIBS@ @DBUS_LIBS@ \
+ -lreadline
+endif
+
+if MONITOR
+bin_PROGRAMS += monitor/btmon
+
+monitor_btmon_SOURCES = monitor/main.c monitor/bt.h \
+ monitor/display.h monitor/display.c \
+ monitor/hcidump.h monitor/hcidump.c \
+ monitor/ellisys.h monitor/ellisys.c \
+ monitor/control.h monitor/control.c \
+ monitor/packet.h monitor/packet.c \
+ monitor/vendor.h monitor/vendor.c \
+ monitor/lmp.h monitor/lmp.c \
+ monitor/crc.h monitor/crc.c \
+ monitor/ll.h monitor/ll.c \
+ monitor/l2cap.h monitor/l2cap.c \
+ monitor/sdp.h monitor/sdp.c \
+ monitor/avctp.h monitor/avctp.c \
+ monitor/avdtp.h monitor/avdtp.c \
+ monitor/a2dp.h monitor/a2dp.c \
+ monitor/rfcomm.h monitor/rfcomm.c \
+ monitor/bnep.h monitor/bnep.c \
+ monitor/uuid.h monitor/uuid.c \
+ monitor/hwdb.h monitor/hwdb.c \
+ monitor/keys.h monitor/keys.c \
+ monitor/analyze.h monitor/analyze.c \
+ monitor/intel.h monitor/intel.c \
+ monitor/broadcom.h monitor/broadcom.c
+monitor_btmon_LDADD = lib/libbluetooth-internal.la \
+ src/libshared-mainloop.la @UDEV_LIBS@
+endif
+
+if EXPERIMENTAL
+noinst_PROGRAMS += emulator/btvirt emulator/b1ee emulator/hfp \
+ peripheral/btsensor tools/3dsp \
+ tools/mgmt-tester tools/gap-tester \
+ tools/l2cap-tester tools/sco-tester \
+ tools/smp-tester tools/hci-tester \
+ tools/rfcomm-tester tools/bnep-tester \
+ tools/userchan-tester
+
+emulator_btvirt_SOURCES = emulator/main.c monitor/bt.h \
+ emulator/serial.h emulator/serial.c \
+ emulator/server.h emulator/server.c \
+ emulator/vhci.h emulator/vhci.c \
+ emulator/btdev.h emulator/btdev.c \
+ emulator/bthost.h emulator/bthost.c \
+ emulator/smp.c \
+ emulator/phy.h emulator/phy.c \
+ emulator/amp.h emulator/amp.c \
+ emulator/le.h emulator/le.c
+emulator_btvirt_LDADD = lib/libbluetooth-internal.la src/libshared-mainloop.la
+
+emulator_b1ee_SOURCES = emulator/b1ee.c
+emulator_b1ee_LDADD = src/libshared-mainloop.la
+
+emulator_hfp_SOURCES = emulator/hfp.c
+emulator_hfp_LDADD = src/libshared-mainloop.la
+
+peripheral_btsensor_SOURCES = peripheral/main.c \
+ peripheral/efivars.h peripheral/efivars.c \
+ peripheral/attach.h peripheral/attach.c \
+ peripheral/log.h peripheral/log.c \
+ peripheral/gap.h peripheral/gap.c \
+ peripheral/gatt.h peripheral/gatt.c
+peripheral_btsensor_LDADD = src/libshared-mainloop.la \
+ lib/libbluetooth-internal.la
+
+tools_3dsp_SOURCES = tools/3dsp.c monitor/bt.h
+tools_3dsp_LDADD = src/libshared-mainloop.la
+
+tools_mgmt_tester_SOURCES = tools/mgmt-tester.c monitor/bt.h \
+ emulator/hciemu.h emulator/hciemu.c \
+ emulator/btdev.h emulator/btdev.c \
+ emulator/bthost.h emulator/bthost.c \
+ emulator/smp.c
+tools_mgmt_tester_LDADD = lib/libbluetooth-internal.la \
+ src/libshared-glib.la @GLIB_LIBS@
+
+tools_l2cap_tester_SOURCES = tools/l2cap-tester.c monitor/bt.h \
+ emulator/hciemu.h emulator/hciemu.c \
+ emulator/btdev.h emulator/btdev.c \
+ emulator/bthost.h emulator/bthost.c \
+ emulator/smp.c
+tools_l2cap_tester_LDADD = lib/libbluetooth-internal.la \
+ src/libshared-glib.la @GLIB_LIBS@
+
+tools_rfcomm_tester_SOURCES = tools/rfcomm-tester.c monitor/bt.h \
+ emulator/hciemu.h emulator/hciemu.c \
+ emulator/btdev.h emulator/btdev.c \
+ emulator/bthost.h emulator/bthost.c \
+ emulator/smp.c
+tools_rfcomm_tester_LDADD = lib/libbluetooth-internal.la \
+ src/libshared-glib.la @GLIB_LIBS@
+
+tools_bnep_tester_SOURCES = tools/bnep-tester.c monitor/bt.h \
+ emulator/hciemu.h emulator/hciemu.c \
+ emulator/btdev.h emulator/btdev.c \
+ emulator/bthost.h emulator/bthost.c \
+ emulator/smp.c
+tools_bnep_tester_LDADD = lib/libbluetooth-internal.la \
+ src/libshared-glib.la @GLIB_LIBS@
+
+tools_smp_tester_SOURCES = tools/smp-tester.c monitor/bt.h \
+ emulator/hciemu.h emulator/hciemu.c \
+ emulator/btdev.h emulator/btdev.c \
+ emulator/bthost.h emulator/bthost.c \
+ emulator/smp.c
+tools_smp_tester_LDADD = lib/libbluetooth-internal.la \
+ src/libshared-glib.la @GLIB_LIBS@
+
+tools_gap_tester_SOURCES = tools/gap-tester.c monitor/bt.h \
+ emulator/hciemu.h emulator/hciemu.c \
+ emulator/btdev.h emulator/btdev.c \
+ emulator/bthost.h emulator/bthost.c \
+ emulator/smp.c
+tools_gap_tester_LDADD = lib/libbluetooth-internal.la \
+ gdbus/libgdbus-internal.la \
+ src/libshared-glib.la \
+ @GLIB_LIBS@ @DBUS_LIBS@
+
+tools_sco_tester_SOURCES = tools/sco-tester.c monitor/bt.h \
+ emulator/hciemu.h emulator/hciemu.c \
+ emulator/btdev.h emulator/btdev.c \
+ emulator/bthost.h emulator/bthost.c \
+ emulator/smp.c
+tools_sco_tester_LDADD = lib/libbluetooth-internal.la \
+ src/libshared-glib.la @GLIB_LIBS@
+
+tools_hci_tester_SOURCES = tools/hci-tester.c monitor/bt.h
+tools_hci_tester_LDADD = src/libshared-glib.la @GLIB_LIBS@
+
+tools_userchan_tester_SOURCES = tools/userchan-tester.c monitor/bt.h \
+ emulator/hciemu.h emulator/hciemu.c \
+ emulator/btdev.h emulator/btdev.c \
+ emulator/bthost.h emulator/bthost.c \
+ emulator/smp.c
+tools_userchan_tester_LDADD = lib/libbluetooth-internal.la \
+ src/libshared-glib.la @GLIB_LIBS@
+endif
+
+if TOOLS
+bin_PROGRAMS += tools/hciattach tools/hciconfig tools/hcitool tools/hcidump \
+ tools/rfcomm tools/rctest tools/l2test tools/l2ping \
+ tools/sdptool tools/ciptool tools/bccmd \
+ tools/bluemoon tools/hex2hcd tools/mpris-proxy
+
+tools_hciattach_SOURCES = tools/hciattach.c tools/hciattach.h \
+ tools/hciattach_st.c \
+ tools/hciattach_ti.c \
+ tools/hciattach_tialt.c \
+ tools/hciattach_ath3k.c \
+ tools/hciattach_qualcomm.c \
+ tools/hciattach_intel.c \
+ tools/hciattach_bcm43xx.c
+tools_hciattach_LDADD = lib/libbluetooth-internal.la
+
+tools_hciconfig_SOURCES = tools/hciconfig.c tools/csr.h tools/csr.c
+tools_hciconfig_LDADD = lib/libbluetooth-internal.la
+
+tools_hcitool_SOURCES = tools/hcitool.c src/oui.h src/oui.c
+tools_hcitool_LDADD = lib/libbluetooth-internal.la @UDEV_LIBS@
+
+tools_hcidump_SOURCES = tools/hcidump.c \
+ tools/parser/parser.h tools/parser/parser.c \
+ tools/parser/lmp.c \
+ tools/parser/hci.c \
+ tools/parser/l2cap.h tools/parser/l2cap.c \
+ tools/parser/amp.c \
+ tools/parser/smp.c \
+ tools/parser/att.c \
+ tools/parser/sdp.h tools/parser/sdp.c \
+ tools/parser/rfcomm.h tools/parser/rfcomm.c \
+ tools/parser/bnep.c \
+ tools/parser/cmtp.c \
+ tools/parser/hidp.c \
+ tools/parser/hcrp.c \
+ tools/parser/avdtp.c \
+ tools/parser/avctp.c \
+ tools/parser/avrcp.c \
+ tools/parser/sap.c \
+ tools/parser/obex.c \
+ tools/parser/capi.c \
+ tools/parser/ppp.c \
+ tools/parser/tcpip.c \
+ tools/parser/ericsson.c \
+ tools/parser/csr.c \
+ tools/parser/bpa.c
+tools_hcidump_LDADD = lib/libbluetooth-internal.la
+
+tools_rfcomm_LDADD = lib/libbluetooth-internal.la
+
+tools_rctest_LDADD = lib/libbluetooth-internal.la
+
+tools_l2test_LDADD = lib/libbluetooth-internal.la
+
+tools_l2ping_LDADD = lib/libbluetooth-internal.la
+
+tools_sdptool_SOURCES = tools/sdptool.c src/sdp-xml.h src/sdp-xml.c
+tools_sdptool_LDADD = lib/libbluetooth-internal.la @GLIB_LIBS@
+
+tools_ciptool_LDADD = lib/libbluetooth-internal.la
+
+tools_bccmd_SOURCES = tools/bccmd.c tools/csr.h tools/csr.c \
+ tools/csr_hci.c tools/csr_usb.c \
+ tools/csr_h4.c tools/csr_3wire.c \
+ tools/csr_bcsp.c tools/ubcsp.h tools/ubcsp.c
+tools_bccmd_LDADD = lib/libbluetooth-internal.la
+
+tools_bluemoon_SOURCES = tools/bluemoon.c monitor/bt.h
+tools_bluemoon_LDADD = src/libshared-mainloop.la
+
+tools_hex2hcd_SOURCES = tools/hex2hcd.c
+
+tools_mpris_proxy_SOURCES = tools/mpris-proxy.c
+tools_mpris_proxy_LDADD = gdbus/libgdbus-internal.la @GLIB_LIBS@ @DBUS_LIBS@
+
+dist_man_MANS += tools/hciattach.1 tools/hciconfig.1 \
+ tools/hcitool.1 tools/hcidump.1 \
+ tools/rfcomm.1 tools/rctest.1 tools/l2ping.1 \
+ tools/sdptool.1 tools/ciptool.1 tools/bccmd.1
+else
+EXTRA_DIST += tools/hciattach.1 tools/hciconfig.1 \
+ tools/hcitool.1 tools/hcidump.1 \
+ tools/rfcomm.1 tools/rctest.1 tools/l2ping.1 \
+ tools/sdptool.1 tools/ciptool.1 tools/bccmd.1
+endif
+
+if HID2HCI
+udevdir = @UDEV_DIR@
+
+udev_PROGRAMS = tools/hid2hci
+
+tools_hid2hci_LDADD = @UDEV_LIBS@
+
+dist_man_MANS += tools/hid2hci.1
+else
+EXTRA_DIST += tools/hid2hci.1
+endif
+
+if EXPERIMENTAL
+bin_PROGRAMS += tools/btattach
+
+noinst_PROGRAMS += tools/bdaddr tools/avinfo tools/avtest \
+ tools/scotest tools/amptest tools/hwdb \
+ tools/hcieventmask tools/hcisecfilter \
+ tools/btinfo \
+ tools/btsnoop tools/btproxy \
+ tools/btiotest tools/bneptest tools/mcaptest \
+ tools/cltest tools/oobtest tools/seq2bseq \
+ tools/nokfw tools/create-image \
+ tools/eddystone tools/ibeacon \
+ tools/btgatt-client tools/btgatt-server \
+ tools/test-runner tools/check-selftest
+
+tools_bdaddr_SOURCES = tools/bdaddr.c src/oui.h src/oui.c
+tools_bdaddr_LDADD = lib/libbluetooth-internal.la @UDEV_LIBS@
+
+tools_avinfo_LDADD = lib/libbluetooth-internal.la
+
+tools_avtest_LDADD = lib/libbluetooth-internal.la
+
+tools_scotest_LDADD = lib/libbluetooth-internal.la
+
+tools_amptest_LDADD = lib/libbluetooth-internal.la
+
+tools_hwdb_LDADD = lib/libbluetooth-internal.la
+
+tools_hcieventmask_LDADD = lib/libbluetooth-internal.la
+
+tools_btinfo_SOURCES = tools/btinfo.c monitor/bt.h
+tools_btinfo_LDADD = src/libshared-mainloop.la
+
+tools_btattach_SOURCES = tools/btattach.c monitor/bt.h
+tools_btattach_LDADD = src/libshared-mainloop.la
+
+tools_btsnoop_SOURCES = tools/btsnoop.c
+tools_btsnoop_LDADD = src/libshared-mainloop.la
+
+tools_btproxy_SOURCES = tools/btproxy.c monitor/bt.h
+tools_btproxy_LDADD = src/libshared-mainloop.la
+
+tools_btiotest_SOURCES = tools/btiotest.c btio/btio.h btio/btio.c
+tools_btiotest_LDADD = lib/libbluetooth-internal.la @GLIB_LIBS@
+
+tools_mcaptest_SOURCES = tools/mcaptest.c \
+ btio/btio.h btio/btio.c \
+ src/log.c src/log.h \
+ profiles/health/mcap.h profiles/health/mcap.c
+tools_mcaptest_LDADD = lib/libbluetooth-internal.la @GLIB_LIBS@ -lrt
+
+tools_bneptest_SOURCES = tools/bneptest.c \
+ btio/btio.h btio/btio.c \
+ src/log.h src/log.c \
+ profiles/network/bnep.h profiles/network/bnep.c
+tools_bneptest_LDADD = lib/libbluetooth-internal.la @GLIB_LIBS@
+
+tools_cltest_SOURCES = tools/cltest.c
+tools_cltest_LDADD = lib/libbluetooth-internal.la src/libshared-mainloop.la
+
+tools_oobtest_SOURCES = tools/oobtest.c
+tools_oobtest_LDADD = lib/libbluetooth-internal.la src/libshared-mainloop.la
+
+tools_seq2bseq_SOURCES = tools/seq2bseq.c
+
+tools_nokfw_SOURCES = tools/nokfw.c
+
+tools_create_image_SOURCES = tools/create-image.c
+
+tools_eddystone_SOURCES = tools/eddystone.c monitor/bt.h
+tools_eddystone_LDADD = src/libshared-mainloop.la
+
+tools_ibeacon_SOURCES = tools/ibeacon.c monitor/bt.h
+tools_ibeacon_LDADD = src/libshared-mainloop.la
+
+tools_btgatt_client_SOURCES = tools/btgatt-client.c src/uuid-helper.c
+tools_btgatt_client_LDADD = src/libshared-mainloop.la \
+ lib/libbluetooth-internal.la
+
+tools_btgatt_server_SOURCES = tools/btgatt-server.c src/uuid-helper.c
+tools_btgatt_server_LDADD = src/libshared-mainloop.la \
+ lib/libbluetooth-internal.la
+
+dist_man_MANS += tools/btattach.1
+
+EXTRA_DIST += tools/bdaddr.1
+else
+EXTRA_DIST += tools/btattach.1
+endif
+
+if READLINE
+noinst_PROGRAMS += attrib/gatttool tools/btmgmt \
+ tools/obex-client-tool tools/obex-server-tool \
+ tools/bluetooth-player tools/obexctl
+
+attrib_gatttool_SOURCES = attrib/gatttool.c attrib/att.c attrib/gatt.c \
+ attrib/gattrib.c btio/btio.c \
+ attrib/gatttool.h attrib/interactive.c \
+ attrib/utils.c src/log.c client/display.c \
+ client/display.h
+attrib_gatttool_LDADD = lib/libbluetooth-internal.la \
+ src/libshared-glib.la @GLIB_LIBS@ -lreadline
+
+tools_obex_client_tool_SOURCES = $(gobex_sources) $(btio_sources) \
+ tools/obex-client-tool.c
+tools_obex_client_tool_LDADD = lib/libbluetooth-internal.la \
+ @GLIB_LIBS@ -lreadline
+
+tools_obex_server_tool_SOURCES = $(gobex_sources) $(btio_sources) \
+ tools/obex-server-tool.c
+tools_obex_server_tool_LDADD = lib/libbluetooth-internal.la @GLIB_LIBS@
+
+tools_bluetooth_player_SOURCES = tools/bluetooth-player.c \
+ client/display.h client/display.c
+tools_bluetooth_player_LDADD = gdbus/libgdbus-internal.la \
+ @GLIB_LIBS@ @DBUS_LIBS@ -lreadline
+
+tools_obexctl_SOURCES = tools/obexctl.c \
+ client/display.h client/display.c
+tools_obexctl_LDADD = gdbus/libgdbus-internal.la \
+ @GLIB_LIBS@ @DBUS_LIBS@ -lreadline
+
+tools_btmgmt_SOURCES = tools/btmgmt.c src/uuid-helper.c client/display.c
+tools_btmgmt_LDADD = lib/libbluetooth-internal.la src/libshared-mainloop.la \
+ -lreadline
+endif
+
+if EXPERIMENTAL
+noinst_PROGRAMS += tools/gatt-service
+
+tools_gatt_service_SOURCES = tools/gatt-service.c
+tools_gatt_service_LDADD = @GLIB_LIBS@ @DBUS_LIBS@ gdbus/libgdbus-internal.la
+
+noinst_PROGRAMS += profiles/iap/iapd
+
+profiles_iap_iapd_SOURCES = profiles/iap/main.c
+profiles_iap_iapd_LDADD = gdbus/libgdbus-internal.la @GLIB_LIBS@ @DBUS_LIBS@
+endif
+
+if CUPS
+cupsdir = $(libdir)/cups/backend
+
+cups_PROGRAMS = profiles/cups/bluetooth
+
+profiles_cups_bluetooth_SOURCES = profiles/cups/main.c \
+ profiles/cups/cups.h \
+ profiles/cups/sdp.c \
+ profiles/cups/spp.c \
+ profiles/cups/hcrp.c
+
+profiles_cups_bluetooth_LDADD = @GLIB_LIBS@ @DBUS_LIBS@ \
+ lib/libbluetooth-internal.la \
+ gdbus/libgdbus-internal.la
+endif
+
+test_scripts += test/sap_client.py test/bluezutils.py \
+ test/dbusdef.py test/monitor-bluetooth test/list-devices \
+ test/test-discovery test/test-manager test/test-adapter \
+ test/test-device test/simple-agent \
+ test/simple-endpoint test/test-sap-server \
+ test/test-proximity test/test-network \
+ test/test-thermometer test/test-profile test/test-health \
+ test/test-health-sink test/service-record.dtd \
+ test/service-did.xml test/service-spp.xml test/service-opp.xml \
+ test/service-ftp.xml test/simple-player test/test-nap \
+ test/test-heartrate test/test-alert test/test-hfp \
+ test/test-cyclingspeed test/opp-client test/ftp-client \
+ test/pbap-client test/map-client test/example-advertisement \
+ test/example-gatt-server test/example-gatt-client \
+ test/test-gatt-profile
diff --git a/repo/NEWS b/repo/NEWS
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/repo/NEWS
diff --git a/repo/README b/repo/README
new file mode 100644
index 0000000..c991ab0
--- /dev/null
+++ b/repo/README
@@ -0,0 +1,127 @@
+BlueZ - Bluetooth protocol stack for Linux
+******************************************
+
+Copyright (C) 2000-2001 Qualcomm Incorporated
+Copyright (C) 2002-2003 Maxim Krasnyansky <maxk@qualcomm.com>
+Copyright (C) 2002-2010 Marcel Holtmann <marcel@holtmann.org>
+
+
+Compilation and installation
+============================
+
+In order to compile Bluetooth utilities you need following software packages:
+ - GCC compiler
+ - GLib library
+ - D-Bus library
+ - udev library (optional)
+ - readline (command line clients)
+
+To configure run:
+ ./configure --prefix=/usr --mandir=/usr/share/man \
+ --sysconfdir=/etc --localstatedir=/var
+
+Configure automatically searches for all required components and packages.
+
+To compile and install run:
+ make && make install
+
+
+Configuration and options
+=========================
+
+For a working system, certain configuration options need to be enabled:
+
+ --enable-library
+
+ Enable installation of Bluetooth library
+
+ By default the Bluetooth library is no longer installed.
+
+ The user interfaces or command line utilities do not
+ require an installed Bluetooth library anymore. This
+ option is provided for legacy third party applications
+ that still depend on the library.
+
+ When the library installation is enabled, it is a good
+ idea to use a separate bluez-library or libbluetooth
+ package for it.
+
+ --disable-tools
+
+ Disable support for Bluetooth utilities
+
+ By default the Bluetooth utilities are built and also
+ installed. For production systems the tools are not
+ needed and this option allows to disable them to save
+ build time and disk space.
+
+ When the tools are selected, it is a good idea to
+ use a separate bluez-tools package for them.
+
+ --disable-cups
+
+ Disable support for CUPS printer backend
+
+ By default the printer backend for CUPS is build and
+ also installed. For systems that do not require printing
+ over Bluetooth, this options allows to disable it.
+
+ When the CUPS backend is selected, it is a good idea to
+ use a separate bluez-cups package for it.
+
+ --disable-monitor
+
+ Disable support for the Bluetooth monitor utility
+
+ By default the monitor utility is enabled. It provides
+ support for HCI level tracing and debugging. For systems
+ that don't require any kind of tracing or debugging
+ capabilities, this options allows to disable it.
+
+ The monitor utility should be placed in the main package
+ along with the daemons. It is universally useful.
+
+ --disable-client
+
+ Disable support for the command line client
+
+ By default the command line client is enabled and uses the
+ readline library. For specific systems where BlueZ is
+ configured by other means, the command line client can be
+ disabled and the dependency on readline is removed.
+
+ The client should be placed in the main package along
+ with the daemons. It is universally useful.
+
+ --disable-systemd
+
+ Disable integration with systemd
+
+ By default the integration with systemd is enabled and
+ installed. This gives the best integration into all
+ distributions based on systemd.
+
+ This option is provided for distributions that do not
+ support systemd. In that case all integration with the
+ init system is up to the package.
+
+ --enable-experimental
+
+ Enable experimental plugins
+
+ By default all plugins that are still in development
+ are disabled. This option can be used to enable them.
+
+ It is not recommended to enable this option for production
+ systems. The APIs or behavior of the experimental plugins
+ is unstable and might still change.
+
+
+Information
+===========
+
+Mailing lists:
+ linux-bluetooth@vger.kernel.org
+
+For additional information about the project visit BlueZ web site:
+ http://www.bluez.org
diff --git a/repo/TODO b/repo/TODO
new file mode 100644
index 0000000..8c710ef
--- /dev/null
+++ b/repo/TODO
@@ -0,0 +1,288 @@
+Background
+==========
+
+- Priority scale: High, Medium and Low
+
+- Complexity scale: C1, C2, C4 and C8. The complexity scale is exponential,
+ with complexity 1 being the lowest complexity. Complexity is a function
+ of both task 'complexity' and task 'scope'.
+
+ The general rule of thumb is that a complexity 1 task should take 1-2 weeks
+ for a person very familiar with BlueZ codebase. Higher complexity tasks
+ require more time and have higher uncertainty.
+
+ Higher complexity tasks should be refined into several lower complexity tasks
+ once the task is better understood.
+
+General
+=======
+
+- UUID handling: Use the new functions created for UUID handling in all parts
+ of BlueZ code. Currently, the new bt_uuid_* functions are being used by
+ GATT-related code only.
+
+ Priority: high
+ Complexity: C4
+
+- Update PBAP client/server implementation to 1.2 and create necessary APIs for
+ new features it introduces.
+
+ Priority: Medium
+ Complexity: C4
+
+- Create GOEP unit tests based on its test specification:
+
+ https://www.bluetooth.org/docman/handlers/DownloadDoc.ashx?doc_id=230559
+
+ Priority: Medium
+ Complexity: C2
+
+- Function in src/adapter.c to convert old storage files to new ini-file format
+ should be removed 6-8 months after first BlueZ 5 release.
+
+ Priority: Low
+ Complexity: C1
+
+- Remove usage of symlinks for drivers, such as profiles/input/suspend.c and
+ profiles/sap/sap.c. Instead, select drivers at runtime by using config
+ options or probing for running D-Bus services (using e.g.
+ g_dbus_add_service_watch()). Idea first mentioned on
+ http://thread.gmane.org/gmane.linux.bluez.kernel/30175/focus=30190.
+
+- Reuse connection handling code of src/profile.c also for built-in profiles
+ so plugins would only need to register their btd_profile and the core takes
+ care of the rest including listen to the right channel and manages the sdp
+ record. Once btd_profile manages the connection it can also notify about
+ their state, this probably remove the need of having callbacks to
+ .connect/.disconnect since their state can be tracked, it also enables any
+ plugin to track any profile state change which can be useful for e.g.
+ a connection policy plugin in case one is needed.
+
+ Priority: Low
+ Complexity: C2
+
+- Add queueing support for src/agent.c, currently if there is any request
+ pending the code fail with error EBUSY which is very inconvenient.
+
+ Priority: Low
+ Complexity: C2
+
+Low Energy
+==========
+
+- Connection modes. Adapter interface needs to be changed to manage
+ connection modes and adapter type. See Volume 3, Part C, section 9.3.
+ 1. Mode management: Peripheral / Central
+
+ Priority: Medium
+ Complexity: C2
+
+- Advertising data. The D-Bus interface needs to be updated to enable setting
+ scan response data, and to read the advertising and scan response data which
+ has been broadcast from other LE devices.
+
+ Priority: Medium
+ Complexity: C2
+
+- Static random address setup and storage. Once this address is written
+ in a given remote, the address can not be changed anymore.
+
+ Priority: Low
+ Complexity: C1
+
+- Device Name Characteristic is a GAP characteristic for Low Energy. This
+ characteristic shall be integrated/used in the discovery procedure. The
+ idea is to report the value of this characteristic using DeviceFound signals.
+ Discussion with the community is needed before to start this task. Other GAP
+ characteristics for LE needs to follow a similar approach. It is not clear
+ if all GAP characteristics can be exposed using properties instead of a primary
+ service characteristics.
+ See Volume 3, Part C, section 12.1 for more information.
+
+ Priority: Low
+ Complexity: C2
+
+ATT/GATT (new shared stack)
+===========================
+
+- Add complete GATT test coverage in unit/test-gatt following the GATT test
+ spec. This could use shared/gatt-client and shared/gatt-server at the same
+ time to test both against eachother. We should definitely have tests for
+ gatt-server and gatt-client simultaneously on one side of the connection.
+
+ Priority: High
+ Complexity: C4
+
+- Write an example using client D-Bus API using C.
+
+ Priority: High
+ Complexity: C2
+
+- Write an example using client D-Bus API using python.
+
+ Priority: High
+ Complexity: C2
+
+- Define packed structs for ATT protocol PDUs in shared/att-types to improve
+ readability. We should probably do this once there are extensive unit tests
+ for gatt-client/gatt-server so that we don't accidentally break working code.
+
+ Priority: Medium
+ Complexity: C2
+
+- Use struct iovec to pass around byte buffers that will be sent over the wire,
+ instead of passing uint8_t and size_t parameters everywhere.
+
+ Priority: Medium
+ Complexity: C1
+
+- Persist client attribute cache across reboots.
+
+ Priority: Medium
+ Complexity: C4
+
+- Move all daemon plugins and profiles that are GATT based to use
+ shared/gatt-client instead of attrib/*. This is a complicated task that
+ potentially needs a new plugin/profile probing interface and a lot of
+ rewriting that can cause regressions in existing functionality.
+
+ Priority: Medium
+ Complexity: C4
+
+- Introduce a way for shared/gatt-server to check security permissions on the
+ current connection through bt_att.
+
+ Priority: Medium
+ Complexity: C2
+
+- Implement other low-priority ATT protocol operations for shared/gatt-server:
+
+ Read Multiple Request
+
+ Priority: Low
+ Complexity: C1
+
+- Implement the server portion of doc/gatt-api.txt using shared/gatt-server once
+ it exists.
+
+ Priority: Medium
+ Complexity: C4
+
+- Send out indications from the "Service Changed" characteristic upon
+ reconnection if a bonded device is not connected when the local database is
+ modified.
+
+ Priority: High
+ Complexity: C2
+
+- Unify the GATT server and client D-Bus implementations into a single module.
+ While these don't share a lot of code, keeping them all in src/gatt-dbus seems
+ to make more sense from an organizational perspective.
+
+ Priority: Low
+ Complexity: C1
+
+- Isolate all GATT code inside the daemon into its own module and perform
+ interaction with other modules (e.g. src/device.c) via callbacks. This
+ includes client/server management, tracking incoming/outgoing connections for
+ ATT, and callbacks to perform profile probing.
+
+ Priority: Low
+ Complexity: C4
+
+- Support included services in the GATT D-Bus client API.
+
+ Priority: Medium
+ Complexity: C1
+
+- The recently added support for ATT signed writes requires the following kernel
+ modules to be enabled:
+
+ CONFIG_CRYPTO_USER_API
+ CONFIG_CRYPTO_USER_API_HASH
+ CONFIG_CRYPTO_USER_API_SKCIPHER
+
+ Currently, if these are not enabled, bt_att_new silently returns NULL. We
+ should handle this more gracefully by not supporting signed writes if we can't
+ initialize bt_crypto while succeeding bt_att initialization regardless.
+
+ This behavior should be documented in the README.
+
+ Priority: High
+ Complexity: C1
+
+
+ATT/GATT (old/outdated)
+=======================
+
+- At the moment authentication and authorization is not supported at the
+ same time, read/write requirements in the attribute server needs to
+ be extended. According to Bluetooth Specification a server shall check
+ authentication and authorization requirements before any other check is
+ performed.
+
+ Priority: Medium
+ Complexity: C1
+
+- Implement ATT PDU validation. Malformed PDUs can cause division by zero
+ when decoding PDUs. A proper error PDU should be returned for this case.
+ See decoding function in att.c file.
+
+ Priority: Medium
+ Complexity: C1
+
+- Refactor read_by_group() and read_by_type() in src/attrib-server.c
+ (they've grown simply too big). First step could be to move out the
+ long for-loops to new functions called e.g. get_groups() and get_types().
+
+ Priority: Low
+ Complexity: C1
+
+- Agent for characteristics: Agent interface should be extended to support
+ authorization per characteristic if the remote is not in the trusted list.
+
+ Priority: Low
+ Complexity: C1
+
+- gatttool should have the ability to wait for req responses before
+ quitting (some servers require a small sleep even with cmd's). Maybe a
+ --delay-exit or --timeout command line switch.
+
+ Priority: Low
+ Complexity: C1
+
+- Client needs to export a property in the Device Characteristic hierarchy
+ to manage characteristic value changes reports in the remote device.
+ Currently, Client Characteristic Configuration attribute is not exposed
+ as an object. The user needs to use gatttool to change the value of the
+ this attribute to receive notification/indications. Export this attribute
+ as a property is a proposal that needs further discussion.
+
+ Priority: Low
+ Complexity: C1
+
+- Attribute server should process queued GATT/ATT commands if the
+ client disconnects. The client can simply send a command and quit,
+ without wait for a response(ex: Write Command). For this scenario
+ that the client disconnects the link quickly the queued received
+ command is ignored.
+
+ Priority: Low
+ Complecity: C1
+
+- Implement Server characteristic Configuration support in the attribute
+ server to manage characteristic value broadcasting. There is a single
+ instance of the Server Characteristic Configuration for all clients.
+ See Volume 3, Part G, section 3.3.3.4 for more information.
+
+ Priority: Low
+ Complexity: C1
+
+- Long write is not implemented. Attribute server, client and command line
+ tool shall be changed to support this feature.
+
+ Priority: Low
+ Complexity: C2
+
+Management Interface
+====================
diff --git a/repo/acinclude.m4 b/repo/acinclude.m4
new file mode 100644
index 0000000..bc39c6d
--- /dev/null
+++ b/repo/acinclude.m4
@@ -0,0 +1,62 @@
+AC_DEFUN([AC_PROG_CC_PIE], [
+ AC_CACHE_CHECK([whether ${CC-cc} accepts -fPIE], ac_cv_prog_cc_pie, [
+ echo 'void f(){}' > conftest.c
+ if test -z "`${CC-cc} -fPIE -pie -c conftest.c 2>&1`"; then
+ ac_cv_prog_cc_pie=yes
+ else
+ ac_cv_prog_cc_pie=no
+ fi
+ rm -rf conftest*
+ ])
+])
+
+AC_DEFUN([COMPILER_FLAGS], [
+ with_cflags=""
+ if (test "$USE_MAINTAINER_MODE" = "yes"); then
+ with_cflags="$with_cflags -Wall -Werror -Wextra"
+ with_cflags="$with_cflags -Wno-unused-parameter"
+ with_cflags="$with_cflags -Wno-missing-field-initializers"
+ with_cflags="$with_cflags -Wdeclaration-after-statement"
+ with_cflags="$with_cflags -Wmissing-declarations"
+ with_cflags="$with_cflags -Wredundant-decls"
+ with_cflags="$with_cflags -Wcast-align"
+ with_cflags="$with_cflags -Wswitch-enum"
+ with_cflags="$with_cflags -Wformat -Wformat-security"
+ with_cflags="$with_cflags -DG_DISABLE_DEPRECATED"
+ with_cflags="$with_cflags -DGLIB_VERSION_MIN_REQUIRED=GLIB_VERSION_2_28"
+ with_cflags="$with_cflags -DGLIB_VERSION_MAX_ALLOWED=GLIB_VERSION_2_28"
+ fi
+ AC_SUBST([WARNING_CFLAGS], $with_cflags)
+])
+
+AC_DEFUN([MISC_FLAGS], [
+ misc_cflags=""
+ misc_ldflags=""
+ AC_ARG_ENABLE(optimization, AC_HELP_STRING([--disable-optimization],
+ [disable code optimization through compiler]), [
+ if (test "${enableval}" = "no"); then
+ misc_cflags="$misc_cflags -O0"
+ fi
+ ])
+ AC_ARG_ENABLE(debug, AC_HELP_STRING([--enable-debug],
+ [enable compiling with debugging information]), [
+ if (test "${enableval}" = "yes" &&
+ test "${ac_cv_prog_cc_g}" = "yes"); then
+ misc_cflags="$misc_cflags -g"
+ fi
+ ])
+ AC_ARG_ENABLE(pie, AC_HELP_STRING([--enable-pie],
+ [enable position independent executables flag]), [
+ if (test "${enableval}" = "yes" &&
+ test "${ac_cv_prog_cc_pie}" = "yes"); then
+ misc_cflags="$misc_cflags -fPIC"
+ misc_ldflags="$misc_ldflags -pie"
+ fi
+ ])
+ if (test "$enable_coverage" = "yes"); then
+ misc_cflags="$misc_cflags --coverage"
+ misc_ldflags="$misc_ldflags --coverage"
+ fi
+ AC_SUBST([MISC_CFLAGS], $misc_cflags)
+ AC_SUBST([MISC_LDFLAGS], $misc_ldflags)
+])
diff --git a/repo/android/Android.mk b/repo/android/Android.mk
new file mode 100644
index 0000000..38ef4aa
--- /dev/null
+++ b/repo/android/Android.mk
@@ -0,0 +1,855 @@
+LOCAL_PATH := external/bluetooth
+
+# Retrieve BlueZ version from configure.ac file
+BLUEZ_VERSION := `grep "^AC_INIT" $(LOCAL_PATH)/bluez/configure.ac | sed -e "s/.*,.\(.*\))/\1/"`
+
+ANDROID_VERSION := $(shell echo $(PLATFORM_VERSION) | awk -F. '{ printf "0x%02d%02d%02d",$$1,$$2,$$3 }')
+
+ANDROID_GE_5_0_0 := $(shell test `echo $$(($(ANDROID_VERSION)))` -lt `echo $$((0x050000))`; echo $$?)
+
+# Specify pathmap for glib and sbc
+pathmap_INCL += glib:external/bluetooth/glib \
+ sbc:external/bluetooth/sbc \
+
+# Specify common compiler flags
+BLUEZ_COMMON_CFLAGS := -DVERSION=\"$(BLUEZ_VERSION)\" \
+ -DANDROID_VERSION=$(ANDROID_VERSION) \
+ -DANDROID_STORAGEDIR=\"/data/misc/bluetooth\" \
+ -DHAVE_LINUX_IF_ALG_H \
+ -DHAVE_LINUX_TYPES_H \
+
+# Enable warnings enabled in autotools build
+BLUEZ_COMMON_CFLAGS += -Wall -Wextra \
+ -Wdeclaration-after-statement \
+ -Wmissing-declarations \
+ -Wredundant-decls \
+ -Wcast-align \
+
+# Disable warnings enabled by Android but not enabled in autotools build
+BLUEZ_COMMON_CFLAGS += -Wno-pointer-arith \
+ -Wno-missing-field-initializers \
+ -Wno-unused-parameter \
+
+#
+# Android BlueZ daemon (bluetoothd)
+#
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := \
+ bluez/android/main.c \
+ bluez/android/bluetooth.c \
+ bluez/profiles/scanparam/scpp.c \
+ bluez/profiles/deviceinfo/dis.c \
+ bluez/profiles/battery/bas.c \
+ bluez/profiles/input/hog-lib.c \
+ bluez/android/hidhost.c \
+ bluez/android/socket.c \
+ bluez/android/ipc.c \
+ bluez/android/avdtp.c \
+ bluez/android/a2dp.c \
+ bluez/android/a2dp-sink.c \
+ bluez/android/avctp.c \
+ bluez/android/avrcp.c \
+ bluez/android/avrcp-lib.c \
+ bluez/android/pan.c \
+ bluez/android/handsfree.c \
+ bluez/android/handsfree-client.c \
+ bluez/android/gatt.c \
+ bluez/android/health.c \
+ bluez/android/sco.c \
+ bluez/profiles/health/mcap.c \
+ bluez/android/map-client.c \
+ bluez/android/log.c \
+ bluez/src/shared/mgmt.c \
+ bluez/src/shared/util.c \
+ bluez/src/shared/queue.c \
+ bluez/src/shared/ringbuf.c \
+ bluez/src/shared/hfp.c \
+ bluez/src/shared/gatt-db.c \
+ bluez/src/shared/io-glib.c \
+ bluez/src/shared/timeout-glib.c \
+ bluez/src/shared/crypto.c \
+ bluez/src/shared/uhid.c \
+ bluez/src/shared/att.c \
+ bluez/src/sdpd-database.c \
+ bluez/src/sdpd-service.c \
+ bluez/src/sdpd-request.c \
+ bluez/src/sdpd-server.c \
+ bluez/src/uuid-helper.c \
+ bluez/src/eir.c \
+ bluez/lib/sdp.c \
+ bluez/lib/bluetooth.c \
+ bluez/lib/hci.c \
+ bluez/lib/uuid.c \
+ bluez/btio/btio.c \
+ bluez/src/sdp-client.c \
+ bluez/profiles/network/bnep.c \
+ bluez/attrib/gattrib.c \
+ bluez/attrib/gatt.c \
+ bluez/attrib/att.c
+
+LOCAL_C_INCLUDES := \
+ $(call include-path-for, glib) \
+ $(call include-path-for, glib)/glib \
+
+LOCAL_C_INCLUDES += \
+ $(LOCAL_PATH)/bluez \
+
+LOCAL_CFLAGS := $(BLUEZ_COMMON_CFLAGS)
+
+LOCAL_SHARED_LIBRARIES := \
+ libglib \
+
+LOCAL_STATIC_LIBRARIES := \
+ bluetooth-headers \
+
+LOCAL_MODULE_TAGS := optional
+
+# for userdebug/eng this module is bluetoothd-main since bluetoothd is used as
+# wrapper to launch bluetooth with Valgrind
+ifneq (,$(filter userdebug eng,$(TARGET_BUILD_VARIANT)))
+LOCAL_MODULE := bluetoothd-main
+LOCAL_STRIP_MODULE := false
+else
+LOCAL_MODULE := bluetoothd
+endif
+
+LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/bluez/configure.ac
+
+include $(BUILD_EXECUTABLE)
+
+#
+# bluetooth.default.so HAL
+#
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := \
+ bluez/android/hal-ipc.c \
+ bluez/android/hal-bluetooth.c \
+ bluez/android/hal-socket.c \
+ bluez/android/hal-hidhost.c \
+ bluez/android/hal-pan.c \
+ bluez/android/hal-a2dp.c \
+ bluez/android/hal-avrcp.c \
+ bluez/android/hal-handsfree.c \
+ bluez/android/hal-gatt.c \
+ bluez/android/hal-utils.c \
+ bluez/android/hal-health.c \
+
+ifeq ($(ANDROID_GE_5_0_0), 1)
+LOCAL_SRC_FILES += \
+ bluez/android/hal-handsfree-client.c \
+ bluez/android/hal-map-client.c \
+ bluez/android/hal-a2dp-sink.c \
+ bluez/android/hal-avrcp-ctrl.c
+endif
+
+LOCAL_C_INCLUDES += \
+ $(call include-path-for, system-core) \
+ $(call include-path-for, libhardware) \
+
+LOCAL_SHARED_LIBRARIES := \
+ libcutils \
+
+LOCAL_CFLAGS := $(BLUEZ_COMMON_CFLAGS)
+
+LOCAL_MODULE := bluetooth.default
+LOCAL_MODULE_TAGS := optional
+LOCAL_MODULE_CLASS := SHARED_LIBRARIES
+LOCAL_REQUIRED_MODULES := bluetoothd bluetoothd-snoop init.bluetooth.rc
+
+ifeq ($(ANDROID_GE_5_0_0), 1)
+LOCAL_MODULE_RELATIVE_PATH := hw
+else
+LOCAL_MODULE_PATH := $(TARGET_OUT_SHARED_LIBRARIES)/hw
+endif
+
+include $(BUILD_SHARED_LIBRARY)
+
+#
+# haltest
+#
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := \
+ bluez/android/client/haltest.c \
+ bluez/android/client/pollhandler.c \
+ bluez/android/client/terminal.c \
+ bluez/android/client/history.c \
+ bluez/android/client/tabcompletion.c \
+ bluez/android/client/if-audio.c \
+ bluez/android/client/if-sco.c \
+ bluez/android/client/if-av.c \
+ bluez/android/client/if-rc.c \
+ bluez/android/client/if-bt.c \
+ bluez/android/client/if-hf.c \
+ bluez/android/client/if-hh.c \
+ bluez/android/client/if-pan.c \
+ bluez/android/client/if-hl.c \
+ bluez/android/client/if-sock.c \
+ bluez/android/client/if-gatt.c \
+ bluez/android/hal-utils.c \
+
+ifeq ($(ANDROID_GE_5_0_0), 1)
+LOCAL_SRC_FILES += \
+ bluez/android/client/if-hf-client.c \
+ bluez/android/client/if-mce.c \
+ bluez/android/client/if-av-sink.c \
+ bluez/android/client/if-rc-ctrl.c
+endif
+
+LOCAL_C_INCLUDES += \
+ $(call include-path-for, system-core) \
+ $(call include-path-for, libhardware) \
+
+LOCAL_C_INCLUDES += \
+ $(LOCAL_PATH)/bluez/android \
+
+LOCAL_CFLAGS := $(BLUEZ_COMMON_CFLAGS) -Wno-declaration-after-statement
+
+LOCAL_SHARED_LIBRARIES := \
+ libhardware \
+ libcutils \
+
+LOCAL_MODULE_PATH := $(TARGET_OUT_OPTIONAL_EXECUTABLES)
+LOCAL_MODULE_TAGS := debug
+LOCAL_MODULE := haltest
+
+include $(BUILD_EXECUTABLE)
+
+#
+# mcaptest
+#
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := \
+ bluez/src/log.c \
+ bluez/btio/btio.c \
+ bluez/lib/bluetooth.c \
+ bluez/lib/hci.c \
+ bluez/profiles/health/mcap.c \
+ bluez/tools/mcaptest.c \
+
+LOCAL_C_INCLUDES := \
+ $(call include-path-for, glib) \
+ $(call include-path-for, glib)/glib \
+
+LOCAL_C_INCLUDES += \
+ $(LOCAL_PATH)/bluez \
+
+LOCAL_CFLAGS := $(BLUEZ_COMMON_CFLAGS)
+
+LOCAL_SHARED_LIBRARIES := \
+ libglib \
+
+LOCAL_STATIC_LIBRARIES := \
+ bluetooth-headers \
+
+LOCAL_MODULE_PATH := $(TARGET_OUT_OPTIONAL_EXECUTABLES)
+LOCAL_MODULE_TAGS := debug
+LOCAL_MODULE := mcaptest
+
+include $(BUILD_EXECUTABLE)
+
+#
+# bneptest
+#
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := \
+ bluez/src/log.c \
+ bluez/btio/btio.c \
+ bluez/lib/bluetooth.c \
+ bluez/lib/hci.c \
+ bluez/profiles/network/bnep.c \
+ bluez/tools/bneptest.c \
+
+LOCAL_C_INCLUDES := \
+ $(call include-path-for, glib) \
+ $(call include-path-for, glib)/glib \
+
+LOCAL_C_INCLUDES += \
+ $(LOCAL_PATH)/bluez \
+
+LOCAL_CFLAGS := $(BLUEZ_COMMON_CFLAGS)
+
+LOCAL_SHARED_LIBRARIES := \
+ libglib \
+
+LOCAL_STATIC_LIBRARIES := \
+ bluetooth-headers \
+
+LOCAL_MODULE_PATH := $(TARGET_OUT_OPTIONAL_EXECUTABLES)
+LOCAL_MODULE_TAGS := debug
+LOCAL_MODULE := bneptest
+
+include $(BUILD_EXECUTABLE)
+
+#
+# avdtptest
+#
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := \
+ bluez/android/avdtptest.c \
+ bluez/android/avdtp.c \
+ bluez/src/log.c \
+ bluez/btio/btio.c \
+ bluez/lib/bluetooth.c \
+ bluez/lib/hci.c \
+ bluez/src/shared/util.c \
+ bluez/src/shared/queue.c \
+
+LOCAL_C_INCLUDES += \
+ $(LOCAL_PATH)/bluez \
+ $(call include-path-for, glib) \
+ $(call include-path-for, glib)/glib \
+
+LOCAL_CFLAGS := $(BLUEZ_COMMON_CFLAGS)
+
+LOCAL_SHARED_LIBRARIES := \
+ libglib \
+
+LOCAL_STATIC_LIBRARIES := \
+ bluetooth-headers \
+
+LOCAL_MODULE_PATH := $(TARGET_OUT_OPTIONAL_EXECUTABLES)
+LOCAL_MODULE_TAGS := debug
+LOCAL_MODULE := avdtptest
+
+include $(BUILD_EXECUTABLE)
+
+#
+# btmon
+#
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := \
+ bluez/monitor/main.c \
+ bluez/monitor/display.c \
+ bluez/monitor/hcidump.c \
+ bluez/monitor/control.c \
+ bluez/monitor/packet.c \
+ bluez/monitor/l2cap.c \
+ bluez/monitor/avctp.c \
+ bluez/monitor/avdtp.c \
+ bluez/monitor/a2dp.c \
+ bluez/monitor/rfcomm.c \
+ bluez/monitor/bnep.c \
+ bluez/monitor/uuid.c \
+ bluez/monitor/sdp.c \
+ bluez/monitor/vendor.c \
+ bluez/monitor/lmp.c \
+ bluez/monitor/crc.c \
+ bluez/monitor/ll.c \
+ bluez/monitor/hwdb.c \
+ bluez/monitor/keys.c \
+ bluez/monitor/ellisys.c \
+ bluez/monitor/analyze.c \
+ bluez/monitor/intel.c \
+ bluez/monitor/broadcom.c \
+ bluez/src/shared/util.c \
+ bluez/src/shared/queue.c \
+ bluez/src/shared/crypto.c \
+ bluez/src/shared/btsnoop.c \
+ bluez/src/shared/mainloop.c \
+ bluez/lib/hci.c \
+ bluez/lib/bluetooth.c \
+
+LOCAL_C_INCLUDES := \
+ $(LOCAL_PATH)/bluez \
+
+LOCAL_CFLAGS := $(BLUEZ_COMMON_CFLAGS)
+
+LOCAL_STATIC_LIBRARIES := \
+ bluetooth-headers \
+
+LOCAL_MODULE_PATH := $(TARGET_OUT_OPTIONAL_EXECUTABLES)
+LOCAL_MODULE_TAGS := debug
+LOCAL_MODULE := btmon
+
+LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/bluez/configure.ac
+
+include $(BUILD_EXECUTABLE)
+
+#
+# btproxy
+#
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := \
+ bluez/tools/btproxy.c \
+ bluez/src/shared/mainloop.c \
+ bluez/src/shared/util.c \
+ bluez/src/shared/ecc.c \
+
+LOCAL_C_INCLUDES := \
+ $(LOCAL_PATH)/bluez \
+
+LOCAL_CFLAGS := $(BLUEZ_COMMON_CFLAGS)
+
+LOCAL_MODULE_PATH := $(TARGET_OUT_OPTIONAL_EXECUTABLES)
+LOCAL_MODULE_TAGS := debug
+LOCAL_MODULE := btproxy
+
+LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/bluez/configure.ac
+
+include $(BUILD_EXECUTABLE)
+
+#
+# A2DP audio
+#
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := \
+ bluez/android/hal-audio.c \
+ bluez/android/hal-audio-sbc.c \
+ bluez/android/hal-audio-aptx.c \
+
+LOCAL_C_INCLUDES = \
+ $(LOCAL_PATH)/bluez \
+ $(call include-path-for, system-core) \
+ $(call include-path-for, libhardware) \
+ $(call include-path-for, sbc) \
+
+LOCAL_SHARED_LIBRARIES := \
+ libcutils \
+ libsbc \
+
+LOCAL_CFLAGS := $(BLUEZ_COMMON_CFLAGS) -Wno-declaration-after-statement
+LOCAL_LDFLAGS := -ldl
+
+LOCAL_MODULE_TAGS := optional
+LOCAL_MODULE := audio.a2dp.default
+
+ifeq ($(ANDROID_GE_5_0_0), 1)
+LOCAL_MODULE_RELATIVE_PATH := hw
+else
+LOCAL_MODULE_PATH := $(TARGET_OUT_SHARED_LIBRARIES)/hw
+endif
+
+include $(BUILD_SHARED_LIBRARY)
+
+#
+# SCO audio
+#
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := bluez/android/hal-sco.c \
+ bluez/android/hal-utils.c
+
+LOCAL_C_INCLUDES = \
+ $(call include-path-for, system-core) \
+ $(call include-path-for, libhardware) \
+ $(call include-path-for, audio-utils) \
+
+LOCAL_SHARED_LIBRARIES := \
+ libcutils \
+ libaudioutils \
+
+LOCAL_CFLAGS := $(BLUEZ_COMMON_CFLAGS) -Wno-declaration-after-statement
+
+LOCAL_MODULE_TAGS := optional
+LOCAL_MODULE := audio.sco.default
+
+ifeq ($(ANDROID_GE_5_0_0), 1)
+LOCAL_MODULE_RELATIVE_PATH := hw
+else
+LOCAL_MODULE_PATH := $(TARGET_OUT_SHARED_LIBRARIES)/hw
+endif
+
+include $(BUILD_SHARED_LIBRARY)
+
+#
+# l2cap-test
+#
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := \
+ bluez/tools/l2test.c \
+ bluez/lib/bluetooth.c \
+ bluez/lib/hci.c \
+
+LOCAL_C_INCLUDES := \
+ $(LOCAL_PATH)/bluez \
+
+LOCAL_CFLAGS := $(BLUEZ_COMMON_CFLAGS)
+
+LOCAL_STATIC_LIBRARIES := \
+ bluetooth-headers \
+
+LOCAL_MODULE_PATH := $(TARGET_OUT_OPTIONAL_EXECUTABLES)
+LOCAL_MODULE_TAGS := debug
+LOCAL_MODULE := l2test
+
+LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/bluez/configure.ac
+
+include $(BUILD_EXECUTABLE)
+
+#
+# bluetoothd-snoop
+#
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := \
+ bluez/android/bluetoothd-snoop.c \
+ bluez/src/shared/mainloop.c \
+ bluez/src/shared/btsnoop.c \
+ bluez/android/log.c \
+
+LOCAL_C_INCLUDES := \
+ $(LOCAL_PATH)/bluez \
+ $(LOCAL_PATH)/bluez/lib \
+
+LOCAL_CFLAGS := $(BLUEZ_COMMON_CFLAGS)
+
+LOCAL_MODULE_TAGS := optional
+LOCAL_MODULE := bluetoothd-snoop
+
+LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/bluez/configure.ac
+
+include $(BUILD_EXECUTABLE)
+
+#
+# init.bluetooth.rc
+#
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := init.bluetooth.rc
+LOCAL_MODULE_CLASS := ETC
+LOCAL_SRC_FILES := bluez/android/$(LOCAL_MODULE)
+LOCAL_MODULE_TAGS := optional
+LOCAL_MODULE_PATH := $(TARGET_ROOT_OUT)
+
+include $(BUILD_PREBUILT)
+
+#
+# btmgmt
+#
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := \
+ bluez/tools/btmgmt.c \
+ bluez/lib/bluetooth.c \
+ bluez/lib/hci.c \
+ bluez/lib/sdp.c \
+ bluez/src/shared/mainloop.c \
+ bluez/src/shared/io-mainloop.c \
+ bluez/src/shared/mgmt.c \
+ bluez/src/shared/queue.c \
+ bluez/src/shared/util.c \
+ bluez/src/shared/gap.c \
+ bluez/src/uuid-helper.c \
+ bluez/client/display.c \
+
+LOCAL_C_INCLUDES := \
+ $(LOCAL_PATH)/bluez \
+ $(LOCAL_PATH)/bluez/android/compat \
+
+LOCAL_CFLAGS := $(BLUEZ_COMMON_CFLAGS)
+
+LOCAL_STATIC_LIBRARIES := \
+ bluetooth-headers \
+
+LOCAL_MODULE_PATH := $(TARGET_OUT_OPTIONAL_EXECUTABLES)
+LOCAL_MODULE_TAGS := debug
+LOCAL_MODULE := btmgmt
+
+LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/bluez/configure.ac
+
+include $(BUILD_EXECUTABLE)
+
+#
+# hcitool
+#
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := \
+ bluez/tools/hcitool.c \
+ bluez/src/oui.c \
+ bluez/lib/bluetooth.c \
+ bluez/lib/hci.c \
+
+LOCAL_C_INCLUDES := \
+ $(LOCAL_PATH)/bluez \
+
+LOCAL_CFLAGS := $(BLUEZ_COMMON_CFLAGS)
+
+LOCAL_STATIC_LIBRARIES := \
+ bluetooth-headers \
+
+LOCAL_MODULE_PATH := $(TARGET_OUT_OPTIONAL_EXECUTABLES)
+LOCAL_MODULE_TAGS := debug
+LOCAL_MODULE := hcitool
+
+LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/bluez/configure.ac
+
+include $(BUILD_EXECUTABLE)
+
+#
+# hciconfig
+#
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES:= \
+ bluez/tools/hciconfig.c \
+ bluez/tools/csr.c \
+ bluez/lib/bluetooth.c \
+ bluez/lib/hci.c \
+
+LOCAL_C_INCLUDES := \
+ $(LOCAL_PATH)/bluez \
+
+LOCAL_CFLAGS := $(BLUEZ_COMMON_CFLAGS)
+
+LOCAL_STATIC_LIBRARIES := \
+ bluetooth-headers \
+
+LOCAL_MODULE_PATH := $(TARGET_OUT_OPTIONAL_EXECUTABLES)
+LOCAL_MODULE_TAGS := debug
+LOCAL_MODULE := hciconfig
+
+LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/bluez/configure.ac
+
+include $(BUILD_EXECUTABLE)
+
+#
+# l2ping
+#
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := \
+ bluez/tools/l2ping.c \
+ bluez/lib/bluetooth.c \
+ bluez/lib/hci.c \
+
+LOCAL_C_INCLUDES := \
+ $(LOCAL_PATH)/bluez \
+
+LOCAL_CFLAGS := $(BLUEZ_COMMON_CFLAGS)
+
+LOCAL_STATIC_LIBRARIES := \
+ bluetooth-headers \
+
+LOCAL_MODULE_PATH := $(TARGET_OUT_OPTIONAL_EXECUTABLES)
+LOCAL_MODULE_TAGS := debug
+LOCAL_MODULE := l2ping
+
+LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/bluez/configure.ac
+
+include $(BUILD_EXECUTABLE)
+
+#
+# avtest
+#
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := \
+ bluez/tools/avtest.c \
+ bluez/lib/bluetooth.c \
+ bluez/lib/hci.c \
+
+LOCAL_C_INCLUDES := \
+ $(LOCAL_PATH)/bluez \
+
+LOCAL_CFLAGS := $(BLUEZ_COMMON_CFLAGS)
+
+LOCAL_STATIC_LIBRARIES := \
+ bluetooth-headers \
+
+LOCAL_MODULE_PATH := $(TARGET_OUT_OPTIONAL_EXECUTABLES)
+LOCAL_MODULE_TAGS := debug
+LOCAL_MODULE := avtest
+
+LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/bluez/configure.ac
+
+include $(BUILD_EXECUTABLE)
+
+#
+# hciattach
+#
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := \
+ bluez/tools/hciattach.c \
+ bluez/tools/hciattach_st.c \
+ bluez/tools/hciattach_ti.c \
+ bluez/tools/hciattach_tialt.c \
+ bluez/tools/hciattach_ath3k.c \
+ bluez/tools/hciattach_qualcomm.c \
+ bluez/tools/hciattach_intel.c \
+ bluez/tools/hciattach_bcm43xx.c \
+ bluez/lib/bluetooth.c \
+ bluez/lib/hci.c \
+
+LOCAL_C_INCLUDES := \
+ $(LOCAL_PATH)/bluez \
+
+LOCAL_CFLAGS := $(BLUEZ_COMMON_CFLAGS)
+
+LOCAL_STATIC_LIBRARIES := \
+ bluetooth-headers \
+
+LOCAL_MODULE_TAGS := optional
+LOCAL_MODULE := hciattach
+
+LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/bluez/configure.ac
+
+include $(BUILD_EXECUTABLE)
+
+#
+# libsbc
+#
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES:= \
+ sbc/sbc/sbc.c \
+ sbc/sbc/sbc_primitives.c \
+ sbc/sbc/sbc_primitives_mmx.c \
+ sbc/sbc/sbc_primitives_neon.c \
+ sbc/sbc/sbc_primitives_armv6.c \
+ sbc/sbc/sbc_primitives_iwmmxt.c \
+
+LOCAL_C_INCLUDES:= \
+ $(LOCAL_PATH)/sbc \
+
+LOCAL_CFLAGS:= \
+ -Os \
+ -Wno-sign-compare \
+ -Wno-missing-field-initializers \
+ -Wno-unused-parameter \
+ -Wno-type-limits \
+ -Wno-empty-body \
+
+LOCAL_MODULE := libsbc
+
+include $(BUILD_SHARED_LIBRARY)
+
+ifneq (,$(filter userdebug eng,$(TARGET_BUILD_VARIANT)))
+
+#
+# bluetoothd (debug)
+# this is just a wrapper used in userdebug/eng to launch bluetoothd-main
+# with/without Valgrind
+#
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := \
+ bluez/android/bluetoothd-wrapper.c \
+ bluez/android/hal-utils.c
+
+LOCAL_CFLAGS := $(BLUEZ_COMMON_CFLAGS)
+
+LOCAL_SHARED_LIBRARIES := \
+ libcutils \
+
+LOCAL_MODULE_PATH := $(TARGET_OUT_EXECUTABLES)
+LOCAL_MODULE_TAGS := optional
+LOCAL_MODULE := bluetoothd
+
+LOCAL_REQUIRED_MODULES := \
+ bluetoothd-main \
+ valgrind \
+ memcheck-$(TARGET_ARCH)-linux \
+ vgpreload_core-$(TARGET_ARCH)-linux \
+ vgpreload_memcheck-$(TARGET_ARCH)-linux \
+ default.supp
+
+include $(BUILD_EXECUTABLE)
+
+endif
+
+#
+# bluetooth-headers
+#
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := bluetooth-headers
+LOCAL_NODULE_TAGS := optional
+LOCAL_MODULE_CLASS := STATIC_LIBRARIES
+
+include_path := $(local-intermediates-dir)/include
+include_files := $(wildcard $(LOCAL_PATH)/bluez/lib/*.h)
+$(shell mkdir -p $(include_path)/bluetooth)
+$(foreach file,$(include_files),$(shell cp -u $(file) $(include_path)/bluetooth))
+
+LOCAL_EXPORT_C_INCLUDE_DIRS := $(include_path)
+
+include $(BUILD_STATIC_LIBRARY)
+
+#
+# avtest
+#
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := \
+ bluez/tools/avinfo.c \
+ bluez/lib/bluetooth.c \
+ bluez/lib/hci.c \
+
+LOCAL_C_INCLUDES := \
+ $(LOCAL_PATH)/bluez \
+
+LOCAL_CFLAGS := $(BLUEZ_COMMON_CFLAGS)
+
+LOCAL_STATIC_LIBRARIES := \
+ bluetooth-headers \
+
+LOCAL_MODULE_PATH := $(TARGET_OUT_OPTIONAL_EXECUTABLES)
+LOCAL_MODULE_TAGS := debug
+LOCAL_MODULE := avinfo
+
+LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/bluez/configure.ac
+
+include $(BUILD_EXECUTABLE)
+
+#
+# rctest
+#
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := \
+ bluez/tools/rctest.c \
+ bluez/lib/bluetooth.c \
+ bluez/lib/hci.c \
+ bluez/lib/sdp.c \
+
+LOCAL_C_INCLUDES := \
+ $(LOCAL_PATH)/bluez \
+
+LOCAL_CFLAGS := $(BLUEZ_COMMON_CFLAGS)
+
+LOCAL_STATIC_LIBRARIES := \
+ bluetooth-headers \
+
+LOCAL_MODULE_PATH := $(TARGET_OUT_OPTIONAL_EXECUTABLES)
+LOCAL_MODULE_TAGS := debug
+LOCAL_MODULE := rctest
+
+LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/bluez/configure.ac
+
+include $(BUILD_EXECUTABLE)
diff --git a/repo/android/Makefile.am b/repo/android/Makefile.am
new file mode 100644
index 0000000..154f8db
--- /dev/null
+++ b/repo/android/Makefile.am
@@ -0,0 +1,320 @@
+if ANDROID
+
+AM_CFLAGS += -DANDROID_VERSION=0x050100
+
+android_plugindir = $(abs_top_srcdir)/android/.libs
+
+noinst_PROGRAMS += android/system-emulator
+
+android_system_emulator_SOURCES = android/system-emulator.c
+android_system_emulator_LDADD = src/libshared-mainloop.la
+
+noinst_PROGRAMS += android/bluetoothd-snoop
+
+android_bluetoothd_snoop_SOURCES = android/bluetoothd-snoop.c src/log.c
+android_bluetoothd_snoop_LDADD = src/libshared-mainloop.la @GLIB_LIBS@
+
+noinst_PROGRAMS += android/bluetoothd
+
+android_bluetoothd_SOURCES = android/main.c \
+ src/log.c \
+ android/hal-msg.h \
+ android/audio-msg.h \
+ android/sco-msg.h \
+ android/utils.h \
+ src/sdpd-database.c src/sdpd-server.c \
+ src/sdpd-service.c src/sdpd-request.c \
+ src/uuid-helper.h src/uuid-helper.c \
+ src/eir.h src/eir.c \
+ android/bluetooth.h android/bluetooth.c \
+ android/hidhost.h android/hidhost.c \
+ profiles/scanparam/scpp.h \
+ profiles/scanparam/scpp.c \
+ profiles/deviceinfo/dis.h \
+ profiles/deviceinfo/dis.c \
+ profiles/battery/bas.h profiles/battery/bas.c \
+ profiles/input/hog-lib.h \
+ profiles/input/hog-lib.c \
+ android/ipc-common.h \
+ android/ipc.h android/ipc.c \
+ android/avdtp.h android/avdtp.c \
+ android/a2dp.h android/a2dp.c \
+ android/a2dp-sink.h android/a2dp-sink.c \
+ android/avctp.h android/avctp.c \
+ android/avrcp.h android/avrcp.c \
+ android/avrcp-lib.h android/avrcp-lib.c \
+ android/socket.h android/socket.c \
+ android/sco.h android/sco.c \
+ android/pan.h android/pan.c \
+ android/handsfree.h android/handsfree.c \
+ android/handsfree-client.c android/handsfree-client.h \
+ android/gatt.h android/gatt.c \
+ android/health.h android/health.c \
+ profiles/health/mcap.h profiles/health/mcap.c \
+ android/map-client.h android/map-client.c \
+ attrib/att.c attrib/att.h \
+ attrib/gatt.c attrib/gatt.h \
+ attrib/gattrib.c attrib/gattrib.h \
+ btio/btio.h btio/btio.c \
+ src/sdp-client.h src/sdp-client.c \
+ profiles/network/bnep.h profiles/network/bnep.c
+android_bluetoothd_LDADD = lib/libbluetooth-internal.la \
+ src/libshared-glib.la @GLIB_LIBS@
+
+plugin_LTLIBRARIES += android/bluetooth.default.la
+
+android_bluetooth_default_la_SOURCES = android/hal.h android/hal-bluetooth.c \
+ android/hal-socket.c \
+ android/hal-hidhost.c \
+ android/hal-health.c \
+ android/hal-pan.c \
+ android/hal-a2dp.c \
+ android/hal-a2dp-sink.c \
+ android/hal-avrcp.c \
+ android/hal-avrcp-ctrl.c \
+ android/hal-handsfree.c \
+ android/hal-handsfree-client.c \
+ android/hal-gatt.c \
+ android/hal-map-client.c \
+ android/hardware/bluetooth.h \
+ android/hardware/bt_av.h \
+ android/hardware/bt_gatt.h \
+ android/hardware/bt_gatt_client.h \
+ android/hardware/bt_gatt_server.h \
+ android/hardware/bt_gatt_types.h \
+ android/hardware/bt_hf.h \
+ android/hardware/bt_hh.h \
+ android/hardware/bt_hl.h \
+ android/hardware/bt_pan.h \
+ android/hardware/bt_rc.h \
+ android/hardware/bt_sock.h \
+ android/hardware/bt_hf_client.h \
+ android/hardware/bt_mce.h \
+ android/hardware/hardware.h \
+ android/cutils/properties.h \
+ android/ipc-common.h \
+ android/hal-log.h \
+ android/hal-ipc.h android/hal-ipc.c \
+ android/hal-utils.h android/hal-utils.c
+android_bluetooth_default_la_CFLAGS = $(AM_CFLAGS) -I$(srcdir)/android
+android_bluetooth_default_la_LDFLAGS = $(AM_LDFLAGS) -module -avoid-version \
+ -no-undefined
+
+noinst_PROGRAMS += android/avdtptest
+
+android_avdtptest_SOURCES = android/avdtptest.c \
+ src/log.h src/log.c \
+ btio/btio.h btio/btio.c \
+ src/shared/util.h src/shared/util.c \
+ src/shared/queue.h src/shared/queue.c \
+ android/avdtp.h android/avdtp.c
+android_avdtptest_CFLAGS = $(AM_CFLAGS)
+android_avdtptest_LDADD = lib/libbluetooth-internal.la @GLIB_LIBS@
+
+noinst_PROGRAMS += android/haltest
+
+android_haltest_SOURCES = android/client/haltest.c \
+ android/client/pollhandler.h \
+ android/client/pollhandler.c \
+ android/client/terminal.h \
+ android/client/terminal.c \
+ android/client/history.h \
+ android/client/history.c \
+ android/client/tabcompletion.c \
+ android/client/if-main.h \
+ android/client/if-av.c \
+ android/client/if-av-sink.c \
+ android/client/if-rc.c \
+ android/client/if-rc-ctrl.c \
+ android/client/if-bt.c \
+ android/client/if-gatt.c \
+ android/client/if-hf.c \
+ android/client/if-hf-client.c \
+ android/client/if-hh.c \
+ android/client/if-pan.c \
+ android/client/if-hl.c \
+ android/client/if-sock.c \
+ android/client/if-audio.c \
+ android/client/if-sco.c \
+ android/client/if-mce.c \
+ android/hardware/hardware.c \
+ android/hal-utils.h android/hal-utils.c
+android_haltest_CFLAGS = $(AM_CFLAGS) -I$(srcdir)/android \
+ -DPLUGINDIR=\""$(android_plugindir)"\"
+android_haltest_LDFLAGS = -pthread -ldl -lm
+
+noinst_PROGRAMS += android/android-tester
+
+android_android_tester_SOURCES = emulator/hciemu.h emulator/hciemu.c \
+ emulator/btdev.h emulator/btdev.c \
+ emulator/bthost.h emulator/bthost.c \
+ emulator/smp.c \
+ monitor/rfcomm.h \
+ android/hardware/hardware.c \
+ android/tester-bluetooth.c \
+ android/tester-socket.c \
+ android/tester-hidhost.c \
+ android/tester-pan.c \
+ android/tester-hdp.c \
+ android/tester-a2dp.c \
+ android/tester-avrcp.c \
+ android/tester-gatt.c \
+ android/tester-map-client.c \
+ android/tester-main.h android/tester-main.c
+android_android_tester_CFLAGS = $(AM_CFLAGS) -I$(srcdir)/android \
+ -DPLUGINDIR=\""$(android_plugindir)"\"
+android_android_tester_LDADD = lib/libbluetooth-internal.la \
+ src/libshared-glib.la @GLIB_LIBS@
+android_android_tester_LDFLAGS = -pthread -ldl
+
+noinst_PROGRAMS += android/ipc-tester
+
+android_ipc_tester_SOURCES = emulator/hciemu.h emulator/hciemu.c \
+ emulator/btdev.h emulator/btdev.c \
+ emulator/bthost.h emulator/bthost.c \
+ emulator/smp.c \
+ android/hal-utils.h android/hal-utils.c \
+ android/ipc-common.h android/ipc-tester.c
+android_ipc_tester_CFLAGS = $(AM_CFLAGS) -I$(srcdir)/android
+android_ipc_tester_LDADD = lib/libbluetooth-internal.la \
+ src/libshared-glib.la @GLIB_LIBS@
+
+plugin_LTLIBRARIES += android/audio.a2dp.default.la
+
+android_audio_a2dp_default_la_SOURCES = android/audio-msg.h \
+ android/hal-msg.h \
+ android/hal-audio.h \
+ android/hal-audio.c \
+ android/hal-audio-sbc.c \
+ android/hal-audio-aptx.c \
+ android/hardware/audio.h \
+ android/hardware/audio_effect.h \
+ android/hardware/hardware.h \
+ android/system/audio.h
+android_audio_a2dp_default_la_CFLAGS = $(AM_CFLAGS) -I$(srcdir)/android \
+ @SBC_CFLAGS@
+android_audio_a2dp_default_la_LIBADD = @SBC_LIBS@
+android_audio_a2dp_default_la_LDFLAGS = $(AM_LDFLAGS) -module -avoid-version \
+ -no-undefined -pthread -lrt
+
+plugin_LTLIBRARIES += android/audio.sco.default.la
+
+android_audio_sco_default_la_SOURCES = android/hal-log.h \
+ android/sco-msg.h \
+ android/hal-sco.c \
+ android/hardware/audio.h \
+ android/hardware/audio_effect.h \
+ android/hardware/hardware.h \
+ android/audio_utils/resampler.c \
+ android/audio_utils/resampler.h \
+ android/system/audio.h
+android_audio_sco_default_la_CFLAGS = $(AM_CFLAGS) -I$(srcdir)/android
+android_audio_sco_default_la_LIBADD = @SPEEXDSP_LIBS@
+android_audio_sco_default_la_LDFLAGS = $(AM_LDFLAGS) -module -avoid-version \
+ -no-undefined -lrt
+unit_tests += android/test-ipc
+
+android_test_ipc_SOURCES = android/test-ipc.c \
+ src/log.h src/log.c \
+ android/ipc-common.h \
+ android/ipc.c android/ipc.h
+android_test_ipc_LDADD = src/libshared-glib.la @GLIB_LIBS@
+
+endif
+
+EXTRA_DIST += android/Android.mk android/README \
+ android/compat/readline/history.h \
+ android/compat/readline/readline.h \
+ android/compat/wordexp.h \
+ android/bluetoothd-wrapper.c \
+ android/log.c \
+ android/bluetoothd.te \
+ android/bluetoothd_snoop.te \
+ android/init.bluetooth.rc \
+ android/hal-ipc-api.txt \
+ android/audio-ipc-api.txt \
+ android/cts.txt \
+ android/pics-rfcomm.txt \
+ android/pics-spp.txt \
+ android/pics-sdp.txt \
+ android/pics-l2cap.txt \
+ android/pics-gap.txt \
+ android/pics-did.txt \
+ android/pics-hid.txt \
+ android/pics-pan.txt \
+ android/pics-opp.txt \
+ android/pics-map.txt \
+ android/pics-pbap.txt \
+ android/pics-a2dp.txt \
+ android/pics-avctp.txt \
+ android/pics-avrcp.txt \
+ android/pics-hsp.txt \
+ android/pics-hfp.txt \
+ android/pics-gatt.txt \
+ android/pics-mcap.txt \
+ android/pics-hdp.txt \
+ android/pics-iopt.txt \
+ android/pics-sm.txt \
+ android/pics-mps.txt \
+ android/pics-hogp.txt \
+ android/pics-scpp.txt \
+ android/pics-dis.txt \
+ android/pics-avdtp.txt \
+ android/pics-gavdp.txt \
+ android/pics-bnep.txt \
+ android/pixit-l2cap.txt \
+ android/pixit-gap.txt \
+ android/pixit-did.txt \
+ android/pixit-hid.txt \
+ android/pixit-pan.txt \
+ android/pixit-opp.txt \
+ android/pixit-map.txt \
+ android/pixit-pbap.txt \
+ android/pixit-a2dp.txt \
+ android/pixit-avctp.txt \
+ android/pixit-avrcp.txt \
+ android/pixit-hsp.txt \
+ android/pixit-hfp.txt \
+ android/pixit-gatt.txt \
+ android/pixit-mcap.txt \
+ android/pixit-hdp.txt \
+ android/pixit-iopt.txt \
+ android/pixit-sm.txt \
+ android/pixit-mps.txt \
+ android/pixit-hogp.txt \
+ android/pixit-scpp.txt \
+ android/pixit-dis.txt \
+ android/pixit-rfcomm.txt \
+ android/pixit-spp.txt \
+ android/pixit-avdtp.txt \
+ android/pixit-gavdp.txt \
+ android/pixit-sdp.txt \
+ android/pixit-bnep.txt \
+ android/pts-rfcomm.txt \
+ android/pts-spp.txt \
+ android/pts-l2cap.txt \
+ android/pts-gap.txt \
+ android/pts-did.txt \
+ android/pts-hid.txt \
+ android/pts-pan.txt \
+ android/pts-opp.txt \
+ android/pts-map.txt \
+ android/pts-a2dp.txt \
+ android/pts-avrcp.txt \
+ android/pts-avctp.txt \
+ android/pts-pbap.txt \
+ android/pts-hfp.txt \
+ android/pts-gatt.txt \
+ android/pts-hsp.txt \
+ android/pts-iopt.txt \
+ android/pts-hdp.txt \
+ android/pts-mcap.txt \
+ android/pts-mps.txt \
+ android/pts-sm.txt \
+ android/pts-hogp.txt \
+ android/pts-scpp.txt \
+ android/pts-dis.txt \
+ android/pts-avdtp.txt \
+ android/pts-gavdp.txt \
+ android/pts-sdp.txt \
+ android/pts-bnep.txt
diff --git a/repo/android/README b/repo/android/README
new file mode 100644
index 0000000..fa4c42a
--- /dev/null
+++ b/repo/android/README
@@ -0,0 +1,454 @@
+BlueZ for Android
+*****************
+
+Since Android 4.2 there exists a well standardized HAL interface that the
+Bluetooth stack is expected to provide and which enables the easy replacement
+of the stack of choice on Android. Android BlueZ is intended as a drop-in
+replacement to Android provided Bluetooth stack.
+
+More details about BlueZ for Android architecture and components can be found
+in android/hal-ipc-api.txt file.
+
+Supported Android version: 4.4 KitKat and 5.0, 5.1 Lollipop
+
+
+Building and running on Android
+===============================
+
+Steps needed to build and run Android Open Source Project with integrated BlueZ.
+
+
+Build requirements
+------------------
+
+- GLib - Android 4.2 or later don't provide GLib and one must provide it in
+'external/bluetooth/glib' folder of Android tree. Sample Android GLib port
+is available at https://github.com/bluez-android/glib
+
+- SBC - A2DP code requires SBC library (version 1.2 or higher) present in
+'external/bluetooth/sbc' directory. Library is build from Android.mk provided
+by BlueZ. SBC code is available at git://git.kernel.org/pub/scm/bluetooth/sbc
+
+- Bionic support - Android 5.0 provides all required functionality. Running
+BlueZ on Android 4.4 requires backporting missing features (epoll_create1 and
+ppoll calls). Sample Bionic for Android 4.4 with all required features
+backported is available at
+https://github.com/bluez-android/aosp_platform_bionic
+
+Runtime requirements
+--------------------
+
+BlueZ HAL library requires 'bluetoothd' and 'bluetoothd-snoop' services to be
+available on Android system. Some permissions settings are also required.
+
+This can be done by importing init.bluetooth.rc file in init.rc file of targeted
+board:
+import init.bluetooth.rc
+
+For convenience examples are provided at:
+https://github.com/bluez-android/aosp_device_lge_mako (Nexus 4)
+https://github.com/bluez-android/aosp_device_lge_hammerhead (Nexus 5)
+https://github.com/bluez-android/aosp_device_asus_flo (Nexus 7 2013)
+
+Security-Enhanced Linux in Android
+----------------------------------
+
+Since 5.0 release Android moved to full enforcement of SELinux. This requires
+proper policy to be provided for all BlueZ for Android services (and services
+interacting with BlueZ). Policies should be placed in external/selinux/ path.
+
+Required policy files are provided at:
+bluetoothd.te
+bluetoothd_snoop.te
+
+For convenience sepolicy.git with all required policies is available at:
+https://github.com/bluez-android/aosp_platform_external_sepolicy
+
+Downloading and building
+------------------------
+
+Building for Android requires full Android AOSP source tree. Sample Android tree
+with all required components present is available at
+https://github.com/bluez-android
+
+This tree provides support for Nexus4 (mako), Nexus 5 (hammerhead) and
+Nexus 7 2013 (flo, deb). Tree does not provide binary blobs needed to run
+Android on supported devices. Those can be obtained from
+https://developers.google.com/android/nexus/drivers. Binary blobs needs to be
+unpacked (EULA acceptance required) into 'vendor' directory of Android tree.
+
+Downloading:
+Android 5.0 - 'lollipop' branch
+Android 4.4 - 'kitkat' branch
+
+repo init -u https://github.com/bluez-android/aosp_platform_manifest \
+ -b lollipop
+repo sync
+
+Building:
+source build/envsetup.sh
+lunch aosp_<target>-userdebug
+make -j8
+
+Flashing:
+adb reboot bootloader
+fastboot flashall -w
+
+After full build is done it is possible to rebuild only BlueZ:
+'cd external/bluetooth/bluez/android/'
+'mm' (or 'mm -B' to force rebuilding of all files)
+'adb sync' to update target device.
+
+Downloading and building for Intel devices
+------------------------------------------
+
+Sample Android tree with all required components for Intel devices based on
+Intel reference image (https://01.org/android-ia) can be reconstructed following
+instructions below.
+
+This tree provides support for Dell XPS12, Minnowboard MAX, Intel NUC,
+Acer Iconia W700 and other devices mentioned in:
+https://01.org/android-ia/guides/devices
+
+Downloading:
+repo init -u https://github.com/01org/android-bluez-manifest.git -b android-ia \
+ -m topic/bluez
+repo sync
+
+Building:
+source build/envsetup.sh
+lunch haswell_generic-eng
+make -j8
+
+Installing:
+Live and Install image is $OUT/live.img
+Flash live.img to USB flash and boot from it. More instructions here:
+https://01.org/android-ia/guides/developers/build-and-install
+
+Linux Kernel requirements
+-------------------------
+
+BlueZ for Android uses Linux Bluetooth subsystem and it must be enabled in
+kernel. Minimal required version of management interface is 1.3. This
+corresponds to Linux 3.9 but latest available version is recommended. Other
+requirements include UHID and network bridge support.
+
+Following kernel options should be enabled:
+CONFIG_BT
+CONFIG_BT_RFCOMM
+CONFIG_BT_RFCOMM_TTY
+CONFIG_BT_BNEP
+CONFIG_BT_BNEP_MC_FILTER
+CONFIG_BT_BNEP_PROTO_FILTER
+CONFIG_BRIDGE
+CONFIG_UHID
+CONFIG_CRYPTO_CMAC
+CONFIG_CRYPTO_USER_API
+CONFIG_CRYPTO_USER_API_HASH
+CONFIG_CRYPTO_USER_API_SKCIPHER
+
+Also BT chip driver needs to be enabled e.g:
+CONFIG_BT_HCIBTUSB
+
+If it is not possible to use new enough Linux kernel one can use updated
+bluetooth subsystem from Backports project. More information about Backports can
+be found at https://backports.wiki.kernel.org. Sample kernels using backports
+for running BlueZ on Android are available at https://github.com/bluez-android.
+
+
+Running with Valgrind
+---------------------
+
+BlueZ for Android is preconfigured to be easily run under Valgrind memcheck.
+Appropriate configuration and required modules are automatically included when
+building either userdebug or eng variant of Android platform.
+
+Valgrind can be enabled in runtime by setting "persist.sys.bluetooth.valgrind"
+property to either literal "true" or any numeric value >0. For example:
+adb root
+adb shell setprop persist.sys.bluetooth.valgrind true
+
+After changing property value Bluetooth need to be restarted to apply changes
+(this can be done using UI, just disable and enable it again). Property is
+persistent, i.e. there's no need to enable Valgrind again after reboot.
+
+It's recommended to have unstripped libglib.so installed which will enable
+complete backtraces in Valgrind output. Otherwise, in many cases backtrace
+will break at e.g. g_free() function without prior callers. It's possible to
+have proper library installed automatically by appropriate entry in Android.mk,
+see https://github.com/bluez-android/glib for an example.
+
+When running with valgrind SElinux needs to be set into permissive mode. This
+can be done by executing 'setenforce 0' from root shell.
+
+
+Enabling BlueZ debugs
+---------------------
+
+BlueZ debug logs can be enabled in runtime by setting
+"persist.sys.bluetooth.debug" property to either literal "true" or any
+numeric value >0. For example:
+adb root
+adb shell setprop persist.sys.bluetooth.debug 1
+
+After changing property value Bluetooth needs to be restarted to apply changes.
+
+There is also a possibility to enable mgmt debug logs which also enables debugs
+as above. To enable it proceed in the same way as described above but use
+system properties called: persist.sys.bluetooth.mgmtdbg
+
+Note: Debugs are only available on NON USER build variants
+
+
+Customization
+-------------
+
+It is possible to customize BlueZ for Android through Android system properties.
+This may include enabling extra profiles or features inside HALs implementation
+These properties are read on Bluetooth stack startup only and require stack
+restart if changed. All customization properties names start with
+"persist.sys.bluetooth." or "ro.bluetooth." followed by specific HAL name e.g.
+"persist.sys.bluetooth.handsfree". If both are present "persist.sys.bluetooth."
+takes precedence. This allows for read only properties to be set during build
+leaving enough flexibility for developing or debugging purposes.
+This section list available customization options.
+
+Property Value Description
+-------------------------------------------
+mode bredr Enable BlueZ in BR/EDR mode
+ le Enable BlueZ in LE mode
+ <none> Enable BlueZ in default mode - enable BR/EDR/LE
+ if available.
+handsfree hfp Enable Handsfree Profile (HFP) with narrowband
+ speech only
+ hfp_wbs Enable Handsfree Profile (HFP) with narrowband
+ and wideband speech support
+ <none> Don't enable Handsfree Profile (HFP)
+vendor <any> Set vendor name in DIS. If not set fallback to
+ "ro.product.manufacturer".
+model <any> Set model name used as default adapter name.
+ If not set fallback to "ro.product.model".
+name <any> Set model number in DIS. If not set fallback to
+ "ro.product.name".
+serialno <any> Set serial number in DIS. If not set fallback to
+ "ro.serialno".
+systemid <uint64> Set system ID in DIS. Hex string encoded uint64.
+pnpid <any> PnP information used in DIS and DID profiles.
+ Required format: "Source:VID:PID:Version".
+ Source must be either "bluetooth" or "usb".
+ VID, PID and Version are uint16. Version is
+ optional.
+fwrev <any> Firmware revision in DIS. If not set fallback to
+ "ro.build.version.release".
+hwrew <any> Hardware revision in DIS. If not set fallback to
+ "ro.board.platform".
+
+
+Building and running on Linux
+-----------------------------
+
+It is possible to build and test BlueZ for Android daemon on Linux (eg. PC).
+Simply follow instructions available at README file in BlueZ top directory.
+Android daemon binary is located at android/bluetoothd. See next section on
+how to test Android daemon on Linux.
+
+
+Testing tool
+------------
+
+BT HAL test tools located in android/haltest is provided for HAL level testing
+of both Android daemon and HAL library. Start it with '-n' parameter and type
+'bluetooth init' in prompt to initialize HAL library. Running without parameter
+will make haltest try to initialize all services after start. On Android
+required bluetoothd service will be started automatically. On Linux it is
+required to start android/bluetoothd manually before init command timeout or
+use provided android/system-emulator, which takes care of launching daemon
+automatically on HAL library initialization. To deinitialize HAL library and
+stop daemon type 'bluetooth cleanup'. Type 'help' for more information. Tab
+completion is also supported.
+
+
+Implementation status
+=====================
+
+Summary of HALs implementation status.
+
+complete - implementation is feature complete and Android Framework is able
+ to use it normally
+partial - implementation is in progress and not all required features are
+ present, Android Framework is able to use some of features
+initial - only initial implementations is present, Android Framework is
+ able to initialize but most likely not able to use it
+not started - no implementation, Android Framework is not able to initialize it
+
+Profile ID HAL header 4.4 Status 5.0 status
+-------------------------------------------------------------
+core bluetooth.h complete partial
+a2dp bt_av.h complete complete
+gatt bt_gatt.h complete partial
+ bt_gatt_client.h complete partial
+ bt_gatt_server.h complete partial
+handsfree bt_hf.h complete complete
+hidhost bt_hh.h complete complete
+health bt_hl.h complete complete
+pan bt_pan.h complete complete
+avrcp bt_rc.h complete complete
+socket bt_sock.h complete partial
+handsfree_client bt_hf_client.h N/A complete
+map_client bt_mce.h N/A complete
+a2dp_sink bt_av.h N/A partial
+avrcp_ctrl bt_rc.h N/A partial
+
+
+Implementation shortcomings
+===========================
+
+It is possible that some of HAL functionality (although being marked as
+complete) is missing implementation due to reasons like feature feasibility or
+necessity for latest Android Framework. This sections provides list of such
+deficiencies. Note that HAL library is always expected to fully implement HAL
+API so missing implementation might happen only in daemon.
+
+
+HAL Bluetooth
+-------------
+
+methods:
+dut_mode_send never called from Android Framework
+le_test_mode never called from Android Framework
+
+callbacks:
+dut_mode_recv_cb empty JNI implementation
+le_test_mode_cb empty JNI implementation
+
+properties:
+BT_PROPERTY_SERVICE_RECORD not supported for adapter, for device this
+ property is returned as a response to
+ get_remote_service_record call
+
+BT_PROPERTY_REMOTE_VERSION_INFO information required by this property (LMP
+ information) are not accessible from mgmt
+ interface, also marking this property as
+ settable is probably a typo in HAL header
+
+HAL Socket
+----------
+
+Support only for BTSOCK_RFCOMM socket type.
+
+
+HAL AVRCP
+---------
+
+methods:
+list_player_app_attr_rsp never called from Android Framework
+list_player_app_value_rsp never called from Android Framework
+get_player_app_value_rsp never called from Android Framework
+get_player_app_attr_text_rsp never called from Android Framework
+get_player_app_value_text_rsp never called from Android Framework
+set_player_app_value_rsp never called from Android Framework
+
+callbacks:
+list_player_app_attr_cb NULL JNI implementation
+list_player_app_values_cb NULL JNI implementation
+get_player_app_value_cb NULL JNI implementation
+get_player_app_attrs_text_cb NULL JNI implementation
+get_player_app_values_text_cb NULL JNI implementation
+set_player_app_value_cb NULL JNI implementation
+
+
+HAL GATT
+--------
+
+methods:
+client->set_adv_data missing kernel support for vendor data
+client->connect is_direct parameter is ignored
+
+
+Audio SCO HAL
+=============
+
+When Bluetooth chip's audio is not wired directly to device audio, Audio SCO
+HAL is used to enable SCO support. It needs to be loaded by AudioFlinger
+following audio_policy.conf configuration. Example of configuration is shown
+below:
+
+...
+ sco {
+ outputs {
+ sco {
+ sampling_rates 8000|44100
+ channel_masks AUDIO_CHANNEL_OUT_STEREO
+ formats AUDIO_FORMAT_PCM_16_BIT
+ devices AUDIO_DEVICE_OUT_ALL_SCO
+ }
+ }
+ inputs {
+ sco {
+ sampling_rates 8000|44100
+ channel_masks AUDIO_CHANNEL_IN_MONO
+ formats AUDIO_FORMAT_PCM_16_BIT
+ devices AUDIO_DEVICE_IN_BLUETOOTH_SCO_HEADSET
+ }
+ }
+ }
+...
+
+Known Android issues
+====================
+
+It is possible that BlueZ is triggering bugs on Android Framework that could
+affect qualification or user experience. This section provides list of
+recommended Android fixes that are not part of latest AOSP release supported by
+BlueZ.
+
+For Android 5.1 Lollipop:
+https://android-review.googlesource.com/177314
+
+For Android 5.0 Lollipop:
+https://android-review.googlesource.com/99761
+https://android-review.googlesource.com/100297
+https://android-review.googlesource.com/102882
+https://android-review.googlesource.com/132733
+https://android-review.googlesource.com/132763
+https://android-review.googlesource.com/177314
+
+For Android 4.4 KitKat:
+https://android-review.googlesource.com/82757
+https://android-review.googlesource.com/87670
+https://android-review.googlesource.com/88384
+https://android-review.googlesource.com/99761
+https://android-review.googlesource.com/99850
+https://android-review.googlesource.com/100297
+https://android-review.googlesource.com/102882
+https://android-review.googlesource.com/177314
+
+Unimplemented Bluetooth features
+================================
+
+Some Bluetooth functionality require support from outside of BT stack
+eg. telephony stack. This sections describes profiles optional features not
+implemented due to lack of support in other Android subsystems or missing API
+in respective BT HALs.
+
+Profile Feature Comments
+--------------------------------------------------------
+HFP Attach a phone number to AT+BINP=1
+ a voice tag
+HFP Enhanced Call Control AT+CHLD={1x,2x}
+HFP Explicit Call Transfer AT+CHLD=4
+HFP Response and Hold AT+BTRH, +BTRH
+HFP In-band Ring Tone +BSIR
+AVRCP Player Settings HAL API present but not used
+AVRCP Browsing No HAL API
+GATT Read multiple characteristics No HAL API
+
+
+Reporting Bugs
+==============
+
+Bugs should be reported at https://01.org/jira/browse/BA. When reporting
+a bug please attach logs from logcat (logcat -v time) and HCI trace. Daemon
+debug logs should be enabled. When reporting daemon crash please run it under
+valgrind if possible. For details on how to enabled debug logs and valgrind see
+"Enabling BlueZ debugs" section.
diff --git a/repo/android/a2dp-sink.c b/repo/android/a2dp-sink.c
new file mode 100644
index 0000000..7c1e1a0
--- /dev/null
+++ b/repo/android/a2dp-sink.c
@@ -0,0 +1,84 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2014 Intel Corporation. All rights reserved.
+ *
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdbool.h>
+#include <glib.h>
+
+#include "lib/bluetooth.h"
+#include "src/log.h"
+#include "hal-msg.h"
+#include "ipc.h"
+#include "a2dp-sink.h"
+
+static struct ipc *hal_ipc = NULL;
+
+static void bt_a2dp_sink_connect(const void *buf, uint16_t len)
+{
+ /* TODO */
+
+ DBG("");
+
+ ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_A2DP_SINK, HAL_OP_A2DP_CONNECT,
+ HAL_STATUS_UNSUPPORTED);
+}
+
+static void bt_a2dp_sink_disconnect(const void *buf, uint16_t len)
+{
+ /* TODO */
+
+ DBG("");
+
+ ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_A2DP_SINK, HAL_OP_A2DP_DISCONNECT,
+ HAL_STATUS_UNSUPPORTED);
+}
+
+static const struct ipc_handler cmd_handlers[] = {
+ /* HAL_OP_A2DP_CONNECT */
+ { bt_a2dp_sink_connect, false, sizeof(struct hal_cmd_a2dp_connect) },
+ /* HAL_OP_A2DP_DISCONNECT */
+ { bt_a2dp_sink_disconnect, false,
+ sizeof(struct hal_cmd_a2dp_disconnect) },
+};
+
+bool bt_a2dp_sink_register(struct ipc *ipc, const bdaddr_t *addr, uint8_t mode)
+{
+ DBG("");
+
+ hal_ipc = ipc;
+ ipc_register(hal_ipc, HAL_SERVICE_ID_A2DP_SINK, cmd_handlers,
+ G_N_ELEMENTS(cmd_handlers));
+
+ return true;
+}
+
+void bt_a2dp_sink_unregister(void)
+{
+ DBG("");
+
+ ipc_unregister(hal_ipc, HAL_SERVICE_ID_A2DP_SINK);
+ hal_ipc = NULL;
+}
diff --git a/repo/android/a2dp-sink.h b/repo/android/a2dp-sink.h
new file mode 100644
index 0000000..d2c5ff4
--- /dev/null
+++ b/repo/android/a2dp-sink.h
@@ -0,0 +1,25 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2014 Intel Corporation. All rights reserved.
+ *
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+bool bt_a2dp_sink_register(struct ipc *ipc, const bdaddr_t *addr, uint8_t mode);
+void bt_a2dp_sink_unregister(void);
diff --git a/repo/android/a2dp.c b/repo/android/a2dp.c
new file mode 100644
index 0000000..f219042
--- /dev/null
+++ b/repo/android/a2dp.c
@@ -0,0 +1,1774 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2013-2014 Intel Corporation. All rights reserved.
+ *
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdint.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <glib.h>
+
+#include "btio/btio.h"
+#include "lib/bluetooth.h"
+#include "lib/sdp.h"
+#include "lib/sdp_lib.h"
+#include "profiles/audio/a2dp-codecs.h"
+#include "src/shared/queue.h"
+#include "src/log.h"
+#include "hal-msg.h"
+#include "ipc-common.h"
+#include "ipc.h"
+#include "a2dp.h"
+#include "utils.h"
+#include "bluetooth.h"
+#include "avdtp.h"
+#include "avrcp.h"
+#include "audio-msg.h"
+
+#define SVC_HINT_CAPTURING 0x08
+#define IDLE_TIMEOUT 1
+#define AUDIO_RETRY_TIMEOUT 2
+
+static GIOChannel *server = NULL;
+static GSList *devices = NULL;
+static GSList *endpoints = NULL;
+static GSList *setups = NULL;
+static bdaddr_t adapter_addr;
+static uint32_t record_id = 0;
+static guint audio_retry_id = 0;
+static bool audio_retrying = false;
+
+static struct ipc *hal_ipc = NULL;
+static struct ipc *audio_ipc = NULL;
+
+static struct queue *lseps = NULL;
+
+struct a2dp_preset {
+ void *data;
+ int8_t len;
+};
+
+struct a2dp_endpoint {
+ uint8_t id;
+ uint8_t codec;
+ struct avdtp_local_sep *sep;
+ struct a2dp_preset *caps;
+ GSList *presets;
+};
+
+struct a2dp_device {
+ bdaddr_t dst;
+ uint8_t state;
+ GIOChannel *io;
+ struct avdtp *session;
+ guint idle_id;
+};
+
+struct a2dp_setup {
+ struct a2dp_device *dev;
+ struct a2dp_endpoint *endpoint;
+ struct a2dp_preset *preset;
+ struct avdtp_stream *stream;
+ uint8_t state;
+};
+
+static int device_cmp(gconstpointer s, gconstpointer user_data)
+{
+ const struct a2dp_device *dev = s;
+ const bdaddr_t *dst = user_data;
+
+ return bacmp(&dev->dst, dst);
+}
+
+static void preset_free(void *data)
+{
+ struct a2dp_preset *preset = data;
+
+ g_free(preset->data);
+ g_free(preset);
+}
+
+static void unregister_endpoint(void *data)
+{
+ struct a2dp_endpoint *endpoint = data;
+
+ if (endpoint->sep)
+ avdtp_unregister_sep(lseps, endpoint->sep);
+
+ if (endpoint->caps)
+ preset_free(endpoint->caps);
+
+ g_slist_free_full(endpoint->presets, preset_free);
+
+ g_free(endpoint);
+}
+
+static void setup_free(void *data)
+{
+ struct a2dp_setup *setup = data;
+
+ if (!g_slist_find(setup->endpoint->presets, setup->preset))
+ preset_free(setup->preset);
+
+ g_free(setup);
+}
+
+static void setup_remove(struct a2dp_setup *setup)
+{
+ setups = g_slist_remove(setups, setup);
+ setup_free(setup);
+}
+
+static void setup_remove_all_by_dev(struct a2dp_device *dev)
+{
+ GSList *l = setups;
+
+ while (l) {
+ struct a2dp_setup *setup = l->data;
+ GSList *next = g_slist_next(l);
+
+ if (setup->dev == dev)
+ setup_remove(setup);
+
+ l = next;
+ }
+}
+
+static void a2dp_device_free(void *data)
+{
+ struct a2dp_device *dev = data;
+
+ if (dev->idle_id > 0)
+ g_source_remove(dev->idle_id);
+
+ if (dev->session)
+ avdtp_unref(dev->session);
+
+ if (dev->io) {
+ g_io_channel_shutdown(dev->io, FALSE, NULL);
+ g_io_channel_unref(dev->io);
+ }
+
+ setup_remove_all_by_dev(dev);
+
+ g_free(dev);
+}
+
+static void a2dp_device_remove(struct a2dp_device *dev)
+{
+ devices = g_slist_remove(devices, dev);
+ a2dp_device_free(dev);
+}
+
+static struct a2dp_device *a2dp_device_new(const bdaddr_t *dst)
+{
+ struct a2dp_device *dev;
+
+ dev = g_new0(struct a2dp_device, 1);
+ bacpy(&dev->dst, dst);
+ devices = g_slist_prepend(devices, dev);
+
+ return dev;
+}
+
+static bool a2dp_device_connect(struct a2dp_device *dev, BtIOConnect cb)
+{
+ GError *err = NULL;
+
+ dev->io = bt_io_connect(cb, dev, NULL, &err,
+ BT_IO_OPT_SOURCE_BDADDR, &adapter_addr,
+ BT_IO_OPT_DEST_BDADDR, &dev->dst,
+ BT_IO_OPT_PSM, AVDTP_PSM,
+ BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_MEDIUM,
+ BT_IO_OPT_INVALID);
+ if (err) {
+ error("%s", err->message);
+ g_error_free(err);
+ return false;
+ }
+
+ return true;
+}
+
+static void bt_a2dp_notify_state(struct a2dp_device *dev, uint8_t state)
+{
+ struct hal_ev_a2dp_conn_state ev;
+ char address[18];
+
+ if (dev->state == state)
+ return;
+
+ dev->state = state;
+
+ ba2str(&dev->dst, address);
+ DBG("device %s state %u", address, state);
+
+ bdaddr2android(&dev->dst, ev.bdaddr);
+ ev.state = state;
+
+ ipc_send_notif(hal_ipc, HAL_SERVICE_ID_A2DP, HAL_EV_A2DP_CONN_STATE,
+ sizeof(ev), &ev);
+
+ if (state != HAL_A2DP_STATE_DISCONNECTED)
+ return;
+
+ bt_avrcp_disconnect(&dev->dst);
+
+ a2dp_device_remove(dev);
+}
+
+static void bt_audio_notify_state(struct a2dp_setup *setup, uint8_t state)
+{
+ struct hal_ev_a2dp_audio_state ev;
+ char address[18];
+
+ if (setup->state == state)
+ return;
+
+ setup->state = state;
+
+ ba2str(&setup->dev->dst, address);
+ DBG("device %s state %u", address, state);
+
+ bdaddr2android(&setup->dev->dst, ev.bdaddr);
+ ev.state = state;
+
+ ipc_send_notif(hal_ipc, HAL_SERVICE_ID_A2DP, HAL_EV_A2DP_AUDIO_STATE,
+ sizeof(ev), &ev);
+}
+
+static void disconnect_cb(void *user_data)
+{
+ struct a2dp_device *dev = user_data;
+
+ bt_a2dp_notify_state(dev, HAL_A2DP_STATE_DISCONNECTED);
+}
+
+static int sbc_check_config(void *caps, uint8_t caps_len, void *conf,
+ uint8_t conf_len)
+{
+ a2dp_sbc_t *cap, *config;
+
+ if (conf_len != caps_len || conf_len != sizeof(a2dp_sbc_t)) {
+ error("SBC: Invalid configuration size (%u)", conf_len);
+ return -EINVAL;
+ }
+
+ cap = caps;
+ config = conf;
+
+ if (!(cap->frequency & config->frequency)) {
+ error("SBC: Unsupported frequency (%u) by endpoint",
+ config->frequency);
+ return -EINVAL;
+ }
+
+ if (!(cap->channel_mode & config->channel_mode)) {
+ error("SBC: Unsupported channel mode (%u) by endpoint",
+ config->channel_mode);
+ return -EINVAL;
+ }
+
+ if (!(cap->block_length & config->block_length)) {
+ error("SBC: Unsupported block length (%u) by endpoint",
+ config->block_length);
+ return -EINVAL;
+ }
+
+ if (!(cap->allocation_method & config->allocation_method)) {
+ error("SBC: Unsupported allocation method (%u) by endpoint",
+ config->block_length);
+ return -EINVAL;
+ }
+
+ if (config->max_bitpool < cap->min_bitpool) {
+ error("SBC: Invalid maximun bitpool (%u < %u)",
+ config->max_bitpool, cap->min_bitpool);
+ return -EINVAL;
+ }
+
+ if (config->min_bitpool > cap->max_bitpool) {
+ error("SBC: Invalid minimun bitpool (%u > %u)",
+ config->min_bitpool, cap->min_bitpool);
+ return -EINVAL;
+ }
+
+ if (config->max_bitpool > cap->max_bitpool)
+ return -ERANGE;
+
+ if (config->min_bitpool < cap->min_bitpool)
+ return -ERANGE;
+
+ return 0;
+}
+
+static int aac_check_config(void *caps, uint8_t caps_len, void *conf,
+ uint8_t conf_len)
+{
+ a2dp_aac_t *cap, *config;
+
+ if (conf_len != caps_len || conf_len != sizeof(a2dp_aac_t)) {
+ error("AAC: Invalid configuration size (%u)", conf_len);
+ return -EINVAL;
+ }
+
+ cap = caps;
+ config = conf;
+
+ if (!(cap->object_type & config->object_type)) {
+ error("AAC: Unsupported object type (%u) by endpoint",
+ config->object_type);
+ return -EINVAL;
+ }
+
+ if (!(AAC_GET_FREQUENCY(*cap) & AAC_GET_FREQUENCY(*config))) {
+ error("AAC: Unsupported frequency (%u) by endpoint",
+ AAC_GET_FREQUENCY(*config));
+ return -EINVAL;
+ }
+
+ if (!(cap->channels & config->channels)) {
+ error("AAC: Unsupported channels (%u) by endpoint",
+ config->channels);
+ return -EINVAL;
+ }
+
+ /* VBR support in SNK is mandatory but let's make sure we don't try to
+ * have VBR on remote which for some reason does not support it
+ */
+ if (!cap->vbr && config->vbr) {
+ error("AAC: Unsupported VBR (%u) by endpoint",
+ config->vbr);
+ return -EINVAL;
+ }
+
+ if (AAC_GET_BITRATE(*cap) < AAC_GET_BITRATE(*config))
+ return -ERANGE;
+
+ return 0;
+}
+
+static int aptx_check_config(void *caps, uint8_t caps_len, void *conf,
+ uint8_t conf_len)
+{
+ a2dp_aptx_t *cap, *config;
+
+ if (conf_len != caps_len || conf_len != sizeof(a2dp_aptx_t)) {
+ error("APTX: Invalid configuration size (%u)", conf_len);
+ return -EINVAL;
+ }
+
+ cap = caps;
+ config = conf;
+
+ if (!(cap->frequency & config->frequency)) {
+ error("APTX: Unsupported frequenct (%u) by endpoint",
+ config->frequency);
+ return -EINVAL;
+ }
+
+ if (!(cap->channel_mode & config->channel_mode)) {
+ error("APTX: Unsupported channel mode (%u) by endpoint",
+ config->channel_mode);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int check_capabilities(struct a2dp_preset *preset,
+ struct avdtp_media_codec_capability *codec,
+ uint8_t codec_len)
+{
+ a2dp_vendor_codec_t *vndcodec;
+
+ /* Codec specific */
+ switch (codec->media_codec_type) {
+ case A2DP_CODEC_SBC:
+ return sbc_check_config(codec->data, codec_len, preset->data,
+ preset->len);
+ case A2DP_CODEC_MPEG24:
+ return aac_check_config(codec->data, codec_len, preset->data,
+ preset->len);
+ case A2DP_CODEC_VENDOR:
+ vndcodec = (void *) codec->data;
+ if (btohl(vndcodec->vendor_id) == APTX_VENDOR_ID &&
+ btohs(vndcodec->codec_id) == APTX_CODEC_ID)
+ return aptx_check_config(codec->data, codec_len,
+ preset->data, preset->len);
+ return -EINVAL;
+ default:
+ return -EINVAL;
+ }
+}
+
+static struct a2dp_preset *sbc_select_range(void *caps, uint8_t caps_len,
+ void *conf, uint8_t conf_len)
+{
+ struct a2dp_preset *p;
+ a2dp_sbc_t *cap, *config;
+
+ cap = caps;
+ config = conf;
+
+ config->min_bitpool = MAX(config->min_bitpool, cap->min_bitpool);
+ config->max_bitpool = MIN(config->max_bitpool, cap->max_bitpool);
+
+ p = g_new0(struct a2dp_preset, 1);
+ p->len = conf_len;
+ p->data = g_memdup(conf, p->len);
+
+ return p;
+}
+
+static struct a2dp_preset *aac_select_range(void *caps, uint8_t caps_len,
+ void *conf, uint8_t conf_len)
+{
+ struct a2dp_preset *p;
+ a2dp_aac_t *cap, *config;
+ uint32_t bitrate;
+
+ cap = caps;
+ config = conf;
+
+ bitrate = MIN(AAC_GET_BITRATE(*cap), AAC_GET_BITRATE(*config));
+ AAC_SET_BITRATE(*config, bitrate);
+
+ p = g_new0(struct a2dp_preset, 1);
+ p->len = conf_len;
+ p->data = g_memdup(conf, p->len);
+
+ return p;
+}
+
+static struct a2dp_preset *select_preset_range(struct a2dp_preset *preset,
+ struct avdtp_media_codec_capability *codec,
+ uint8_t codec_len)
+{
+ /* Codec specific */
+ switch (codec->media_codec_type) {
+ case A2DP_CODEC_SBC:
+ return sbc_select_range(codec->data, codec_len, preset->data,
+ preset->len);
+ case A2DP_CODEC_MPEG24:
+ return aac_select_range(codec->data, codec_len, preset->data,
+ preset->len);
+ default:
+ return NULL;
+ }
+}
+
+static struct a2dp_preset *select_preset(struct a2dp_endpoint *endpoint,
+ struct avdtp_remote_sep *rsep)
+{
+ struct avdtp_service_capability *service;
+ struct avdtp_media_codec_capability *codec;
+ GSList *l;
+ uint8_t codec_len;
+
+ service = avdtp_get_codec(rsep);
+ codec = (struct avdtp_media_codec_capability *) service->data;
+ codec_len = service->length - sizeof(*codec);
+
+ for (l = endpoint->presets; l; l = g_slist_next(l)) {
+ struct a2dp_preset *preset = l->data;
+ int err;
+
+ err = check_capabilities(preset, codec, codec_len);
+ if (err == 0)
+ return preset;
+
+ if (err == -ERANGE)
+ return select_preset_range(preset, codec, codec_len);
+ }
+
+ return NULL;
+}
+
+static void setup_add(struct a2dp_device *dev, struct a2dp_endpoint *endpoint,
+ struct a2dp_preset *preset, struct avdtp_stream *stream)
+{
+ struct a2dp_setup *setup;
+
+ setup = g_new0(struct a2dp_setup, 1);
+ setup->dev = dev;
+ setup->endpoint = endpoint;
+ setup->preset = preset;
+ setup->stream = stream;
+ setups = g_slist_append(setups, setup);
+
+ if (dev->idle_id > 0) {
+ g_source_remove(dev->idle_id);
+ dev->idle_id = 0;
+ }
+}
+
+static int select_configuration(struct a2dp_device *dev,
+ struct a2dp_endpoint *endpoint,
+ struct avdtp_remote_sep *rsep)
+{
+ struct a2dp_preset *preset;
+ struct avdtp_stream *stream;
+ struct avdtp_service_capability *service;
+ struct avdtp_media_codec_capability *codec;
+ GSList *caps;
+ int err;
+
+ preset = select_preset(endpoint, rsep);
+ if (!preset) {
+ error("Unable to select codec preset");
+ return -EINVAL;
+ }
+
+ service = avdtp_service_cap_new(AVDTP_MEDIA_TRANSPORT, NULL, 0);
+ caps = g_slist_append(NULL, service);
+
+ codec = g_malloc0(sizeof(*codec) + preset->len);
+ codec->media_type = AVDTP_MEDIA_TYPE_AUDIO;
+ codec->media_codec_type = endpoint->codec;
+ memcpy(codec->data, preset->data, preset->len);
+
+ service = avdtp_service_cap_new(AVDTP_MEDIA_CODEC, codec,
+ sizeof(*codec) + preset->len);
+ caps = g_slist_append(caps, service);
+
+ g_free(codec);
+
+ err = avdtp_set_configuration(dev->session, rsep, endpoint->sep, caps,
+ &stream);
+ g_slist_free_full(caps, g_free);
+ if (err < 0) {
+ error("avdtp_set_configuration: %s", strerror(-err));
+ return err;
+ }
+
+ setup_add(dev, endpoint, preset, stream);
+
+ return 0;
+}
+
+static void discover_cb(struct avdtp *session, GSList *seps,
+ struct avdtp_error *err, void *user_data)
+{
+ struct a2dp_device *dev = user_data;
+ struct a2dp_endpoint *endpoint = NULL;
+ struct avdtp_remote_sep *rsep = NULL;
+ GSList *l;
+
+ for (l = endpoints; l; l = g_slist_next(l)) {
+ endpoint = l->data;
+
+ rsep = avdtp_find_remote_sep(session, endpoint->sep);
+ if (rsep)
+ break;
+ }
+
+ if (!rsep) {
+ error("Unable to find matching endpoint");
+ goto failed;
+ }
+
+ if (select_configuration(dev, endpoint, rsep) < 0)
+ goto failed;
+
+ return;
+
+failed:
+ avdtp_shutdown(session);
+}
+
+static gboolean idle_timeout(gpointer user_data)
+{
+ struct a2dp_device *dev = user_data;
+ int err;
+
+ dev->idle_id = 0;
+
+ err = avdtp_discover(dev->session, discover_cb, dev);
+ if (err == 0)
+ return FALSE;
+
+ error("avdtp_discover: %s", strerror(-err));
+ bt_a2dp_notify_state(dev, HAL_A2DP_STATE_DISCONNECTED);
+
+ return FALSE;
+}
+
+static void signaling_connect_cb(GIOChannel *chan, GError *err,
+ gpointer user_data)
+{
+ struct a2dp_device *dev = user_data;
+ struct avdtp *session;
+ uint16_t imtu, omtu;
+ GError *gerr = NULL;
+ int fd;
+
+ if (err) {
+ bt_a2dp_notify_state(dev, HAL_A2DP_STATE_DISCONNECTED);
+ error("%s", err->message);
+ return;
+ }
+
+ bt_io_get(chan, &gerr,
+ BT_IO_OPT_IMTU, &imtu,
+ BT_IO_OPT_OMTU, &omtu,
+ BT_IO_OPT_INVALID);
+ if (gerr) {
+ error("%s", gerr->message);
+ g_error_free(gerr);
+ goto failed;
+ }
+
+ fd = g_io_channel_unix_get_fd(chan);
+
+ /* FIXME: Add proper version */
+ session = avdtp_new(fd, imtu, omtu, 0x0100, lseps);
+ if (!session)
+ goto failed;
+
+ dev->session = session;
+
+ avdtp_add_disconnect_cb(dev->session, disconnect_cb, dev);
+
+ /* Proceed to stream setup if initiator */
+ if (dev->io) {
+ int perr;
+
+ g_io_channel_unref(dev->io);
+ dev->io = NULL;
+
+ perr = avdtp_discover(dev->session, discover_cb, dev);
+ if (perr < 0) {
+ error("avdtp_discover: %s", strerror(-perr));
+ goto failed;
+ }
+ bt_avrcp_connect(&dev->dst);
+ } else /* Init idle timeout to discover */
+ dev->idle_id = g_timeout_add_seconds(IDLE_TIMEOUT, idle_timeout,
+ dev);
+
+ return;
+
+failed:
+ bt_a2dp_notify_state(dev, HAL_A2DP_STATE_DISCONNECTED);
+}
+
+static void bt_a2dp_connect(const void *buf, uint16_t len)
+{
+ const struct hal_cmd_a2dp_connect *cmd = buf;
+ struct a2dp_device *dev;
+ uint8_t status;
+ char addr[18];
+ bdaddr_t dst;
+ GSList *l;
+
+ DBG("");
+
+ android2bdaddr(&cmd->bdaddr, &dst);
+
+ l = g_slist_find_custom(devices, &dst, device_cmp);
+ if (l) {
+ status = HAL_STATUS_FAILED;
+ goto failed;
+ }
+
+ dev = a2dp_device_new(&dst);
+ if (!a2dp_device_connect(dev, signaling_connect_cb)) {
+ a2dp_device_remove(dev);
+ status = HAL_STATUS_FAILED;
+ goto failed;
+ }
+
+ ba2str(&dev->dst, addr);
+ DBG("connecting to %s", addr);
+
+ bt_a2dp_notify_state(dev, HAL_A2DP_STATE_CONNECTING);
+
+ status = HAL_STATUS_SUCCESS;
+
+failed:
+ ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_A2DP, HAL_OP_A2DP_CONNECT, status);
+}
+
+static void bt_a2dp_disconnect(const void *buf, uint16_t len)
+{
+ const struct hal_cmd_a2dp_connect *cmd = buf;
+ uint8_t status;
+ struct a2dp_device *dev;
+ GSList *l;
+ bdaddr_t dst;
+
+ DBG("");
+
+ android2bdaddr(&cmd->bdaddr, &dst);
+
+ l = g_slist_find_custom(devices, &dst, device_cmp);
+ if (!l) {
+ status = HAL_STATUS_FAILED;
+ goto failed;
+ }
+
+ dev = l->data;
+ status = HAL_STATUS_SUCCESS;
+
+ if (dev->io) {
+ bt_a2dp_notify_state(dev, HAL_A2DP_STATE_DISCONNECTED);
+ goto failed;
+ }
+
+ /* Wait AVDTP session to shutdown */
+ avdtp_shutdown(dev->session);
+ bt_a2dp_notify_state(dev, HAL_A2DP_STATE_DISCONNECTING);
+
+failed:
+ ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_A2DP, HAL_OP_A2DP_DISCONNECT,
+ status);
+}
+
+static const struct ipc_handler cmd_handlers[] = {
+ /* HAL_OP_A2DP_CONNECT */
+ { bt_a2dp_connect, false, sizeof(struct hal_cmd_a2dp_connect) },
+ /* HAL_OP_A2DP_DISCONNECT */
+ { bt_a2dp_disconnect, false, sizeof(struct hal_cmd_a2dp_disconnect) },
+};
+
+static struct a2dp_setup *find_setup_by_device(struct a2dp_device *dev)
+{
+ GSList *l;
+
+ for (l = setups; l; l = g_slist_next(l)) {
+ struct a2dp_setup *setup = l->data;
+
+ if (setup->dev == dev)
+ return setup;
+ }
+
+ return NULL;
+}
+
+static void transport_connect_cb(GIOChannel *chan, GError *err,
+ gpointer user_data)
+{
+ struct a2dp_device *dev = user_data;
+ struct a2dp_setup *setup;
+ uint16_t imtu, omtu;
+ GError *gerr = NULL;
+ int fd;
+
+ if (err) {
+ error("%s", err->message);
+ return;
+ }
+
+ setup = find_setup_by_device(dev);
+ if (!setup) {
+ error("Unable to find stream setup");
+ return;
+ }
+
+ bt_io_get(chan, &gerr,
+ BT_IO_OPT_IMTU, &imtu,
+ BT_IO_OPT_OMTU, &omtu,
+ BT_IO_OPT_INVALID);
+ if (gerr) {
+ error("%s", gerr->message);
+ g_error_free(gerr);
+ return;
+ }
+
+ fd = g_io_channel_unix_get_fd(chan);
+
+ if (!avdtp_stream_set_transport(setup->stream, fd, imtu, omtu)) {
+ error("avdtp_stream_set_transport: failed");
+ return;
+ }
+
+ g_io_channel_set_close_on_unref(chan, FALSE);
+
+ if (dev->io) {
+ g_io_channel_unref(dev->io);
+ dev->io = NULL;
+ }
+
+ bt_a2dp_notify_state(dev, HAL_A2DP_STATE_CONNECTED);
+}
+
+static void connect_cb(GIOChannel *chan, GError *err, gpointer user_data)
+{
+ struct a2dp_device *dev;
+ bdaddr_t dst;
+ char address[18];
+ GError *gerr = NULL;
+ GSList *l;
+
+ if (err) {
+ error("%s", err->message);
+ return;
+ }
+
+ bt_io_get(chan, &gerr,
+ BT_IO_OPT_DEST_BDADDR, &dst,
+ BT_IO_OPT_INVALID);
+ if (gerr) {
+ error("%s", gerr->message);
+ g_error_free(gerr);
+ g_io_channel_shutdown(chan, TRUE, NULL);
+ return;
+ }
+
+ ba2str(&dst, address);
+ DBG("Incoming connection from %s", address);
+
+ l = g_slist_find_custom(devices, &dst, device_cmp);
+ if (l) {
+ transport_connect_cb(chan, err, l->data);
+ return;
+ }
+
+ dev = a2dp_device_new(&dst);
+ bt_a2dp_notify_state(dev, HAL_A2DP_STATE_CONNECTING);
+ signaling_connect_cb(chan, err, dev);
+}
+
+static sdp_record_t *a2dp_record(void)
+{
+ sdp_list_t *svclass_id, *pfseq, *apseq, *root;
+ uuid_t root_uuid, l2cap_uuid, avdtp_uuid, a2dp_uuid;
+ sdp_profile_desc_t profile[1];
+ sdp_list_t *aproto, *proto[2];
+ sdp_record_t *record;
+ sdp_data_t *psm, *version, *features;
+ uint16_t lp = AVDTP_UUID;
+ uint16_t a2dp_ver = 0x0103, avdtp_ver = 0x0103, feat = 0x000f;
+
+ record = sdp_record_alloc();
+ if (!record)
+ return NULL;
+
+ sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
+ root = sdp_list_append(NULL, &root_uuid);
+ sdp_set_browse_groups(record, root);
+
+ sdp_uuid16_create(&a2dp_uuid, AUDIO_SOURCE_SVCLASS_ID);
+ svclass_id = sdp_list_append(NULL, &a2dp_uuid);
+ sdp_set_service_classes(record, svclass_id);
+
+ sdp_uuid16_create(&profile[0].uuid, ADVANCED_AUDIO_PROFILE_ID);
+ profile[0].version = a2dp_ver;
+ pfseq = sdp_list_append(NULL, &profile[0]);
+ sdp_set_profile_descs(record, pfseq);
+
+ sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID);
+ proto[0] = sdp_list_append(NULL, &l2cap_uuid);
+ psm = sdp_data_alloc(SDP_UINT16, &lp);
+ proto[0] = sdp_list_append(proto[0], psm);
+ apseq = sdp_list_append(NULL, proto[0]);
+
+ sdp_uuid16_create(&avdtp_uuid, AVDTP_UUID);
+ proto[1] = sdp_list_append(NULL, &avdtp_uuid);
+ version = sdp_data_alloc(SDP_UINT16, &avdtp_ver);
+ proto[1] = sdp_list_append(proto[1], version);
+ apseq = sdp_list_append(apseq, proto[1]);
+
+ aproto = sdp_list_append(NULL, apseq);
+ sdp_set_access_protos(record, aproto);
+
+ features = sdp_data_alloc(SDP_UINT16, &feat);
+ sdp_attr_add(record, SDP_ATTR_SUPPORTED_FEATURES, features);
+
+ sdp_set_info_attr(record, "Audio Source", NULL, NULL);
+
+ sdp_data_free(psm);
+ sdp_data_free(version);
+ sdp_list_free(proto[0], NULL);
+ sdp_list_free(proto[1], NULL);
+ sdp_list_free(apseq, NULL);
+ sdp_list_free(pfseq, NULL);
+ sdp_list_free(aproto, NULL);
+ sdp_list_free(root, NULL);
+ sdp_list_free(svclass_id, NULL);
+
+ return record;
+}
+
+static gboolean sep_getcap_ind(struct avdtp *session,
+ struct avdtp_local_sep *sep,
+ GSList **caps, uint8_t *err,
+ void *user_data)
+{
+ struct a2dp_endpoint *endpoint = user_data;
+ struct a2dp_preset *cap = endpoint->caps;
+ struct avdtp_service_capability *service;
+ struct avdtp_media_codec_capability *codec;
+
+ *caps = NULL;
+
+ service = avdtp_service_cap_new(AVDTP_MEDIA_TRANSPORT, NULL, 0);
+ *caps = g_slist_append(*caps, service);
+
+ codec = g_malloc0(sizeof(*codec) + cap->len);
+ codec->media_type = AVDTP_MEDIA_TYPE_AUDIO;
+ codec->media_codec_type = endpoint->codec;
+ memcpy(codec->data, cap->data, cap->len);
+
+ service = avdtp_service_cap_new(AVDTP_MEDIA_CODEC, codec,
+ sizeof(*codec) + cap->len);
+ *caps = g_slist_append(*caps, service);
+ g_free(codec);
+
+ return TRUE;
+}
+
+static int check_config(struct a2dp_endpoint *endpoint,
+ struct a2dp_preset *config)
+{
+ GSList *l;
+ struct a2dp_preset *caps;
+
+ for (l = endpoint->presets; l; l = g_slist_next(l)) {
+ struct a2dp_preset *preset = l->data;
+
+ if (preset->len != config->len)
+ continue;
+
+ if (memcmp(preset->data, config->data, preset->len) == 0)
+ return 0;
+ }
+
+ caps = endpoint->caps;
+
+ /* Codec specific */
+ switch (endpoint->codec) {
+ case A2DP_CODEC_SBC:
+ return sbc_check_config(caps->data, caps->len, config->data,
+ config->len);
+ default:
+ return -EINVAL;
+ }
+}
+
+static struct a2dp_device *find_device_by_session(struct avdtp *session)
+{
+ GSList *l;
+
+ for (l = devices; l; l = g_slist_next(l)) {
+ struct a2dp_device *dev = l->data;
+
+ if (dev->session == session)
+ return dev;
+ }
+
+ return NULL;
+}
+
+static struct a2dp_setup *find_setup(uint8_t id)
+{
+ GSList *l;
+
+ for (l = setups; l; l = g_slist_next(l)) {
+ struct a2dp_setup *setup = l->data;
+
+ if (setup->endpoint->id == id)
+ return setup;
+ }
+
+ return NULL;
+}
+
+static void setup_remove_by_id(uint8_t id)
+{
+ struct a2dp_setup *setup;
+
+ setup = find_setup(id);
+ if (!setup) {
+ error("Unable to find stream setup for endpoint %u", id);
+ return;
+ }
+
+ setup_remove(setup);
+}
+
+static gboolean sep_setconf_ind(struct avdtp *session,
+ struct avdtp_local_sep *sep,
+ struct avdtp_stream *stream,
+ GSList *caps,
+ avdtp_set_configuration_cb cb,
+ void *user_data)
+{
+ struct a2dp_endpoint *endpoint = user_data;
+ struct a2dp_device *dev;
+ struct a2dp_preset *preset = NULL;
+
+ DBG("");
+
+ dev = find_device_by_session(session);
+ if (!dev) {
+ error("Unable to find device for session %p", session);
+ return FALSE;
+ }
+
+ for (; caps != NULL; caps = g_slist_next(caps)) {
+ struct avdtp_service_capability *cap = caps->data;
+ struct avdtp_media_codec_capability *codec;
+
+ if (cap->category == AVDTP_DELAY_REPORTING)
+ return FALSE;
+
+ if (cap->category != AVDTP_MEDIA_CODEC)
+ continue;
+
+ codec = (struct avdtp_media_codec_capability *) cap->data;
+
+ if (codec->media_codec_type != endpoint->codec)
+ return FALSE;
+
+ preset = g_new0(struct a2dp_preset, 1);
+ preset->len = cap->length - sizeof(*codec);
+ preset->data = g_memdup(codec->data, preset->len);
+
+ if (check_config(endpoint, preset) < 0) {
+ preset_free(preset);
+ return FALSE;
+ }
+ }
+
+ if (!preset)
+ return FALSE;
+
+ setup_add(dev, endpoint, preset, stream);
+
+ cb(session, stream, NULL);
+
+ return TRUE;
+}
+
+static gboolean sep_open_ind(struct avdtp *session, struct avdtp_local_sep *sep,
+ struct avdtp_stream *stream, uint8_t *err,
+ void *user_data)
+{
+ struct a2dp_endpoint *endpoint = user_data;
+ struct a2dp_setup *setup;
+
+ DBG("");
+
+ setup = find_setup(endpoint->id);
+ if (!setup) {
+ error("Unable to find stream setup for endpoint %u",
+ endpoint->id);
+ *err = AVDTP_SEP_NOT_IN_USE;
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static gboolean sep_close_ind(struct avdtp *session,
+ struct avdtp_local_sep *sep,
+ struct avdtp_stream *stream,
+ uint8_t *err,
+ void *user_data)
+{
+ struct a2dp_endpoint *endpoint = user_data;
+ struct a2dp_setup *setup;
+
+ DBG("");
+
+ setup = find_setup(endpoint->id);
+ if (!setup) {
+ error("Unable to find stream setup for endpoint %u",
+ endpoint->id);
+ *err = AVDTP_SEP_NOT_IN_USE;
+ return FALSE;
+ }
+
+ bt_audio_notify_state(setup, HAL_AUDIO_STOPPED);
+
+ setup_remove(setup);
+
+ return TRUE;
+}
+
+static gboolean sep_start_ind(struct avdtp *session,
+ struct avdtp_local_sep *sep,
+ struct avdtp_stream *stream,
+ uint8_t *err,
+ void *user_data)
+{
+ struct a2dp_endpoint *endpoint = user_data;
+ struct a2dp_setup *setup;
+
+ DBG("");
+
+ setup = find_setup(endpoint->id);
+ if (!setup) {
+ error("Unable to find stream setup for endpoint %u",
+ endpoint->id);
+ *err = AVDTP_SEP_NOT_IN_USE;
+ return FALSE;
+ }
+
+ bt_audio_notify_state(setup, HAL_AUDIO_STARTED);
+
+ return TRUE;
+}
+
+static gboolean sep_suspend_ind(struct avdtp *session,
+ struct avdtp_local_sep *sep,
+ struct avdtp_stream *stream,
+ uint8_t *err,
+ void *user_data)
+{
+ struct a2dp_endpoint *endpoint = user_data;
+ struct a2dp_setup *setup;
+
+ DBG("");
+
+ setup = find_setup(endpoint->id);
+ if (!setup) {
+ error("Unable to find stream setup for endpoint %u",
+ endpoint->id);
+ *err = AVDTP_SEP_NOT_IN_USE;
+ return FALSE;
+ }
+
+ bt_audio_notify_state(setup, HAL_AUDIO_SUSPEND);
+
+ return TRUE;
+}
+
+static struct avdtp_sep_ind sep_ind = {
+ .get_capability = sep_getcap_ind,
+ .set_configuration = sep_setconf_ind,
+ .open = sep_open_ind,
+ .close = sep_close_ind,
+ .start = sep_start_ind,
+ .suspend = sep_suspend_ind,
+};
+
+static void sep_setconf_cfm(struct avdtp *session, struct avdtp_local_sep *sep,
+ struct avdtp_stream *stream,
+ struct avdtp_error *err, void *user_data)
+{
+ struct a2dp_endpoint *endpoint = user_data;
+ struct a2dp_setup *setup;
+ int ret;
+
+ DBG("");
+
+ setup = find_setup(endpoint->id);
+ if (!setup) {
+ error("Unable to find stream setup for endpoint %u",
+ endpoint->id);
+ return;
+ }
+
+ if (err)
+ goto failed;
+
+ ret = avdtp_open(session, stream);
+ if (ret < 0) {
+ error("avdtp_open: %s", strerror(-ret));
+ goto failed;
+ }
+
+ return;
+
+failed:
+ setup_remove(setup);
+}
+
+static void sep_open_cfm(struct avdtp *session, struct avdtp_local_sep *sep,
+ struct avdtp_stream *stream, struct avdtp_error *err,
+ void *user_data)
+{
+ struct a2dp_endpoint *endpoint = user_data;
+ struct a2dp_device *dev;
+
+ DBG("");
+
+ if (err)
+ goto failed;
+
+ dev = find_device_by_session(session);
+ if (!dev) {
+ error("Unable to find device for session");
+ goto failed;
+ }
+
+ a2dp_device_connect(dev, transport_connect_cb);
+
+ return;
+
+failed:
+ setup_remove_by_id(endpoint->id);
+}
+
+static void sep_start_cfm(struct avdtp *session, struct avdtp_local_sep *sep,
+ struct avdtp_stream *stream, struct avdtp_error *err,
+ void *user_data)
+{
+ struct a2dp_endpoint *endpoint = user_data;
+ struct a2dp_setup *setup;
+
+ DBG("");
+
+ if (err) {
+ setup_remove_by_id(endpoint->id);
+ return;
+ }
+
+ setup = find_setup(endpoint->id);
+ if (!setup) {
+ error("Unable to find stream setup for %u endpoint",
+ endpoint->id);
+ return;
+ }
+
+ bt_audio_notify_state(setup, HAL_AUDIO_STARTED);
+}
+
+static void sep_suspend_cfm(struct avdtp *session, struct avdtp_local_sep *sep,
+ struct avdtp_stream *stream, struct avdtp_error *err,
+ void *user_data)
+{
+ struct a2dp_endpoint *endpoint = user_data;
+ struct a2dp_setup *setup;
+
+ DBG("");
+
+ if (err) {
+ setup_remove_by_id(endpoint->id);
+ return;
+ }
+
+ setup = find_setup(endpoint->id);
+ if (!setup) {
+ error("Unable to find stream setup for %u endpoint",
+ endpoint->id);
+ return;
+ }
+
+ bt_audio_notify_state(setup, HAL_AUDIO_STOPPED);
+}
+
+static void sep_close_cfm(struct avdtp *session, struct avdtp_local_sep *sep,
+ struct avdtp_stream *stream, struct avdtp_error *err,
+ void *user_data)
+{
+ struct a2dp_endpoint *endpoint = user_data;
+ struct a2dp_setup *setup;
+
+ DBG("");
+
+ if (err)
+ return;
+
+ setup = find_setup(endpoint->id);
+ if (!setup) {
+ error("Unable to find stream setup for %u endpoint",
+ endpoint->id);
+ return;
+ }
+
+ bt_audio_notify_state(setup, HAL_AUDIO_STOPPED);
+
+ setup_remove(setup);
+}
+
+static void sep_abort_cfm(struct avdtp *session, struct avdtp_local_sep *sep,
+ struct avdtp_stream *stream, struct avdtp_error *err,
+ void *user_data)
+{
+ struct a2dp_endpoint *endpoint = user_data;
+
+ DBG("");
+
+ if (err)
+ return;
+
+ setup_remove_by_id(endpoint->id);
+}
+
+static struct avdtp_sep_cfm sep_cfm = {
+ .set_configuration = sep_setconf_cfm,
+ .open = sep_open_cfm,
+ .start = sep_start_cfm,
+ .suspend = sep_suspend_cfm,
+ .close = sep_close_cfm,
+ .abort = sep_abort_cfm,
+};
+
+static uint8_t register_endpoint(const uint8_t *uuid, uint8_t codec,
+ GSList *presets)
+{
+ struct a2dp_endpoint *endpoint;
+
+ /* FIXME: Add proper check for uuid */
+
+ endpoint = g_new0(struct a2dp_endpoint, 1);
+ endpoint->id = g_slist_length(endpoints) + 1;
+ endpoint->codec = codec;
+ endpoint->sep = avdtp_register_sep(lseps, AVDTP_SEP_TYPE_SOURCE,
+ AVDTP_MEDIA_TYPE_AUDIO,
+ codec, FALSE, &sep_ind,
+ &sep_cfm, endpoint);
+ endpoint->caps = presets->data;
+ endpoint->presets = g_slist_copy(g_slist_nth(presets, 1));
+
+ if (endpoint->codec == A2DP_CODEC_VENDOR) {
+ a2dp_vendor_codec_t *vndcodec = (void *) endpoint->caps->data;
+
+ avdtp_sep_set_vendor_codec(endpoint->sep,
+ btohl(vndcodec->vendor_id),
+ btohs(vndcodec->codec_id));
+ }
+
+ endpoints = g_slist_append(endpoints, endpoint);
+
+ return endpoint->id;
+}
+
+static GSList *parse_presets(const struct audio_preset *p, uint8_t count,
+ uint16_t len)
+{
+ GSList *l = NULL;
+ uint8_t i;
+
+ for (i = 0; count > i; i++) {
+ const uint8_t *ptr = (const uint8_t *) p;
+ struct a2dp_preset *preset;
+
+ if (len < sizeof(struct audio_preset)) {
+ DBG("Invalid preset index %u", i);
+ g_slist_free_full(l, preset_free);
+ return NULL;
+ }
+
+ len -= sizeof(struct audio_preset);
+ if (len == 0 || len < p->len) {
+ DBG("Invalid preset size of %u for index %u", len, i);
+ g_slist_free_full(l, preset_free);
+ return NULL;
+ }
+
+ preset = g_new0(struct a2dp_preset, 1);
+ preset->len = p->len;
+ preset->data = g_memdup(p->data, preset->len);
+ l = g_slist_append(l, preset);
+
+ len -= preset->len;
+ ptr += sizeof(*p) + preset->len;
+ p = (const struct audio_preset *) ptr;
+ }
+
+ return l;
+}
+
+static void bt_audio_open(const void *buf, uint16_t len)
+{
+ const struct audio_cmd_open *cmd = buf;
+ struct audio_rsp_open rsp;
+ GSList *presets;
+
+ DBG("");
+
+ audio_retrying = false;
+
+ if (cmd->presets == 0) {
+ error("No audio presets found");
+ goto failed;
+ }
+
+ presets = parse_presets(cmd->preset, cmd->presets, len - sizeof(*cmd));
+ if (!presets) {
+ error("No audio presets found");
+ goto failed;
+ }
+
+ rsp.id = register_endpoint(cmd->uuid, cmd->codec, presets);
+ if (rsp.id == 0) {
+ g_slist_free_full(presets, preset_free);
+ error("Unable to register endpoint");
+ goto failed;
+ }
+
+ g_slist_free(presets);
+
+ ipc_send_rsp_full(audio_ipc, AUDIO_SERVICE_ID, AUDIO_OP_OPEN,
+ sizeof(rsp), &rsp, -1);
+
+ return;
+
+failed:
+ ipc_send_rsp(audio_ipc, AUDIO_SERVICE_ID, AUDIO_OP_OPEN,
+ AUDIO_STATUS_FAILED);
+}
+
+static struct a2dp_endpoint *find_endpoint(uint8_t id)
+{
+ GSList *l;
+
+ for (l = endpoints; l; l = g_slist_next(l)) {
+ struct a2dp_endpoint *endpoint = l->data;
+
+ if (endpoint->id == id)
+ return endpoint;
+ }
+
+ return NULL;
+}
+
+static void bt_audio_close(const void *buf, uint16_t len)
+{
+ const struct audio_cmd_close *cmd = buf;
+ struct a2dp_endpoint *endpoint;
+
+ DBG("");
+
+ endpoint = find_endpoint(cmd->id);
+ if (!endpoint) {
+ error("Unable to find endpoint %u", cmd->id);
+ ipc_send_rsp(audio_ipc, AUDIO_SERVICE_ID, AUDIO_OP_CLOSE,
+ AUDIO_STATUS_FAILED);
+ return;
+ }
+
+ endpoints = g_slist_remove(endpoints, endpoint);
+ unregister_endpoint(endpoint);
+
+ ipc_send_rsp(audio_ipc, AUDIO_SERVICE_ID, AUDIO_OP_CLOSE,
+ AUDIO_STATUS_SUCCESS);
+}
+
+static void bt_stream_open(const void *buf, uint16_t len)
+{
+ const struct audio_cmd_open_stream *cmd = buf;
+ struct audio_rsp_open_stream *rsp;
+ struct a2dp_setup *setup;
+ int fd;
+ uint16_t omtu;
+
+ DBG("");
+
+ if (cmd->id)
+ setup = find_setup(cmd->id);
+ else
+ setup = setups ? setups->data : NULL;
+ if (!setup) {
+ error("Unable to find stream for endpoint %u", cmd->id);
+ ipc_send_rsp(audio_ipc, AUDIO_SERVICE_ID, AUDIO_OP_OPEN_STREAM,
+ AUDIO_STATUS_FAILED);
+ return;
+ }
+
+ if (!avdtp_stream_get_transport(setup->stream, &fd, NULL, &omtu,
+ NULL)) {
+ error("avdtp_stream_get_transport: failed");
+ ipc_send_rsp(audio_ipc, AUDIO_SERVICE_ID, AUDIO_OP_OPEN_STREAM,
+ AUDIO_STATUS_FAILED);
+ return;
+ }
+
+ len = sizeof(struct audio_rsp_open_stream) +
+ sizeof(struct audio_preset) + setup->preset->len;
+ rsp = g_malloc0(len);
+ rsp->id = setup->endpoint->id;
+ rsp->mtu = omtu;
+ rsp->preset->len = setup->preset->len;
+ memcpy(rsp->preset->data, setup->preset->data, setup->preset->len);
+
+ ipc_send_rsp_full(audio_ipc, AUDIO_SERVICE_ID, AUDIO_OP_OPEN_STREAM,
+ len, rsp, fd);
+
+ g_free(rsp);
+}
+
+static void bt_stream_close(const void *buf, uint16_t len)
+{
+ const struct audio_cmd_close_stream *cmd = buf;
+ struct a2dp_setup *setup;
+ int err;
+
+ DBG("");
+
+ setup = find_setup(cmd->id);
+ if (!setup) {
+ error("Unable to find stream for endpoint %u", cmd->id);
+ goto failed;
+ }
+
+ err = avdtp_close(setup->dev->session, setup->stream, FALSE);
+ if (err < 0) {
+ error("avdtp_close: %s", strerror(-err));
+ goto failed;
+ }
+
+ ipc_send_rsp(audio_ipc, AUDIO_SERVICE_ID, AUDIO_OP_CLOSE_STREAM,
+ AUDIO_STATUS_SUCCESS);
+
+ return;
+
+failed:
+ ipc_send_rsp(audio_ipc, AUDIO_SERVICE_ID, AUDIO_OP_CLOSE_STREAM,
+ AUDIO_STATUS_FAILED);
+}
+
+static void bt_stream_resume(const void *buf, uint16_t len)
+{
+ const struct audio_cmd_resume_stream *cmd = buf;
+ struct a2dp_setup *setup;
+ int err;
+
+ DBG("");
+
+ setup = find_setup(cmd->id);
+ if (!setup) {
+ error("Unable to find stream for endpoint %u", cmd->id);
+ goto failed;
+ }
+
+ if (setup->state != HAL_AUDIO_STARTED) {
+ err = avdtp_start(setup->dev->session, setup->stream);
+ if (err < 0) {
+ error("avdtp_start: %s", strerror(-err));
+ goto failed;
+ }
+ }
+
+ ipc_send_rsp(audio_ipc, AUDIO_SERVICE_ID, AUDIO_OP_RESUME_STREAM,
+ AUDIO_STATUS_SUCCESS);
+
+ return;
+
+failed:
+ ipc_send_rsp(audio_ipc, AUDIO_SERVICE_ID, AUDIO_OP_RESUME_STREAM,
+ AUDIO_STATUS_FAILED);
+}
+
+static void bt_stream_suspend(const void *buf, uint16_t len)
+{
+ const struct audio_cmd_suspend_stream *cmd = buf;
+ struct a2dp_setup *setup;
+ int err;
+
+ DBG("");
+
+ setup = find_setup(cmd->id);
+ if (!setup) {
+ error("Unable to find stream for endpoint %u", cmd->id);
+ goto failed;
+ }
+
+ err = avdtp_suspend(setup->dev->session, setup->stream);
+ if (err < 0) {
+ error("avdtp_suspend: %s", strerror(-err));
+ goto failed;
+ }
+
+ ipc_send_rsp(audio_ipc, AUDIO_SERVICE_ID, AUDIO_OP_SUSPEND_STREAM,
+ AUDIO_STATUS_SUCCESS);
+
+ return;
+
+failed:
+ ipc_send_rsp(audio_ipc, AUDIO_SERVICE_ID, AUDIO_OP_SUSPEND_STREAM,
+ AUDIO_STATUS_FAILED);
+}
+
+static const struct ipc_handler audio_handlers[] = {
+ /* AUDIO_OP_OPEN */
+ { bt_audio_open, true, sizeof(struct audio_cmd_open) },
+ /* AUDIO_OP_CLOSE */
+ { bt_audio_close, false, sizeof(struct audio_cmd_close) },
+ /* AUDIO_OP_OPEN_STREAM */
+ { bt_stream_open, false, sizeof(struct audio_cmd_open_stream) },
+ /* AUDIO_OP_CLOSE_STREAM */
+ { bt_stream_close, false, sizeof(struct audio_cmd_close_stream) },
+ /* AUDIO_OP_RESUME_STREAM */
+ { bt_stream_resume, false, sizeof(struct audio_cmd_resume_stream) },
+ /* AUDIO_OP_SUSPEND_STREAM */
+ { bt_stream_suspend, false, sizeof(struct audio_cmd_suspend_stream) },
+};
+
+static void bt_audio_unregister(void)
+{
+ DBG("");
+
+ if (audio_retry_id > 0)
+ g_source_remove(audio_retry_id);
+
+ g_slist_free_full(endpoints, unregister_endpoint);
+ endpoints = NULL;
+
+ g_slist_free_full(setups, setup_free);
+ setups = NULL;
+
+ ipc_cleanup(audio_ipc);
+ audio_ipc = NULL;
+
+ queue_destroy(lseps, NULL);
+}
+
+static bool bt_audio_register(ipc_disconnect_cb disconnect)
+{
+ DBG("");
+
+ audio_ipc = ipc_init(BLUEZ_AUDIO_SK_PATH, sizeof(BLUEZ_AUDIO_SK_PATH),
+ AUDIO_SERVICE_ID_MAX, false, disconnect, NULL);
+ if (!audio_ipc)
+ return false;
+
+ ipc_register(audio_ipc, AUDIO_SERVICE_ID, audio_handlers,
+ G_N_ELEMENTS(audio_handlers));
+
+ return true;
+}
+
+static gboolean audio_retry_register(void *data)
+{
+ ipc_disconnect_cb cb = data;
+
+ audio_retry_id = 0;
+ audio_retrying = true;
+
+ bt_audio_register(cb);
+
+ return FALSE;
+}
+
+static void audio_disconnected(void *data)
+{
+ GSList *l;
+ bool restart;
+
+ DBG("");
+
+ if (audio_retrying)
+ goto retry;
+
+ restart = endpoints != NULL ? true : false;
+
+ bt_audio_unregister();
+
+ for (l = devices; l; l = g_slist_next(l)) {
+ struct a2dp_device *dev = l->data;
+
+ avdtp_shutdown(dev->session);
+ }
+
+ if (!restart)
+ return;
+
+retry:
+ audio_retry_id = g_timeout_add_seconds(AUDIO_RETRY_TIMEOUT,
+ audio_retry_register,
+ audio_disconnected);
+}
+
+bool bt_a2dp_register(struct ipc *ipc, const bdaddr_t *addr, uint8_t mode)
+{
+ GError *err = NULL;
+ sdp_record_t *rec;
+
+ DBG("");
+
+ bacpy(&adapter_addr, addr);
+
+ lseps = queue_new();
+
+ server = bt_io_listen(connect_cb, NULL, NULL, NULL, &err,
+ BT_IO_OPT_SOURCE_BDADDR, &adapter_addr,
+ BT_IO_OPT_PSM, AVDTP_PSM,
+ BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_MEDIUM,
+ BT_IO_OPT_MASTER, true,
+ BT_IO_OPT_INVALID);
+ if (!server) {
+ error("Failed to listen on AVDTP channel: %s", err->message);
+ g_error_free(err);
+ return false;
+ }
+
+ rec = a2dp_record();
+ if (!rec) {
+ error("Failed to allocate A2DP record");
+ goto fail;
+ }
+
+ if (bt_adapter_add_record(rec, SVC_HINT_CAPTURING) < 0) {
+ error("Failed to register A2DP record");
+ sdp_record_free(rec);
+ goto fail;
+ }
+ record_id = rec->handle;
+
+ hal_ipc = ipc;
+
+ ipc_register(hal_ipc, HAL_SERVICE_ID_A2DP, cmd_handlers,
+ G_N_ELEMENTS(cmd_handlers));
+
+ if (bt_audio_register(audio_disconnected))
+ return true;
+
+fail:
+ g_io_channel_shutdown(server, TRUE, NULL);
+ g_io_channel_unref(server);
+ server = NULL;
+ return false;
+}
+
+void bt_a2dp_unregister(void)
+{
+ DBG("");
+
+ g_slist_free_full(setups, setup_free);
+ setups = NULL;
+
+ g_slist_free_full(endpoints, unregister_endpoint);
+ endpoints = NULL;
+
+ g_slist_free_full(devices, a2dp_device_free);
+ devices = NULL;
+
+ ipc_unregister(hal_ipc, HAL_SERVICE_ID_A2DP);
+ hal_ipc = NULL;
+
+ bt_adapter_remove_record(record_id);
+ record_id = 0;
+
+ if (server) {
+ g_io_channel_shutdown(server, TRUE, NULL);
+ g_io_channel_unref(server);
+ server = NULL;
+ }
+
+ if (audio_ipc) {
+ ipc_unregister(audio_ipc, AUDIO_SERVICE_ID);
+ ipc_cleanup(audio_ipc);
+ audio_ipc = NULL;
+ }
+}
diff --git a/repo/android/a2dp.h b/repo/android/a2dp.h
new file mode 100644
index 0000000..8a70407
--- /dev/null
+++ b/repo/android/a2dp.h
@@ -0,0 +1,25 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2013-2014 Intel Corporation. All rights reserved.
+ *
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+bool bt_a2dp_register(struct ipc *ipc, const bdaddr_t *addr, uint8_t mode);
+void bt_a2dp_unregister(void);
diff --git a/repo/android/audio-ipc-api.txt b/repo/android/audio-ipc-api.txt
new file mode 100644
index 0000000..f4a497d
--- /dev/null
+++ b/repo/android/audio-ipc-api.txt
@@ -0,0 +1,87 @@
+Bluetooth Audio Plugin
+======================
+
+The audio plugin happen to be in a different socket but all the rules for
+HAL socket apply here as well, the abstract socket name is
+"\0bluez_audio_socket" (tentative):
+
+ .---Audio---. .--Android--.
+ | Plugin | | Daemon |
+ | | Command | |
+ | | --------------------------> | |
+ | | | |
+ | | <-------------------------- | |
+ | | Response | |
+ | | | |
+ | | | |
+ | | | |
+ '-----------' '-----------'
+
+
+ Audio HAL Daemon
+ ----------------------------------------------------
+
+ call dev->open() --> command 0x01
+ return dev->open() <-- response 0x01
+
+ call dev->open_output_stream() --> command 0x03
+ return dev->open_output_stream() <-- response 0x03
+
+ call stream->write() --> command 0x05
+ return stream->write() <-- response 0x05
+
+ call stream->common.standby() --> command 0x06
+ return stream->common.standby() <-- response 0x06
+
+ call dev->close_output_stream() --> command 0x04
+ return dev->close_output_stream() <-- response 0x04
+
+ call dev->close() --> command 0x02
+ return dev->close() <-- response 0x02
+
+Audio Service (ID 0)
+====================
+
+ Opcode 0x00 - Error response
+
+ Response parameters: Status (1 octet)
+
+ Opcode 0x01 - Open Audio Endpoint commmand
+
+ Command parameters: Service UUID (16 octets)
+ Codec ID (1 octet)
+ Number of codec presets (1 octet)
+ Codec capabilities length (1 octet)
+ Codec capabilities (variable)
+ Codec preset # length (1 octet)
+ Codec preset # configuration (variable)
+ ...
+ Response parameters: Endpoint ID (1 octet)
+
+ Opcode 0x02 - Close Audio Endpoint command
+
+ Command parameters: Endpoint ID (1 octet)
+ Response parameters: <none>
+
+ Opcode 0x03 - Open Stream command
+
+ Command parameters: Endpoint ID (1 octet)
+ Response parameters: Outgoing MTU (2 octets)
+ Codec configuration length (1 octet)
+ Codec configuration (1 octet)
+ File descriptor (inline)
+
+ Opcode 0x04 - Close Stream command
+
+ Command parameters: Endpoint ID (1 octet)
+ Response parameters: <none>
+
+ Opcode 0x05 - Resume Stream command
+
+ Command parameters: Endpoint ID (1 octet)
+ Response parameters: <none>
+
+ Opcode 0x06 - Suspend Stream command
+
+ Command parameters: Endpoint ID (1 octet)
+ Response parameters: <none>
diff --git a/repo/android/audio-msg.h b/repo/android/audio-msg.h
new file mode 100644
index 0000000..7b9553b
--- /dev/null
+++ b/repo/android/audio-msg.h
@@ -0,0 +1,82 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2014 Intel Corporation. All rights reserved.
+ *
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#define BLUEZ_AUDIO_MTU 1024
+
+static const char BLUEZ_AUDIO_SK_PATH[] = "\0bluez_audio_socket";
+
+#define AUDIO_SERVICE_ID 0
+#define AUDIO_SERVICE_ID_MAX AUDIO_SERVICE_ID
+
+#define AUDIO_STATUS_SUCCESS IPC_STATUS_SUCCESS
+#define AUDIO_STATUS_FAILED 0x01
+
+#define AUDIO_OP_STATUS IPC_OP_STATUS
+
+#define AUDIO_OP_OPEN 0x01
+struct audio_preset {
+ uint8_t len;
+ uint8_t data[0];
+} __attribute__((packed));
+
+struct audio_cmd_open {
+ uint8_t uuid[16];
+ uint8_t codec;
+ uint8_t presets;
+ struct audio_preset preset[0];
+} __attribute__((packed));
+
+struct audio_rsp_open {
+ uint8_t id;
+} __attribute__((packed));
+
+#define AUDIO_OP_CLOSE 0x02
+struct audio_cmd_close {
+ uint8_t id;
+} __attribute__((packed));
+
+#define AUDIO_OP_OPEN_STREAM 0x03
+struct audio_cmd_open_stream {
+ uint8_t id;
+} __attribute__((packed));
+
+struct audio_rsp_open_stream {
+ uint16_t id;
+ uint16_t mtu;
+ struct audio_preset preset[0];
+} __attribute__((packed));
+
+#define AUDIO_OP_CLOSE_STREAM 0x04
+struct audio_cmd_close_stream {
+ uint8_t id;
+} __attribute__((packed));
+
+#define AUDIO_OP_RESUME_STREAM 0x05
+struct audio_cmd_resume_stream {
+ uint8_t id;
+} __attribute__((packed));
+
+#define AUDIO_OP_SUSPEND_STREAM 0x06
+struct audio_cmd_suspend_stream {
+ uint8_t id;
+} __attribute__((packed));
diff --git a/repo/android/audio_utils/resampler.c b/repo/android/audio_utils/resampler.c
new file mode 100644
index 0000000..ce30375
--- /dev/null
+++ b/repo/android/audio_utils/resampler.c
@@ -0,0 +1,270 @@
+/*
+** Copyright 2011, The Android Open-Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+//#define LOG_NDEBUG 0
+
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <system/audio.h>
+#include <audio_utils/resampler.h>
+#include <speex/speex_resampler.h>
+
+#include "hal-log.h"
+
+struct resampler {
+ struct resampler_itfe itfe;
+ SpeexResamplerState *speex_resampler; // handle on speex resampler
+ struct resampler_buffer_provider *provider; // buffer provider installed by client
+ uint32_t in_sample_rate; // input sampling rate in Hz
+ uint32_t out_sample_rate; // output sampling rate in Hz
+ uint32_t channel_count; // number of channels (interleaved)
+ int16_t *in_buf; // input buffer
+ size_t in_buf_size; // input buffer size
+ size_t frames_in; // number of frames in input buffer
+ size_t frames_rq; // cached number of output frames
+ size_t frames_needed; // minimum number of input frames to produce
+ // frames_rq output frames
+ int32_t speex_delay_ns; // delay introduced by speex resampler in ns
+};
+
+
+//------------------------------------------------------------------------------
+// speex based resampler
+//------------------------------------------------------------------------------
+
+static void resampler_reset(struct resampler_itfe *resampler)
+{
+ struct resampler *rsmp = (struct resampler *)resampler;
+
+ rsmp->frames_in = 0;
+ rsmp->frames_rq = 0;
+
+ if (rsmp != NULL && rsmp->speex_resampler != NULL) {
+ speex_resampler_reset_mem(rsmp->speex_resampler);
+ }
+}
+
+static int32_t resampler_delay_ns(struct resampler_itfe *resampler)
+{
+ struct resampler *rsmp = (struct resampler *)resampler;
+
+ int32_t delay = (int32_t)((1000000000 * (int64_t)rsmp->frames_in) / rsmp->in_sample_rate);
+ delay += rsmp->speex_delay_ns;
+
+ return delay;
+}
+
+// outputs a number of frames less or equal to *outFrameCount and updates *outFrameCount
+// with the actual number of frames produced.
+static int resampler_resample_from_provider(struct resampler_itfe *resampler,
+ int16_t *out,
+ size_t *outFrameCount)
+{
+ struct resampler *rsmp = (struct resampler *)resampler;
+ size_t framesRq;
+ size_t framesWr;
+ size_t inFrames;
+
+ if (rsmp == NULL || out == NULL || outFrameCount == NULL) {
+ return -EINVAL;
+ }
+ if (rsmp->provider == NULL) {
+ *outFrameCount = 0;
+ return -ENOSYS;
+ }
+
+ framesRq = *outFrameCount;
+ // update and cache the number of frames needed at the input sampling rate to produce
+ // the number of frames requested at the output sampling rate
+ if (framesRq != rsmp->frames_rq) {
+ rsmp->frames_needed = (framesRq * rsmp->in_sample_rate) / rsmp->out_sample_rate + 1;
+ rsmp->frames_rq = framesRq;
+ }
+
+ framesWr = 0;
+ inFrames = 0;
+ while (framesWr < framesRq) {
+ size_t outFrames;
+ if (rsmp->frames_in < rsmp->frames_needed) {
+ struct resampler_buffer buf;
+ // make sure that the number of frames present in rsmp->in_buf (rsmp->frames_in) is at
+ // least the number of frames needed to produce the number of frames requested at
+ // the output sampling rate
+ if (rsmp->in_buf_size < rsmp->frames_needed) {
+ rsmp->in_buf_size = rsmp->frames_needed;
+ rsmp->in_buf = (int16_t *)realloc(rsmp->in_buf,
+ rsmp->in_buf_size * rsmp->channel_count * sizeof(int16_t));
+ }
+ buf.frame_count = rsmp->frames_needed - rsmp->frames_in;
+ rsmp->provider->get_next_buffer(rsmp->provider, &buf);
+ if (buf.raw == NULL) {
+ break;
+ }
+ memcpy(rsmp->in_buf + rsmp->frames_in * rsmp->channel_count,
+ buf.raw,
+ buf.frame_count * rsmp->channel_count * sizeof(int16_t));
+ rsmp->frames_in += buf.frame_count;
+ rsmp->provider->release_buffer(rsmp->provider, &buf);
+ }
+
+ outFrames = framesRq - framesWr;
+ inFrames = rsmp->frames_in;
+ if (rsmp->channel_count == 1) {
+ speex_resampler_process_int(rsmp->speex_resampler,
+ 0,
+ rsmp->in_buf,
+ (void *) &inFrames,
+ out + framesWr,
+ (void *) &outFrames);
+ } else {
+ speex_resampler_process_interleaved_int(rsmp->speex_resampler,
+ rsmp->in_buf,
+ (void *) &inFrames,
+ out + framesWr * rsmp->channel_count,
+ (void *) &outFrames);
+ }
+ framesWr += outFrames;
+ rsmp->frames_in -= inFrames;
+
+ if ((framesWr != framesRq) && (rsmp->frames_in != 0))
+ warn("ReSampler::resample() remaining %zd frames in and %zd out",
+ rsmp->frames_in, (framesRq - framesWr));
+ }
+ if (rsmp->frames_in) {
+ memmove(rsmp->in_buf,
+ rsmp->in_buf + inFrames * rsmp->channel_count,
+ rsmp->frames_in * rsmp->channel_count * sizeof(int16_t));
+ }
+ *outFrameCount = framesWr;
+
+ return 0;
+}
+
+static int resampler_resample_from_input(struct resampler_itfe *resampler,
+ int16_t *in,
+ size_t *inFrameCount,
+ int16_t *out,
+ size_t *outFrameCount)
+{
+ struct resampler *rsmp = (struct resampler *)resampler;
+
+ if (rsmp == NULL || in == NULL || inFrameCount == NULL ||
+ out == NULL || outFrameCount == NULL) {
+ return -EINVAL;
+ }
+ if (rsmp->provider != NULL) {
+ *outFrameCount = 0;
+ return -ENOSYS;
+ }
+
+ if (rsmp->channel_count == 1) {
+ speex_resampler_process_int(rsmp->speex_resampler,
+ 0,
+ in,
+ (void *) inFrameCount,
+ out,
+ (void *) outFrameCount);
+ } else {
+ speex_resampler_process_interleaved_int(rsmp->speex_resampler,
+ in,
+ (void *) inFrameCount,
+ out,
+ (void *) outFrameCount);
+ }
+
+ DBG("resampler_resample_from_input() DONE in %zd out %zd", *inFrameCount, *outFrameCount);
+
+ return 0;
+}
+
+int create_resampler(uint32_t inSampleRate,
+ uint32_t outSampleRate,
+ uint32_t channelCount,
+ uint32_t quality,
+ struct resampler_buffer_provider* provider,
+ struct resampler_itfe **resampler)
+{
+ int error;
+ struct resampler *rsmp;
+ int frames;
+
+ DBG("create_resampler() In SR %d Out SR %d channels %d",
+ inSampleRate, outSampleRate, channelCount);
+
+ if (resampler == NULL) {
+ return -EINVAL;
+ }
+
+ *resampler = NULL;
+
+ if (quality <= RESAMPLER_QUALITY_MIN || quality >= RESAMPLER_QUALITY_MAX) {
+ return -EINVAL;
+ }
+
+ rsmp = (struct resampler *)calloc(1, sizeof(struct resampler));
+
+ rsmp->speex_resampler = speex_resampler_init(channelCount,
+ inSampleRate,
+ outSampleRate,
+ quality,
+ &error);
+ if (rsmp->speex_resampler == NULL) {
+ error("ReSampler: Cannot create speex resampler: %s", speex_resampler_strerror(error));
+ free(rsmp);
+ return -ENODEV;
+ }
+
+ rsmp->itfe.reset = resampler_reset;
+ rsmp->itfe.resample_from_provider = resampler_resample_from_provider;
+ rsmp->itfe.resample_from_input = resampler_resample_from_input;
+ rsmp->itfe.delay_ns = resampler_delay_ns;
+
+ rsmp->provider = provider;
+ rsmp->in_sample_rate = inSampleRate;
+ rsmp->out_sample_rate = outSampleRate;
+ rsmp->channel_count = channelCount;
+ rsmp->in_buf = NULL;
+ rsmp->in_buf_size = 0;
+
+ resampler_reset(&rsmp->itfe);
+
+ frames = speex_resampler_get_input_latency(rsmp->speex_resampler);
+ rsmp->speex_delay_ns = (int32_t)((1000000000 * (int64_t)frames) / rsmp->in_sample_rate);
+ frames = speex_resampler_get_output_latency(rsmp->speex_resampler);
+ rsmp->speex_delay_ns += (int32_t)((1000000000 * (int64_t)frames) / rsmp->out_sample_rate);
+
+ *resampler = &rsmp->itfe;
+ DBG("create_resampler() DONE rsmp %p &rsmp->itfe %p speex %p",
+ rsmp, &rsmp->itfe, rsmp->speex_resampler);
+ return 0;
+}
+
+void release_resampler(struct resampler_itfe *resampler)
+{
+ struct resampler *rsmp = (struct resampler *)resampler;
+
+ if (rsmp == NULL) {
+ return;
+ }
+
+ free(rsmp->in_buf);
+
+ if (rsmp->speex_resampler != NULL) {
+ speex_resampler_destroy(rsmp->speex_resampler);
+ }
+ free(rsmp);
+}
diff --git a/repo/android/audio_utils/resampler.h b/repo/android/audio_utils/resampler.h
new file mode 100644
index 0000000..0c7046f
--- /dev/null
+++ b/repo/android/audio_utils/resampler.h
@@ -0,0 +1,109 @@
+/*
+** Copyright 2008, The Android Open-Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#ifndef ANDROID_RESAMPLER_H
+#define ANDROID_RESAMPLER_H
+
+#include <stdint.h>
+#include <sys/time.h>
+
+__BEGIN_DECLS
+
+
+#define RESAMPLER_QUALITY_MAX 10
+#define RESAMPLER_QUALITY_MIN 0
+#define RESAMPLER_QUALITY_DEFAULT 4
+#define RESAMPLER_QUALITY_VOIP 3
+#define RESAMPLER_QUALITY_DESKTOP 5
+
+struct resampler_buffer {
+ union {
+ void* raw;
+ short* i16;
+ int8_t* i8;
+ };
+ size_t frame_count;
+};
+
+/* call back interface used by the resampler to get new data */
+struct resampler_buffer_provider
+{
+ /**
+ * get a new buffer of data:
+ * as input: buffer->frame_count is the number of frames requested
+ * as output: buffer->frame_count is the number of frames returned
+ * buffer->raw points to data returned
+ */
+ int (*get_next_buffer)(struct resampler_buffer_provider *provider,
+ struct resampler_buffer *buffer);
+ /**
+ * release a consumed buffer of data:
+ * as input: buffer->frame_count is the number of frames released
+ * buffer->raw points to data released
+ */
+ void (*release_buffer)(struct resampler_buffer_provider *provider,
+ struct resampler_buffer *buffer);
+};
+
+/* resampler interface */
+struct resampler_itfe {
+ /**
+ * reset resampler state
+ */
+ void (*reset)(struct resampler_itfe *resampler);
+ /**
+ * resample input from buffer provider and output at most *outFrameCount to out buffer.
+ * *outFrameCount is updated with the actual number of frames produced.
+ */
+ int (*resample_from_provider)(struct resampler_itfe *resampler,
+ int16_t *out,
+ size_t *outFrameCount);
+ /**
+ * resample at most *inFrameCount frames from in buffer and output at most
+ * *outFrameCount to out buffer. *inFrameCount and *outFrameCount are updated respectively
+ * with the number of frames remaining in input and written to output.
+ */
+ int (*resample_from_input)(struct resampler_itfe *resampler,
+ int16_t *in,
+ size_t *inFrameCount,
+ int16_t *out,
+ size_t *outFrameCount);
+ /**
+ * return the latency introduced by the resampler in ns.
+ */
+ int32_t (*delay_ns)(struct resampler_itfe *resampler);
+};
+
+/**
+ * create a resampler according to input parameters passed.
+ * If resampler_buffer_provider is not NULL only resample_from_provider() can be called.
+ * If resampler_buffer_provider is NULL only resample_from_input() can be called.
+ */
+int create_resampler(uint32_t inSampleRate,
+ uint32_t outSampleRate,
+ uint32_t channelCount,
+ uint32_t quality,
+ struct resampler_buffer_provider *provider,
+ struct resampler_itfe **);
+
+/**
+ * release resampler resources.
+ */
+void release_resampler(struct resampler_itfe *);
+
+__END_DECLS
+
+#endif // ANDROID_RESAMPLER_H
diff --git a/repo/android/avctp.c b/repo/android/avctp.c
new file mode 100644
index 0000000..6aa64cf
--- /dev/null
+++ b/repo/android/avctp.c
@@ -0,0 +1,1653 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2006-2010 Nokia Corporation
+ * Copyright (C) 2004-2010 Marcel Holtmann <marcel@holtmann.org>
+ * Copyright (C) 2011 Texas Instruments, Inc.
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdlib.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <errno.h>
+#include <unistd.h>
+#include <assert.h>
+#include <signal.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <netinet/in.h>
+
+#include <glib.h>
+
+#include "lib/sdp.h"
+
+#include "src/log.h"
+#include "src/uinput.h"
+
+#include "avctp.h"
+
+/*
+ * AV/C Panel 1.23, page 76:
+ * command with the pressed value is valid for two seconds
+ */
+#define AVC_PRESS_TIMEOUT 2
+
+#define QUIRK_NO_RELEASE 1 << 0
+
+/* Message types */
+#define AVCTP_COMMAND 0
+#define AVCTP_RESPONSE 1
+
+/* Packet types */
+#define AVCTP_PACKET_SINGLE 0
+#define AVCTP_PACKET_START 1
+#define AVCTP_PACKET_CONTINUE 2
+#define AVCTP_PACKET_END 3
+
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+
+struct avctp_header {
+ uint8_t ipid:1;
+ uint8_t cr:1;
+ uint8_t packet_type:2;
+ uint8_t transaction:4;
+ uint16_t pid;
+} __attribute__ ((packed));
+
+struct avc_header {
+ uint8_t code:4;
+ uint8_t _hdr0:4;
+ uint8_t subunit_id:3;
+ uint8_t subunit_type:5;
+ uint8_t opcode;
+} __attribute__ ((packed));
+
+#elif __BYTE_ORDER == __BIG_ENDIAN
+
+struct avctp_header {
+ uint8_t transaction:4;
+ uint8_t packet_type:2;
+ uint8_t cr:1;
+ uint8_t ipid:1;
+ uint16_t pid;
+} __attribute__ ((packed));
+
+struct avc_header {
+ uint8_t _hdr0:4;
+ uint8_t code:4;
+ uint8_t subunit_type:5;
+ uint8_t subunit_id:3;
+ uint8_t opcode;
+} __attribute__ ((packed));
+
+#else
+#error "Unknown byte order"
+#endif
+
+struct avctp_control_req {
+ struct avctp_pending_req *p;
+ uint8_t code;
+ uint8_t subunit;
+ uint8_t op;
+ struct iovec *iov;
+ int iov_cnt;
+ avctp_rsp_cb func;
+ void *user_data;
+};
+
+struct avctp_browsing_req {
+ struct avctp_pending_req *p;
+ struct iovec *iov;
+ int iov_cnt;
+ avctp_browsing_rsp_cb func;
+ void *user_data;
+};
+
+typedef int (*avctp_process_cb) (void *data);
+
+struct avctp_pending_req {
+ struct avctp_channel *chan;
+ uint8_t transaction;
+ guint timeout;
+ int err;
+ avctp_process_cb process;
+ void *data;
+ avctp_destroy_cb_t destroy;
+};
+
+struct avctp_channel {
+ struct avctp *session;
+ GIOChannel *io;
+ uint8_t transaction;
+ guint watch;
+ uint16_t imtu;
+ uint16_t omtu;
+ uint8_t *buffer;
+ GSList *handlers;
+ struct avctp_pending_req *p;
+ GQueue *queue;
+ GSList *processed;
+ guint process_id;
+ avctp_destroy_cb_t destroy;
+};
+
+struct key_pressed {
+ uint8_t op;
+ uint8_t *params;
+ size_t params_len;
+ guint timer;
+};
+
+struct avctp {
+ unsigned int ref;
+ int uinput;
+
+ unsigned int passthrough_id;
+ unsigned int unit_id;
+ unsigned int subunit_id;
+
+ struct avctp_channel *control;
+ struct avctp_channel *browsing;
+
+ struct avctp_passthrough_handler *handler;
+
+ uint8_t key_quirks[256];
+ struct key_pressed key;
+ uint16_t version;
+
+ avctp_destroy_cb_t destroy;
+ void *data;
+};
+
+struct avctp_passthrough_handler {
+ avctp_passthrough_cb cb;
+ void *user_data;
+ unsigned int id;
+};
+
+struct avctp_pdu_handler {
+ uint8_t opcode;
+ avctp_control_pdu_cb cb;
+ void *user_data;
+ unsigned int id;
+};
+
+struct avctp_browsing_pdu_handler {
+ avctp_browsing_pdu_cb cb;
+ void *user_data;
+ unsigned int id;
+ avctp_destroy_cb_t destroy;
+};
+
+static struct {
+ const char *name;
+ uint8_t avc;
+ uint16_t uinput;
+} key_map[] = {
+ { "SELECT", AVC_SELECT, KEY_SELECT },
+ { "UP", AVC_UP, KEY_UP },
+ { "DOWN", AVC_DOWN, KEY_DOWN },
+ { "LEFT", AVC_LEFT, KEY_LEFT },
+ { "RIGHT", AVC_RIGHT, KEY_RIGHT },
+ { "ROOT MENU", AVC_ROOT_MENU, KEY_MENU },
+ { "CONTENTS MENU", AVC_CONTENTS_MENU, KEY_PROGRAM },
+ { "FAVORITE MENU", AVC_FAVORITE_MENU, KEY_FAVORITES },
+ { "EXIT", AVC_EXIT, KEY_EXIT },
+ { "ON DEMAND MENU", AVC_ON_DEMAND_MENU, KEY_MENU },
+ { "APPS MENU", AVC_APPS_MENU, KEY_MENU },
+ { "0", AVC_0, KEY_0 },
+ { "1", AVC_1, KEY_1 },
+ { "2", AVC_2, KEY_2 },
+ { "3", AVC_3, KEY_3 },
+ { "4", AVC_4, KEY_4 },
+ { "5", AVC_5, KEY_5 },
+ { "6", AVC_6, KEY_6 },
+ { "7", AVC_7, KEY_7 },
+ { "8", AVC_8, KEY_8 },
+ { "9", AVC_9, KEY_9 },
+ { "DOT", AVC_DOT, KEY_DOT },
+ { "ENTER", AVC_ENTER, KEY_ENTER },
+ { "CHANNEL UP", AVC_CHANNEL_UP, KEY_CHANNELUP },
+ { "CHANNEL DOWN", AVC_CHANNEL_DOWN, KEY_CHANNELDOWN },
+ { "CHANNEL PREVIOUS", AVC_CHANNEL_PREVIOUS, KEY_LAST },
+ { "INPUT SELECT", AVC_INPUT_SELECT, KEY_CONFIG },
+ { "INFO", AVC_INFO, KEY_INFO },
+ { "HELP", AVC_HELP, KEY_HELP },
+ { "POWER", AVC_POWER, KEY_POWER2 },
+ { "VOLUME UP", AVC_VOLUME_UP, KEY_VOLUMEUP },
+ { "VOLUME DOWN", AVC_VOLUME_DOWN, KEY_VOLUMEDOWN },
+ { "MUTE", AVC_MUTE, KEY_MUTE },
+ { "PLAY", AVC_PLAY, KEY_PLAYCD },
+ { "STOP", AVC_STOP, KEY_STOPCD },
+ { "PAUSE", AVC_PAUSE, KEY_PAUSECD },
+ { "FORWARD", AVC_FORWARD, KEY_NEXTSONG },
+ { "BACKWARD", AVC_BACKWARD, KEY_PREVIOUSSONG },
+ { "RECORD", AVC_RECORD, KEY_RECORD },
+ { "REWIND", AVC_REWIND, KEY_REWIND },
+ { "FAST FORWARD", AVC_FAST_FORWARD, KEY_FASTFORWARD },
+ { "LIST", AVC_LIST, KEY_LIST },
+ { "F1", AVC_F1, KEY_F1 },
+ { "F2", AVC_F2, KEY_F2 },
+ { "F3", AVC_F3, KEY_F3 },
+ { "F4", AVC_F4, KEY_F4 },
+ { "F5", AVC_F5, KEY_F5 },
+ { "F6", AVC_F6, KEY_F6 },
+ { "F7", AVC_F7, KEY_F7 },
+ { "F8", AVC_F8, KEY_F8 },
+ { "F9", AVC_F9, KEY_F9 },
+ { "RED", AVC_RED, KEY_RED },
+ { "GREEN", AVC_GREEN, KEY_GREEN },
+ { "BLUE", AVC_BLUE, KEY_BLUE },
+ { "YELLOW", AVC_YELLOW, KEY_YELLOW },
+ { NULL }
+};
+
+static gboolean process_queue(gpointer user_data);
+static gboolean avctp_passthrough_rsp(struct avctp *session, uint8_t code,
+ uint8_t subunit, uint8_t *operands,
+ size_t operand_count, void *user_data);
+
+static int send_event(int fd, uint16_t type, uint16_t code, int32_t value)
+{
+ struct uinput_event event;
+ int err;
+
+ memset(&event, 0, sizeof(event));
+ event.type = type;
+ event.code = code;
+ event.value = value;
+
+ do {
+ err = write(fd, &event, sizeof(event));
+ } while (err < 0 && errno == EINTR);
+
+ if (err < 0) {
+ err = -errno;
+ error("send_event: %s (%d)", strerror(-err), -err);
+ }
+
+ return err;
+}
+
+static void send_key(int fd, uint16_t key, int pressed)
+{
+ send_event(fd, EV_KEY, key, pressed);
+ send_event(fd, EV_SYN, SYN_REPORT, 0);
+}
+
+static gboolean auto_release(gpointer user_data)
+{
+ struct avctp *session = user_data;
+
+ session->key.timer = 0;
+
+ DBG("AV/C: key press timeout");
+
+ send_key(session->uinput, session->key.op, 0);
+
+ return FALSE;
+}
+
+static ssize_t handle_panel_passthrough(struct avctp *session,
+ uint8_t transaction, uint8_t *code,
+ uint8_t *subunit, uint8_t *operands,
+ size_t operand_count, void *user_data)
+{
+ struct avctp_passthrough_handler *handler = session->handler;
+ const char *status;
+ int pressed, i;
+
+ if (*code != AVC_CTYPE_CONTROL || *subunit != AVC_SUBUNIT_PANEL) {
+ *code = AVC_CTYPE_REJECTED;
+ return operand_count;
+ }
+
+ if (operand_count == 0)
+ goto done;
+
+ if (operands[0] & 0x80) {
+ status = "released";
+ pressed = 0;
+ } else {
+ status = "pressed";
+ pressed = 1;
+ }
+
+ if (session->key.timer == 0 && handler != NULL) {
+ if (handler->cb(session, operands[0] & 0x7F,
+ pressed, handler->user_data))
+ goto done;
+ }
+
+ if (session->uinput < 0) {
+ DBG("AV/C: uinput not initialized");
+ *code = AVC_CTYPE_NOT_IMPLEMENTED;
+ return 0;
+ }
+
+ for (i = 0; key_map[i].name != NULL; i++) {
+ uint8_t key_quirks;
+
+ if ((operands[0] & 0x7F) != key_map[i].avc)
+ continue;
+
+ DBG("AV/C: %s %s", key_map[i].name, status);
+
+ key_quirks = session->key_quirks[key_map[i].avc];
+
+ if (key_quirks & QUIRK_NO_RELEASE) {
+ if (!pressed) {
+ DBG("AV/C: Ignoring release");
+ break;
+ }
+
+ DBG("AV/C: treating key press as press + release");
+ send_key(session->uinput, key_map[i].uinput, 1);
+ send_key(session->uinput, key_map[i].uinput, 0);
+ break;
+ }
+
+ if (pressed) {
+ if (session->key.timer > 0) {
+ g_source_remove(session->key.timer);
+ send_key(session->uinput, session->key.op, 0);
+ }
+
+ session->key.op = key_map[i].uinput;
+ session->key.timer = g_timeout_add_seconds(
+ AVC_PRESS_TIMEOUT,
+ auto_release,
+ session);
+ } else if (session->key.timer > 0) {
+ g_source_remove(session->key.timer);
+ session->key.timer = 0;
+ }
+
+ send_key(session->uinput, key_map[i].uinput, pressed);
+ break;
+ }
+
+ if (key_map[i].name == NULL) {
+ DBG("AV/C: unknown button 0x%02X %s",
+ operands[0] & 0x7F, status);
+ *code = AVC_CTYPE_NOT_IMPLEMENTED;
+ return operand_count;
+ }
+
+done:
+ *code = AVC_CTYPE_ACCEPTED;
+ return operand_count;
+}
+
+static ssize_t handle_unit_info(struct avctp *session,
+ uint8_t transaction, uint8_t *code,
+ uint8_t *subunit, uint8_t *operands,
+ size_t operand_count, void *user_data)
+{
+ if (*code != AVC_CTYPE_STATUS) {
+ *code = AVC_CTYPE_REJECTED;
+ return 0;
+ }
+
+ *code = AVC_CTYPE_STABLE;
+
+ /*
+ * The first operand should be 0x07 for the UNITINFO response.
+ * Neither AVRCP (section 22.1, page 117) nor AVC Digital
+ * Interface Command Set (section 9.2.1, page 45) specs
+ * explain this value but both use it
+ */
+ if (operand_count >= 1)
+ operands[0] = 0x07;
+ if (operand_count >= 2)
+ operands[1] = AVC_SUBUNIT_PANEL << 3;
+
+ DBG("reply to AVC_OP_UNITINFO");
+
+ return operand_count;
+}
+
+static ssize_t handle_subunit_info(struct avctp *session,
+ uint8_t transaction, uint8_t *code,
+ uint8_t *subunit, uint8_t *operands,
+ size_t operand_count, void *user_data)
+{
+ if (*code != AVC_CTYPE_STATUS) {
+ *code = AVC_CTYPE_REJECTED;
+ return 0;
+ }
+
+ *code = AVC_CTYPE_STABLE;
+
+ /*
+ * The first operand should be 0x07 for the UNITINFO response.
+ * Neither AVRCP (section 22.1, page 117) nor AVC Digital
+ * Interface Command Set (section 9.2.1, page 45) specs
+ * explain this value but both use it
+ */
+ if (operand_count >= 2)
+ operands[1] = AVC_SUBUNIT_PANEL << 3;
+
+ DBG("reply to AVC_OP_SUBUNITINFO");
+
+ return operand_count;
+}
+
+static struct avctp_pdu_handler *find_handler(GSList *list, uint8_t opcode)
+{
+ for (; list; list = list->next) {
+ struct avctp_pdu_handler *handler = list->data;
+
+ if (handler->opcode == opcode)
+ return handler;
+ }
+
+ return NULL;
+}
+
+static void pending_destroy(gpointer data, gpointer user_data)
+{
+ struct avctp_pending_req *req = data;
+
+ if (req->destroy)
+ req->destroy(req->data);
+
+ if (req->timeout > 0)
+ g_source_remove(req->timeout);
+
+ g_free(req);
+}
+
+static void avctp_channel_destroy(struct avctp_channel *chan)
+{
+ g_io_channel_shutdown(chan->io, TRUE, NULL);
+ g_io_channel_unref(chan->io);
+
+ if (chan->watch)
+ g_source_remove(chan->watch);
+
+ if (chan->p)
+ pending_destroy(chan->p, NULL);
+
+ if (chan->process_id > 0)
+ g_source_remove(chan->process_id);
+
+ if (chan->destroy)
+ chan->destroy(chan);
+
+ g_free(chan->buffer);
+ g_queue_foreach(chan->queue, pending_destroy, NULL);
+ g_queue_free(chan->queue);
+ g_slist_foreach(chan->processed, pending_destroy, NULL);
+ g_slist_free(chan->processed);
+ g_slist_free_full(chan->handlers, g_free);
+ g_free(chan);
+}
+
+static int avctp_send(struct avctp_channel *control, uint8_t transaction,
+ uint8_t cr, uint8_t code,
+ uint8_t subunit, uint8_t opcode,
+ const struct iovec *iov, int iov_cnt)
+{
+ struct avctp_header avctp;
+ struct avc_header avc;
+ struct msghdr msg;
+ int sk, err = 0;
+ struct iovec pdu[iov_cnt + 2];
+ int i;
+ size_t len = sizeof(avctp) + sizeof(avc);
+
+ DBG("");
+
+ pdu[0].iov_base = &avctp;
+ pdu[0].iov_len = sizeof(avctp);
+ pdu[1].iov_base = &avc;
+ pdu[1].iov_len = sizeof(avc);
+
+ for (i = 0; i < iov_cnt; i++) {
+ pdu[i + 2].iov_base = iov[i].iov_base;
+ pdu[i + 2].iov_len = iov[i].iov_len;
+ len += iov[i].iov_len;
+ }
+
+ if (control->omtu < len)
+ return -EOVERFLOW;
+
+ sk = g_io_channel_unix_get_fd(control->io);
+
+ memset(&avctp, 0, sizeof(avctp));
+
+ avctp.transaction = transaction;
+ avctp.packet_type = AVCTP_PACKET_SINGLE;
+ avctp.cr = cr;
+ avctp.pid = htons(AV_REMOTE_SVCLASS_ID);
+
+ memset(&avc, 0, sizeof(avc));
+
+ avc.code = code;
+ avc.subunit_type = subunit;
+ avc.opcode = opcode;
+
+ memset(&msg, 0, sizeof(msg));
+ msg.msg_iov = pdu;
+ msg.msg_iovlen = iov_cnt + 2;
+
+ if (sendmsg(sk, &msg, 0) < 0)
+ err = -errno;
+
+ return err;
+}
+
+static int avctp_browsing_send(struct avctp_channel *browsing,
+ uint8_t transaction, uint8_t cr,
+ const struct iovec *iov, int iov_cnt)
+{
+ struct avctp_header avctp;
+ struct msghdr msg;
+ struct iovec pdu[iov_cnt + 1];
+ int sk, err = 0;
+ int i;
+ size_t len = sizeof(avctp);
+
+ for (i = 0; i < iov_cnt; i++) {
+ pdu[i + 1].iov_base = iov[i].iov_base;
+ pdu[i + 1].iov_len = iov[i].iov_len;
+ len += iov[i].iov_len;
+ }
+
+ pdu[0].iov_base = &avctp;
+ pdu[0].iov_len = sizeof(avctp);
+
+ if (browsing->omtu < len)
+ return -EOVERFLOW;
+
+ sk = g_io_channel_unix_get_fd(browsing->io);
+
+ memset(&avctp, 0, sizeof(avctp));
+
+ avctp.transaction = transaction;
+ avctp.packet_type = AVCTP_PACKET_SINGLE;
+ avctp.cr = cr;
+ avctp.pid = htons(AV_REMOTE_SVCLASS_ID);
+
+ memset(&msg, 0, sizeof(msg));
+ msg.msg_iov = pdu;
+ msg.msg_iovlen = iov_cnt + 1;
+
+ if (sendmsg(sk, &msg, 0) < 0)
+ err = -errno;
+
+ return err;
+}
+
+static void control_req_destroy(void *data)
+{
+ struct avctp_control_req *req = data;
+ struct avctp_pending_req *p = req->p;
+ struct avctp *session = p->chan->session;
+ int i;
+
+ if (p->err == 0 || req->func == NULL)
+ goto done;
+
+ req->func(session, AVC_CTYPE_REJECTED, req->subunit, NULL, 0,
+ req->user_data);
+
+done:
+ for (i = 0; i < req->iov_cnt; i++)
+ g_free(req->iov[i].iov_base);
+
+ g_free(req->iov);
+ g_free(req);
+}
+
+static void browsing_req_destroy(void *data)
+{
+ struct avctp_browsing_req *req = data;
+ struct avctp_pending_req *p = req->p;
+ struct avctp *session = p->chan->session;
+ int i;
+
+ if (p->err == 0 || req->func == NULL)
+ goto done;
+
+ req->func(session, NULL, 0, req->user_data);
+
+done:
+ for (i = 0; i < req->iov_cnt; i++)
+ g_free(req->iov[i].iov_base);
+
+ g_free(req->iov);
+ g_free(req);
+}
+
+static gboolean req_timeout(gpointer user_data)
+{
+ struct avctp_channel *chan = user_data;
+ struct avctp_pending_req *p = chan->p;
+
+ DBG("transaction %u", p->transaction);
+
+ p->timeout = 0;
+ p->err = -ETIMEDOUT;
+
+ pending_destroy(p, NULL);
+ chan->p = NULL;
+
+ if (chan->process_id == 0)
+ chan->process_id = g_idle_add(process_queue, chan);
+
+ return FALSE;
+}
+
+static int process_control(void *data)
+{
+ struct avctp_control_req *req = data;
+ struct avctp_pending_req *p = req->p;
+
+ return avctp_send(p->chan, p->transaction, AVCTP_COMMAND, req->code,
+ req->subunit, req->op, req->iov, req->iov_cnt);
+}
+
+static int process_browsing(void *data)
+{
+ struct avctp_browsing_req *req = data;
+ struct avctp_pending_req *p = req->p;
+
+ return avctp_browsing_send(p->chan, p->transaction, AVCTP_COMMAND,
+ req->iov, req->iov_cnt);
+}
+
+static gboolean process_queue(void *user_data)
+{
+ struct avctp_channel *chan = user_data;
+ struct avctp_pending_req *p = chan->p;
+
+ chan->process_id = 0;
+
+ if (p != NULL)
+ return FALSE;
+
+ while ((p = g_queue_pop_head(chan->queue))) {
+
+ if (p->process(p->data) == 0)
+ break;
+
+ pending_destroy(p, NULL);
+ }
+
+ if (p == NULL)
+ return FALSE;
+
+ chan->p = p;
+ p->timeout = g_timeout_add_seconds(2, req_timeout, chan);
+
+ return FALSE;
+
+}
+
+static struct avctp *avctp_ref(struct avctp *session)
+{
+ __sync_fetch_and_add(&session->ref, 1);
+
+ DBG("%p: ref=%d", session, session->ref);
+
+ return session;
+}
+
+static void avctp_unref(struct avctp *session)
+{
+ DBG("%p: ref=%d", session, session->ref);
+
+ if (__sync_sub_and_fetch(&session->ref, 1))
+ return;
+
+ if (session->browsing)
+ avctp_channel_destroy(session->browsing);
+
+ if (session->control)
+ avctp_channel_destroy(session->control);
+
+ if (session->destroy)
+ session->destroy(session->data);
+
+ g_free(session->handler);
+
+ if (session->key.timer > 0)
+ g_source_remove(session->key.timer);
+
+ if (session->uinput >= 0) {
+ DBG("AVCTP: closing uinput");
+
+ ioctl(session->uinput, UI_DEV_DESTROY);
+ close(session->uinput);
+ session->uinput = -1;
+ }
+
+ g_free(session);
+}
+
+static void control_response(struct avctp_channel *control,
+ struct avctp_header *avctp,
+ struct avc_header *avc,
+ uint8_t *operands,
+ size_t operand_count)
+{
+ struct avctp_pending_req *p = control->p;
+ struct avctp_control_req *req;
+ GSList *l;
+
+ if (p && p->transaction == avctp->transaction) {
+ control->processed = g_slist_prepend(control->processed, p);
+
+ if (p->timeout > 0) {
+ g_source_remove(p->timeout);
+ p->timeout = 0;
+ }
+
+ control->p = NULL;
+
+ if (control->process_id == 0)
+ control->process_id = g_idle_add(process_queue,
+ control);
+ }
+
+ avctp_ref(control->session);
+
+ for (l = control->processed; l; l = l->next) {
+ p = l->data;
+ req = p->data;
+
+ if (p->transaction != avctp->transaction)
+ continue;
+
+ if (req->func && req->func(control->session, avc->code,
+ avc->subunit_type,
+ operands, operand_count,
+ req->user_data))
+ break;
+
+ control->processed = g_slist_remove(control->processed, p);
+ pending_destroy(p, NULL);
+
+ break;
+ }
+
+ avctp_unref(control->session);
+}
+
+static void browsing_response(struct avctp_channel *browsing,
+ struct avctp_header *avctp,
+ uint8_t *operands,
+ size_t operand_count)
+{
+ struct avctp_pending_req *p = browsing->p;
+ struct avctp_browsing_req *req;
+ GSList *l;
+
+ if (p && p->transaction == avctp->transaction) {
+ browsing->processed = g_slist_prepend(browsing->processed, p);
+
+ if (p->timeout > 0) {
+ g_source_remove(p->timeout);
+ p->timeout = 0;
+ }
+
+ browsing->p = NULL;
+
+ if (browsing->process_id == 0)
+ browsing->process_id = g_idle_add(process_queue,
+ browsing);
+ }
+
+ avctp_ref(browsing->session);
+
+ for (l = browsing->processed; l; l = l->next) {
+ p = l->data;
+ req = p->data;
+
+ if (p->transaction != avctp->transaction)
+ continue;
+
+ if (req->func && req->func(browsing->session, operands,
+ operand_count, req->user_data))
+ break;
+
+ browsing->processed = g_slist_remove(browsing->processed, p);
+ pending_destroy(p, NULL);
+
+ break;
+ }
+
+ avctp_unref(browsing->session);
+}
+
+static gboolean session_browsing_cb(GIOChannel *chan, GIOCondition cond,
+ gpointer data)
+{
+ struct avctp *session = data;
+ struct avctp_channel *browsing = session->browsing;
+ uint8_t *buf = browsing->buffer;
+ uint8_t *operands;
+ struct avctp_header *avctp;
+ int sock, ret, packet_size, operand_count;
+ struct avctp_browsing_pdu_handler *handler;
+
+ if (cond & (G_IO_ERR | G_IO_HUP | G_IO_NVAL))
+ goto failed;
+
+ sock = g_io_channel_unix_get_fd(chan);
+
+ ret = read(sock, buf, browsing->imtu);
+ if (ret <= 0)
+ goto failed;
+
+ if (ret < AVCTP_HEADER_LENGTH) {
+ error("Too small AVCTP packet");
+ goto failed;
+ }
+
+ avctp = (struct avctp_header *) buf;
+
+ if (avctp->packet_type != AVCTP_PACKET_SINGLE) {
+ error("Invalid packet type");
+ goto failed;
+ }
+
+ operands = buf + AVCTP_HEADER_LENGTH;
+ ret -= AVCTP_HEADER_LENGTH;
+ operand_count = ret;
+
+ if (avctp->cr == AVCTP_RESPONSE) {
+ browsing_response(browsing, avctp, operands, operand_count);
+ return TRUE;
+ }
+
+ packet_size = AVCTP_HEADER_LENGTH;
+ avctp->cr = AVCTP_RESPONSE;
+
+ handler = g_slist_nth_data(browsing->handlers, 0);
+ if (handler == NULL) {
+ DBG("handler not found");
+ /* FIXME: Add general reject */
+ /* packet_size += avrcp_browsing_general_reject(operands); */
+ goto send;
+ }
+
+ ret = handler->cb(session, avctp->transaction, operands, operand_count,
+ handler->user_data);
+ if (ret < 0) {
+ if (ret == -EAGAIN)
+ return TRUE;
+ goto failed;
+ }
+
+ packet_size += ret;
+
+send:
+ if (packet_size != 0) {
+ ret = write(sock, buf, packet_size);
+ if (ret != packet_size)
+ goto failed;
+ }
+
+ return TRUE;
+
+failed:
+ DBG("AVCTP Browsing: disconnected");
+
+ if (session->browsing) {
+ avctp_channel_destroy(session->browsing);
+ session->browsing = NULL;
+ }
+
+ return FALSE;
+}
+
+static gboolean session_cb(GIOChannel *chan, GIOCondition cond, gpointer data)
+{
+ struct avctp *session = data;
+ struct avctp_channel *control = session->control;
+ uint8_t *buf = control->buffer;
+ uint8_t *operands, code, subunit;
+ struct avctp_header *avctp;
+ struct avc_header *avc;
+ int packet_size, operand_count, sock;
+ struct avctp_pdu_handler *handler;
+ ssize_t ret;
+
+ if (cond & (G_IO_ERR | G_IO_HUP | G_IO_NVAL))
+ goto failed;
+
+ sock = g_io_channel_unix_get_fd(chan);
+
+ ret = read(sock, buf, control->imtu);
+ if (ret <= 0)
+ goto failed;
+
+ if (ret < AVCTP_HEADER_LENGTH) {
+ error("Too small AVCTP packet");
+ goto failed;
+ }
+
+ avctp = (struct avctp_header *) buf;
+
+ ret -= AVCTP_HEADER_LENGTH;
+ if (ret < AVC_HEADER_LENGTH) {
+ error("Too small AVC packet");
+ goto failed;
+ }
+
+ avc = (struct avc_header *) (buf + AVCTP_HEADER_LENGTH);
+
+ ret -= AVC_HEADER_LENGTH;
+
+ operands = (uint8_t *) avc + AVC_HEADER_LENGTH;
+ operand_count = ret;
+
+ if (avctp->cr == AVCTP_RESPONSE) {
+ control_response(control, avctp, avc, operands, operand_count);
+ return TRUE;
+ }
+
+ packet_size = AVCTP_HEADER_LENGTH + AVC_HEADER_LENGTH;
+ avctp->cr = AVCTP_RESPONSE;
+
+ if (avctp->packet_type != AVCTP_PACKET_SINGLE) {
+ avc->code = AVC_CTYPE_NOT_IMPLEMENTED;
+ goto done;
+ }
+
+ if (avctp->pid != htons(AV_REMOTE_SVCLASS_ID)) {
+ avctp->ipid = 1;
+ packet_size = AVCTP_HEADER_LENGTH;
+ goto done;
+ }
+
+ handler = find_handler(control->handlers, avc->opcode);
+ if (!handler) {
+ DBG("handler not found for 0x%02x", avc->opcode);
+ avc->code = AVC_CTYPE_REJECTED;
+ goto done;
+ }
+
+ code = avc->code;
+ subunit = avc->subunit_type;
+
+ ret = handler->cb(session, avctp->transaction, &code,
+ &subunit, operands, operand_count,
+ handler->user_data);
+ if (ret < 0) {
+ if (ret == -EAGAIN)
+ return TRUE;
+ goto failed;
+ }
+
+ packet_size += ret;
+ avc->code = code;
+ avc->subunit_type = subunit;
+
+done:
+ ret = write(sock, buf, packet_size);
+ if (ret != packet_size)
+ goto failed;
+
+ return TRUE;
+
+failed:
+ DBG("AVCTP session %p got disconnected", session);
+ avctp_shutdown(session);
+ return FALSE;
+}
+
+static int uinput_create(const char *name)
+{
+ struct uinput_dev dev;
+ int fd, err, i;
+
+ fd = open("/dev/uinput", O_RDWR);
+ if (fd < 0) {
+ fd = open("/dev/input/uinput", O_RDWR);
+ if (fd < 0) {
+ fd = open("/dev/misc/uinput", O_RDWR);
+ if (fd < 0) {
+ err = -errno;
+ error("Can't open input device: %s (%d)",
+ strerror(-err), -err);
+ return err;
+ }
+ }
+ }
+
+ memset(&dev, 0, sizeof(dev));
+ if (name)
+ strncpy(dev.name, name, UINPUT_MAX_NAME_SIZE - 1);
+
+ dev.id.bustype = BUS_BLUETOOTH;
+ dev.id.vendor = 0x0000;
+ dev.id.product = 0x0000;
+ dev.id.version = 0x0000;
+
+ if (write(fd, &dev, sizeof(dev)) < 0) {
+ err = -errno;
+ error("Can't write device information: %s (%d)",
+ strerror(-err), -err);
+ close(fd);
+ return err;
+ }
+
+ ioctl(fd, UI_SET_EVBIT, EV_KEY);
+ ioctl(fd, UI_SET_EVBIT, EV_REL);
+ ioctl(fd, UI_SET_EVBIT, EV_REP);
+ ioctl(fd, UI_SET_EVBIT, EV_SYN);
+
+ for (i = 0; key_map[i].name != NULL; i++)
+ ioctl(fd, UI_SET_KEYBIT, key_map[i].uinput);
+
+ if (ioctl(fd, UI_DEV_CREATE, NULL) < 0) {
+ err = -errno;
+ error("Can't create uinput device: %s (%d)",
+ strerror(-err), -err);
+ close(fd);
+ return err;
+ }
+
+ return fd;
+}
+
+int avctp_init_uinput(struct avctp *session, const char *name,
+ const char *address)
+{
+ if (g_str_equal(name, "Nokia CK-20W")) {
+ session->key_quirks[AVC_FORWARD] |= QUIRK_NO_RELEASE;
+ session->key_quirks[AVC_BACKWARD] |= QUIRK_NO_RELEASE;
+ session->key_quirks[AVC_PLAY] |= QUIRK_NO_RELEASE;
+ session->key_quirks[AVC_PAUSE] |= QUIRK_NO_RELEASE;
+ }
+
+ session->uinput = uinput_create(address);
+ if (session->uinput < 0) {
+ error("AVCTP: failed to init uinput for %s", address);
+ return session->uinput;
+ }
+
+ return 0;
+}
+
+static struct avctp_channel *avctp_channel_create(struct avctp *session, int fd,
+ size_t imtu, size_t omtu,
+ avctp_destroy_cb_t destroy)
+{
+ struct avctp_channel *chan;
+
+ chan = g_new0(struct avctp_channel, 1);
+ chan->session = session;
+ chan->io = g_io_channel_unix_new(fd);
+ chan->queue = g_queue_new();
+ chan->imtu = imtu;
+ chan->omtu = omtu;
+ chan->buffer = g_malloc0(MAX(imtu, omtu));
+ chan->destroy = destroy;
+
+ return chan;
+}
+
+static void handler_free(void *data)
+{
+ struct avctp_browsing_pdu_handler *handler = data;
+
+ if (handler->destroy)
+ handler->destroy(handler->user_data);
+
+ g_free(data);
+}
+
+static void avctp_destroy_browsing(void *data)
+{
+ struct avctp_channel *chan = data;
+
+ g_slist_free_full(chan->handlers, handler_free);
+
+ chan->handlers = NULL;
+}
+
+static struct avctp_pending_req *pending_create(struct avctp_channel *chan,
+ avctp_process_cb process,
+ void *data,
+ avctp_destroy_cb_t destroy)
+{
+ struct avctp_pending_req *p;
+ GSList *l, *tmp;
+
+ if (!chan->processed)
+ goto done;
+
+ tmp = g_slist_copy(chan->processed);
+
+ /* Find first unused transaction id */
+ for (l = tmp; l; l = g_slist_next(l)) {
+ struct avctp_pending_req *req = l->data;
+
+ if (req->transaction == chan->transaction) {
+ chan->transaction++;
+ chan->transaction %= 16;
+ tmp = g_slist_delete_link(tmp, l);
+ l = tmp;
+ }
+ }
+
+ g_slist_free(tmp);
+
+done:
+ p = g_new0(struct avctp_pending_req, 1);
+ p->chan = chan;
+ p->transaction = chan->transaction;
+ p->process = process;
+ p->data = data;
+ p->destroy = destroy;
+
+ chan->transaction++;
+ chan->transaction %= 16;
+
+ return p;
+}
+
+static int avctp_send_req(struct avctp *session, uint8_t code, uint8_t subunit,
+ uint8_t opcode, const struct iovec *iov, int iov_cnt,
+ avctp_rsp_cb func, void *user_data)
+{
+ struct avctp_channel *control = session->control;
+ struct avctp_pending_req *p;
+ struct avctp_control_req *req;
+ struct iovec *pdu;
+ int i;
+
+ if (control == NULL)
+ return -ENOTCONN;
+
+ pdu = g_new0(struct iovec, iov_cnt);
+
+ for (i = 0; i < iov_cnt; i++) {
+ pdu[i].iov_len = iov[i].iov_len;
+ pdu[i].iov_base = g_memdup(iov[i].iov_base, iov[i].iov_len);
+ }
+
+ req = g_new0(struct avctp_control_req, 1);
+ req->code = code;
+ req->subunit = subunit;
+ req->op = opcode;
+ req->func = func;
+ req->iov = pdu;
+ req->iov_cnt = iov_cnt;
+ req->user_data = user_data;
+
+ p = pending_create(control, process_control, req, control_req_destroy);
+
+ req->p = p;
+
+ g_queue_push_tail(control->queue, p);
+
+ if (control->process_id == 0)
+ control->process_id = g_idle_add(process_queue, control);
+
+ return 0;
+}
+
+int avctp_send_browsing_req(struct avctp *session,
+ const struct iovec *iov, int iov_cnt,
+ avctp_browsing_rsp_cb func, void *user_data)
+{
+ struct avctp_channel *browsing = session->browsing;
+ struct avctp_pending_req *p;
+ struct avctp_browsing_req *req;
+ struct iovec *pdu;
+ int i;
+
+ if (browsing == NULL)
+ return -ENOTCONN;
+
+ pdu = g_new0(struct iovec, iov_cnt);
+
+ for (i = 0; i < iov_cnt; i++) {
+ pdu[i].iov_len = iov[i].iov_len;
+ pdu[i].iov_base = g_memdup(iov[i].iov_base, iov[i].iov_len);
+ }
+
+ req = g_new0(struct avctp_browsing_req, 1);
+ req->func = func;
+ req->iov = pdu;
+ req->iov_cnt = iov_cnt;
+ req->user_data = user_data;
+
+ p = pending_create(browsing, process_browsing, req,
+ browsing_req_destroy);
+
+ req->p = p;
+
+ g_queue_push_tail(browsing->queue, p);
+
+ /* Connection did not complete, delay process of the request */
+ if (browsing->watch == 0)
+ return 0;
+
+ if (browsing->process_id == 0)
+ browsing->process_id = g_idle_add(process_queue, browsing);
+
+ return 0;
+}
+
+int avctp_send_browsing(struct avctp *session, uint8_t transaction,
+ const struct iovec *iov, int iov_cnt)
+{
+ struct avctp_channel *browsing = session->browsing;
+
+ if (browsing == NULL)
+ return -ENOTCONN;
+
+ return avctp_browsing_send(browsing, transaction, AVCTP_RESPONSE,
+ iov, iov_cnt);
+}
+
+static const char *op2str(uint8_t op)
+{
+ int i;
+
+ for (i = 0; key_map[i].name != NULL; i++) {
+ if ((op & 0x7F) == key_map[i].avc)
+ return key_map[i].name;
+ }
+
+ return "UNKNOWN";
+}
+
+static int avctp_passthrough_press(struct avctp *session, uint8_t op,
+ uint8_t *params, size_t params_len)
+{
+ struct iovec iov[2];
+ int iov_cnt;
+ uint8_t operands[2];
+
+ DBG("%s", op2str(op));
+
+ iov[0].iov_base = operands;
+ iov[0].iov_len = sizeof(operands);
+
+ /* Button pressed */
+ operands[0] = op & 0x7f;
+
+ if (params_len > 0) {
+ iov[1].iov_base = params;
+ iov[1].iov_len = params_len;
+ iov_cnt = 2;
+ operands[1] = params_len;
+ } else {
+ iov_cnt = 1;
+ operands[1] = 0;
+ }
+
+ return avctp_send_req(session, AVC_CTYPE_CONTROL,
+ AVC_SUBUNIT_PANEL, AVC_OP_PASSTHROUGH,
+ iov, iov_cnt, avctp_passthrough_rsp, NULL);
+}
+
+static int avctp_passthrough_release(struct avctp *session, uint8_t op,
+ uint8_t *params, size_t params_len)
+{
+ struct iovec iov[2];
+ int iov_cnt;
+ uint8_t operands[2];
+
+ DBG("%s", op2str(op));
+
+ iov[0].iov_base = operands;
+ iov[0].iov_len = sizeof(operands);
+
+ /* Button released */
+ operands[0] = op | 0x80;
+
+ if (params_len > 0) {
+ iov[1].iov_base = params;
+ iov[1].iov_len = params_len;
+ iov_cnt = 2;
+ operands[1] = params_len;
+ } else {
+ iov_cnt = 1;
+ operands[1] = 0;
+ }
+
+ return avctp_send_req(session, AVC_CTYPE_CONTROL,
+ AVC_SUBUNIT_PANEL, AVC_OP_PASSTHROUGH,
+ iov, iov_cnt, NULL, NULL);
+}
+
+static gboolean repeat_timeout(gpointer user_data)
+{
+ struct avctp *session = user_data;
+
+ avctp_passthrough_release(session, session->key.op, session->key.params,
+ session->key.params_len);
+ avctp_passthrough_press(session, session->key.op, session->key.params,
+ session->key.params_len);
+
+ return TRUE;
+}
+
+static void release_pressed(struct avctp *session)
+{
+ avctp_passthrough_release(session, session->key.op, session->key.params,
+ session->key.params_len);
+
+ if (session->key.timer > 0)
+ g_source_remove(session->key.timer);
+
+ session->key.timer = 0;
+}
+
+static bool set_pressed(struct avctp *session, uint8_t op, uint8_t *params,
+ size_t params_len)
+{
+ if (session->key.timer > 0) {
+ if (session->key.op == op)
+ return TRUE;
+ release_pressed(session);
+ }
+
+ if (op != AVC_FAST_FORWARD && op != AVC_REWIND)
+ return FALSE;
+
+ session->key.op = op;
+ session->key.params = params;
+ session->key.params_len = params_len;
+ session->key.timer = g_timeout_add_seconds(AVC_PRESS_TIMEOUT,
+ repeat_timeout,
+ session);
+
+ return TRUE;
+}
+
+static gboolean avctp_passthrough_rsp(struct avctp *session, uint8_t code,
+ uint8_t subunit, uint8_t *operands,
+ size_t operand_count, void *user_data)
+{
+ uint8_t *params;
+ size_t params_len;
+
+ DBG("code 0x%02x operand_count %zd", code, operand_count);
+
+ if (code != AVC_CTYPE_ACCEPTED)
+ return FALSE;
+
+ if (operands[0] == AVC_VENDOR_UNIQUE) {
+ params = &operands[2];
+ params_len = operand_count - 2;
+ } else {
+ params = NULL;
+ params_len = 0;
+ }
+
+ if (set_pressed(session, operands[0], params, params_len))
+ return FALSE;
+
+ avctp_passthrough_release(session, operands[0], params, params_len);
+
+ return FALSE;
+}
+
+int avctp_send_passthrough(struct avctp *session, uint8_t op, uint8_t *params,
+ size_t params_len)
+{
+ /* Auto release if key pressed */
+ if (session->key.timer > 0)
+ release_pressed(session);
+
+ return avctp_passthrough_press(session, op, params, params_len);
+}
+
+int avctp_send_vendor(struct avctp *session, uint8_t transaction,
+ uint8_t code, uint8_t subunit,
+ const struct iovec *iov, int iov_cnt)
+{
+ struct avctp_channel *control = session->control;
+
+ if (control == NULL)
+ return -ENOTCONN;
+
+ return avctp_send(control, transaction, AVCTP_RESPONSE, code, subunit,
+ AVC_OP_VENDORDEP, iov, iov_cnt);
+}
+
+int avctp_send_vendor_req(struct avctp *session, uint8_t code, uint8_t subunit,
+ const struct iovec *iov, int iov_cnt,
+ avctp_rsp_cb func, void *user_data)
+{
+ return avctp_send_req(session, code, subunit, AVC_OP_VENDORDEP, iov,
+ iov_cnt, func, user_data);
+}
+
+unsigned int avctp_register_passthrough_handler(struct avctp *session,
+ avctp_passthrough_cb cb,
+ void *user_data)
+{
+ struct avctp_channel *control = session->control;
+ struct avctp_passthrough_handler *handler;
+ static unsigned int id = 0;
+
+ if (control == NULL || session->handler != NULL)
+ return 0;
+
+ handler = g_new(struct avctp_passthrough_handler, 1);
+ handler->cb = cb;
+ handler->user_data = user_data;
+ handler->id = ++id;
+
+ session->handler = handler;
+
+ return handler->id;
+}
+
+bool avctp_unregister_passthrough_handler(struct avctp *session,
+ unsigned int id)
+{
+ if (session->handler == NULL)
+ return false;
+
+ if (session->handler->id != id)
+ return false;
+
+ g_free(session->handler);
+ session->handler = NULL;
+ return true;
+}
+
+unsigned int avctp_register_pdu_handler(struct avctp *session, uint8_t opcode,
+ avctp_control_pdu_cb cb,
+ void *user_data)
+{
+ struct avctp_channel *control = session->control;
+ struct avctp_pdu_handler *handler;
+ static unsigned int id = 0;
+
+ if (control == NULL)
+ return 0;
+
+ handler = find_handler(control->handlers, opcode);
+ if (handler)
+ return 0;
+
+ handler = g_new(struct avctp_pdu_handler, 1);
+ handler->opcode = opcode;
+ handler->cb = cb;
+ handler->user_data = user_data;
+ handler->id = ++id;
+
+ control->handlers = g_slist_append(control->handlers, handler);
+
+ return handler->id;
+}
+
+unsigned int avctp_register_browsing_pdu_handler(struct avctp *session,
+ avctp_browsing_pdu_cb cb,
+ void *user_data,
+ avctp_destroy_cb_t destroy)
+{
+ struct avctp_channel *browsing = session->browsing;
+ struct avctp_browsing_pdu_handler *handler;
+ static unsigned int id = 0;
+
+ if (browsing == NULL)
+ return 0;
+
+ if (browsing->handlers != NULL)
+ return 0;
+
+ handler = g_new(struct avctp_browsing_pdu_handler, 1);
+ handler->cb = cb;
+ handler->user_data = user_data;
+ handler->id = ++id;
+ handler->destroy = destroy;
+
+ browsing->handlers = g_slist_append(browsing->handlers, handler);
+
+ return handler->id;
+}
+
+bool avctp_unregister_pdu_handler(struct avctp *session, unsigned int id)
+{
+ struct avctp_channel *control = session->control;
+ GSList *l;
+
+ if (!control)
+ return false;
+
+ for (l = control->handlers; l; l = g_slist_next(l)) {
+ struct avctp_pdu_handler *handler = l->data;
+
+ if (handler->id != id)
+ continue;
+
+ control->handlers = g_slist_remove(control->handlers, handler);
+ g_free(handler);
+ return true;
+ }
+
+ return false;
+}
+
+bool avctp_unregister_browsing_pdu_handler(struct avctp *session,
+ unsigned int id)
+{
+ struct avctp_channel *browsing = session->browsing;
+ GSList *l;
+
+ if (browsing == NULL)
+ return false;
+
+ for (l = browsing->handlers; l; l = g_slist_next(l)) {
+ struct avctp_browsing_pdu_handler *handler = l->data;
+
+ if (handler->id != id)
+ continue;
+
+ browsing->handlers = g_slist_remove(browsing->handlers,
+ handler);
+ g_free(handler);
+ return true;
+ }
+
+ return false;
+}
+
+struct avctp *avctp_new(int fd, size_t imtu, size_t omtu, uint16_t version)
+{
+ struct avctp *session;
+ struct avctp_channel *control;
+ GIOCondition cond = G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL;
+
+ session = g_new0(struct avctp, 1);
+ session->version = version;
+
+ control = avctp_channel_create(session, fd, imtu, omtu, NULL);
+ if (!control) {
+ g_free(session);
+ return NULL;
+ }
+
+ session->uinput = -1;
+ session->control = control;
+ session->passthrough_id = avctp_register_pdu_handler(session,
+ AVC_OP_PASSTHROUGH,
+ handle_panel_passthrough,
+ NULL);
+ session->unit_id = avctp_register_pdu_handler(session,
+ AVC_OP_UNITINFO,
+ handle_unit_info,
+ NULL);
+ session->subunit_id = avctp_register_pdu_handler(session,
+ AVC_OP_SUBUNITINFO,
+ handle_subunit_info,
+ NULL);
+
+ control->watch = g_io_add_watch(session->control->io, cond,
+ (GIOFunc) session_cb, session);
+
+ return avctp_ref(session);
+}
+
+int avctp_connect_browsing(struct avctp *session, int fd, size_t imtu,
+ size_t omtu)
+{
+ struct avctp_channel *browsing;
+ GIOCondition cond = G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL;
+
+ if (session->browsing)
+ return -EISCONN;
+
+ browsing = avctp_channel_create(session, fd, imtu, omtu,
+ avctp_destroy_browsing);
+ if (!browsing)
+ return -EINVAL;
+
+ session->browsing = browsing;
+ browsing->watch = g_io_add_watch(session->browsing->io, cond,
+ (GIOFunc) session_browsing_cb, session);
+
+ return 0;
+}
+
+void avctp_set_destroy_cb(struct avctp *session, avctp_destroy_cb_t cb,
+ void *user_data)
+{
+ session->destroy = cb;
+ session->data = user_data;
+}
+
+void avctp_shutdown(struct avctp *session)
+{
+ if (!session)
+ return;
+
+ avctp_unref(session);
+}
diff --git a/repo/android/avctp.h b/repo/android/avctp.h
new file mode 100644
index 0000000..f0da2b3
--- /dev/null
+++ b/repo/android/avctp.h
@@ -0,0 +1,183 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2006-2010 Nokia Corporation
+ * Copyright (C) 2004-2010 Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#define AVCTP_CONTROL_PSM 23
+#define AVCTP_BROWSING_PSM 27
+
+#define AVCTP_HEADER_LENGTH 3
+#define AVC_HEADER_LENGTH 3
+
+#define AVC_DATA_OFFSET AVCTP_HEADER_LENGTH + AVC_HEADER_LENGTH
+#define AVC_DATA_MTU 512
+
+/* ctype entries */
+#define AVC_CTYPE_CONTROL 0x0
+#define AVC_CTYPE_STATUS 0x1
+#define AVC_CTYPE_NOTIFY 0x3
+#define AVC_CTYPE_NOT_IMPLEMENTED 0x8
+#define AVC_CTYPE_ACCEPTED 0x9
+#define AVC_CTYPE_REJECTED 0xA
+#define AVC_CTYPE_STABLE 0xC
+#define AVC_CTYPE_CHANGED 0xD
+#define AVC_CTYPE_INTERIM 0xF
+
+/* opcodes */
+#define AVC_OP_VENDORDEP 0x00
+#define AVC_OP_UNITINFO 0x30
+#define AVC_OP_SUBUNITINFO 0x31
+#define AVC_OP_PASSTHROUGH 0x7c
+
+/* subunits of interest */
+#define AVC_SUBUNIT_PANEL 0x09
+
+/* operands in passthrough commands */
+#define AVC_SELECT 0x00
+#define AVC_UP 0x01
+#define AVC_DOWN 0x02
+#define AVC_LEFT 0x03
+#define AVC_RIGHT 0x04
+#define AVC_ROOT_MENU 0x09
+#define AVC_CONTENTS_MENU 0x0b
+#define AVC_FAVORITE_MENU 0x0c
+#define AVC_EXIT 0x0d
+#define AVC_ON_DEMAND_MENU 0x0e
+#define AVC_APPS_MENU 0x0f
+#define AVC_0 0x20
+#define AVC_1 0x21
+#define AVC_2 0x22
+#define AVC_3 0x23
+#define AVC_4 0x24
+#define AVC_5 0x25
+#define AVC_6 0x26
+#define AVC_7 0x27
+#define AVC_8 0x28
+#define AVC_9 0x29
+#define AVC_DOT 0x2a
+#define AVC_ENTER 0x2b
+#define AVC_CHANNEL_UP 0x30
+#define AVC_CHANNEL_DOWN 0x31
+#define AVC_CHANNEL_PREVIOUS 0x32
+#define AVC_INPUT_SELECT 0x34
+#define AVC_INFO 0x35
+#define AVC_HELP 0x36
+#define AVC_PAGE_UP 0x37
+#define AVC_PAGE_DOWN 0x38
+#define AVC_LOCK 0x3a
+#define AVC_POWER 0x40
+#define AVC_VOLUME_UP 0x41
+#define AVC_VOLUME_DOWN 0x42
+#define AVC_MUTE 0x43
+#define AVC_PLAY 0x44
+#define AVC_STOP 0x45
+#define AVC_PAUSE 0x46
+#define AVC_RECORD 0x47
+#define AVC_REWIND 0x48
+#define AVC_FAST_FORWARD 0x49
+#define AVC_EJECT 0x4a
+#define AVC_FORWARD 0x4b
+#define AVC_BACKWARD 0x4c
+#define AVC_LIST 0x4d
+#define AVC_F1 0x71
+#define AVC_F2 0x72
+#define AVC_F3 0x73
+#define AVC_F4 0x74
+#define AVC_F5 0x75
+#define AVC_F6 0x76
+#define AVC_F7 0x77
+#define AVC_F8 0x78
+#define AVC_F9 0x79
+#define AVC_RED 0x7a
+#define AVC_GREEN 0x7b
+#define AVC_BLUE 0x7c
+#define AVC_YELLOW 0x7c
+
+#define AVC_VENDOR_UNIQUE 0x7e
+
+#define AVC_VENDOR_NEXT_GROUP 0x00
+#define AVC_VENDOR_PREV_GROUP 0x01
+
+struct avctp;
+
+typedef bool (*avctp_passthrough_cb) (struct avctp *session,
+ uint8_t op, bool pressed,
+ void *user_data);
+typedef ssize_t (*avctp_control_pdu_cb) (struct avctp *session,
+ uint8_t transaction, uint8_t *code,
+ uint8_t *subunit, uint8_t *operands,
+ size_t operand_count, void *user_data);
+typedef gboolean (*avctp_rsp_cb) (struct avctp *session, uint8_t code,
+ uint8_t subunit, uint8_t *operands,
+ size_t operand_count, void *user_data);
+typedef gboolean (*avctp_browsing_rsp_cb) (struct avctp *session,
+ uint8_t *operands, size_t operand_count,
+ void *user_data);
+typedef ssize_t (*avctp_browsing_pdu_cb) (struct avctp *session,
+ uint8_t transaction,
+ uint8_t *operands, size_t operand_count,
+ void *user_data);
+
+typedef void (*avctp_destroy_cb_t) (void *user_data);
+
+struct avctp *avctp_new(int fd, size_t imtu, size_t omtu, uint16_t version);
+void avctp_set_destroy_cb(struct avctp *session, avctp_destroy_cb_t cb,
+ void *user_data);
+
+int avctp_init_uinput(struct avctp *session, const char *name,
+ const char *address);
+int avctp_connect_browsing(struct avctp *session, int fd, size_t imtu,
+ size_t omtu);
+
+void avctp_shutdown(struct avctp *session);
+
+unsigned int avctp_register_passthrough_handler(struct avctp *session,
+ avctp_passthrough_cb cb,
+ void *user_data);
+bool avctp_unregister_passthrough_handler(struct avctp *session,
+ unsigned int id);
+
+unsigned int avctp_register_pdu_handler(struct avctp *session, uint8_t opcode,
+ avctp_control_pdu_cb cb,
+ void *user_data);
+bool avctp_unregister_pdu_handler(struct avctp *session, unsigned int id);
+
+unsigned int avctp_register_browsing_pdu_handler(struct avctp *session,
+ avctp_browsing_pdu_cb cb,
+ void *user_data,
+ avctp_destroy_cb_t destroy);
+bool avctp_unregister_browsing_pdu_handler(struct avctp *session,
+ unsigned int id);
+
+int avctp_send_passthrough(struct avctp *session, uint8_t op, uint8_t *params,
+ size_t params_len);
+int avctp_send_vendor(struct avctp *session, uint8_t transaction,
+ uint8_t code, uint8_t subunit,
+ const struct iovec *iov, int iov_cnt);
+int avctp_send_vendor_req(struct avctp *session, uint8_t code, uint8_t subunit,
+ const struct iovec *iov, int iov_cnt,
+ avctp_rsp_cb func, void *user_data);
+int avctp_send_browsing(struct avctp *session, uint8_t transaction,
+ const struct iovec *iov, int iov_cnt);
+int avctp_send_browsing_req(struct avctp *session,
+ const struct iovec *iov, int iov_cnt,
+ avctp_browsing_rsp_cb func, void *user_data);
diff --git a/repo/android/avdtp.c b/repo/android/avdtp.c
new file mode 100644
index 0000000..bab305b
--- /dev/null
+++ b/repo/android/avdtp.c
@@ -0,0 +1,3484 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2006-2010 Nokia Corporation
+ * Copyright (C) 2004-2010 Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <errno.h>
+#include <unistd.h>
+#include <assert.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+
+#include <glib.h>
+
+#include "lib/bluetooth.h"
+#include "src/log.h"
+#include "src/shared/util.h"
+#include "src/shared/queue.h"
+#include "avdtp.h"
+#include "../profiles/audio/a2dp-codecs.h"
+
+#define MAX_SEID 0x3E
+static unsigned int seids;
+
+#ifndef MAX
+# define MAX(x, y) ((x) > (y) ? (x) : (y))
+#endif
+
+#define AVDTP_DISCOVER 0x01
+#define AVDTP_GET_CAPABILITIES 0x02
+#define AVDTP_SET_CONFIGURATION 0x03
+#define AVDTP_GET_CONFIGURATION 0x04
+#define AVDTP_RECONFIGURE 0x05
+#define AVDTP_OPEN 0x06
+#define AVDTP_START 0x07
+#define AVDTP_CLOSE 0x08
+#define AVDTP_SUSPEND 0x09
+#define AVDTP_ABORT 0x0A
+#define AVDTP_SECURITY_CONTROL 0x0B
+#define AVDTP_GET_ALL_CAPABILITIES 0x0C
+#define AVDTP_DELAY_REPORT 0x0D
+
+#define AVDTP_PKT_TYPE_SINGLE 0x00
+#define AVDTP_PKT_TYPE_START 0x01
+#define AVDTP_PKT_TYPE_CONTINUE 0x02
+#define AVDTP_PKT_TYPE_END 0x03
+
+#define AVDTP_MSG_TYPE_COMMAND 0x00
+#define AVDTP_MSG_TYPE_GEN_REJECT 0x01
+#define AVDTP_MSG_TYPE_ACCEPT 0x02
+#define AVDTP_MSG_TYPE_REJECT 0x03
+
+#define REQ_TIMEOUT 6
+#define ABORT_TIMEOUT 2
+#define DISCONNECT_TIMEOUT 1
+#define START_TIMEOUT 1
+
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+
+struct avdtp_common_header {
+ uint8_t message_type:2;
+ uint8_t packet_type:2;
+ uint8_t transaction:4;
+} __attribute__ ((packed));
+
+struct avdtp_single_header {
+ uint8_t message_type:2;
+ uint8_t packet_type:2;
+ uint8_t transaction:4;
+ uint8_t signal_id:6;
+ uint8_t rfa0:2;
+} __attribute__ ((packed));
+
+struct avdtp_start_header {
+ uint8_t message_type:2;
+ uint8_t packet_type:2;
+ uint8_t transaction:4;
+ uint8_t no_of_packets;
+ uint8_t signal_id:6;
+ uint8_t rfa0:2;
+} __attribute__ ((packed));
+
+struct avdtp_continue_header {
+ uint8_t message_type:2;
+ uint8_t packet_type:2;
+ uint8_t transaction:4;
+} __attribute__ ((packed));
+
+struct seid_info {
+ uint8_t rfa0:1;
+ uint8_t inuse:1;
+ uint8_t seid:6;
+ uint8_t rfa2:3;
+ uint8_t type:1;
+ uint8_t media_type:4;
+} __attribute__ ((packed));
+
+struct seid {
+ uint8_t rfa0:2;
+ uint8_t seid:6;
+} __attribute__ ((packed));
+
+#elif __BYTE_ORDER == __BIG_ENDIAN
+
+struct avdtp_common_header {
+ uint8_t transaction:4;
+ uint8_t packet_type:2;
+ uint8_t message_type:2;
+} __attribute__ ((packed));
+
+struct avdtp_single_header {
+ uint8_t transaction:4;
+ uint8_t packet_type:2;
+ uint8_t message_type:2;
+ uint8_t rfa0:2;
+ uint8_t signal_id:6;
+} __attribute__ ((packed));
+
+struct avdtp_start_header {
+ uint8_t transaction:4;
+ uint8_t packet_type:2;
+ uint8_t message_type:2;
+ uint8_t no_of_packets;
+ uint8_t rfa0:2;
+ uint8_t signal_id:6;
+} __attribute__ ((packed));
+
+struct avdtp_continue_header {
+ uint8_t transaction:4;
+ uint8_t packet_type:2;
+ uint8_t message_type:2;
+} __attribute__ ((packed));
+
+struct seid_info {
+ uint8_t seid:6;
+ uint8_t inuse:1;
+ uint8_t rfa0:1;
+ uint8_t media_type:4;
+ uint8_t type:1;
+ uint8_t rfa2:3;
+} __attribute__ ((packed));
+
+struct seid {
+ uint8_t seid:6;
+ uint8_t rfa0:2;
+} __attribute__ ((packed));
+
+#else
+#error "Unknown byte order"
+#endif
+
+/* packets */
+
+struct discover_resp {
+ struct seid_info seps[0];
+} __attribute__ ((packed));
+
+struct getcap_resp {
+ uint8_t caps[0];
+} __attribute__ ((packed));
+
+struct start_req {
+ struct seid first_seid;
+ struct seid other_seids[0];
+} __attribute__ ((packed));
+
+struct suspend_req {
+ struct seid first_seid;
+ struct seid other_seids[0];
+} __attribute__ ((packed));
+
+struct seid_rej {
+ uint8_t error;
+} __attribute__ ((packed));
+
+struct conf_rej {
+ uint8_t category;
+ uint8_t error;
+} __attribute__ ((packed));
+
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+
+struct seid_req {
+ uint8_t rfa0:2;
+ uint8_t acp_seid:6;
+} __attribute__ ((packed));
+
+struct setconf_req {
+ uint8_t rfa0:2;
+ uint8_t acp_seid:6;
+ uint8_t rfa1:2;
+ uint8_t int_seid:6;
+
+ uint8_t caps[0];
+} __attribute__ ((packed));
+
+struct stream_rej {
+ uint8_t rfa0:2;
+ uint8_t acp_seid:6;
+ uint8_t error;
+} __attribute__ ((packed));
+
+struct reconf_req {
+ uint8_t rfa0:2;
+ uint8_t acp_seid:6;
+
+ uint8_t serv_cap;
+ uint8_t serv_cap_len;
+
+ uint8_t caps[0];
+} __attribute__ ((packed));
+
+struct delay_req {
+ uint8_t rfa0:2;
+ uint8_t acp_seid:6;
+ uint16_t delay;
+} __attribute__ ((packed));
+
+#elif __BYTE_ORDER == __BIG_ENDIAN
+
+struct seid_req {
+ uint8_t acp_seid:6;
+ uint8_t rfa0:2;
+} __attribute__ ((packed));
+
+struct setconf_req {
+ uint8_t acp_seid:6;
+ uint8_t rfa0:2;
+ uint8_t int_seid:6;
+ uint8_t rfa1:2;
+
+ uint8_t caps[0];
+} __attribute__ ((packed));
+
+struct stream_rej {
+ uint8_t acp_seid:6;
+ uint8_t rfa0:2;
+ uint8_t error;
+} __attribute__ ((packed));
+
+struct reconf_req {
+ uint8_t acp_seid:6;
+ uint8_t rfa0:2;
+
+ uint8_t serv_cap;
+ uint8_t serv_cap_len;
+
+ uint8_t caps[0];
+} __attribute__ ((packed));
+
+struct delay_req {
+ uint8_t acp_seid:6;
+ uint8_t rfa0:2;
+ uint16_t delay;
+} __attribute__ ((packed));
+
+#else
+#error "Unknown byte order"
+#endif
+
+struct in_buf {
+ gboolean active;
+ int no_of_packets;
+ uint8_t transaction;
+ uint8_t message_type;
+ uint8_t signal_id;
+ uint8_t buf[1024];
+ uint8_t data_size;
+};
+
+struct pending_req {
+ uint8_t transaction;
+ uint8_t signal_id;
+ void *data;
+ size_t data_size;
+ struct avdtp_stream *stream; /* Set if the request targeted a stream */
+ guint timeout;
+ gboolean collided;
+};
+
+struct avdtp_remote_sep {
+ uint8_t seid;
+ uint8_t type;
+ uint8_t media_type;
+ struct avdtp_service_capability *codec;
+ gboolean delay_reporting;
+ GSList *caps; /* of type struct avdtp_service_capability */
+ struct avdtp_stream *stream;
+};
+
+struct avdtp_local_sep {
+ avdtp_state_t state;
+ struct avdtp_stream *stream;
+ struct seid_info info;
+ uint8_t codec;
+ uint32_t vndcodec_vendor;
+ uint16_t vndcodec_codec;
+ gboolean delay_reporting;
+ GSList *caps;
+ struct avdtp_sep_ind *ind;
+ struct avdtp_sep_cfm *cfm;
+ void *user_data;
+};
+
+struct stream_callback {
+ avdtp_stream_state_cb cb;
+ void *user_data;
+ unsigned int id;
+};
+
+struct discover_callback {
+ unsigned int id;
+ avdtp_discover_cb_t cb;
+ void *user_data;
+};
+
+struct disconnect_callback {
+ unsigned int id;
+ avdtp_disconnect_cb_t cb;
+ void *user_data;
+};
+
+struct avdtp_stream {
+ GIOChannel *io;
+ uint16_t imtu;
+ uint16_t omtu;
+ struct avdtp *session;
+ struct avdtp_local_sep *lsep;
+ uint8_t rseid;
+ GSList *caps;
+ GSList *callbacks;
+ struct avdtp_service_capability *codec;
+ guint io_id; /* Transport GSource ID */
+ guint timer; /* Waiting for other side to close or open
+ * the transport channel */
+ gboolean open_acp; /* If we are in ACT role for Open */
+ gboolean close_int; /* If we are in INT role for Close */
+ gboolean abort_int; /* If we are in INT role for Abort */
+ guint start_timer; /* Wait START command timer */
+ gboolean delay_reporting;
+ uint16_t delay; /* AVDTP 1.3 Delay Reporting feature */
+ gboolean starting; /* only valid while sep state == OPEN */
+};
+
+/* Structure describing an AVDTP connection between two devices */
+
+struct avdtp {
+ unsigned int ref;
+
+ uint16_t version;
+
+ guint auth_id;
+
+ GIOChannel *io;
+ guint io_id;
+
+ GSList *seps; /* Elements of type struct avdtp_remote_sep * */
+ struct queue *lseps; /* Elements of type struct avdtp_local_sep * */
+
+ GSList *streams; /* Elements of type struct avdtp_stream * */
+
+ GSList *req_queue; /* Elements of type struct pending_req * */
+ GSList *prio_queue; /* Same as req_queue but is processed before it */
+
+ struct avdtp_stream *pending_open;
+
+ uint16_t imtu;
+ uint16_t omtu;
+
+ struct in_buf in;
+
+ char *buf;
+
+ struct discover_callback *discover;
+ struct pending_req *req;
+
+ GSList *disconnect;
+
+ bool shutdown;
+};
+
+static int send_request(struct avdtp *session, gboolean priority,
+ struct avdtp_stream *stream, uint8_t signal_id,
+ void *buffer, size_t size);
+static gboolean avdtp_parse_resp(struct avdtp *session,
+ struct avdtp_stream *stream,
+ uint8_t transaction, uint8_t signal_id,
+ void *buf, int size);
+static gboolean avdtp_parse_rej(struct avdtp *session,
+ struct avdtp_stream *stream,
+ uint8_t transaction, uint8_t signal_id,
+ void *buf, int size);
+static int process_queue(struct avdtp *session);
+static void avdtp_sep_set_state(struct avdtp *session,
+ struct avdtp_local_sep *sep,
+ avdtp_state_t state);
+
+static const char *avdtp_statestr(avdtp_state_t state)
+{
+ switch (state) {
+ case AVDTP_STATE_IDLE:
+ return "IDLE";
+ case AVDTP_STATE_CONFIGURED:
+ return "CONFIGURED";
+ case AVDTP_STATE_OPEN:
+ return "OPEN";
+ case AVDTP_STATE_STREAMING:
+ return "STREAMING";
+ case AVDTP_STATE_CLOSING:
+ return "CLOSING";
+ case AVDTP_STATE_ABORTING:
+ return "ABORTING";
+ default:
+ return "<unknown state>";
+ }
+}
+
+static gboolean try_send(int sk, void *data, size_t len)
+{
+ int err;
+
+ do {
+ err = send(sk, data, len, 0);
+ } while (err < 0 && errno == EINTR);
+
+ if (err < 0) {
+ error("send: %s (%d)", strerror(errno), errno);
+ return FALSE;
+ } else if ((size_t) err != len) {
+ error("try_send: complete buffer not sent (%d/%zu bytes)",
+ err, len);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static gboolean avdtp_send(struct avdtp *session, uint8_t transaction,
+ uint8_t message_type, uint8_t signal_id,
+ void *data, size_t len)
+{
+ unsigned int cont_fragments, sent;
+ struct avdtp_start_header start;
+ struct avdtp_continue_header cont;
+ int sock;
+
+ if (session->io == NULL) {
+ error("avdtp_send: session is closed");
+ return FALSE;
+ }
+
+ sock = g_io_channel_unix_get_fd(session->io);
+
+ /* Single packet - no fragmentation */
+ if (sizeof(struct avdtp_single_header) + len <= session->omtu) {
+ struct avdtp_single_header single;
+
+ memset(&single, 0, sizeof(single));
+
+ single.transaction = transaction;
+ single.packet_type = AVDTP_PKT_TYPE_SINGLE;
+ single.message_type = message_type;
+ single.signal_id = signal_id;
+
+ memcpy(session->buf, &single, sizeof(single));
+ memcpy(session->buf + sizeof(single), data, len);
+
+ return try_send(sock, session->buf, sizeof(single) + len);
+ }
+
+ /* Check if there is enough space to start packet */
+ if (session->omtu < sizeof(start)) {
+ error("No enough space to fragment packet");
+ return FALSE;
+ }
+
+ /* Count the number of needed fragments */
+ cont_fragments = (len - (session->omtu - sizeof(start))) /
+ (session->omtu - sizeof(cont)) + 1;
+
+ DBG("%zu bytes split into %d fragments", len, cont_fragments + 1);
+
+ /* Send the start packet */
+ memset(&start, 0, sizeof(start));
+ start.transaction = transaction;
+ start.packet_type = AVDTP_PKT_TYPE_START;
+ start.message_type = message_type;
+ start.no_of_packets = cont_fragments + 1;
+ start.signal_id = signal_id;
+
+ memcpy(session->buf, &start, sizeof(start));
+ memcpy(session->buf + sizeof(start), data,
+ session->omtu - sizeof(start));
+
+ if (!try_send(sock, session->buf, session->omtu))
+ return FALSE;
+
+ DBG("first packet with %zu bytes sent", session->omtu - sizeof(start));
+
+ sent = session->omtu - sizeof(start);
+
+ /* Send the continue fragments and the end packet */
+ while (sent < len) {
+ int left, to_copy;
+
+ left = len - sent;
+ if (left + sizeof(cont) > session->omtu) {
+ cont.packet_type = AVDTP_PKT_TYPE_CONTINUE;
+ to_copy = session->omtu - sizeof(cont);
+ DBG("sending continue with %d bytes", to_copy);
+ } else {
+ cont.packet_type = AVDTP_PKT_TYPE_END;
+ to_copy = left;
+ DBG("sending end with %d bytes", to_copy);
+ }
+
+ cont.transaction = transaction;
+ cont.message_type = message_type;
+
+ memcpy(session->buf, &cont, sizeof(cont));
+ memcpy(session->buf + sizeof(cont), data + sent, to_copy);
+
+ if (!try_send(sock, session->buf, to_copy + sizeof(cont)))
+ return FALSE;
+
+ sent += to_copy;
+ }
+
+ return TRUE;
+}
+
+static void pending_req_free(void *data)
+{
+ struct pending_req *req = data;
+
+ if (req->timeout)
+ g_source_remove(req->timeout);
+ g_free(req->data);
+ g_free(req);
+}
+
+static void close_stream(struct avdtp_stream *stream)
+{
+ int sock;
+
+ if (stream->io == NULL)
+ return;
+
+ sock = g_io_channel_unix_get_fd(stream->io);
+
+ shutdown(sock, SHUT_RDWR);
+
+ g_io_channel_shutdown(stream->io, FALSE, NULL);
+
+ g_io_channel_unref(stream->io);
+ stream->io = NULL;
+}
+
+static gboolean stream_close_timeout(gpointer user_data)
+{
+ struct avdtp_stream *stream = user_data;
+
+ DBG("Timed out waiting for peer to close the transport channel");
+
+ stream->timer = 0;
+
+ close_stream(stream);
+
+ return FALSE;
+}
+
+static gboolean stream_open_timeout(gpointer user_data)
+{
+ struct avdtp_stream *stream = user_data;
+
+ DBG("Timed out waiting for peer to open the transport channel");
+
+ stream->timer = 0;
+
+ stream->session->pending_open = NULL;
+
+ avdtp_abort(stream->session, stream);
+
+ return FALSE;
+}
+
+void avdtp_error_init(struct avdtp_error *err, uint8_t category, int id)
+{
+ err->category = category;
+
+ if (category == AVDTP_ERRNO)
+ err->err.posix_errno = id;
+ else
+ err->err.error_code = id;
+}
+
+uint8_t avdtp_error_category(struct avdtp_error *err)
+{
+ return err->category;
+}
+
+int avdtp_error_error_code(struct avdtp_error *err)
+{
+ assert(err->category != AVDTP_ERRNO);
+ return err->err.error_code;
+}
+
+int avdtp_error_posix_errno(struct avdtp_error *err)
+{
+ assert(err->category == AVDTP_ERRNO);
+ return err->err.posix_errno;
+}
+
+static struct avdtp_stream *find_stream_by_rseid(struct avdtp *session,
+ uint8_t rseid)
+{
+ GSList *l;
+
+ for (l = session->streams; l != NULL; l = g_slist_next(l)) {
+ struct avdtp_stream *stream = l->data;
+
+ if (stream->rseid == rseid)
+ return stream;
+ }
+
+ return NULL;
+}
+
+static struct avdtp_remote_sep *find_remote_sep(GSList *seps, uint8_t seid)
+{
+ GSList *l;
+
+ for (l = seps; l != NULL; l = g_slist_next(l)) {
+ struct avdtp_remote_sep *sep = l->data;
+
+ if (sep->seid == seid)
+ return sep;
+ }
+
+ return NULL;
+}
+
+static void stream_free(void *data)
+{
+ struct avdtp_stream *stream = data;
+ struct avdtp_remote_sep *rsep;
+
+ stream->lsep->info.inuse = 0;
+ stream->lsep->stream = NULL;
+
+ rsep = find_remote_sep(stream->session->seps, stream->rseid);
+ if (rsep)
+ rsep->stream = NULL;
+
+ if (stream->timer)
+ g_source_remove(stream->timer);
+
+ if (stream->start_timer > 0)
+ g_source_remove(stream->start_timer);
+
+ if (stream->io)
+ close_stream(stream);
+
+ if (stream->io_id)
+ g_source_remove(stream->io_id);
+
+ g_slist_free_full(stream->callbacks, g_free);
+ g_slist_free_full(stream->caps, g_free);
+
+ g_free(stream);
+}
+
+static gboolean transport_cb(GIOChannel *chan, GIOCondition cond,
+ gpointer data)
+{
+ struct avdtp_stream *stream = data;
+ struct avdtp_local_sep *sep = stream->lsep;
+
+ if (stream->close_int && sep->cfm && sep->cfm->close)
+ sep->cfm->close(stream->session, sep, stream, NULL,
+ sep->user_data);
+
+ if (!(cond & G_IO_NVAL))
+ close_stream(stream);
+
+ stream->io_id = 0;
+
+ if (!stream->abort_int)
+ avdtp_sep_set_state(stream->session, sep, AVDTP_STATE_IDLE);
+
+ return FALSE;
+}
+
+static void handle_transport_connect(struct avdtp *session, GIOChannel *io,
+ uint16_t imtu, uint16_t omtu)
+{
+ struct avdtp_stream *stream = session->pending_open;
+ struct avdtp_local_sep *sep = stream->lsep;
+
+ session->pending_open = NULL;
+
+ if (stream->timer) {
+ g_source_remove(stream->timer);
+ stream->timer = 0;
+ }
+
+ if (io == NULL)
+ return;
+
+ if (stream->io == NULL)
+ stream->io = g_io_channel_ref(io);
+
+ stream->omtu = omtu;
+ stream->imtu = imtu;
+
+ avdtp_sep_set_state(session, sep, AVDTP_STATE_OPEN);
+
+ stream->io_id = g_io_add_watch(io, G_IO_ERR | G_IO_HUP | G_IO_NVAL,
+ (GIOFunc) transport_cb, stream);
+}
+
+static int pending_req_cmp(gconstpointer a, gconstpointer b)
+{
+ const struct pending_req *req = a;
+ const struct avdtp_stream *stream = b;
+
+ if (req->stream == stream)
+ return 0;
+
+ return -1;
+}
+
+static void cleanup_queue(struct avdtp *session, struct avdtp_stream *stream)
+{
+ GSList *l;
+ struct pending_req *req;
+
+ while ((l = g_slist_find_custom(session->prio_queue, stream,
+ pending_req_cmp))) {
+ req = l->data;
+ pending_req_free(req);
+ session->prio_queue = g_slist_remove(session->prio_queue, req);
+ }
+
+ while ((l = g_slist_find_custom(session->req_queue, stream,
+ pending_req_cmp))) {
+ req = l->data;
+ pending_req_free(req);
+ session->req_queue = g_slist_remove(session->req_queue, req);
+ }
+}
+
+static void handle_unanswered_req(struct avdtp *session,
+ struct avdtp_stream *stream)
+{
+ struct pending_req *req;
+ struct avdtp_local_sep *lsep;
+ struct avdtp_error err;
+
+ if (!session->req->timeout)
+ /* Request is in process */
+ return;
+
+ if (session->req->signal_id == AVDTP_ABORT) {
+ /* Avoid freeing the Abort request here */
+ DBG("handle_unanswered_req: Abort req, returning");
+ session->req->stream = NULL;
+ return;
+ }
+
+ req = session->req;
+ session->req = NULL;
+
+ avdtp_error_init(&err, AVDTP_ERRNO, EIO);
+
+ lsep = stream->lsep;
+
+ switch (req->signal_id) {
+ case AVDTP_RECONFIGURE:
+ error("No reply to Reconfigure request");
+ if (lsep && lsep->cfm && lsep->cfm->reconfigure)
+ lsep->cfm->reconfigure(session, lsep, stream, &err,
+ lsep->user_data);
+ break;
+ case AVDTP_OPEN:
+ error("No reply to Open request");
+ if (lsep && lsep->cfm && lsep->cfm->open)
+ lsep->cfm->open(session, lsep, stream, &err,
+ lsep->user_data);
+ break;
+ case AVDTP_START:
+ error("No reply to Start request");
+ if (lsep && lsep->cfm && lsep->cfm->start)
+ lsep->cfm->start(session, lsep, stream, &err,
+ lsep->user_data);
+ break;
+ case AVDTP_SUSPEND:
+ error("No reply to Suspend request");
+ if (lsep && lsep->cfm && lsep->cfm->suspend)
+ lsep->cfm->suspend(session, lsep, stream, &err,
+ lsep->user_data);
+ break;
+ case AVDTP_CLOSE:
+ error("No reply to Close request");
+ if (lsep && lsep->cfm && lsep->cfm->close)
+ lsep->cfm->close(session, lsep, stream, &err,
+ lsep->user_data);
+ break;
+ case AVDTP_SET_CONFIGURATION:
+ error("No reply to SetConfiguration request");
+ if (lsep && lsep->cfm && lsep->cfm->set_configuration)
+ lsep->cfm->set_configuration(session, lsep, stream,
+ &err, lsep->user_data);
+ }
+
+ pending_req_free(req);
+}
+
+static void avdtp_sep_set_state(struct avdtp *session,
+ struct avdtp_local_sep *sep,
+ avdtp_state_t state)
+{
+ struct avdtp_stream *stream = sep->stream;
+ avdtp_state_t old_state;
+ struct avdtp_error err, *err_ptr = NULL;
+ GSList *l;
+
+ if (!stream) {
+ error("Error changing sep state: stream not available");
+ return;
+ }
+
+ if (sep->state == state) {
+ avdtp_error_init(&err, AVDTP_ERRNO, EIO);
+ DBG("stream state change failed: %s", avdtp_strerror(&err));
+ err_ptr = &err;
+ } else {
+ err_ptr = NULL;
+ DBG("stream state changed: %s -> %s",
+ avdtp_statestr(sep->state),
+ avdtp_statestr(state));
+ }
+
+ old_state = sep->state;
+ sep->state = state;
+
+ switch (state) {
+ case AVDTP_STATE_CONFIGURED:
+ if (sep->info.type == AVDTP_SEP_TYPE_SINK)
+ avdtp_delay_report(session, stream, stream->delay);
+ break;
+ case AVDTP_STATE_OPEN:
+ stream->starting = FALSE;
+ break;
+ case AVDTP_STATE_STREAMING:
+ if (stream->start_timer) {
+ g_source_remove(stream->start_timer);
+ stream->start_timer = 0;
+ }
+ stream->open_acp = FALSE;
+ break;
+ case AVDTP_STATE_CLOSING:
+ case AVDTP_STATE_ABORTING:
+ if (stream->start_timer) {
+ g_source_remove(stream->start_timer);
+ stream->start_timer = 0;
+ }
+ break;
+ case AVDTP_STATE_IDLE:
+ if (stream->start_timer) {
+ g_source_remove(stream->start_timer);
+ stream->start_timer = 0;
+ }
+ if (session->pending_open == stream)
+ handle_transport_connect(session, NULL, 0, 0);
+ if (session->req && session->req->stream == stream)
+ handle_unanswered_req(session, stream);
+ /* Remove pending commands for this stream from the queue */
+ cleanup_queue(session, stream);
+ break;
+ default:
+ break;
+ }
+
+ l = stream->callbacks;
+ while (l != NULL) {
+ struct stream_callback *cb = l->data;
+ l = g_slist_next(l);
+ cb->cb(stream, old_state, state, err_ptr, cb->user_data);
+ }
+
+ if (state == AVDTP_STATE_IDLE &&
+ g_slist_find(session->streams, stream)) {
+ session->streams = g_slist_remove(session->streams, stream);
+ stream_free(stream);
+ }
+
+ if (session->io && session->shutdown && session->streams == NULL) {
+ int sock = g_io_channel_unix_get_fd(session->io);
+ shutdown(sock, SHUT_RDWR);
+ }
+}
+
+static void finalize_discovery(struct avdtp *session, int err)
+{
+ struct discover_callback *discover = session->discover;
+ struct avdtp_error avdtp_err;
+
+ if (!discover)
+ return;
+
+ session->discover = NULL;
+
+ avdtp_error_init(&avdtp_err, AVDTP_ERRNO, err);
+
+ if (discover->id > 0)
+ g_source_remove(discover->id);
+
+ if (discover->cb)
+ discover->cb(session, session->seps, err ? &avdtp_err : NULL,
+ discover->user_data);
+ g_free(discover);
+}
+
+static void release_stream(struct avdtp_stream *stream, struct avdtp *session)
+{
+ struct avdtp_local_sep *sep = stream->lsep;
+
+ if (sep->cfm && sep->cfm->abort &&
+ (sep->state != AVDTP_STATE_ABORTING ||
+ stream->abort_int))
+ sep->cfm->abort(session, sep, stream, NULL, sep->user_data);
+
+ avdtp_sep_set_state(session, sep, AVDTP_STATE_IDLE);
+}
+
+static void sep_free(gpointer data)
+{
+ struct avdtp_remote_sep *sep = data;
+
+ g_slist_free_full(sep->caps, g_free);
+ g_free(sep);
+}
+
+static void avdtp_free(void *data)
+{
+ struct avdtp *session = data;
+
+ DBG("%p", session);
+
+ g_slist_free_full(session->streams, stream_free);
+
+ if (session->io) {
+ g_io_channel_shutdown(session->io, FALSE, NULL);
+ g_io_channel_unref(session->io);
+ }
+
+ if (session->io_id) {
+ g_source_remove(session->io_id);
+ session->io_id = 0;
+ }
+
+ if (session->req)
+ pending_req_free(session->req);
+
+ g_slist_free_full(session->req_queue, pending_req_free);
+ g_slist_free_full(session->prio_queue, pending_req_free);
+ g_slist_free_full(session->seps, sep_free);
+ g_slist_free_full(session->disconnect, g_free);
+
+ /* Free copy of the SEP list */
+ session->lseps = NULL;
+
+ g_free(session->buf);
+
+ g_free(session);
+}
+
+static void process_disconnect(void *data)
+{
+ struct disconnect_callback *callback = data;
+
+ callback->cb(callback->user_data);
+
+ g_free(callback);
+}
+
+static void connection_lost(struct avdtp *session, int err)
+{
+ DBG("Disconnected: %s (%d)", strerror(err), err);
+
+ g_slist_foreach(session->streams, (GFunc) release_stream, session);
+ session->streams = NULL;
+
+ avdtp_ref(session);
+
+ finalize_discovery(session, err);
+
+ g_slist_free_full(session->disconnect, process_disconnect);
+ session->disconnect = NULL;
+
+ avdtp_unref(session);
+}
+
+void avdtp_unref(struct avdtp *session)
+{
+ if (!session)
+ return;
+
+ session->ref--;
+
+ DBG("%p: ref=%d", session, session->ref);
+
+ if (session->ref > 0)
+ return;
+
+ finalize_discovery(session, ECONNABORTED);
+
+ avdtp_free(session);
+}
+
+struct avdtp *avdtp_ref(struct avdtp *session)
+{
+ session->ref++;
+
+ DBG("%p: ref=%d", session, session->ref);
+
+ return session;
+}
+
+static bool match_by_seid(const void *data, const void *user_data)
+{
+ const struct avdtp_local_sep *sep = data;
+ uint8_t seid = PTR_TO_UINT(user_data);
+
+ return sep->info.seid == seid;
+}
+
+static struct avdtp_local_sep *find_local_sep_by_seid(struct avdtp *session,
+ uint8_t seid)
+{
+ return queue_find(session->lseps, match_by_seid, INT_TO_PTR(seid));
+}
+
+struct avdtp_remote_sep *avdtp_find_remote_sep(struct avdtp *session,
+ struct avdtp_local_sep *lsep)
+{
+ GSList *l;
+
+ if (lsep->info.inuse)
+ return NULL;
+
+ for (l = session->seps; l != NULL; l = g_slist_next(l)) {
+ struct avdtp_remote_sep *sep = l->data;
+ struct avdtp_service_capability *cap;
+ struct avdtp_media_codec_capability *codec_data;
+
+ /* Type must be different: source <-> sink */
+ if (sep->type == lsep->info.type)
+ continue;
+
+ if (sep->media_type != lsep->info.media_type)
+ continue;
+
+ if (!sep->codec)
+ continue;
+
+ cap = sep->codec;
+ codec_data = (void *) cap->data;
+
+ if (codec_data->media_codec_type != lsep->codec)
+ continue;
+
+ /* FIXME: Add Vendor Specific Codec match to SEP callback */
+ if (lsep->codec == A2DP_CODEC_VENDOR) {
+ a2dp_vendor_codec_t *vndcodec =
+ (void *) codec_data->data;
+
+ if (btohl(vndcodec->vendor_id) != lsep->vndcodec_vendor)
+ continue;
+
+ if (btohs(vndcodec->codec_id) != lsep->vndcodec_codec)
+ continue;
+ }
+
+ if (sep->stream == NULL)
+ return sep;
+ }
+
+ return NULL;
+}
+
+static GSList *caps_to_list(uint8_t *data, int size,
+ struct avdtp_service_capability **codec,
+ gboolean *delay_reporting)
+{
+ GSList *caps;
+ int processed;
+
+ if (delay_reporting)
+ *delay_reporting = FALSE;
+
+ for (processed = 0, caps = NULL; processed + 2 <= size;) {
+ struct avdtp_service_capability *cap;
+ uint8_t length, category;
+
+ category = data[0];
+ length = data[1];
+
+ if (processed + 2 + length > size) {
+ error("Invalid capability data in getcap resp");
+ break;
+ }
+
+ cap = g_malloc(sizeof(struct avdtp_service_capability) +
+ length);
+ memcpy(cap, data, 2 + length);
+
+ processed += 2 + length;
+ data += 2 + length;
+
+ caps = g_slist_append(caps, cap);
+
+ if (category == AVDTP_MEDIA_CODEC &&
+ length >=
+ sizeof(struct avdtp_media_codec_capability))
+ *codec = cap;
+ else if (category == AVDTP_DELAY_REPORTING && delay_reporting)
+ *delay_reporting = TRUE;
+ }
+
+ return caps;
+}
+
+static gboolean avdtp_unknown_cmd(struct avdtp *session, uint8_t transaction,
+ uint8_t signal_id)
+{
+ return avdtp_send(session, transaction, AVDTP_MSG_TYPE_GEN_REJECT,
+ signal_id, NULL, 0);
+}
+
+static void copy_seps(void *data, void *user_data)
+{
+ struct avdtp_local_sep *sep = data;
+ struct seid_info **p = user_data;
+
+ memcpy(*p, &sep->info, sizeof(struct seid_info));
+ *p = *p + 1;
+}
+
+static gboolean avdtp_discover_cmd(struct avdtp *session, uint8_t transaction,
+ void *buf, int size)
+{
+ unsigned int rsp_size, sep_count;
+ struct seid_info *seps, *p;
+ gboolean ret;
+
+ sep_count = queue_length(session->lseps);
+
+ if (sep_count == 0) {
+ uint8_t err = AVDTP_NOT_SUPPORTED_COMMAND;
+ return avdtp_send(session, transaction, AVDTP_MSG_TYPE_REJECT,
+ AVDTP_DISCOVER, &err, sizeof(err));
+ }
+
+ rsp_size = sep_count * sizeof(struct seid_info);
+
+ seps = g_new0(struct seid_info, sep_count);
+ p = seps;
+
+ queue_foreach(session->lseps, copy_seps, &p);
+
+ ret = avdtp_send(session, transaction, AVDTP_MSG_TYPE_ACCEPT,
+ AVDTP_DISCOVER, seps, rsp_size);
+ g_free(seps);
+
+ return ret;
+}
+
+static gboolean avdtp_getcap_cmd(struct avdtp *session, uint8_t transaction,
+ struct seid_req *req, unsigned int size,
+ gboolean get_all)
+{
+ GSList *l, *caps;
+ struct avdtp_local_sep *sep = NULL;
+ unsigned int rsp_size;
+ uint8_t err, buf[1024], *ptr = buf;
+ uint8_t cmd;
+
+ cmd = get_all ? AVDTP_GET_ALL_CAPABILITIES : AVDTP_GET_CAPABILITIES;
+
+ if (size < sizeof(struct seid_req)) {
+ err = AVDTP_BAD_LENGTH;
+ goto failed;
+ }
+
+ sep = find_local_sep_by_seid(session, req->acp_seid);
+ if (!sep) {
+ err = AVDTP_BAD_ACP_SEID;
+ goto failed;
+ }
+
+ if (!sep->ind->get_capability(session, sep, &caps, &err,
+ sep->user_data))
+ goto failed;
+
+ for (l = caps, rsp_size = 0; l != NULL; l = g_slist_next(l)) {
+ struct avdtp_service_capability *cap = l->data;
+
+ if (rsp_size + cap->length + 2 > sizeof(buf))
+ break;
+
+ memcpy(ptr, cap, cap->length + 2);
+ rsp_size += cap->length + 2;
+ ptr += cap->length + 2;
+
+ g_free(cap);
+ }
+
+ if (get_all && sep->delay_reporting) {
+ ptr[0] = AVDTP_DELAY_REPORTING;
+ ptr[1] = 0x00;
+ rsp_size += 2;
+ }
+
+ g_slist_free(caps);
+
+ return avdtp_send(session, transaction, AVDTP_MSG_TYPE_ACCEPT, cmd,
+ buf, rsp_size);
+
+failed:
+ return avdtp_send(session, transaction, AVDTP_MSG_TYPE_REJECT, cmd,
+ &err, sizeof(err));
+}
+
+static void setconf_cb(struct avdtp *session, struct avdtp_stream *stream,
+ struct avdtp_error *err)
+{
+ struct conf_rej rej;
+ struct avdtp_local_sep *sep;
+
+ if (err != NULL) {
+ rej.error = AVDTP_UNSUPPORTED_CONFIGURATION;
+ rej.category = err->err.error_code;
+ avdtp_send(session, session->in.transaction,
+ AVDTP_MSG_TYPE_REJECT, AVDTP_SET_CONFIGURATION,
+ &rej, sizeof(rej));
+ return;
+ }
+
+ if (!avdtp_send(session, session->in.transaction, AVDTP_MSG_TYPE_ACCEPT,
+ AVDTP_SET_CONFIGURATION, NULL, 0)) {
+ stream_free(stream);
+ return;
+ }
+
+ sep = stream->lsep;
+ sep->stream = stream;
+ sep->info.inuse = 1;
+ session->streams = g_slist_append(session->streams, stream);
+
+ avdtp_sep_set_state(session, sep, AVDTP_STATE_CONFIGURED);
+}
+
+static gboolean avdtp_setconf_cmd(struct avdtp *session, uint8_t transaction,
+ struct setconf_req *req, unsigned int size)
+{
+ struct conf_rej rej;
+ struct avdtp_local_sep *sep;
+ struct avdtp_stream *stream;
+ uint8_t err, category = 0x00;
+ GSList *l;
+
+ if (size < sizeof(struct setconf_req)) {
+ error("Too short getcap request");
+ return FALSE;
+ }
+
+ sep = find_local_sep_by_seid(session, req->acp_seid);
+ if (!sep) {
+ err = AVDTP_BAD_ACP_SEID;
+ goto failed;
+ }
+
+ if (sep->stream) {
+ err = AVDTP_SEP_IN_USE;
+ goto failed;
+ }
+
+ stream = g_new0(struct avdtp_stream, 1);
+ stream->session = session;
+ stream->lsep = sep;
+ stream->rseid = req->int_seid;
+ stream->caps = caps_to_list(req->caps,
+ size - sizeof(struct setconf_req),
+ &stream->codec,
+ &stream->delay_reporting);
+
+ /*
+ * Verify that the Media Transport capability's length = 0.
+ * Reject otherwise
+ */
+ for (l = stream->caps; l != NULL; l = g_slist_next(l)) {
+ struct avdtp_service_capability *cap = l->data;
+
+ if (cap->category == AVDTP_MEDIA_TRANSPORT &&
+ cap->length != 0) {
+ err = AVDTP_BAD_MEDIA_TRANSPORT_FORMAT;
+ goto failed_stream;
+ }
+ }
+
+ if (stream->delay_reporting && session->version < 0x0103)
+ session->version = 0x0103;
+
+ if (sep->ind && sep->ind->set_configuration) {
+ if (!sep->ind->set_configuration(session, sep, stream,
+ stream->caps,
+ setconf_cb,
+ sep->user_data)) {
+ err = AVDTP_UNSUPPORTED_CONFIGURATION;
+ category = 0x00;
+ goto failed_stream;
+ }
+ } else {
+ if (!avdtp_send(session, transaction, AVDTP_MSG_TYPE_ACCEPT,
+ AVDTP_SET_CONFIGURATION, NULL, 0)) {
+ stream_free(stream);
+ return FALSE;
+ }
+
+ sep->stream = stream;
+ sep->info.inuse = 1;
+ session->streams = g_slist_append(session->streams, stream);
+
+ avdtp_sep_set_state(session, sep, AVDTP_STATE_CONFIGURED);
+ }
+
+ return TRUE;
+
+failed_stream:
+ stream_free(stream);
+failed:
+ rej.error = err;
+ rej.category = category;
+ return avdtp_send(session, transaction, AVDTP_MSG_TYPE_REJECT,
+ AVDTP_SET_CONFIGURATION, &rej, sizeof(rej));
+}
+
+static gboolean avdtp_getconf_cmd(struct avdtp *session, uint8_t transaction,
+ struct seid_req *req, int size)
+{
+ GSList *l;
+ struct avdtp_local_sep *sep = NULL;
+ int rsp_size;
+ uint8_t err;
+ uint8_t buf[1024];
+ uint8_t *ptr = buf;
+
+ if (size < (int) sizeof(struct seid_req)) {
+ error("Too short getconf request");
+ return FALSE;
+ }
+
+ memset(buf, 0, sizeof(buf));
+
+ sep = find_local_sep_by_seid(session, req->acp_seid);
+ if (!sep) {
+ err = AVDTP_BAD_ACP_SEID;
+ goto failed;
+ }
+ if (!sep->stream || !sep->stream->caps) {
+ err = AVDTP_UNSUPPORTED_CONFIGURATION;
+ goto failed;
+ }
+
+ for (l = sep->stream->caps, rsp_size = 0; l; l = g_slist_next(l)) {
+ struct avdtp_service_capability *cap = l->data;
+
+ if (rsp_size + cap->length + 2 > (int) sizeof(buf))
+ break;
+
+ memcpy(ptr, cap, cap->length + 2);
+ rsp_size += cap->length + 2;
+ ptr += cap->length + 2;
+ }
+
+ return avdtp_send(session, transaction, AVDTP_MSG_TYPE_ACCEPT,
+ AVDTP_GET_CONFIGURATION, buf, rsp_size);
+
+failed:
+ return avdtp_send(session, transaction, AVDTP_MSG_TYPE_REJECT,
+ AVDTP_GET_CONFIGURATION, &err, sizeof(err));
+}
+
+static gboolean avdtp_reconf_cmd(struct avdtp *session, uint8_t transaction,
+ struct seid_req *req, int size)
+{
+ struct conf_rej rej;
+
+ rej.error = AVDTP_NOT_SUPPORTED_COMMAND;
+ rej.category = 0x00;
+
+ return avdtp_send(session, transaction, AVDTP_MSG_TYPE_REJECT,
+ AVDTP_RECONFIGURE, &rej, sizeof(rej));
+}
+
+static void check_seid_collision(struct pending_req *req, uint8_t id)
+{
+ struct seid_req *seid = req->data;
+
+ if (seid->acp_seid == id)
+ req->collided = TRUE;
+}
+
+static void check_start_collision(struct pending_req *req, uint8_t id)
+{
+ struct start_req *start = req->data;
+ struct seid *seid = &start->first_seid;
+ int count = 1 + req->data_size - sizeof(struct start_req);
+ int i;
+
+ for (i = 0; i < count; i++, seid++) {
+ if (seid->seid == id) {
+ req->collided = TRUE;
+ return;
+ }
+ }
+}
+
+static void check_suspend_collision(struct pending_req *req, uint8_t id)
+{
+ struct suspend_req *suspend = req->data;
+ struct seid *seid = &suspend->first_seid;
+ int count = 1 + req->data_size - sizeof(struct suspend_req);
+ int i;
+
+ for (i = 0; i < count; i++, seid++) {
+ if (seid->seid == id) {
+ req->collided = TRUE;
+ return;
+ }
+ }
+}
+
+static void avdtp_check_collision(struct avdtp *session, uint8_t cmd,
+ struct avdtp_stream *stream)
+{
+ struct pending_req *req = session->req;
+
+ if (req == NULL || (req->signal_id != cmd && cmd != AVDTP_ABORT))
+ return;
+
+ if (cmd == AVDTP_ABORT)
+ cmd = req->signal_id;
+
+ switch (cmd) {
+ case AVDTP_OPEN:
+ case AVDTP_CLOSE:
+ check_seid_collision(req, stream->rseid);
+ break;
+ case AVDTP_START:
+ check_start_collision(req, stream->rseid);
+ break;
+ case AVDTP_SUSPEND:
+ check_suspend_collision(req, stream->rseid);
+ break;
+ }
+}
+
+static gboolean avdtp_open_cmd(struct avdtp *session, uint8_t transaction,
+ struct seid_req *req, unsigned int size)
+{
+ struct avdtp_local_sep *sep;
+ struct avdtp_stream *stream;
+ uint8_t err;
+
+ if (size < sizeof(struct seid_req)) {
+ error("Too short abort request");
+ return FALSE;
+ }
+
+ sep = find_local_sep_by_seid(session, req->acp_seid);
+ if (!sep) {
+ err = AVDTP_BAD_ACP_SEID;
+ goto failed;
+ }
+
+ if (sep->state != AVDTP_STATE_CONFIGURED) {
+ err = AVDTP_BAD_STATE;
+ goto failed;
+ }
+
+ stream = sep->stream;
+
+ if (sep->ind && sep->ind->open) {
+ if (!sep->ind->open(session, sep, stream, &err,
+ sep->user_data))
+ goto failed;
+ }
+
+ avdtp_check_collision(session, AVDTP_OPEN, stream);
+
+ if (!avdtp_send(session, transaction, AVDTP_MSG_TYPE_ACCEPT,
+ AVDTP_OPEN, NULL, 0))
+ return FALSE;
+
+ stream->open_acp = TRUE;
+ session->pending_open = stream;
+ stream->timer = g_timeout_add_seconds(REQ_TIMEOUT,
+ stream_open_timeout,
+ stream);
+
+ return TRUE;
+
+failed:
+ return avdtp_send(session, transaction, AVDTP_MSG_TYPE_REJECT,
+ AVDTP_OPEN, &err, sizeof(err));
+}
+
+static gboolean avdtp_start_cmd(struct avdtp *session, uint8_t transaction,
+ struct start_req *req, unsigned int size)
+{
+ struct avdtp_local_sep *sep;
+ struct avdtp_stream *stream;
+ struct stream_rej rej;
+ struct seid *seid;
+ uint8_t err, failed_seid;
+ int seid_count, i;
+
+ if (size < sizeof(struct start_req)) {
+ error("Too short start request");
+ return FALSE;
+ }
+
+ seid_count = 1 + size - sizeof(struct start_req);
+
+ seid = &req->first_seid;
+
+ for (i = 0; i < seid_count; i++, seid++) {
+ failed_seid = seid->seid;
+
+ sep = find_local_sep_by_seid(session, seid->seid);
+ if (!sep || !sep->stream) {
+ err = AVDTP_BAD_ACP_SEID;
+ goto failed;
+ }
+
+ stream = sep->stream;
+
+ /* Also reject start cmd if state is not open */
+ if (sep->state != AVDTP_STATE_OPEN) {
+ err = AVDTP_BAD_STATE;
+ goto failed;
+ }
+ stream->starting = TRUE;
+
+ if (sep->ind && sep->ind->start) {
+ if (!sep->ind->start(session, sep, stream, &err,
+ sep->user_data))
+ goto failed;
+ }
+
+ avdtp_check_collision(session, AVDTP_START, stream);
+
+ avdtp_sep_set_state(session, sep, AVDTP_STATE_STREAMING);
+ }
+
+ return avdtp_send(session, transaction, AVDTP_MSG_TYPE_ACCEPT,
+ AVDTP_START, NULL, 0);
+
+failed:
+ DBG("Rejecting (%d)", err);
+ memset(&rej, 0, sizeof(rej));
+ rej.acp_seid = failed_seid;
+ rej.error = err;
+ return avdtp_send(session, transaction, AVDTP_MSG_TYPE_REJECT,
+ AVDTP_START, &rej, sizeof(rej));
+}
+
+static gboolean avdtp_close_cmd(struct avdtp *session, uint8_t transaction,
+ struct seid_req *req, unsigned int size)
+{
+ struct avdtp_local_sep *sep;
+ struct avdtp_stream *stream;
+ uint8_t err;
+
+ if (size < sizeof(struct seid_req)) {
+ error("Too short close request");
+ return FALSE;
+ }
+
+ sep = find_local_sep_by_seid(session, req->acp_seid);
+ if (!sep || !sep->stream) {
+ err = AVDTP_BAD_ACP_SEID;
+ goto failed;
+ }
+
+ if (sep->state != AVDTP_STATE_OPEN &&
+ sep->state != AVDTP_STATE_STREAMING) {
+ err = AVDTP_BAD_STATE;
+ goto failed;
+ }
+
+ stream = sep->stream;
+
+ if (sep->ind && sep->ind->close) {
+ if (!sep->ind->close(session, sep, stream, &err,
+ sep->user_data))
+ goto failed;
+ }
+
+ avdtp_check_collision(session, AVDTP_CLOSE, stream);
+
+ avdtp_sep_set_state(session, sep, AVDTP_STATE_CLOSING);
+
+ if (!avdtp_send(session, transaction, AVDTP_MSG_TYPE_ACCEPT,
+ AVDTP_CLOSE, NULL, 0))
+ return FALSE;
+
+ stream->timer = g_timeout_add_seconds(REQ_TIMEOUT,
+ stream_close_timeout,
+ stream);
+
+ return TRUE;
+
+failed:
+ return avdtp_send(session, transaction, AVDTP_MSG_TYPE_REJECT,
+ AVDTP_CLOSE, &err, sizeof(err));
+}
+
+static gboolean avdtp_suspend_cmd(struct avdtp *session, uint8_t transaction,
+ struct suspend_req *req, unsigned int size)
+{
+ struct avdtp_local_sep *sep;
+ struct avdtp_stream *stream;
+ struct stream_rej rej;
+ struct seid *seid;
+ uint8_t err, failed_seid;
+ int seid_count, i;
+
+ if (size < sizeof(struct suspend_req)) {
+ error("Too short suspend request");
+ return FALSE;
+ }
+
+ seid_count = 1 + size - sizeof(struct suspend_req);
+
+ seid = &req->first_seid;
+
+ for (i = 0; i < seid_count; i++, seid++) {
+ failed_seid = seid->seid;
+
+ sep = find_local_sep_by_seid(session, seid->seid);
+ if (!sep || !sep->stream) {
+ err = AVDTP_BAD_ACP_SEID;
+ goto failed;
+ }
+
+ stream = sep->stream;
+
+ if (sep->state != AVDTP_STATE_STREAMING) {
+ err = AVDTP_BAD_STATE;
+ goto failed;
+ }
+
+ if (sep->ind && sep->ind->suspend) {
+ if (!sep->ind->suspend(session, sep, stream, &err,
+ sep->user_data))
+ goto failed;
+ }
+
+ avdtp_check_collision(session, AVDTP_SUSPEND, stream);
+
+ avdtp_sep_set_state(session, sep, AVDTP_STATE_OPEN);
+ }
+
+ return avdtp_send(session, transaction, AVDTP_MSG_TYPE_ACCEPT,
+ AVDTP_SUSPEND, NULL, 0);
+
+failed:
+ memset(&rej, 0, sizeof(rej));
+ rej.acp_seid = failed_seid;
+ rej.error = err;
+ return avdtp_send(session, transaction, AVDTP_MSG_TYPE_REJECT,
+ AVDTP_SUSPEND, &rej, sizeof(rej));
+}
+
+static gboolean avdtp_abort_cmd(struct avdtp *session, uint8_t transaction,
+ struct seid_req *req, unsigned int size)
+{
+ struct avdtp_local_sep *sep;
+ uint8_t err;
+ gboolean ret;
+
+ if (size < sizeof(struct seid_req)) {
+ error("Too short abort request");
+ return FALSE;
+ }
+
+ sep = find_local_sep_by_seid(session, req->acp_seid);
+ if (!sep || !sep->stream)
+ return TRUE;
+
+ if (sep->ind && sep->ind->abort)
+ sep->ind->abort(session, sep, sep->stream, &err,
+ sep->user_data);
+
+ avdtp_check_collision(session, AVDTP_ABORT, sep->stream);
+
+ ret = avdtp_send(session, transaction, AVDTP_MSG_TYPE_ACCEPT,
+ AVDTP_ABORT, NULL, 0);
+ if (ret)
+ avdtp_sep_set_state(session, sep, AVDTP_STATE_ABORTING);
+
+ return ret;
+}
+
+static gboolean avdtp_secctl_cmd(struct avdtp *session, uint8_t transaction,
+ struct seid_req *req, int size)
+{
+ return avdtp_unknown_cmd(session, transaction, AVDTP_SECURITY_CONTROL);
+}
+
+static gboolean avdtp_delayreport_cmd(struct avdtp *session,
+ uint8_t transaction,
+ struct delay_req *req,
+ unsigned int size)
+{
+ struct avdtp_local_sep *sep;
+ struct avdtp_stream *stream;
+ uint8_t err;
+
+ if (size < sizeof(struct delay_req)) {
+ error("Too short delay report request");
+ return FALSE;
+ }
+
+ sep = find_local_sep_by_seid(session, req->acp_seid);
+ if (!sep || !sep->stream) {
+ err = AVDTP_BAD_ACP_SEID;
+ goto failed;
+ }
+
+ stream = sep->stream;
+
+ switch (sep->state) {
+ case AVDTP_STATE_IDLE:
+ case AVDTP_STATE_ABORTING:
+ case AVDTP_STATE_CLOSING:
+ err = AVDTP_BAD_STATE;
+ goto failed;
+ case AVDTP_STATE_CONFIGURED:
+ case AVDTP_STATE_OPEN:
+ case AVDTP_STATE_STREAMING:
+ default:
+ break;
+ }
+
+ stream->delay = ntohs(req->delay);
+
+ if (sep->ind && sep->ind->delayreport) {
+ if (!sep->ind->delayreport(session, sep, stream->rseid,
+ stream->delay, &err,
+ sep->user_data))
+ goto failed;
+ }
+
+ return avdtp_send(session, transaction, AVDTP_MSG_TYPE_ACCEPT,
+ AVDTP_DELAY_REPORT, NULL, 0);
+
+failed:
+ return avdtp_send(session, transaction, AVDTP_MSG_TYPE_REJECT,
+ AVDTP_DELAY_REPORT, &err, sizeof(err));
+}
+
+static gboolean avdtp_parse_cmd(struct avdtp *session, uint8_t transaction,
+ uint8_t signal_id, void *buf, int size)
+{
+ switch (signal_id) {
+ case AVDTP_DISCOVER:
+ DBG("Received DISCOVER_CMD");
+ return avdtp_discover_cmd(session, transaction, buf, size);
+ case AVDTP_GET_CAPABILITIES:
+ DBG("Received GET_CAPABILITIES_CMD");
+ return avdtp_getcap_cmd(session, transaction, buf, size,
+ FALSE);
+ case AVDTP_GET_ALL_CAPABILITIES:
+ DBG("Received GET_ALL_CAPABILITIES_CMD");
+ return avdtp_getcap_cmd(session, transaction, buf, size, TRUE);
+ case AVDTP_SET_CONFIGURATION:
+ DBG("Received SET_CONFIGURATION_CMD");
+ return avdtp_setconf_cmd(session, transaction, buf, size);
+ case AVDTP_GET_CONFIGURATION:
+ DBG("Received GET_CONFIGURATION_CMD");
+ return avdtp_getconf_cmd(session, transaction, buf, size);
+ case AVDTP_RECONFIGURE:
+ DBG("Received RECONFIGURE_CMD");
+ return avdtp_reconf_cmd(session, transaction, buf, size);
+ case AVDTP_OPEN:
+ DBG("Received OPEN_CMD");
+ return avdtp_open_cmd(session, transaction, buf, size);
+ case AVDTP_START:
+ DBG("Received START_CMD");
+ return avdtp_start_cmd(session, transaction, buf, size);
+ case AVDTP_CLOSE:
+ DBG("Received CLOSE_CMD");
+ return avdtp_close_cmd(session, transaction, buf, size);
+ case AVDTP_SUSPEND:
+ DBG("Received SUSPEND_CMD");
+ return avdtp_suspend_cmd(session, transaction, buf, size);
+ case AVDTP_ABORT:
+ DBG("Received ABORT_CMD");
+ return avdtp_abort_cmd(session, transaction, buf, size);
+ case AVDTP_SECURITY_CONTROL:
+ DBG("Received SECURITY_CONTROL_CMD");
+ return avdtp_secctl_cmd(session, transaction, buf, size);
+ case AVDTP_DELAY_REPORT:
+ DBG("Received DELAY_REPORT_CMD");
+ return avdtp_delayreport_cmd(session, transaction, buf, size);
+ default:
+ DBG("Received unknown request id %u", signal_id);
+ return avdtp_unknown_cmd(session, transaction, signal_id);
+ }
+}
+
+enum avdtp_parse_result { PARSE_ERROR, PARSE_FRAGMENT, PARSE_SUCCESS };
+
+static enum avdtp_parse_result avdtp_parse_data(struct avdtp *session,
+ void *buf, size_t size)
+{
+ struct avdtp_common_header *header = buf;
+ struct avdtp_single_header *single = (void *) session->buf;
+ struct avdtp_start_header *start = (void *) session->buf;
+ void *payload;
+ gsize payload_size;
+
+ switch (header->packet_type) {
+ case AVDTP_PKT_TYPE_SINGLE:
+ if (size < sizeof(*single)) {
+ error("Received too small single packet (%zu bytes)",
+ size);
+ return PARSE_ERROR;
+ }
+ if (session->in.active) {
+ error("SINGLE: Invalid AVDTP packet fragmentation");
+ return PARSE_ERROR;
+ }
+
+ payload = session->buf + sizeof(*single);
+ payload_size = size - sizeof(*single);
+
+ session->in.active = TRUE;
+ session->in.data_size = 0;
+ session->in.no_of_packets = 1;
+ session->in.transaction = header->transaction;
+ session->in.message_type = header->message_type;
+ session->in.signal_id = single->signal_id;
+
+ break;
+ case AVDTP_PKT_TYPE_START:
+ if (size < sizeof(*start)) {
+ error("Received too small start packet (%zu bytes)",
+ size);
+ return PARSE_ERROR;
+ }
+ if (session->in.active) {
+ error("START: Invalid AVDTP packet fragmentation");
+ return PARSE_ERROR;
+ }
+
+ session->in.active = TRUE;
+ session->in.data_size = 0;
+ session->in.transaction = header->transaction;
+ session->in.message_type = header->message_type;
+ session->in.no_of_packets = start->no_of_packets;
+ session->in.signal_id = start->signal_id;
+
+ payload = session->buf + sizeof(*start);
+ payload_size = size - sizeof(*start);
+
+ break;
+ case AVDTP_PKT_TYPE_CONTINUE:
+ if (size < sizeof(struct avdtp_continue_header)) {
+ error("Received too small continue packet (%zu bytes)",
+ size);
+ return PARSE_ERROR;
+ }
+ if (!session->in.active) {
+ error("CONTINUE: Invalid AVDTP packet fragmentation");
+ return PARSE_ERROR;
+ }
+ if (session->in.transaction != header->transaction) {
+ error("Continue transaction id doesn't match");
+ return PARSE_ERROR;
+ }
+ if (session->in.no_of_packets <= 1) {
+ error("Too few continue packets");
+ return PARSE_ERROR;
+ }
+
+ payload = session->buf + sizeof(struct avdtp_continue_header);
+ payload_size = size - sizeof(struct avdtp_continue_header);
+
+ break;
+ case AVDTP_PKT_TYPE_END:
+ if (size < sizeof(struct avdtp_continue_header)) {
+ error("Received too small end packet (%zu bytes)",
+ size);
+ return PARSE_ERROR;
+ }
+ if (!session->in.active) {
+ error("END: Invalid AVDTP packet fragmentation");
+ return PARSE_ERROR;
+ }
+ if (session->in.transaction != header->transaction) {
+ error("End transaction id doesn't match");
+ return PARSE_ERROR;
+ }
+ if (session->in.no_of_packets > 1) {
+ error("Got an end packet too early");
+ return PARSE_ERROR;
+ }
+
+ payload = session->buf + sizeof(struct avdtp_continue_header);
+ payload_size = size - sizeof(struct avdtp_continue_header);
+
+ break;
+ default:
+ error("Invalid AVDTP packet type 0x%02X", header->packet_type);
+ return PARSE_ERROR;
+ }
+
+ if (session->in.data_size + payload_size >
+ sizeof(session->in.buf)) {
+ error("Not enough incoming buffer space!");
+ return PARSE_ERROR;
+ }
+
+ memcpy(session->in.buf + session->in.data_size, payload, payload_size);
+ session->in.data_size += payload_size;
+
+ if (session->in.no_of_packets > 1) {
+ session->in.no_of_packets--;
+ DBG("Received AVDTP fragment. %d to go",
+ session->in.no_of_packets);
+ return PARSE_FRAGMENT;
+ }
+
+ session->in.active = FALSE;
+
+ return PARSE_SUCCESS;
+}
+
+static gboolean session_cb(GIOChannel *chan, GIOCondition cond,
+ gpointer data)
+{
+ struct avdtp *session = data;
+ struct avdtp_common_header *header;
+ ssize_t size;
+ int fd;
+
+ DBG("");
+
+ if (cond & G_IO_NVAL) {
+ session->io_id = 0;
+
+ return FALSE;
+ }
+
+ header = (void *) session->buf;
+
+ if (cond & (G_IO_HUP | G_IO_ERR))
+ goto failed;
+
+ fd = g_io_channel_unix_get_fd(chan);
+ size = read(fd, session->buf, session->imtu);
+ if (size < 0) {
+ error("IO Channel read error");
+ goto failed;
+ }
+
+ if ((size_t) size < sizeof(struct avdtp_common_header)) {
+ error("Received too small packet (%zu bytes)", size);
+ goto failed;
+ }
+
+ switch (avdtp_parse_data(session, session->buf, size)) {
+ case PARSE_ERROR:
+ goto failed;
+ case PARSE_FRAGMENT:
+ return TRUE;
+ case PARSE_SUCCESS:
+ break;
+ }
+
+ /* Take a reference to protect against callback destroying session */
+ avdtp_ref(session);
+
+ if (session->in.message_type == AVDTP_MSG_TYPE_COMMAND) {
+ if (!avdtp_parse_cmd(session, session->in.transaction,
+ session->in.signal_id,
+ session->in.buf,
+ session->in.data_size)) {
+ error("Unable to handle command. Disconnecting");
+ goto failed;
+ }
+
+ if (session->req && session->req->collided) {
+ DBG("Collision detected");
+ goto next;
+ }
+
+ avdtp_unref(session);
+ return TRUE;
+ }
+
+ if (session->req == NULL) {
+ error("No pending request, ignoring message");
+ avdtp_unref(session);
+ return TRUE;
+ }
+
+ if (header->transaction != session->req->transaction) {
+ error("Transaction label doesn't match");
+ avdtp_unref(session);
+ return TRUE;
+ }
+
+ if (session->in.signal_id != session->req->signal_id) {
+ error("Response signal doesn't match");
+ avdtp_unref(session);
+ return TRUE;
+ }
+
+ g_source_remove(session->req->timeout);
+ session->req->timeout = 0;
+
+ switch (header->message_type) {
+ case AVDTP_MSG_TYPE_ACCEPT:
+ if (!avdtp_parse_resp(session, session->req->stream,
+ session->in.transaction,
+ session->in.signal_id,
+ session->in.buf,
+ session->in.data_size)) {
+ error("Unable to parse accept response");
+ goto failed;
+ }
+ break;
+ case AVDTP_MSG_TYPE_REJECT:
+ if (!avdtp_parse_rej(session, session->req->stream,
+ session->in.transaction,
+ session->in.signal_id,
+ session->in.buf,
+ session->in.data_size)) {
+ error("Unable to parse reject response");
+ goto failed;
+ }
+ break;
+ case AVDTP_MSG_TYPE_GEN_REJECT:
+ error("Received a General Reject message");
+ break;
+ default:
+ error("Unknown message type 0x%02X", header->message_type);
+ break;
+ }
+
+next:
+ pending_req_free(session->req);
+ session->req = NULL;
+
+ if (session->ref > 1)
+ process_queue(session);
+
+ avdtp_unref(session);
+
+ return TRUE;
+
+failed:
+ session->io_id = 0;
+ connection_lost(session, EIO);
+
+ return FALSE;
+}
+
+static int set_priority(int fd, int priority)
+{
+ int err;
+
+ err = setsockopt(fd, SOL_SOCKET, SO_PRIORITY, &priority,
+ sizeof(priority));
+ if (err == 0 || errno == ENOTSOCK)
+ return 0;
+
+ err = -errno;
+ error("setsockopt(SO_PRIORITY): %s (%d)", strerror(-err), -err);
+
+ return err;
+}
+
+struct avdtp *avdtp_new(int fd, size_t imtu, size_t omtu, uint16_t version,
+ struct queue *lseps)
+{
+ struct avdtp *session;
+ GIOCondition cond = G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL;
+ int new_fd;
+
+ if (!lseps)
+ return NULL;
+
+ new_fd = dup(fd);
+ if (new_fd < 0) {
+ error("dup(): %s (%d)", strerror(errno), errno);
+ return NULL;
+ }
+
+ if (set_priority(new_fd, 6) < 0)
+ return NULL;
+
+ session = g_new0(struct avdtp, 1);
+ session->io = g_io_channel_unix_new(new_fd);
+ session->version = version;
+ session->imtu = imtu;
+ session->omtu = omtu;
+ session->buf = g_malloc0(MAX(session->imtu, session->omtu));
+
+ /* This watch should be low priority since otherwise the
+ * connect callback might be dispatched before the session
+ * callback if the kernel wakes us up at the same time for
+ * them. This could happen if a headset is very quick in
+ * sending the Start command after connecting the stream
+ * transport channel.
+ */
+ session->io_id = g_io_add_watch_full(session->io, G_PRIORITY_LOW, cond,
+ (GIOFunc) session_cb, session,
+ NULL);
+
+ session->lseps = lseps;
+
+ return avdtp_ref(session);
+}
+
+unsigned int avdtp_add_disconnect_cb(struct avdtp *session,
+ avdtp_disconnect_cb_t cb,
+ void *user_data)
+{
+ struct disconnect_callback *callback;
+ static unsigned int id = 0;
+
+ callback = g_new0(struct disconnect_callback, 1);
+ callback->id = ++id;
+ callback->cb = cb;
+ callback->user_data = user_data;
+ session->disconnect = g_slist_append(session->disconnect, callback);
+
+ return id;
+}
+
+gboolean avdtp_remove_disconnect_cb(struct avdtp *session, unsigned int id)
+{
+ GSList *l;
+
+ for (l = session->disconnect; l; l = g_slist_next(l)) {
+ struct disconnect_callback *callback = l->data;
+
+ if (callback->id != id)
+ continue;
+
+ session->disconnect = g_slist_remove(session->disconnect,
+ callback);
+ g_free(callback);
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+void avdtp_shutdown(struct avdtp *session)
+{
+ GSList *l;
+ bool aborting = false;
+
+ if (!session->io)
+ return;
+
+ for (l = session->streams; l; l = g_slist_next(l)) {
+ struct avdtp_stream *stream = l->data;
+
+ if (stream->abort_int ||
+ avdtp_close(session, stream, TRUE) == 0)
+ aborting = true;
+ }
+
+ if (aborting) {
+ /* defer shutdown until all streams are aborted properly */
+ session->shutdown = true;
+ } else {
+ int sock = g_io_channel_unix_get_fd(session->io);
+
+ shutdown(sock, SHUT_RDWR);
+ }
+}
+
+static void queue_request(struct avdtp *session, struct pending_req *req,
+ gboolean priority)
+{
+ if (priority)
+ session->prio_queue = g_slist_append(session->prio_queue, req);
+ else
+ session->req_queue = g_slist_append(session->req_queue, req);
+}
+
+static uint8_t req_get_seid(struct pending_req *req)
+{
+ if (req->signal_id == AVDTP_DISCOVER)
+ return 0;
+
+ return ((struct seid_req *) (req->data))->acp_seid;
+}
+
+static int cancel_request(struct avdtp *session, int err)
+{
+ struct pending_req *req;
+ struct seid_req sreq;
+ struct avdtp_local_sep *lsep;
+ struct avdtp_stream *stream;
+ uint8_t seid;
+ struct avdtp_error averr;
+
+ req = session->req;
+ session->req = NULL;
+
+ avdtp_error_init(&averr, AVDTP_ERRNO, err);
+
+ seid = req_get_seid(req);
+ if (seid)
+ stream = find_stream_by_rseid(session, seid);
+ else
+ stream = NULL;
+
+ if (stream) {
+ stream->abort_int = TRUE;
+ lsep = stream->lsep;
+ } else
+ lsep = NULL;
+
+ switch (req->signal_id) {
+ case AVDTP_RECONFIGURE:
+ error("Reconfigure: %s (%d)", strerror(err), err);
+ if (lsep && lsep->cfm && lsep->cfm->reconfigure)
+ lsep->cfm->reconfigure(session, lsep, stream, &averr,
+ lsep->user_data);
+ break;
+ case AVDTP_OPEN:
+ error("Open: %s (%d)", strerror(err), err);
+ if (lsep && lsep->cfm && lsep->cfm->open)
+ lsep->cfm->open(session, lsep, stream, &averr,
+ lsep->user_data);
+ break;
+ case AVDTP_START:
+ error("Start: %s (%d)", strerror(err), err);
+ if (lsep && lsep->cfm && lsep->cfm->start) {
+ lsep->cfm->start(session, lsep, stream, &averr,
+ lsep->user_data);
+ if (stream)
+ stream->starting = FALSE;
+ }
+ break;
+ case AVDTP_SUSPEND:
+ error("Suspend: %s (%d)", strerror(err), err);
+ if (lsep && lsep->cfm && lsep->cfm->suspend)
+ lsep->cfm->suspend(session, lsep, stream, &averr,
+ lsep->user_data);
+ break;
+ case AVDTP_CLOSE:
+ error("Close: %s (%d)", strerror(err), err);
+ if (lsep && lsep->cfm && lsep->cfm->close) {
+ lsep->cfm->close(session, lsep, stream, &averr,
+ lsep->user_data);
+ if (stream)
+ stream->close_int = FALSE;
+ }
+ break;
+ case AVDTP_SET_CONFIGURATION:
+ error("SetConfiguration: %s (%d)", strerror(err), err);
+ if (lsep && lsep->cfm && lsep->cfm->set_configuration)
+ lsep->cfm->set_configuration(session, lsep, stream,
+ &averr, lsep->user_data);
+ goto failed;
+ case AVDTP_DISCOVER:
+ error("Discover: %s (%d)", strerror(err), err);
+ goto failed;
+ case AVDTP_GET_CAPABILITIES:
+ error("GetCapabilities: %s (%d)", strerror(err), err);
+ goto failed;
+ case AVDTP_ABORT:
+ error("Abort: %s (%d)", strerror(err), err);
+ goto failed;
+ }
+
+ if (!stream)
+ goto failed;
+
+ memset(&sreq, 0, sizeof(sreq));
+ sreq.acp_seid = seid;
+
+ err = send_request(session, TRUE, stream, AVDTP_ABORT, &sreq,
+ sizeof(sreq));
+ if (err < 0) {
+ error("Unable to send abort request");
+ goto failed;
+ }
+
+ goto done;
+
+failed:
+ connection_lost(session, err);
+done:
+ pending_req_free(req);
+ return err;
+}
+
+static gboolean request_timeout(gpointer user_data)
+{
+ struct avdtp *session = user_data;
+
+ cancel_request(session, ETIMEDOUT);
+
+ return FALSE;
+}
+
+static int send_req(struct avdtp *session, gboolean priority,
+ struct pending_req *req)
+{
+ static int transaction = 0;
+ int err;
+
+ if (session->req != NULL) {
+ queue_request(session, req, priority);
+ return 0;
+ }
+
+ req->transaction = transaction++;
+ transaction %= 16;
+
+ /* FIXME: Should we retry to send if the buffer
+ was not totally sent or in case of EINTR? */
+ if (!avdtp_send(session, req->transaction, AVDTP_MSG_TYPE_COMMAND,
+ req->signal_id, req->data, req->data_size)) {
+ err = -EIO;
+ goto failed;
+ }
+
+ session->req = req;
+
+ req->timeout = g_timeout_add_seconds(req->signal_id == AVDTP_ABORT ?
+ ABORT_TIMEOUT : REQ_TIMEOUT,
+ request_timeout,
+ session);
+ return 0;
+
+failed:
+ g_free(req->data);
+ g_free(req);
+ return err;
+}
+
+static int send_request(struct avdtp *session, gboolean priority,
+ struct avdtp_stream *stream, uint8_t signal_id,
+ void *buffer, size_t size)
+{
+ struct pending_req *req;
+
+ if (size > 0 && !buffer) {
+ DBG("Invalid buffer %p", buffer);
+ return -EINVAL;
+ }
+
+ if (stream && stream->abort_int && signal_id != AVDTP_ABORT) {
+ DBG("Unable to send requests while aborting");
+ return -EINVAL;
+ }
+
+ req = g_new0(struct pending_req, 1);
+ req->signal_id = signal_id;
+ req->data_size = size;
+ req->stream = stream;
+
+ if (size > 0) {
+ req->data = g_malloc(size);
+ memcpy(req->data, buffer, size);
+ }
+
+ return send_req(session, priority, req);
+}
+
+static gboolean avdtp_discover_resp(struct avdtp *session,
+ struct discover_resp *resp, int size)
+{
+ int sep_count, i;
+ uint8_t getcap_cmd;
+ int ret = 0;
+ gboolean getcap_pending = FALSE;
+
+ if (session->version >= 0x0103)
+ getcap_cmd = AVDTP_GET_ALL_CAPABILITIES;
+ else
+ getcap_cmd = AVDTP_GET_CAPABILITIES;
+
+ sep_count = size / sizeof(struct seid_info);
+
+ for (i = 0; i < sep_count; i++) {
+ struct avdtp_remote_sep *sep;
+ struct avdtp_stream *stream;
+ struct seid_req req;
+
+ DBG("seid %d type %d media %d in use %d",
+ resp->seps[i].seid, resp->seps[i].type,
+ resp->seps[i].media_type, resp->seps[i].inuse);
+
+ stream = find_stream_by_rseid(session, resp->seps[i].seid);
+
+ sep = find_remote_sep(session->seps, resp->seps[i].seid);
+ if (!sep) {
+ if (resp->seps[i].inuse && !stream)
+ continue;
+ sep = g_new0(struct avdtp_remote_sep, 1);
+ session->seps = g_slist_append(session->seps, sep);
+ }
+
+ sep->stream = stream;
+ sep->seid = resp->seps[i].seid;
+ sep->type = resp->seps[i].type;
+ sep->media_type = resp->seps[i].media_type;
+
+ memset(&req, 0, sizeof(req));
+ req.acp_seid = sep->seid;
+
+ ret = send_request(session, TRUE, NULL, getcap_cmd,
+ &req, sizeof(req));
+ if (ret < 0)
+ break;
+ getcap_pending = TRUE;
+ }
+
+ if (!getcap_pending)
+ finalize_discovery(session, -ret);
+
+ return TRUE;
+}
+
+static gboolean avdtp_get_capabilities_resp(struct avdtp *session,
+ struct getcap_resp *resp,
+ unsigned int size)
+{
+ struct avdtp_remote_sep *sep;
+ uint8_t seid;
+
+ /* Check for minimum required packet size includes:
+ * 1. getcap resp header
+ * 2. media transport capability (2 bytes)
+ * 3. media codec capability type + length (2 bytes)
+ * 4. the actual media codec elements
+ * */
+ if (size < (sizeof(struct getcap_resp) + 4 +
+ sizeof(struct avdtp_media_codec_capability))) {
+ error("Too short getcap resp packet");
+ return FALSE;
+ }
+
+ seid = ((struct seid_req *) session->req->data)->acp_seid;
+
+ sep = find_remote_sep(session->seps, seid);
+
+ DBG("seid %d type %d media %d", sep->seid,
+ sep->type, sep->media_type);
+
+ if (sep->caps) {
+ g_slist_free_full(sep->caps, g_free);
+ sep->caps = NULL;
+ sep->codec = NULL;
+ sep->delay_reporting = FALSE;
+ }
+
+ sep->caps = caps_to_list(resp->caps, size - sizeof(struct getcap_resp),
+ &sep->codec, &sep->delay_reporting);
+
+ return TRUE;
+}
+
+static gboolean avdtp_set_configuration_resp(struct avdtp *session,
+ struct avdtp_stream *stream,
+ struct avdtp_single_header *resp,
+ int size)
+{
+ struct avdtp_local_sep *sep = stream->lsep;
+
+ avdtp_sep_set_state(session, sep, AVDTP_STATE_CONFIGURED);
+
+ if (sep->cfm && sep->cfm->set_configuration)
+ sep->cfm->set_configuration(session, sep, stream, NULL,
+ sep->user_data);
+
+ return TRUE;
+}
+
+static gboolean avdtp_reconfigure_resp(struct avdtp *session,
+ struct avdtp_stream *stream,
+ struct avdtp_single_header *resp,
+ int size)
+{
+ return TRUE;
+}
+
+static gboolean avdtp_open_resp(struct avdtp *session,
+ struct avdtp_stream *stream,
+ struct seid_rej *resp, int size)
+{
+ struct avdtp_local_sep *sep = stream->lsep;
+
+ session->pending_open = stream;
+
+ if (!stream->open_acp && sep->cfm && sep->cfm->open)
+ sep->cfm->open(session, sep, stream, NULL, sep->user_data);
+
+ return TRUE;
+}
+
+static gboolean avdtp_start_resp(struct avdtp *session,
+ struct avdtp_stream *stream,
+ struct seid_rej *resp, int size)
+{
+ struct avdtp_local_sep *sep = stream->lsep;
+
+ /* We might be in STREAMING already if both sides send START_CMD at the
+ * same time and the one in SNK role doesn't reject it as it should */
+ if (sep->state != AVDTP_STATE_STREAMING)
+ avdtp_sep_set_state(session, sep, AVDTP_STATE_STREAMING);
+
+ if (sep->cfm && sep->cfm->start)
+ sep->cfm->start(session, sep, stream, NULL, sep->user_data);
+
+ return TRUE;
+}
+
+static gboolean avdtp_close_resp(struct avdtp *session,
+ struct avdtp_stream *stream,
+ struct seid_rej *resp, int size)
+{
+ struct avdtp_local_sep *sep = stream->lsep;
+
+ avdtp_sep_set_state(session, sep, AVDTP_STATE_CLOSING);
+
+ close_stream(stream);
+
+ return TRUE;
+}
+
+static gboolean avdtp_suspend_resp(struct avdtp *session,
+ struct avdtp_stream *stream,
+ void *data, int size)
+{
+ struct avdtp_local_sep *sep = stream->lsep;
+
+ avdtp_sep_set_state(session, sep, AVDTP_STATE_OPEN);
+
+ if (sep->cfm && sep->cfm->suspend)
+ sep->cfm->suspend(session, sep, stream, NULL, sep->user_data);
+
+ return TRUE;
+}
+
+static gboolean avdtp_abort_resp(struct avdtp *session,
+ struct avdtp_stream *stream,
+ struct seid_rej *resp, int size)
+{
+ struct avdtp_local_sep *sep = stream->lsep;
+
+ avdtp_sep_set_state(session, sep, AVDTP_STATE_ABORTING);
+
+ if (sep->cfm && sep->cfm->abort)
+ sep->cfm->abort(session, sep, stream, NULL, sep->user_data);
+
+ avdtp_sep_set_state(session, sep, AVDTP_STATE_IDLE);
+
+ return TRUE;
+}
+
+static gboolean avdtp_delay_report_resp(struct avdtp *session,
+ struct avdtp_stream *stream,
+ void *data, int size)
+{
+ struct avdtp_local_sep *sep = stream->lsep;
+
+ if (sep->cfm && sep->cfm->delay_report)
+ sep->cfm->delay_report(session, sep, stream, NULL,
+ sep->user_data);
+
+ return TRUE;
+}
+
+static gboolean avdtp_parse_resp(struct avdtp *session,
+ struct avdtp_stream *stream,
+ uint8_t transaction, uint8_t signal_id,
+ void *buf, int size)
+{
+ struct pending_req *next;
+ const char *get_all = "";
+
+ if (session->prio_queue)
+ next = session->prio_queue->data;
+ else if (session->req_queue)
+ next = session->req_queue->data;
+ else
+ next = NULL;
+
+ switch (signal_id) {
+ case AVDTP_DISCOVER:
+ DBG("DISCOVER request succeeded");
+ return avdtp_discover_resp(session, buf, size);
+ case AVDTP_GET_ALL_CAPABILITIES:
+ get_all = "ALL_";
+ case AVDTP_GET_CAPABILITIES:
+ DBG("GET_%sCAPABILITIES request succeeded", get_all);
+ if (!avdtp_get_capabilities_resp(session, buf, size))
+ return FALSE;
+ if (!(next && (next->signal_id == AVDTP_GET_CAPABILITIES ||
+ next->signal_id == AVDTP_GET_ALL_CAPABILITIES)))
+ finalize_discovery(session, 0);
+ return TRUE;
+ }
+
+ /* The remaining commands require an existing stream so bail out
+ * here if the stream got unexpectedly disconnected */
+ if (!stream) {
+ DBG("AVDTP: stream was closed while waiting for reply");
+ return TRUE;
+ }
+
+ switch (signal_id) {
+ case AVDTP_SET_CONFIGURATION:
+ DBG("SET_CONFIGURATION request succeeded");
+ return avdtp_set_configuration_resp(session, stream,
+ buf, size);
+ case AVDTP_RECONFIGURE:
+ DBG("RECONFIGURE request succeeded");
+ return avdtp_reconfigure_resp(session, stream, buf, size);
+ case AVDTP_OPEN:
+ DBG("OPEN request succeeded");
+ return avdtp_open_resp(session, stream, buf, size);
+ case AVDTP_SUSPEND:
+ DBG("SUSPEND request succeeded");
+ return avdtp_suspend_resp(session, stream, buf, size);
+ case AVDTP_START:
+ DBG("START request succeeded");
+ return avdtp_start_resp(session, stream, buf, size);
+ case AVDTP_CLOSE:
+ DBG("CLOSE request succeeded");
+ return avdtp_close_resp(session, stream, buf, size);
+ case AVDTP_ABORT:
+ DBG("ABORT request succeeded");
+ return avdtp_abort_resp(session, stream, buf, size);
+ case AVDTP_DELAY_REPORT:
+ DBG("DELAY_REPORT request succeeded");
+ return avdtp_delay_report_resp(session, stream, buf, size);
+ }
+
+ error("Unknown signal id in accept response: %u", signal_id);
+ return TRUE;
+}
+
+static gboolean seid_rej_to_err(struct seid_rej *rej, unsigned int size,
+ struct avdtp_error *err)
+{
+ if (size < sizeof(struct seid_rej)) {
+ error("Too small packet for seid_rej");
+ return FALSE;
+ }
+
+ avdtp_error_init(err, 0x00, rej->error);
+
+ return TRUE;
+}
+
+static gboolean conf_rej_to_err(struct conf_rej *rej, unsigned int size,
+ struct avdtp_error *err)
+{
+ if (size < sizeof(struct conf_rej)) {
+ error("Too small packet for conf_rej");
+ return FALSE;
+ }
+
+ avdtp_error_init(err, rej->category, rej->error);
+
+ return TRUE;
+}
+
+static gboolean stream_rej_to_err(struct stream_rej *rej, unsigned int size,
+ struct avdtp_error *err,
+ uint8_t *acp_seid)
+{
+ if (size < sizeof(struct stream_rej)) {
+ error("Too small packet for stream_rej");
+ return FALSE;
+ }
+
+ avdtp_error_init(err, 0x00, rej->error);
+
+ if (acp_seid)
+ *acp_seid = rej->acp_seid;
+
+ return TRUE;
+}
+
+static gboolean avdtp_parse_rej(struct avdtp *session,
+ struct avdtp_stream *stream,
+ uint8_t transaction, uint8_t signal_id,
+ void *buf, int size)
+{
+ struct avdtp_error err;
+ uint8_t acp_seid;
+ struct avdtp_local_sep *sep = stream ? stream->lsep : NULL;
+
+ switch (signal_id) {
+ case AVDTP_DISCOVER:
+ case AVDTP_GET_CAPABILITIES:
+ case AVDTP_GET_ALL_CAPABILITIES:
+ if (!seid_rej_to_err(buf, size, &err))
+ return FALSE;
+ error("%s request rejected: %s (%d)",
+ signal_id == AVDTP_DISCOVER ? "DISCOVER" :
+ signal_id == AVDTP_GET_CAPABILITIES ?
+ "GET_CAPABILITIES" : "GET_ALL_CAPABILITIES",
+ avdtp_strerror(&err), err.err.error_code);
+ if (session->discover) {
+ session->discover->cb(session, session->seps, &err,
+ session->discover->user_data);
+ g_free(session->discover);
+ session->discover = NULL;
+ }
+ return TRUE;
+ case AVDTP_OPEN:
+ if (!seid_rej_to_err(buf, size, &err))
+ return FALSE;
+ error("OPEN request rejected: %s (%d)",
+ avdtp_strerror(&err), err.err.error_code);
+ if (sep && sep->cfm && sep->cfm->open)
+ sep->cfm->open(session, sep, stream, &err,
+ sep->user_data);
+ return TRUE;
+ case AVDTP_SET_CONFIGURATION:
+ if (!conf_rej_to_err(buf, size, &err))
+ return FALSE;
+ error("SET_CONFIGURATION request rejected: %s (%d)",
+ avdtp_strerror(&err), err.err.error_code);
+ if (sep && sep->cfm && sep->cfm->set_configuration)
+ sep->cfm->set_configuration(session, sep, stream,
+ &err, sep->user_data);
+ return TRUE;
+ case AVDTP_GET_CONFIGURATION:
+ if (!seid_rej_to_err(buf, size, &err))
+ return FALSE;
+ error("GET_CONFIGURATION request rejected: %s (%d)",
+ avdtp_strerror(&err), err.err.error_code);
+ if (sep && sep->cfm && sep->cfm->get_configuration)
+ sep->cfm->get_configuration(session, sep, stream, &err,
+ sep->user_data);
+ return TRUE;
+ case AVDTP_RECONFIGURE:
+ if (!conf_rej_to_err(buf, size, &err))
+ return FALSE;
+ error("RECONFIGURE request rejected: %s (%d)",
+ avdtp_strerror(&err), err.err.error_code);
+ if (sep && sep->cfm && sep->cfm->reconfigure)
+ sep->cfm->reconfigure(session, sep, stream, &err,
+ sep->user_data);
+ return TRUE;
+ case AVDTP_START:
+ if (!stream_rej_to_err(buf, size, &err, &acp_seid))
+ return FALSE;
+ error("START request rejected: %s (%d)",
+ avdtp_strerror(&err), err.err.error_code);
+ if (sep && sep->cfm && sep->cfm->start) {
+ stream->starting = FALSE;
+ sep->cfm->start(session, sep, stream, &err,
+ sep->user_data);
+ }
+ return TRUE;
+ case AVDTP_SUSPEND:
+ if (!stream_rej_to_err(buf, size, &err, &acp_seid))
+ return FALSE;
+ error("SUSPEND request rejected: %s (%d)",
+ avdtp_strerror(&err), err.err.error_code);
+ if (sep && sep->cfm && sep->cfm->suspend)
+ sep->cfm->suspend(session, sep, stream, &err,
+ sep->user_data);
+ return TRUE;
+ case AVDTP_CLOSE:
+ if (!stream_rej_to_err(buf, size, &err, &acp_seid))
+ return FALSE;
+ error("CLOSE request rejected: %s (%d)",
+ avdtp_strerror(&err), err.err.error_code);
+ if (sep && sep->cfm && sep->cfm->close) {
+ sep->cfm->close(session, sep, stream, &err,
+ sep->user_data);
+ stream->close_int = FALSE;
+ }
+ return TRUE;
+ case AVDTP_ABORT:
+ if (!stream_rej_to_err(buf, size, &err, &acp_seid))
+ return FALSE;
+ error("ABORT request rejected: %s (%d)",
+ avdtp_strerror(&err), err.err.error_code);
+ if (sep && sep->cfm && sep->cfm->abort)
+ sep->cfm->abort(session, sep, stream, &err,
+ sep->user_data);
+ return FALSE;
+ case AVDTP_DELAY_REPORT:
+ if (!stream_rej_to_err(buf, size, &err, &acp_seid))
+ return FALSE;
+ error("DELAY_REPORT request rejected: %s (%d)",
+ avdtp_strerror(&err), err.err.error_code);
+ if (sep && sep->cfm && sep->cfm->delay_report)
+ sep->cfm->delay_report(session, sep, stream, &err,
+ sep->user_data);
+ return TRUE;
+ default:
+ error("Unknown reject response signal id: %u", signal_id);
+ return TRUE;
+ }
+}
+
+struct avdtp_service_capability *avdtp_stream_get_codec(
+ struct avdtp_stream *stream)
+{
+ GSList *l;
+
+ for (l = stream->caps; l; l = l->next) {
+ struct avdtp_service_capability *cap = l->data;
+
+ if (cap->category == AVDTP_MEDIA_CODEC)
+ return cap;
+ }
+
+ return NULL;
+}
+
+static gboolean avdtp_stream_has_capability(struct avdtp_stream *stream,
+ struct avdtp_service_capability *cap)
+{
+ GSList *l;
+ struct avdtp_service_capability *stream_cap;
+
+ for (l = stream->caps; l; l = g_slist_next(l)) {
+ stream_cap = l->data;
+
+ if (stream_cap->category != cap->category ||
+ stream_cap->length != cap->length)
+ continue;
+
+ if (memcmp(stream_cap->data, cap->data, cap->length) == 0)
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+gboolean avdtp_stream_has_capabilities(struct avdtp_stream *stream,
+ GSList *caps)
+{
+ for (; caps; caps = g_slist_next(caps)) {
+ struct avdtp_service_capability *cap = caps->data;
+
+ if (!avdtp_stream_has_capability(stream, cap))
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+struct avdtp_remote_sep *avdtp_stream_get_remote_sep(
+ struct avdtp_stream *stream)
+{
+ GSList *l;
+
+ for (l = stream->session->seps; l; l = l->next) {
+ struct avdtp_remote_sep *sep = l->data;
+
+ if (sep->seid == stream->rseid)
+ return sep;
+ }
+
+ return NULL;
+}
+
+gboolean avdtp_stream_set_transport(struct avdtp_stream *stream, int fd,
+ size_t imtu, size_t omtu)
+{
+ GIOChannel *io;
+
+ if (stream != stream->session->pending_open)
+ return FALSE;
+
+ if (set_priority(fd, 5) < 0)
+ return FALSE;
+
+ io = g_io_channel_unix_new(fd);
+
+ handle_transport_connect(stream->session, io, imtu, omtu);
+
+ g_io_channel_unref(io);
+
+ return TRUE;
+}
+
+gboolean avdtp_stream_get_transport(struct avdtp_stream *stream, int *sock,
+ uint16_t *imtu, uint16_t *omtu,
+ GSList **caps)
+{
+ if (stream->io == NULL)
+ return FALSE;
+
+ if (sock)
+ *sock = g_io_channel_unix_get_fd(stream->io);
+
+ if (omtu)
+ *omtu = stream->omtu;
+
+ if (imtu)
+ *imtu = stream->imtu;
+
+ if (caps)
+ *caps = stream->caps;
+
+ return TRUE;
+}
+
+static int process_queue(struct avdtp *session)
+{
+ GSList **queue, *l;
+ struct pending_req *req;
+
+ if (session->req)
+ return 0;
+
+ if (session->prio_queue)
+ queue = &session->prio_queue;
+ else
+ queue = &session->req_queue;
+
+ if (!*queue)
+ return 0;
+
+ l = *queue;
+ req = l->data;
+
+ *queue = g_slist_remove(*queue, req);
+
+ return send_req(session, FALSE, req);
+}
+
+struct avdtp_service_capability *avdtp_get_codec(struct avdtp_remote_sep *sep)
+{
+ return sep->codec;
+}
+
+struct avdtp_service_capability *avdtp_service_cap_new(uint8_t category,
+ const void *data,
+ int length)
+{
+ struct avdtp_service_capability *cap;
+
+ if (category < AVDTP_MEDIA_TRANSPORT ||
+ category > AVDTP_DELAY_REPORTING)
+ return NULL;
+
+ if (length > 0 && !data)
+ return NULL;
+
+ cap = g_malloc(sizeof(struct avdtp_service_capability) + length);
+ cap->category = category;
+ cap->length = length;
+
+ if (length > 0)
+ memcpy(cap->data, data, length);
+
+ return cap;
+}
+
+static gboolean process_discover(gpointer data)
+{
+ struct avdtp *session = data;
+
+ session->discover->id = 0;
+
+ finalize_discovery(session, 0);
+
+ return FALSE;
+}
+
+int avdtp_discover(struct avdtp *session, avdtp_discover_cb_t cb,
+ void *user_data)
+{
+ int err;
+
+ if (session->discover)
+ return -EBUSY;
+
+ session->discover = g_new0(struct discover_callback, 1);
+
+ if (session->seps) {
+ session->discover->cb = cb;
+ session->discover->user_data = user_data;
+ session->discover->id = g_idle_add(process_discover, session);
+ return 0;
+ }
+
+ err = send_request(session, FALSE, NULL, AVDTP_DISCOVER, NULL, 0);
+ if (err == 0) {
+ session->discover->cb = cb;
+ session->discover->user_data = user_data;
+ }
+
+ return err;
+}
+
+gboolean avdtp_stream_remove_cb(struct avdtp *session,
+ struct avdtp_stream *stream,
+ unsigned int id)
+{
+ GSList *l;
+ struct stream_callback *cb;
+
+ if (!stream)
+ return FALSE;
+
+ for (cb = NULL, l = stream->callbacks; l != NULL; l = l->next) {
+ struct stream_callback *tmp = l->data;
+ if (tmp && tmp->id == id) {
+ cb = tmp;
+ break;
+ }
+ }
+
+ if (!cb)
+ return FALSE;
+
+ stream->callbacks = g_slist_remove(stream->callbacks, cb);
+ g_free(cb);
+
+ return TRUE;
+}
+
+unsigned int avdtp_stream_add_cb(struct avdtp *session,
+ struct avdtp_stream *stream,
+ avdtp_stream_state_cb cb, void *data)
+{
+ struct stream_callback *stream_cb;
+ static unsigned int id = 0;
+
+ stream_cb = g_new(struct stream_callback, 1);
+ stream_cb->cb = cb;
+ stream_cb->user_data = data;
+ stream_cb->id = ++id;
+
+ stream->callbacks = g_slist_append(stream->callbacks, stream_cb);
+
+ return stream_cb->id;
+}
+
+int avdtp_get_configuration(struct avdtp *session, struct avdtp_stream *stream)
+{
+ struct seid_req req;
+
+ memset(&req, 0, sizeof(req));
+ req.acp_seid = stream->rseid;
+
+ return send_request(session, FALSE, stream, AVDTP_GET_CONFIGURATION,
+ &req, sizeof(req));
+}
+
+static void copy_capabilities(gpointer data, gpointer user_data)
+{
+ struct avdtp_service_capability *src_cap = data;
+ struct avdtp_service_capability *dst_cap;
+ GSList **l = user_data;
+
+ dst_cap = avdtp_service_cap_new(src_cap->category, src_cap->data,
+ src_cap->length);
+
+ *l = g_slist_append(*l, dst_cap);
+}
+
+int avdtp_set_configuration(struct avdtp *session,
+ struct avdtp_remote_sep *rsep,
+ struct avdtp_local_sep *lsep,
+ GSList *caps,
+ struct avdtp_stream **stream)
+{
+ struct setconf_req *req;
+ struct avdtp_stream *new_stream;
+ unsigned char *ptr;
+ int err, caps_len;
+ struct avdtp_service_capability *cap;
+ GSList *l;
+
+ if (!(lsep && rsep))
+ return -EINVAL;
+
+ DBG("%p: int_seid=%u, acp_seid=%u", session,
+ lsep->info.seid, rsep->seid);
+
+ new_stream = g_new0(struct avdtp_stream, 1);
+ new_stream->session = session;
+ new_stream->lsep = lsep;
+ new_stream->rseid = rsep->seid;
+
+ if (rsep->delay_reporting && lsep->delay_reporting) {
+ struct avdtp_service_capability *delay_reporting;
+
+ delay_reporting = avdtp_service_cap_new(AVDTP_DELAY_REPORTING,
+ NULL, 0);
+ caps = g_slist_append(caps, delay_reporting);
+ new_stream->delay_reporting = TRUE;
+ }
+
+ g_slist_foreach(caps, copy_capabilities, &new_stream->caps);
+
+ /* Calculate total size of request */
+ for (l = caps, caps_len = 0; l != NULL; l = g_slist_next(l)) {
+ cap = l->data;
+ caps_len += cap->length + 2;
+ }
+
+ req = g_malloc0(sizeof(struct setconf_req) + caps_len);
+
+ req->int_seid = lsep->info.seid;
+ req->acp_seid = rsep->seid;
+
+ /* Copy the capabilities into the request */
+ for (l = caps, ptr = req->caps; l != NULL; l = g_slist_next(l)) {
+ cap = l->data;
+ memcpy(ptr, cap, cap->length + 2);
+ ptr += cap->length + 2;
+ }
+
+ err = send_request(session, FALSE, new_stream,
+ AVDTP_SET_CONFIGURATION, req,
+ sizeof(struct setconf_req) + caps_len);
+ if (err < 0)
+ stream_free(new_stream);
+ else {
+ lsep->info.inuse = 1;
+ lsep->stream = new_stream;
+ rsep->stream = new_stream;
+ session->streams = g_slist_append(session->streams, new_stream);
+ if (stream)
+ *stream = new_stream;
+ }
+
+ g_free(req);
+
+ return err;
+}
+
+int avdtp_open(struct avdtp *session, struct avdtp_stream *stream)
+{
+ struct seid_req req;
+
+ if (!g_slist_find(session->streams, stream))
+ return -EINVAL;
+
+ if (stream->lsep->state > AVDTP_STATE_CONFIGURED)
+ return -EINVAL;
+
+ memset(&req, 0, sizeof(req));
+ req.acp_seid = stream->rseid;
+
+ return send_request(session, FALSE, stream, AVDTP_OPEN,
+ &req, sizeof(req));
+}
+
+static gboolean start_timeout(gpointer user_data)
+{
+ struct avdtp_stream *stream = user_data;
+ struct avdtp *session = stream->session;
+
+ stream->open_acp = FALSE;
+
+ if (avdtp_start(session, stream) < 0)
+ error("wait_timeout: avdtp_start failed");
+
+ stream->start_timer = 0;
+
+ return FALSE;
+}
+
+int avdtp_start(struct avdtp *session, struct avdtp_stream *stream)
+{
+ struct start_req req;
+ int ret;
+
+ if (!g_slist_find(session->streams, stream))
+ return -EINVAL;
+
+ if (stream->lsep->state != AVDTP_STATE_OPEN)
+ return -EINVAL;
+
+ /* Recommendation 12:
+ * If the RD has configured and opened a stream it is also responsible
+ * to start the streaming via GAVDP_START.
+ */
+ if (stream->open_acp) {
+ /* If timer already active wait it */
+ if (stream->start_timer)
+ return 0;
+
+ stream->start_timer = g_timeout_add_seconds(START_TIMEOUT,
+ start_timeout,
+ stream);
+ return 0;
+ }
+
+ if (stream->close_int == TRUE) {
+ error("avdtp_start: rejecting start since close is initiated");
+ return -EINVAL;
+ }
+
+ if (stream->starting == TRUE) {
+ DBG("stream already started");
+ return -EINPROGRESS;
+ }
+
+ memset(&req, 0, sizeof(req));
+ req.first_seid.seid = stream->rseid;
+
+ ret = send_request(session, FALSE, stream, AVDTP_START,
+ &req, sizeof(req));
+ if (ret == 0)
+ stream->starting = TRUE;
+
+ return ret;
+}
+
+int avdtp_close(struct avdtp *session, struct avdtp_stream *stream,
+ gboolean immediate)
+{
+ struct seid_req req;
+ int ret;
+
+ if (!g_slist_find(session->streams, stream))
+ return -EINVAL;
+
+ if (stream->close_int == TRUE) {
+ error("avdtp_close: rejecting since close is already initiated");
+ return -EINVAL;
+ }
+
+ /* If stream is not yet in the OPEN state, let's use ABORT_CMD */
+ if (stream->lsep->state < AVDTP_STATE_OPEN)
+ return avdtp_abort(session, stream);
+
+ if (immediate && session->req && stream == session->req->stream)
+ return avdtp_abort(session, stream);
+
+ memset(&req, 0, sizeof(req));
+ req.acp_seid = stream->rseid;
+
+ ret = send_request(session, FALSE, stream, AVDTP_CLOSE,
+ &req, sizeof(req));
+ if (ret == 0)
+ stream->close_int = TRUE;
+
+ return ret;
+}
+
+int avdtp_suspend(struct avdtp *session, struct avdtp_stream *stream)
+{
+ struct seid_req req;
+
+ if (!g_slist_find(session->streams, stream))
+ return -EINVAL;
+
+ if (stream->lsep->state <= AVDTP_STATE_OPEN || stream->close_int)
+ return -EINVAL;
+
+ memset(&req, 0, sizeof(req));
+ req.acp_seid = stream->rseid;
+
+ return send_request(session, FALSE, stream, AVDTP_SUSPEND,
+ &req, sizeof(req));
+}
+
+int avdtp_abort(struct avdtp *session, struct avdtp_stream *stream)
+{
+ struct seid_req req;
+ int ret;
+
+ if (!g_slist_find(session->streams, stream))
+ return -EINVAL;
+
+ if (stream->lsep->state == AVDTP_STATE_ABORTING)
+ return -EINVAL;
+
+ if (session->req && session->req->timeout > 0 &&
+ stream == session->req->stream)
+ return cancel_request(session, ECANCELED);
+
+ memset(&req, 0, sizeof(req));
+ req.acp_seid = stream->rseid;
+
+ ret = send_request(session, TRUE, stream, AVDTP_ABORT,
+ &req, sizeof(req));
+ if (ret == 0)
+ stream->abort_int = TRUE;
+
+ return ret;
+}
+
+int avdtp_delay_report(struct avdtp *session, struct avdtp_stream *stream,
+ uint16_t delay)
+{
+ struct delay_req req;
+
+ if (!g_slist_find(session->streams, stream))
+ return -EINVAL;
+
+ if (stream->lsep->state != AVDTP_STATE_CONFIGURED &&
+ stream->lsep->state != AVDTP_STATE_STREAMING)
+ return -EINVAL;
+
+ if (!stream->delay_reporting || session->version < 0x0103)
+ return -EINVAL;
+
+ stream->delay = delay;
+
+ memset(&req, 0, sizeof(req));
+ req.acp_seid = stream->rseid;
+ req.delay = htons(delay);
+
+ return send_request(session, TRUE, stream, AVDTP_DELAY_REPORT,
+ &req, sizeof(req));
+}
+
+struct avdtp_local_sep *avdtp_register_sep(struct queue *lseps, uint8_t type,
+ uint8_t media_type,
+ uint8_t codec_type,
+ gboolean delay_reporting,
+ struct avdtp_sep_ind *ind,
+ struct avdtp_sep_cfm *cfm,
+ void *user_data)
+{
+ struct avdtp_local_sep *sep;
+ uint8_t seid = util_get_uid(&seids, MAX_SEID);
+
+ if (!seid)
+ return NULL;
+
+ sep = g_new0(struct avdtp_local_sep, 1);
+
+ sep->state = AVDTP_STATE_IDLE;
+ sep->info.seid = seid;
+ sep->info.type = type;
+ sep->info.media_type = media_type;
+ sep->codec = codec_type;
+ sep->ind = ind;
+ sep->cfm = cfm;
+ sep->user_data = user_data;
+ sep->delay_reporting = delay_reporting;
+
+ DBG("SEP %p registered: type:%d codec:%d seid:%d", sep,
+ sep->info.type, sep->codec, sep->info.seid);
+
+ queue_push_tail(lseps, sep);
+
+ return sep;
+}
+
+void avdtp_sep_set_vendor_codec(struct avdtp_local_sep *sep, uint32_t vendor_id,
+ uint16_t codec_id)
+{
+ sep->vndcodec_vendor = vendor_id;
+ sep->vndcodec_codec = codec_id;
+}
+
+int avdtp_unregister_sep(struct queue *lseps, struct avdtp_local_sep *sep)
+{
+ if (!sep)
+ return -EINVAL;
+
+ if (sep->stream)
+ release_stream(sep->stream, sep->stream->session);
+
+ DBG("SEP %p unregistered: type:%d codec:%d seid:%d", sep,
+ sep->info.type, sep->codec, sep->info.seid);
+
+ util_clear_uid(&seids, sep->info.seid);
+ queue_remove(lseps, sep);
+ g_free(sep);
+
+ return 0;
+}
+
+const char *avdtp_strerror(struct avdtp_error *err)
+{
+ if (err->category == AVDTP_ERRNO)
+ return strerror(err->err.posix_errno);
+
+ switch (err->err.error_code) {
+ case AVDTP_BAD_HEADER_FORMAT:
+ return "Bad Header Format";
+ case AVDTP_BAD_LENGTH:
+ return "Bad Packet Length";
+ case AVDTP_BAD_ACP_SEID:
+ return "Bad Acceptor SEID";
+ case AVDTP_SEP_IN_USE:
+ return "Stream End Point in Use";
+ case AVDTP_SEP_NOT_IN_USE:
+ return "Stream End Point Not in Use";
+ case AVDTP_BAD_SERV_CATEGORY:
+ return "Bad Service Category";
+ case AVDTP_BAD_PAYLOAD_FORMAT:
+ return "Bad Payload format";
+ case AVDTP_NOT_SUPPORTED_COMMAND:
+ return "Command Not Supported";
+ case AVDTP_INVALID_CAPABILITIES:
+ return "Invalid Capabilities";
+ case AVDTP_BAD_RECOVERY_TYPE:
+ return "Bad Recovery Type";
+ case AVDTP_BAD_MEDIA_TRANSPORT_FORMAT:
+ return "Bad Media Transport Format";
+ case AVDTP_BAD_RECOVERY_FORMAT:
+ return "Bad Recovery Format";
+ case AVDTP_BAD_ROHC_FORMAT:
+ return "Bad Header Compression Format";
+ case AVDTP_BAD_CP_FORMAT:
+ return "Bad Content Protection Format";
+ case AVDTP_BAD_MULTIPLEXING_FORMAT:
+ return "Bad Multiplexing Format";
+ case AVDTP_UNSUPPORTED_CONFIGURATION:
+ return "Configuration not supported";
+ case AVDTP_BAD_STATE:
+ return "Bad State";
+ default:
+ return "Unknown error";
+ }
+}
+
+avdtp_state_t avdtp_sep_get_state(struct avdtp_local_sep *sep)
+{
+ return sep->state;
+}
+
+gboolean avdtp_has_stream(struct avdtp *session, struct avdtp_stream *stream)
+{
+ return g_slist_find(session->streams, stream) ? TRUE : FALSE;
+}
diff --git a/repo/android/avdtp.h b/repo/android/avdtp.h
new file mode 100644
index 0000000..07516a8
--- /dev/null
+++ b/repo/android/avdtp.h
@@ -0,0 +1,291 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2006-2010 Nokia Corporation
+ * Copyright (C) 2004-2010 Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+struct avdtp;
+struct avdtp_stream;
+struct avdtp_local_sep;
+struct avdtp_remote_sep;
+struct avdtp_error {
+ uint8_t category;
+ union {
+ uint8_t error_code;
+ int posix_errno;
+ } err;
+};
+
+#define AVDTP_PSM 25
+
+/* SEP capability categories */
+#define AVDTP_MEDIA_TRANSPORT 0x01
+#define AVDTP_REPORTING 0x02
+#define AVDTP_RECOVERY 0x03
+#define AVDTP_CONTENT_PROTECTION 0x04
+#define AVDTP_HEADER_COMPRESSION 0x05
+#define AVDTP_MULTIPLEXING 0x06
+#define AVDTP_MEDIA_CODEC 0x07
+#define AVDTP_DELAY_REPORTING 0x08
+#define AVDTP_ERRNO 0xff
+
+/* AVDTP error definitions */
+#define AVDTP_BAD_HEADER_FORMAT 0x01
+#define AVDTP_BAD_LENGTH 0x11
+#define AVDTP_BAD_ACP_SEID 0x12
+#define AVDTP_SEP_IN_USE 0x13
+#define AVDTP_SEP_NOT_IN_USE 0x14
+#define AVDTP_BAD_SERV_CATEGORY 0x17
+#define AVDTP_BAD_PAYLOAD_FORMAT 0x18
+#define AVDTP_NOT_SUPPORTED_COMMAND 0x19
+#define AVDTP_INVALID_CAPABILITIES 0x1A
+#define AVDTP_BAD_RECOVERY_TYPE 0x22
+#define AVDTP_BAD_MEDIA_TRANSPORT_FORMAT 0x23
+#define AVDTP_BAD_RECOVERY_FORMAT 0x25
+#define AVDTP_BAD_ROHC_FORMAT 0x26
+#define AVDTP_BAD_CP_FORMAT 0x27
+#define AVDTP_BAD_MULTIPLEXING_FORMAT 0x28
+#define AVDTP_UNSUPPORTED_CONFIGURATION 0x29
+#define AVDTP_BAD_STATE 0x31
+
+/* SEP types definitions */
+#define AVDTP_SEP_TYPE_SOURCE 0x00
+#define AVDTP_SEP_TYPE_SINK 0x01
+
+/* Media types definitions */
+#define AVDTP_MEDIA_TYPE_AUDIO 0x00
+#define AVDTP_MEDIA_TYPE_VIDEO 0x01
+#define AVDTP_MEDIA_TYPE_MULTIMEDIA 0x02
+
+typedef enum {
+ AVDTP_STATE_IDLE,
+ AVDTP_STATE_CONFIGURED,
+ AVDTP_STATE_OPEN,
+ AVDTP_STATE_STREAMING,
+ AVDTP_STATE_CLOSING,
+ AVDTP_STATE_ABORTING,
+} avdtp_state_t;
+
+struct avdtp_service_capability {
+ uint8_t category;
+ uint8_t length;
+ uint8_t data[0];
+} __attribute__ ((packed));
+
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+
+struct avdtp_media_codec_capability {
+ uint8_t rfa0:4;
+ uint8_t media_type:4;
+ uint8_t media_codec_type;
+ uint8_t data[0];
+} __attribute__ ((packed));
+
+#elif __BYTE_ORDER == __BIG_ENDIAN
+
+struct avdtp_media_codec_capability {
+ uint8_t media_type:4;
+ uint8_t rfa0:4;
+ uint8_t media_codec_type;
+ uint8_t data[0];
+} __attribute__ ((packed));
+
+#else
+#error "Unknown byte order"
+#endif
+
+typedef void (*avdtp_stream_state_cb) (struct avdtp_stream *stream,
+ avdtp_state_t old_state,
+ avdtp_state_t new_state,
+ struct avdtp_error *err,
+ void *user_data);
+
+typedef void (*avdtp_set_configuration_cb) (struct avdtp *session,
+ struct avdtp_stream *stream,
+ struct avdtp_error *err);
+
+/* Callbacks for when a reply is received to a command that we sent */
+struct avdtp_sep_cfm {
+ void (*set_configuration) (struct avdtp *session,
+ struct avdtp_local_sep *lsep,
+ struct avdtp_stream *stream,
+ struct avdtp_error *err,
+ void *user_data);
+ void (*get_configuration) (struct avdtp *session,
+ struct avdtp_local_sep *lsep,
+ struct avdtp_stream *stream,
+ struct avdtp_error *err,
+ void *user_data);
+ void (*open) (struct avdtp *session, struct avdtp_local_sep *lsep,
+ struct avdtp_stream *stream, struct avdtp_error *err,
+ void *user_data);
+ void (*start) (struct avdtp *session, struct avdtp_local_sep *lsep,
+ struct avdtp_stream *stream, struct avdtp_error *err,
+ void *user_data);
+ void (*suspend) (struct avdtp *session, struct avdtp_local_sep *lsep,
+ struct avdtp_stream *stream,
+ struct avdtp_error *err, void *user_data);
+ void (*close) (struct avdtp *session, struct avdtp_local_sep *lsep,
+ struct avdtp_stream *stream,
+ struct avdtp_error *err, void *user_data);
+ void (*abort) (struct avdtp *session, struct avdtp_local_sep *lsep,
+ struct avdtp_stream *stream,
+ struct avdtp_error *err, void *user_data);
+ void (*reconfigure) (struct avdtp *session,
+ struct avdtp_local_sep *lsep,
+ struct avdtp_stream *stream,
+ struct avdtp_error *err, void *user_data);
+ void (*delay_report) (struct avdtp *session, struct avdtp_local_sep *lsep,
+ struct avdtp_stream *stream,
+ struct avdtp_error *err, void *user_data);
+};
+
+/*
+ * Callbacks for indicating when we received a new command. The return value
+ * indicates whether the command should be rejected or accepted
+ */
+struct avdtp_sep_ind {
+ gboolean (*get_capability) (struct avdtp *session,
+ struct avdtp_local_sep *sep,
+ GSList **caps, uint8_t *err,
+ void *user_data);
+ gboolean (*set_configuration) (struct avdtp *session,
+ struct avdtp_local_sep *lsep,
+ struct avdtp_stream *stream,
+ GSList *caps,
+ avdtp_set_configuration_cb cb,
+ void *user_data);
+ gboolean (*get_configuration) (struct avdtp *session,
+ struct avdtp_local_sep *lsep,
+ uint8_t *err, void *user_data);
+ gboolean (*open) (struct avdtp *session, struct avdtp_local_sep *lsep,
+ struct avdtp_stream *stream, uint8_t *err,
+ void *user_data);
+ gboolean (*start) (struct avdtp *session, struct avdtp_local_sep *lsep,
+ struct avdtp_stream *stream, uint8_t *err,
+ void *user_data);
+ gboolean (*suspend) (struct avdtp *session,
+ struct avdtp_local_sep *sep,
+ struct avdtp_stream *stream, uint8_t *err,
+ void *user_data);
+ gboolean (*close) (struct avdtp *session, struct avdtp_local_sep *sep,
+ struct avdtp_stream *stream, uint8_t *err,
+ void *user_data);
+ void (*abort) (struct avdtp *session, struct avdtp_local_sep *sep,
+ struct avdtp_stream *stream, uint8_t *err,
+ void *user_data);
+ gboolean (*reconfigure) (struct avdtp *session,
+ struct avdtp_local_sep *lsep,
+ uint8_t *err, void *user_data);
+ gboolean (*delayreport) (struct avdtp *session,
+ struct avdtp_local_sep *lsep,
+ uint8_t rseid, uint16_t delay,
+ uint8_t *err, void *user_data);
+};
+
+typedef void (*avdtp_discover_cb_t) (struct avdtp *session, GSList *seps,
+ struct avdtp_error *err, void *user_data);
+typedef void (*avdtp_disconnect_cb_t) (void *user_data);
+
+struct avdtp *avdtp_new(int fd, size_t imtu, size_t omtu, uint16_t version,
+ struct queue *lseps);
+
+unsigned int avdtp_add_disconnect_cb(struct avdtp *session,
+ avdtp_disconnect_cb_t cb,
+ void *user_data);
+gboolean avdtp_remove_disconnect_cb(struct avdtp *session, unsigned int id);
+
+void avdtp_shutdown(struct avdtp *session);
+
+void avdtp_unref(struct avdtp *session);
+struct avdtp *avdtp_ref(struct avdtp *session);
+
+struct avdtp_service_capability *avdtp_service_cap_new(uint8_t category,
+ const void *data,
+ int size);
+
+struct avdtp_service_capability *avdtp_get_codec(struct avdtp_remote_sep *sep);
+
+int avdtp_discover(struct avdtp *session, avdtp_discover_cb_t cb,
+ void *user_data);
+
+gboolean avdtp_has_stream(struct avdtp *session, struct avdtp_stream *stream);
+
+unsigned int avdtp_stream_add_cb(struct avdtp *session,
+ struct avdtp_stream *stream,
+ avdtp_stream_state_cb cb, void *data);
+gboolean avdtp_stream_remove_cb(struct avdtp *session,
+ struct avdtp_stream *stream,
+ unsigned int id);
+
+gboolean avdtp_stream_set_transport(struct avdtp_stream *stream, int fd,
+ size_t imtu, size_t omtu);
+gboolean avdtp_stream_get_transport(struct avdtp_stream *stream, int *sock,
+ uint16_t *imtu, uint16_t *omtu,
+ GSList **caps);
+struct avdtp_service_capability *avdtp_stream_get_codec(
+ struct avdtp_stream *stream);
+gboolean avdtp_stream_has_capabilities(struct avdtp_stream *stream,
+ GSList *caps);
+struct avdtp_remote_sep *avdtp_stream_get_remote_sep(
+ struct avdtp_stream *stream);
+
+int avdtp_set_configuration(struct avdtp *session,
+ struct avdtp_remote_sep *rsep,
+ struct avdtp_local_sep *lsep,
+ GSList *caps,
+ struct avdtp_stream **stream);
+
+int avdtp_get_configuration(struct avdtp *session,
+ struct avdtp_stream *stream);
+
+int avdtp_open(struct avdtp *session, struct avdtp_stream *stream);
+int avdtp_start(struct avdtp *session, struct avdtp_stream *stream);
+int avdtp_suspend(struct avdtp *session, struct avdtp_stream *stream);
+int avdtp_close(struct avdtp *session, struct avdtp_stream *stream,
+ gboolean immediate);
+int avdtp_abort(struct avdtp *session, struct avdtp_stream *stream);
+int avdtp_delay_report(struct avdtp *session, struct avdtp_stream *stream,
+ uint16_t delay);
+
+struct avdtp_local_sep *avdtp_register_sep(struct queue *lseps, uint8_t type,
+ uint8_t media_type,
+ uint8_t codec_type,
+ gboolean delay_reporting,
+ struct avdtp_sep_ind *ind,
+ struct avdtp_sep_cfm *cfm,
+ void *user_data);
+void avdtp_sep_set_vendor_codec(struct avdtp_local_sep *sep, uint32_t vendor_id,
+ uint16_t codec_id);
+
+/* Find a matching pair of local and remote SEP ID's */
+struct avdtp_remote_sep *avdtp_find_remote_sep(struct avdtp *session,
+ struct avdtp_local_sep *lsep);
+
+int avdtp_unregister_sep(struct queue *lseps, struct avdtp_local_sep *sep);
+
+avdtp_state_t avdtp_sep_get_state(struct avdtp_local_sep *sep);
+
+void avdtp_error_init(struct avdtp_error *err, uint8_t type, int id);
+const char *avdtp_strerror(struct avdtp_error *err);
+uint8_t avdtp_error_category(struct avdtp_error *err);
+int avdtp_error_error_code(struct avdtp_error *err);
+int avdtp_error_posix_errno(struct avdtp_error *err);
diff --git a/repo/android/avdtptest.c b/repo/android/avdtptest.c
new file mode 100644
index 0000000..ce34443
--- /dev/null
+++ b/repo/android/avdtptest.c
@@ -0,0 +1,909 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2014 Intel Corporation
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <getopt.h>
+#include <unistd.h>
+#include <stdbool.h>
+#include <errno.h>
+
+#include <glib.h>
+
+#include "lib/bluetooth.h"
+#include "lib/hci.h"
+#include "lib/hci_lib.h"
+
+#include "btio/btio.h"
+#include "src/shared/util.h"
+#include "src/shared/queue.h"
+#include "avdtp.h"
+
+static GMainLoop *mainloop = NULL;
+static int dev_role = AVDTP_SEP_TYPE_SOURCE;
+static bool preconf = false;
+static struct avdtp *avdtp = NULL;
+struct avdtp_stream *avdtp_stream = NULL;
+struct avdtp_local_sep *local_sep = NULL;
+struct avdtp_remote_sep *remote_sep = NULL;
+static GIOChannel *io = NULL;
+static bool reject = false;
+static bdaddr_t src;
+static bdaddr_t dst;
+static uint16_t version = 0x0103;
+static guint media_player = 0;
+static guint media_recorder = 0;
+static guint idle_id = 0;
+static struct queue *lseps = NULL;
+
+static bool fragment = false;
+
+static enum {
+ CMD_GET_CONF,
+ CMD_OPEN,
+ CMD_START,
+ CMD_SUSPEND,
+ CMD_CLOSE,
+ CMD_ABORT,
+ CMD_DELAY,
+ CMD_NONE,
+} command = CMD_NONE;
+
+static const char sbc_codec[] = {0x00, 0x00, 0x11, 0x15, 0x02, 0x40};
+static const char sbc_media_frame[] = {
+ 0x00, 0x60, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
+ 0x01, 0x9c, 0xfd, 0x40, 0xbd, 0xde, 0xa9, 0x75, 0x43, 0x20, 0x87, 0x64,
+ 0x44, 0x32, 0x7f, 0xbe, 0xf7, 0x76, 0xfe, 0xf7, 0xbb, 0xbb, 0x7f, 0xbe,
+ 0xf7, 0x76, 0xfe, 0xf7, 0xbb, 0xbb, 0x7f, 0xbe, 0xf7, 0x76, 0xfe, 0xf7,
+ 0xbb, 0xbb, 0x80, 0x3e, 0xf7, 0x76, 0xfe, 0xf7, 0xbb, 0xbb, 0x83, 0x41,
+ 0x07, 0x77, 0x09, 0x07, 0x43, 0xb3, 0x81, 0xbc, 0xf8, 0x77, 0x02, 0xe5,
+ 0xa4, 0x3a, 0xa0, 0xcb, 0x38, 0xbb, 0x57, 0x90, 0xd9, 0x08, 0x9c, 0x1d,
+ 0x86, 0x59, 0x01, 0x0c, 0x21, 0x44, 0x68, 0x35, 0xa8, 0x57, 0x97, 0x0e,
+ 0x9b, 0xbb, 0x62, 0xc4, 0xca, 0x57, 0x04, 0xa1, 0xca, 0x3b, 0xa3, 0x48,
+ 0xd2, 0x66, 0x11, 0x33, 0x6a, 0x3b, 0xb4, 0xbb, 0x08, 0x77, 0x17, 0x03,
+ 0xb4, 0x3b, 0x79, 0x3b, 0x46, 0x97, 0x0e, 0xf7, 0x3d, 0xbb, 0x3d, 0x49,
+ 0x25, 0x86, 0x88, 0xb4, 0xad, 0x3b, 0x62, 0xbb, 0xa4, 0x47, 0x29, 0x99,
+ 0x3b, 0x3b, 0xaf, 0xc6, 0xd4, 0x37, 0x68, 0x94, 0x0a, 0xbb
+ };
+
+static void parse_command(const char *cmd)
+{
+ if (!strncmp(cmd, "getconf", sizeof("getconf"))) {
+ command = CMD_GET_CONF;
+ } else if (!strncmp(cmd, "open", sizeof("open"))) {
+ command = CMD_OPEN;
+ } else if (!strncmp(cmd, "start", sizeof("start"))) {
+ command = CMD_START;
+ } else if (!strncmp(cmd, "suspend", sizeof("suspend"))) {
+ command = CMD_SUSPEND;
+ } else if (!strncmp(cmd, "close", sizeof("close"))) {
+ command = CMD_CLOSE;
+ } else if (!strncmp(cmd, "abort", sizeof("abort"))) {
+ command = CMD_ABORT;
+ } else if (!strncmp(cmd, "delay", sizeof("delay"))) {
+ command = CMD_DELAY;
+ } else {
+ printf("Unknown command '%s'\n", cmd);
+ printf("(getconf open start suspend close abort delay)\n");
+ exit(1);
+ }
+}
+
+static void send_command(void)
+{
+ avdtp_state_t state = avdtp_sep_get_state(local_sep);
+
+ switch (command) {
+ case CMD_GET_CONF:
+ avdtp_get_configuration(avdtp, avdtp_stream);
+ break;
+ case CMD_OPEN:
+ if (state == AVDTP_STATE_CONFIGURED)
+ avdtp_open(avdtp, avdtp_stream);
+ break;
+ case CMD_START:
+ if (state == AVDTP_STATE_OPEN)
+ avdtp_start(avdtp, avdtp_stream);
+ break;
+ case CMD_SUSPEND:
+ if (state == AVDTP_STATE_STREAMING)
+ avdtp_suspend(avdtp , avdtp_stream);
+ break;
+ case CMD_CLOSE:
+ if (state == AVDTP_STATE_STREAMING)
+ avdtp_close(avdtp, avdtp_stream, FALSE);
+ break;
+ case CMD_ABORT:
+ avdtp_abort(avdtp , avdtp_stream);
+ break;
+ case CMD_DELAY:
+ avdtp_delay_report(avdtp , avdtp_stream , 250);
+ break;
+ case CMD_NONE:
+ default:
+ break;
+ }
+}
+
+static gboolean media_writer(gpointer user_data)
+{
+ uint16_t omtu;
+ int fd;
+ int to_write;
+
+ if (!avdtp_stream_get_transport(avdtp_stream, &fd, NULL, &omtu, NULL))
+ return TRUE;
+
+ if (omtu < sizeof(sbc_media_frame))
+ to_write = omtu;
+ else
+ to_write = sizeof(sbc_media_frame);
+
+ if (write(fd, sbc_media_frame, to_write) < 0)
+ return TRUE;
+
+ send_command();
+
+ return TRUE;
+}
+
+static bool start_media_player(void)
+{
+ int fd;
+ uint16_t omtu;
+
+ printf("Media streaming started\n");
+
+ if (media_player || !avdtp_stream)
+ return false;
+
+ if (!avdtp_stream_get_transport(avdtp_stream, &fd, NULL, &omtu, NULL))
+ return false;
+
+ media_player = g_timeout_add(200, media_writer, NULL);
+ if (!media_player)
+ return false;
+
+ return true;
+}
+
+static void stop_media_player(void)
+{
+ if (!media_player)
+ return;
+
+ printf("Media streaming stopped\n");
+
+ g_source_remove(media_player);
+ media_player = 0;
+}
+
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+
+struct rtp_header {
+ unsigned cc:4;
+ unsigned x:1;
+ unsigned p:1;
+ unsigned v:2;
+
+ unsigned pt:7;
+ unsigned m:1;
+
+ uint16_t sequence_number;
+ uint32_t timestamp;
+ uint32_t ssrc;
+ uint32_t csrc[0];
+} __attribute__ ((packed));
+
+#elif __BYTE_ORDER == __BIG_ENDIAN
+
+struct rtp_header {
+ unsigned v:2;
+ unsigned p:1;
+ unsigned x:1;
+ unsigned cc:4;
+
+ unsigned m:1;
+ unsigned pt:7;
+
+ uint16_t sequence_number;
+ uint32_t timestamp;
+ uint32_t ssrc;
+ uint32_t csrc[0];
+} __attribute__ ((packed));
+
+#else
+#error "Unknown byte order"
+#endif
+
+static gboolean media_reader(GIOChannel *source, GIOCondition condition,
+ gpointer data)
+{
+ char buf[UINT16_MAX];
+ struct rtp_header *rtp = (void *) buf;
+ static bool decode = false;
+ uint16_t imtu;
+ int fd, ret;
+
+ if (!avdtp_stream_get_transport(avdtp_stream, &fd, &imtu, NULL, NULL))
+ return TRUE;
+
+ ret = read(fd, buf, imtu);
+ if (ret < 0) {
+ printf("Reading failed (%s)\n", strerror(errno));
+ return TRUE;
+ }
+
+ if (ret < (int) sizeof(*rtp)) {
+ printf("Not enough media data received (%u bytes)", ret);
+ return TRUE;
+ }
+
+ if (!decode) {
+ printf("V=%u P=%u X=%u CC=%u M=%u PT=%u SeqNr=%d\n",
+ rtp->v, rtp->p, rtp->x, rtp->cc, rtp->m, rtp->pt,
+ be16_to_cpu(rtp->sequence_number));
+ decode = true;
+ }
+
+ send_command();
+
+ return TRUE;
+}
+
+static bool start_media_recorder(void)
+{
+ int fd;
+ uint16_t omtu;
+ GIOChannel *chan;
+
+ printf("Media recording started\n");
+
+ if (media_recorder || !avdtp_stream)
+ return false;
+
+ if (!avdtp_stream_get_transport(avdtp_stream, &fd, NULL, &omtu, NULL))
+ return false;
+
+ chan = g_io_channel_unix_new(fd);
+
+ media_recorder = g_io_add_watch(chan, G_IO_IN, media_reader, NULL);
+ g_io_channel_unref(chan);
+
+ if (!media_recorder)
+ return false;
+
+ return true;
+}
+
+static void stop_media_recorder(void)
+{
+ if (!media_recorder)
+ return;
+
+ printf("Media recording stopped\n");
+
+ g_source_remove(media_recorder);
+ media_recorder = 0;
+}
+
+static void set_configuration_cfm(struct avdtp *session,
+ struct avdtp_local_sep *lsep,
+ struct avdtp_stream *stream,
+ struct avdtp_error *err,
+ void *user_data)
+{
+ printf("%s\n", __func__);
+
+ if (preconf)
+ avdtp_open(avdtp, avdtp_stream);
+}
+
+static void get_configuration_cfm(struct avdtp *session,
+ struct avdtp_local_sep *lsep,
+ struct avdtp_stream *stream,
+ struct avdtp_error *err,
+ void *user_data)
+ {
+ printf("%s\n", __func__);
+}
+
+static void disconnect_cb(void *user_data)
+{
+ printf("Disconnected\n");
+
+ g_main_loop_quit(mainloop);
+}
+
+static void discover_cb(struct avdtp *session, GSList *seps,
+ struct avdtp_error *err, void *user_data)
+{
+ struct avdtp_service_capability *service;
+ GSList *caps = NULL;
+ int ret;
+
+ remote_sep = avdtp_find_remote_sep(avdtp, local_sep);
+ if (!remote_sep) {
+ printf("Unable to find matching endpoint\n");
+ avdtp_shutdown(session);
+ return;
+ }
+
+ printf("Matching endpoint found\n");
+
+ service = avdtp_service_cap_new(AVDTP_MEDIA_TRANSPORT, NULL, 0);
+ caps = g_slist_append(caps, service);
+
+ service = avdtp_service_cap_new(AVDTP_MEDIA_CODEC, sbc_codec,
+ sizeof(sbc_codec));
+ caps = g_slist_append(caps, service);
+
+ ret = avdtp_set_configuration(avdtp, remote_sep, local_sep, caps,
+ &avdtp_stream);
+
+ g_slist_free_full(caps, g_free);
+
+ if (ret < 0) {
+ printf("Failed to set configuration (%s)\n", strerror(-ret));
+ avdtp_shutdown(session);
+ }
+}
+
+static void connect_cb(GIOChannel *chan, GError *err, gpointer user_data)
+{
+ uint16_t imtu, omtu;
+ GError *gerr = NULL;
+ int fd;
+
+ if (err) {
+ printf("%s\n", err->message);
+ g_main_loop_quit(mainloop);
+ return;
+ }
+
+ bt_io_get(chan, &gerr,
+ BT_IO_OPT_IMTU, &imtu,
+ BT_IO_OPT_OMTU, &omtu,
+ BT_IO_OPT_DEST_BDADDR, &dst,
+ BT_IO_OPT_INVALID);
+ if (gerr) {
+ printf("%s\n", gerr->message);
+ g_main_loop_quit(mainloop);
+ return;
+ }
+
+ printf("Connected (imtu=%d omtu=%d)\n", imtu, omtu);
+
+ fd = g_io_channel_unix_get_fd(chan);
+
+ if (avdtp && avdtp_stream) {
+ if (!avdtp_stream_set_transport(avdtp_stream, fd, imtu, omtu)) {
+ printf("avdtp_stream_set_transport: failed\n");
+ g_main_loop_quit(mainloop);
+ }
+
+ g_io_channel_set_close_on_unref(chan, FALSE);
+
+ send_command();
+
+ return;
+ }
+
+ avdtp = avdtp_new(fd, imtu, omtu, version, lseps);
+ if (!avdtp) {
+ printf("Failed to create avdtp instance\n");
+ g_main_loop_quit(mainloop);
+ return;
+ }
+
+ avdtp_add_disconnect_cb(avdtp, disconnect_cb, NULL);
+
+ if (preconf) {
+ int ret;
+
+ ret = avdtp_discover(avdtp, discover_cb, NULL);
+ if (ret < 0) {
+ printf("avdtp_discover failed: %s", strerror(-ret));
+ g_main_loop_quit(mainloop);
+ }
+ }
+}
+
+static GIOChannel *do_connect(GError **err)
+{
+ if (fragment)
+ return bt_io_connect(connect_cb, NULL, NULL, err,
+ BT_IO_OPT_SOURCE_BDADDR, &src,
+ BT_IO_OPT_DEST_BDADDR, &dst,
+ BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_LOW,
+ BT_IO_OPT_PSM, AVDTP_PSM,
+ BT_IO_OPT_MTU, 48,
+ BT_IO_OPT_INVALID);
+
+ return bt_io_connect(connect_cb, NULL, NULL, err,
+ BT_IO_OPT_SOURCE_BDADDR, &src,
+ BT_IO_OPT_DEST_BDADDR, &dst,
+ BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_LOW,
+ BT_IO_OPT_PSM, AVDTP_PSM,
+ BT_IO_OPT_INVALID);
+}
+
+static void open_cfm(struct avdtp *session, struct avdtp_local_sep *lsep,
+ struct avdtp_stream *stream, struct avdtp_error *err,
+ void *user_data)
+{
+ GError *gerr = NULL;
+
+ printf("%s\n", __func__);
+
+ do_connect(&gerr);
+ if (gerr) {
+ printf("connect failed: %s\n", gerr->message);
+ g_error_free(gerr);
+ g_main_loop_quit(mainloop);
+ }
+}
+
+static void start_cfm(struct avdtp *session, struct avdtp_local_sep *lsep,
+ struct avdtp_stream *stream, struct avdtp_error *err,
+ void *user_data)
+{
+ printf("%s\n", __func__);
+
+ if (dev_role == AVDTP_SEP_TYPE_SOURCE)
+ start_media_player();
+ else
+ start_media_recorder();
+}
+
+static void suspend_cfm(struct avdtp *session, struct avdtp_local_sep *lsep,
+ struct avdtp_stream *stream,
+ struct avdtp_error *err, void *user_data)
+{
+ printf("%s\n", __func__);
+
+ if (dev_role == AVDTP_SEP_TYPE_SOURCE)
+ stop_media_player();
+ else
+ stop_media_recorder();
+}
+
+static void close_cfm(struct avdtp *session, struct avdtp_local_sep *lsep,
+ struct avdtp_stream *stream,
+ struct avdtp_error *err, void *user_data)
+{
+ printf("%s\n", __func__);
+
+ if (dev_role == AVDTP_SEP_TYPE_SOURCE)
+ stop_media_player();
+ else
+ stop_media_recorder();
+
+ avdtp_stream = NULL;
+}
+
+static void abort_cfm(struct avdtp *session, struct avdtp_local_sep *lsep,
+ struct avdtp_stream *stream,
+ struct avdtp_error *err, void *user_data)
+{
+ printf("%s\n", __func__);
+
+ if (dev_role == AVDTP_SEP_TYPE_SOURCE)
+ stop_media_player();
+ else
+ stop_media_recorder();
+
+ avdtp_stream = NULL;
+}
+
+static void reconfigure_cfm(struct avdtp *session,
+ struct avdtp_local_sep *lsep,
+ struct avdtp_stream *stream,
+ struct avdtp_error *err, void *user_data)
+{
+ printf("%s\n", __func__);
+}
+
+static void delay_report_cfm(struct avdtp *session,
+ struct avdtp_local_sep *lsep,
+ struct avdtp_stream *stream,
+ struct avdtp_error *err, void *user_data)
+{
+ printf("%s\n", __func__);
+}
+
+static struct avdtp_sep_cfm sep_cfm = {
+ .set_configuration = set_configuration_cfm,
+ .get_configuration = get_configuration_cfm,
+ .open = open_cfm,
+ .start = start_cfm,
+ .suspend = suspend_cfm,
+ .close = close_cfm,
+ .abort = abort_cfm,
+ .reconfigure = reconfigure_cfm,
+ .delay_report = delay_report_cfm,
+};
+
+static gboolean get_capability_ind(struct avdtp *session,
+ struct avdtp_local_sep *sep,
+ GSList **caps, uint8_t *err,
+ void *user_data)
+{
+ struct avdtp_service_capability *service;
+ int i;
+
+ printf("%s\n", __func__);
+
+ if (idle_id > 0) {
+ g_source_remove(idle_id);
+ idle_id = 0;
+ }
+
+ if (reject)
+ return FALSE;
+
+ *caps = NULL;
+
+ service = avdtp_service_cap_new(AVDTP_MEDIA_TRANSPORT, NULL, 0);
+ *caps = g_slist_append(*caps, service);
+
+ service = avdtp_service_cap_new(AVDTP_MEDIA_CODEC, sbc_codec,
+ sizeof(sbc_codec));
+ *caps = g_slist_append(*caps, service);
+
+ if (fragment)
+ for (i = 0; i < 10; i++) {
+ service = avdtp_service_cap_new(AVDTP_MEDIA_CODEC,
+ sbc_codec,
+ sizeof(sbc_codec));
+ *caps = g_slist_append(*caps, service);
+ }
+
+ return TRUE;
+}
+
+static gboolean set_configuration_ind(struct avdtp *session,
+ struct avdtp_local_sep *lsep,
+ struct avdtp_stream *stream,
+ GSList *caps,
+ avdtp_set_configuration_cb cb,
+ void *user_data)
+{
+ printf("%s\n", __func__);
+
+ if (reject)
+ return FALSE;
+
+ if (idle_id > 0) {
+ g_source_remove(idle_id);
+ idle_id = 0;
+ }
+
+ avdtp_stream = stream;
+
+ cb(session, stream, NULL);
+
+ send_command();
+
+ return TRUE;
+}
+
+static gboolean get_configuration_ind(struct avdtp *session,
+ struct avdtp_local_sep *lsep,
+ uint8_t *err, void *user_data)
+{
+ printf("%s\n", __func__);
+
+ if (reject)
+ return FALSE;
+
+ return TRUE;
+}
+
+static gboolean open_ind(struct avdtp *session, struct avdtp_local_sep *lsep,
+ struct avdtp_stream *stream, uint8_t *err,
+ void *user_data)
+{
+ printf("%s\n", __func__);
+
+ if (reject)
+ return FALSE;
+
+ send_command();
+
+ return TRUE;
+}
+
+static gboolean start_ind(struct avdtp *session, struct avdtp_local_sep *lsep,
+ struct avdtp_stream *stream, uint8_t *err,
+ void *user_data)
+{
+ printf("%s\n", __func__);
+
+ if (reject)
+ return FALSE;
+
+ if (dev_role == AVDTP_SEP_TYPE_SOURCE)
+ start_media_player();
+ else
+ start_media_recorder();
+
+ send_command();
+
+ return TRUE;
+}
+
+static gboolean suspend_ind(struct avdtp *session,
+ struct avdtp_local_sep *sep,
+ struct avdtp_stream *stream, uint8_t *err,
+ void *user_data)
+{
+ printf("%s\n", __func__);
+
+ if (reject)
+ return FALSE;
+
+ if (dev_role == AVDTP_SEP_TYPE_SOURCE)
+ stop_media_player();
+ else
+ stop_media_recorder();
+
+ return TRUE;
+}
+
+static gboolean close_ind(struct avdtp *session, struct avdtp_local_sep *sep,
+ struct avdtp_stream *stream, uint8_t *err,
+ void *user_data)
+{
+ printf("%s\n", __func__);
+
+ if (reject)
+ return FALSE;
+
+ if (dev_role == AVDTP_SEP_TYPE_SOURCE)
+ stop_media_player();
+ else
+ stop_media_recorder();
+
+ avdtp_stream = NULL;
+
+ return TRUE;
+}
+
+static void abort_ind(struct avdtp *session, struct avdtp_local_sep *sep,
+ struct avdtp_stream *stream, uint8_t *err,
+ void *user_data)
+{
+ printf("%s\n", __func__);
+
+ if (dev_role == AVDTP_SEP_TYPE_SOURCE)
+ stop_media_player();
+ else
+ stop_media_recorder();
+
+ avdtp_stream = NULL;
+}
+
+static gboolean reconfigure_ind(struct avdtp *session,
+ struct avdtp_local_sep *lsep,
+ uint8_t *err, void *user_data)
+{
+ printf("%s\n", __func__);
+
+ if (reject)
+ return FALSE;
+
+ return TRUE;
+}
+
+static gboolean delayreport_ind(struct avdtp *session,
+ struct avdtp_local_sep *lsep,
+ uint8_t rseid, uint16_t delay,
+ uint8_t *err, void *user_data)
+{
+ printf("%s\n", __func__);
+
+ if (reject)
+ return FALSE;
+
+ return TRUE;
+}
+
+static struct avdtp_sep_ind sep_ind = {
+ .get_capability = get_capability_ind,
+ .set_configuration = set_configuration_ind,
+ .get_configuration = get_configuration_ind,
+ .open = open_ind,
+ .close = close_ind,
+ .start = start_ind,
+ .suspend = suspend_ind,
+ .abort = abort_ind,
+ .reconfigure = reconfigure_ind,
+ .delayreport = delayreport_ind,
+};
+
+static void usage(void)
+{
+ printf("avdtptest - AVDTP testing ver %s\n", VERSION);
+ printf("Usage:\n"
+ "\tavdtptest [options]\n");
+ printf("options:\n"
+ "\t-d <device_role> SRC (source) or SINK (sink)\n"
+ "\t-i <hcidev> HCI adapter\n"
+ "\t-c <bdaddr> connect\n"
+ "\t-l listen\n"
+ "\t-r reject commands\n"
+ "\t-f fragment\n"
+ "\t-p configure stream\n"
+ "\t-s <command> send command\n"
+ "\t-v <version> set version (0x0100, 0x0102, 0x0103\n");
+}
+
+static struct option main_options[] = {
+ { "help", 0, 0, 'h' },
+ { "device_role", 1, 0, 'd' },
+ { "adapter", 1, 0, 'i' },
+ { "connect", 1, 0, 'c' },
+ { "listen", 0, 0, 'l' },
+ { "reject", 0, 0, 'r' },
+ { "fragment", 0, 0, 'f' },
+ { "preconf", 0, 0, 'p' },
+ { "send", 1, 0, 's' },
+ { "version", 1, 0, 'v' },
+ { 0, 0, 0, 0 }
+};
+
+static GIOChannel *do_listen(GError **err)
+{
+ if (fragment)
+ return bt_io_listen(connect_cb, NULL, NULL, NULL, err,
+ BT_IO_OPT_SOURCE_BDADDR, &src,
+ BT_IO_OPT_PSM, AVDTP_PSM,
+ BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_LOW,
+ BT_IO_OPT_MTU, 48,
+ BT_IO_OPT_INVALID);
+
+ return bt_io_listen(connect_cb, NULL, NULL, NULL, err,
+ BT_IO_OPT_SOURCE_BDADDR, &src,
+ BT_IO_OPT_PSM, AVDTP_PSM,
+ BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_LOW,
+ BT_IO_OPT_INVALID);
+}
+
+int main(int argc, char *argv[])
+{
+ GError *err = NULL;
+ int opt;
+
+ bacpy(&src, BDADDR_ANY);
+ bacpy(&dst, BDADDR_ANY);
+
+ mainloop = g_main_loop_new(NULL, FALSE);
+ if (!mainloop) {
+ printf("Failed to create main loop\n");
+
+ exit(1);
+ }
+
+ while ((opt = getopt_long(argc, argv, "d:hi:s:c:v:lrfp",
+ main_options, NULL)) != EOF) {
+ switch (opt) {
+ case 'i':
+ if (!strncmp(optarg, "hci", 3))
+ hci_devba(atoi(optarg + 3), &src);
+ else
+ str2ba(optarg, &src);
+ break;
+ case 'd':
+ if (!strncasecmp(optarg, "SRC", sizeof("SRC"))) {
+ dev_role = AVDTP_SEP_TYPE_SOURCE;
+ } else if (!strncasecmp(optarg, "SINK",
+ sizeof("SINK"))) {
+ dev_role = AVDTP_SEP_TYPE_SINK;
+ } else {
+ usage();
+ exit(1);
+ }
+ break;
+ case 'c':
+ if (str2ba(optarg, &dst) < 0) {
+ usage();
+ exit(1);
+ }
+ break;
+ case 'l':
+ bacpy(&dst, BDADDR_ANY);
+ break;
+ case 'r':
+ reject = true;
+ break;
+ case 'f':
+ fragment = true;
+ break;
+ case 'p':
+ preconf = true;
+ break;
+ case 's':
+ parse_command(optarg);
+ break;
+ case 'v':
+ version = strtol(optarg, NULL, 0);
+ if (version != 0x0100 && version != 0x0102 &&
+ version != 0x0103) {
+ printf("invalid version\n");
+ exit(1);
+ }
+
+ break;
+ case 'h':
+ usage();
+ exit(0);
+ default:
+ usage();
+ exit(1);
+ }
+ }
+
+ lseps = queue_new();
+
+ local_sep = avdtp_register_sep(lseps, dev_role, AVDTP_MEDIA_TYPE_AUDIO,
+ 0x00, TRUE, &sep_ind, &sep_cfm, NULL);
+ if (!local_sep) {
+ printf("Failed to register sep\n");
+ exit(1);
+ }
+
+ queue_push_tail(lseps, local_sep);
+
+ if (!bacmp(&dst, BDADDR_ANY)) {
+ printf("Listening...\n");
+ io = do_listen(&err);
+ } else {
+ printf("Connecting...\n");
+ io = do_connect(&err);
+ }
+
+ if (!io) {
+ printf("Failed: %s\n", err->message);
+ g_error_free(err);
+ exit(1);
+ }
+
+ g_main_loop_run(mainloop);
+
+ printf("Done\n");
+
+ queue_destroy(lseps, NULL);
+
+ avdtp_unref(avdtp);
+ avdtp = NULL;
+
+ g_main_loop_unref(mainloop);
+ mainloop = NULL;
+
+ return 0;
+}
diff --git a/repo/android/avrcp-lib.c b/repo/android/avrcp-lib.c
new file mode 100644
index 0000000..4edfd0e
--- /dev/null
+++ b/repo/android/avrcp-lib.c
@@ -0,0 +1,3615 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2014 Intel Corporation. All rights reserved.
+ *
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdbool.h>
+#include <glib.h>
+#include <errno.h>
+#include <string.h>
+
+#include "lib/bluetooth.h"
+
+#include "src/shared/util.h"
+#include "src/log.h"
+
+#include "avctp.h"
+#include "avrcp-lib.h"
+
+
+/* Packet types */
+#define AVRCP_PACKET_TYPE_SINGLE 0x00
+#define AVRCP_PACKET_TYPE_START 0x01
+#define AVRCP_PACKET_TYPE_CONTINUING 0x02
+#define AVRCP_PACKET_TYPE_END 0x03
+
+#define AVRCP_CHARSET_UTF8 0x006a
+
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+
+struct avrcp_header {
+ uint8_t company_id[3];
+ uint8_t pdu_id;
+ uint8_t packet_type:2;
+ uint8_t rsvd:6;
+ uint16_t params_len;
+ uint8_t params[0];
+} __attribute__ ((packed));
+#define AVRCP_HEADER_LENGTH 7
+
+#elif __BYTE_ORDER == __BIG_ENDIAN
+
+struct avrcp_header {
+ uint8_t company_id[3];
+ uint8_t pdu_id;
+ uint8_t rsvd:6;
+ uint8_t packet_type:2;
+ uint16_t params_len;
+ uint8_t params[0];
+} __attribute__ ((packed));
+#define AVRCP_HEADER_LENGTH 7
+
+#else
+#error "Unknown byte order"
+#endif
+
+struct avrcp_browsing_header {
+ uint8_t pdu_id;
+ uint16_t params_len;
+ uint8_t params[0];
+} __attribute__ ((packed));
+#define AVRCP_BROWSING_HEADER_LENGTH 3
+
+struct get_capabilities_req {
+ uint8_t cap;
+ uint8_t params[0];
+} __attribute__ ((packed));
+
+struct get_capabilities_rsp {
+ uint8_t cap;
+ uint8_t number;
+ uint8_t params[0];
+} __attribute__ ((packed));
+
+struct list_attributes_rsp {
+ uint8_t number;
+ uint8_t params[0];
+} __attribute__ ((packed));
+
+struct list_values_req {
+ uint8_t attr;
+} __attribute__ ((packed));
+
+struct list_values_rsp {
+ uint8_t number;
+ uint8_t params[0];
+} __attribute__ ((packed));
+
+struct get_value_req {
+ uint8_t number;
+ uint8_t attrs[0];
+} __attribute__ ((packed));
+
+struct attr_value {
+ uint8_t attr;
+ uint8_t value;
+} __attribute__ ((packed));
+
+struct value_rsp {
+ uint8_t number;
+ struct attr_value values[0];
+} __attribute__ ((packed));
+
+struct set_value_req {
+ uint8_t number;
+ struct attr_value values[0];
+} __attribute__ ((packed));
+
+struct get_attribute_text_req {
+ uint8_t number;
+ uint8_t attrs[0];
+} __attribute__ ((packed));
+
+struct text_value {
+ uint8_t attr;
+ uint16_t charset;
+ uint8_t len;
+ char data[0];
+} __attribute__ ((packed));
+
+struct get_attribute_text_rsp {
+ uint8_t number;
+ struct text_value values[0];
+} __attribute__ ((packed));
+
+struct get_value_text_req {
+ uint8_t attr;
+ uint8_t number;
+ uint8_t values[0];
+} __attribute__ ((packed));
+
+struct get_value_text_rsp {
+ uint8_t number;
+ struct text_value values[0];
+} __attribute__ ((packed));
+
+struct media_item {
+ uint32_t attr;
+ uint16_t charset;
+ uint16_t len;
+ char data[0];
+} __attribute__ ((packed));
+
+struct get_element_attributes_req {
+ uint64_t id;
+ uint8_t number;
+ uint32_t attrs[0];
+} __attribute__ ((packed));
+
+struct get_element_attributes_rsp {
+ uint8_t number;
+ struct media_item items[0];
+} __attribute__ ((packed));
+
+struct get_play_status_rsp {
+ uint32_t duration;
+ uint32_t position;
+ uint8_t status;
+} __attribute__ ((packed));
+
+struct register_notification_req {
+ uint8_t event;
+ uint32_t interval;
+} __attribute__ ((packed));
+
+struct register_notification_rsp {
+ uint8_t event;
+ uint8_t data[0];
+} __attribute__ ((packed));
+
+struct set_volume_req {
+ uint8_t value;
+} __attribute__ ((packed));
+
+struct set_volume_rsp {
+ uint8_t value;
+} __attribute__ ((packed));
+
+struct set_addressed_req {
+ uint16_t id;
+} __attribute__ ((packed));
+
+struct set_addressed_rsp {
+ uint8_t status;
+} __attribute__ ((packed));
+
+struct set_browsed_req {
+ uint16_t id;
+} __attribute__ ((packed));
+
+struct set_browsed_rsp {
+ uint8_t status;
+ uint16_t counter;
+ uint32_t items;
+ uint16_t charset;
+ uint8_t depth;
+ uint8_t data[0];
+} __attribute__ ((packed));
+
+struct get_folder_items_req {
+ uint8_t scope;
+ uint32_t start;
+ uint32_t end;
+ uint8_t number;
+ uint32_t attrs[0];
+} __attribute__ ((packed));
+
+struct get_folder_items_rsp {
+ uint8_t status;
+ uint16_t counter;
+ uint16_t number;
+ uint8_t data[0];
+} __attribute__ ((packed));
+
+struct change_path_req {
+ uint16_t counter;
+ uint8_t direction;
+ uint64_t uid;
+} __attribute__ ((packed));
+
+struct change_path_rsp {
+ uint8_t status;
+ uint32_t items;
+} __attribute__ ((packed));
+
+struct get_item_attributes_req {
+ uint8_t scope;
+ uint64_t uid;
+ uint16_t counter;
+ uint8_t number;
+ uint32_t attrs[0];
+} __attribute__ ((packed));
+
+struct get_item_attributes_rsp {
+ uint8_t status;
+ uint8_t number;
+ struct media_item items[0];
+} __attribute__ ((packed));
+
+struct play_item_req {
+ uint8_t scope;
+ uint64_t uid;
+ uint16_t counter;
+} __attribute__ ((packed));
+
+struct play_item_rsp {
+ uint8_t status;
+} __attribute__ ((packed));
+
+struct search_req {
+ uint16_t charset;
+ uint16_t len;
+ char string[0];
+} __attribute__ ((packed));
+
+struct search_rsp {
+ uint8_t status;
+ uint16_t counter;
+ uint32_t items;
+} __attribute__ ((packed));
+
+struct add_to_now_playing_req {
+ uint8_t scope;
+ uint64_t uid;
+ uint16_t counter;
+} __attribute__ ((packed));
+
+struct add_to_now_playing_rsp {
+ uint8_t status;
+} __attribute__ ((packed));
+
+struct avrcp_control_handler {
+ uint8_t id;
+ uint8_t code;
+ uint8_t rsp;
+ ssize_t (*func) (struct avrcp *session, uint8_t transaction,
+ uint16_t params_len, uint8_t *params, void *user_data);
+};
+
+struct avrcp_browsing_handler {
+ uint8_t id;
+ ssize_t (*func) (struct avrcp *session, uint8_t transaction,
+ uint16_t params_len, uint8_t *params, void *user_data);
+};
+
+struct avrcp_continuing {
+ uint8_t pdu_id;
+ struct iovec pdu;
+};
+
+struct avrcp {
+ struct avctp *conn;
+ struct avrcp_player *player;
+
+ const struct avrcp_control_handler *control_handlers;
+ void *control_data;
+ unsigned int control_id;
+ uint16_t control_mtu;
+
+ struct avrcp_continuing *continuing;
+
+ const struct avrcp_passthrough_handler *passthrough_handlers;
+ void *passthrough_data;
+ unsigned int passthrough_id;
+
+ const struct avrcp_browsing_handler *browsing_handlers;
+ void *browsing_data;
+ unsigned int browsing_id;
+
+ avrcp_destroy_cb_t destroy;
+ void *destroy_data;
+};
+
+struct avrcp_player {
+ const struct avrcp_control_ind *ind;
+ const struct avrcp_control_cfm *cfm;
+
+ void *user_data;
+};
+
+static inline uint32_t ntoh24(const uint8_t src[3])
+{
+ return src[0] << 16 | src[1] << 8 | src[2];
+}
+
+static inline void hton24(uint8_t dst[3], uint32_t src)
+{
+ dst[0] = (src & 0xff0000) >> 16;
+ dst[1] = (src & 0x00ff00) >> 8;
+ dst[2] = (src & 0x0000ff);
+}
+
+static void continuing_free(struct avrcp_continuing *continuing)
+{
+ g_free(continuing->pdu.iov_base);
+ g_free(continuing);
+}
+
+void avrcp_shutdown(struct avrcp *session)
+{
+ if (session->conn) {
+ if (session->control_id > 0)
+ avctp_unregister_pdu_handler(session->conn,
+ session->control_id);
+ if (session->passthrough_id > 0)
+ avctp_unregister_passthrough_handler(session->conn,
+ session->passthrough_id);
+
+ if (session->browsing_id > 0)
+ avctp_unregister_browsing_pdu_handler(session->conn,
+ session->browsing_id);
+
+ /* clear destroy callback that would call shutdown again */
+ avctp_set_destroy_cb(session->conn, NULL, NULL);
+ avctp_shutdown(session->conn);
+ }
+
+ if (session->destroy)
+ session->destroy(session->destroy_data);
+
+ if (session->continuing)
+ continuing_free(session->continuing);
+
+ g_free(session->player);
+ g_free(session);
+}
+
+static struct avrcp_header *parse_pdu(uint8_t *operands, size_t operand_count)
+{
+ struct avrcp_header *pdu;
+
+ if (!operands || operand_count < sizeof(*pdu)) {
+ error("AVRCP: packet too small (%zu bytes)", operand_count);
+ return NULL;
+ }
+
+ pdu = (void *) operands;
+ pdu->params_len = ntohs(pdu->params_len);
+
+ if (operand_count != pdu->params_len + sizeof(*pdu)) {
+ error("AVRCP: invalid parameter length (%u bytes)",
+ pdu->params_len);
+ return NULL;
+ }
+
+ return pdu;
+}
+
+static struct avrcp_browsing_header *parse_browsing_pdu(uint8_t *operands,
+ size_t operand_count)
+{
+ struct avrcp_browsing_header *pdu;
+
+ if (!operands || operand_count < sizeof(*pdu)) {
+ error("AVRCP: packet too small (%zu bytes)", operand_count);
+ return NULL;
+ }
+
+ pdu = (void *) operands;
+ pdu->params_len = ntohs(pdu->params_len);
+
+ if (operand_count != pdu->params_len + sizeof(*pdu)) {
+ error("AVRCP: invalid parameter length (%u bytes)",
+ pdu->params_len);
+ return NULL;
+ }
+
+ return pdu;
+}
+
+static uint8_t errno2status(int err)
+{
+ switch (err) {
+ case -ENOSYS:
+ return AVRCP_STATUS_INVALID_COMMAND;
+ case -EINVAL:
+ return AVRCP_STATUS_INVALID_PARAM;
+ case 0:
+ return AVRCP_STATUS_SUCCESS;
+ case -ENOTDIR:
+ return AVRCP_STATUS_NOT_DIRECTORY;
+ case -EBADRQC:
+ return AVRCP_STATUS_INVALID_SCOPE;
+ case -ERANGE:
+ return AVRCP_STATUS_OUT_OF_BOUNDS;
+ case -ENOENT:
+ return AVRCP_STATUS_DOES_NOT_EXIST;
+ default:
+ return AVRCP_STATUS_INTERNAL_ERROR;
+ }
+}
+
+static ssize_t handle_vendordep_pdu(struct avctp *conn, uint8_t transaction,
+ uint8_t *code, uint8_t *subunit,
+ uint8_t *operands, size_t operand_count,
+ void *user_data)
+{
+ struct avrcp *session = user_data;
+ const struct avrcp_control_handler *handler;
+ struct avrcp_header *pdu;
+ uint32_t company_id;
+ ssize_t ret;
+
+ pdu = parse_pdu(operands, operand_count);
+ if (!pdu) {
+ pdu = (void *) operands;
+ pdu->params[0] = AVRCP_STATUS_INVALID_COMMAND;
+ goto reject;
+ }
+
+ company_id = ntoh24(pdu->company_id);
+ if (company_id != IEEEID_BTSIG) {
+ *code = AVC_CTYPE_NOT_IMPLEMENTED;
+ return 0;
+ }
+
+ DBG("AVRCP PDU 0x%02X, len 0x%04X", pdu->pdu_id, pdu->params_len);
+
+ pdu->packet_type = 0;
+ pdu->rsvd = 0;
+
+ if (!session->control_handlers)
+ goto reject;
+
+ for (handler = session->control_handlers; handler->id; handler++) {
+ if (handler->id == pdu->pdu_id)
+ break;
+ }
+
+ if (handler->id != pdu->pdu_id || handler->code != *code) {
+ pdu->params[0] = AVRCP_STATUS_INVALID_COMMAND;
+ goto reject;
+ }
+
+ if (!handler->func) {
+ pdu->params[0] = AVRCP_STATUS_INVALID_PARAM;
+ goto reject;
+ }
+
+ ret = handler->func(session, transaction, pdu->params_len, pdu->params,
+ session->control_data);
+ if (ret == 0)
+ return -EAGAIN;
+
+ if (ret < 0) {
+ if (ret == -EAGAIN)
+ return ret;
+ pdu->params[0] = errno2status(ret);
+ goto reject;
+ }
+
+ *code = handler->rsp;
+ pdu->params_len = htons(ret);
+
+ return AVRCP_HEADER_LENGTH + ret;
+
+reject:
+ pdu->params_len = htons(1);
+ *code = AVC_CTYPE_REJECTED;
+
+ return AVRCP_HEADER_LENGTH + 1;
+}
+
+static bool handle_passthrough_pdu(struct avctp *conn, uint8_t op,
+ bool pressed, void *user_data)
+{
+ struct avrcp *session = user_data;
+ const struct avrcp_passthrough_handler *handler;
+
+ if (!session->passthrough_handlers)
+ return false;
+
+ for (handler = session->passthrough_handlers; handler->func;
+ handler++) {
+ if (handler->op == op)
+ break;
+ }
+
+ if (handler->func == NULL)
+ return false;
+
+ return handler->func(session, pressed, session->passthrough_data);
+}
+
+static void disconnect_cb(void *data)
+{
+ struct avrcp *session = data;
+
+ session->conn = NULL;
+
+ avrcp_shutdown(session);
+}
+
+struct avrcp *avrcp_new(int fd, size_t imtu, size_t omtu, uint16_t version)
+{
+ struct avrcp *session;
+
+ session = g_new0(struct avrcp, 1);
+
+ session->conn = avctp_new(fd, imtu, omtu, version);
+ if (!session->conn) {
+ g_free(session);
+ return NULL;
+ }
+
+ session->passthrough_id = avctp_register_passthrough_handler(
+ session->conn,
+ handle_passthrough_pdu,
+ session);
+ session->control_id = avctp_register_pdu_handler(session->conn,
+ AVC_OP_VENDORDEP,
+ handle_vendordep_pdu,
+ session);
+ session->control_mtu = omtu - AVC_DATA_OFFSET;
+
+ /*
+ * 27.1.2 AV/C Command Frame
+ * An AV/C command frame contains up to 512 octets of data
+ */
+ if (session->control_mtu > AVC_DATA_MTU)
+ session->control_mtu = AVC_DATA_MTU;
+
+ avctp_set_destroy_cb(session->conn, disconnect_cb, session);
+
+ return session;
+}
+
+static ssize_t handle_browsing_pdu(struct avctp *conn,
+ uint8_t transaction, uint8_t *operands,
+ size_t operand_count, void *user_data)
+{
+ struct avrcp *session = user_data;
+ const struct avrcp_browsing_handler *handler;
+ struct avrcp_browsing_header *pdu;
+ int ret;
+
+ pdu = parse_browsing_pdu(operands, operand_count);
+ if (!pdu) {
+ pdu = (void *) operands;
+ pdu->params[0] = AVRCP_STATUS_INVALID_COMMAND;
+ goto reject;
+ }
+
+ DBG("AVRCP Browsing PDU 0x%02X, len 0x%04X", pdu->pdu_id,
+ pdu->params_len);
+
+ if (!session->browsing_handlers) {
+ pdu->pdu_id = AVRCP_GENERAL_REJECT;
+ pdu->params[0] = AVRCP_STATUS_INTERNAL_ERROR;
+ goto reject;
+ }
+
+ for (handler = session->browsing_handlers; handler->id; handler++) {
+ if (handler->id == pdu->pdu_id)
+ break;
+ }
+
+ if (handler->id != pdu->pdu_id) {
+ pdu->pdu_id = AVRCP_GENERAL_REJECT;
+ pdu->params[0] = AVRCP_STATUS_INVALID_COMMAND;
+ goto reject;
+ }
+
+ if (!handler->func) {
+ pdu->params[0] = AVRCP_STATUS_INVALID_PARAM;
+ goto reject;
+ }
+
+ ret = handler->func(session, transaction, pdu->params_len, pdu->params,
+ session->control_data);
+ if (ret == 0)
+ return -EAGAIN;
+
+ if (ret < 0) {
+ if (ret == -EAGAIN)
+ return ret;
+ pdu->params[0] = errno2status(ret);
+ goto reject;
+ }
+
+ pdu->params_len = htons(ret);
+
+ return AVRCP_BROWSING_HEADER_LENGTH + ret;
+
+reject:
+ pdu->params_len = htons(1);
+
+ return AVRCP_BROWSING_HEADER_LENGTH + 1;
+}
+
+static void browsing_disconnect_cb(void *data)
+{
+ struct avrcp *session = data;
+
+ session->browsing_id = 0;
+}
+
+int avrcp_connect_browsing(struct avrcp *session, int fd, size_t imtu,
+ size_t omtu)
+{
+ int err;
+
+ err = avctp_connect_browsing(session->conn, fd, imtu, omtu);
+ if (err < 0)
+ return err;
+
+ session->browsing_id = avctp_register_browsing_pdu_handler(
+ session->conn,
+ handle_browsing_pdu,
+ session,
+ browsing_disconnect_cb);
+
+ return 0;
+}
+
+void avrcp_set_destroy_cb(struct avrcp *session, avrcp_destroy_cb_t cb,
+ void *user_data)
+{
+ session->destroy = cb;
+ session->destroy_data = user_data;
+}
+
+static ssize_t get_capabilities(struct avrcp *session, uint8_t transaction,
+ uint16_t params_len, uint8_t *params,
+ void *user_data)
+{
+ struct avrcp_player *player = user_data;
+ struct get_capabilities_req *req;
+
+ if (!params || params_len != sizeof(*req))
+ return -EINVAL;
+
+ req = (void *) params;
+
+ switch (req->cap) {
+ case CAP_COMPANY_ID:
+ req->params[0] = 1;
+ hton24(&req->params[1], IEEEID_BTSIG);
+ return 5;
+ case CAP_EVENTS_SUPPORTED:
+ if (!player->ind || !player->ind->get_capabilities)
+ return -ENOSYS;
+ return player->ind->get_capabilities(session, transaction,
+ player->user_data);
+ }
+
+ return -EINVAL;
+}
+
+static ssize_t list_attributes(struct avrcp *session, uint8_t transaction,
+ uint16_t params_len, uint8_t *params,
+ void *user_data)
+{
+ struct avrcp_player *player = user_data;
+
+ DBG("");
+
+ if (!player->ind || !player->ind->list_attributes)
+ return -ENOSYS;
+
+ return player->ind->list_attributes(session, transaction,
+ player->user_data);
+}
+
+static bool check_attributes(uint8_t number, const uint8_t *attrs)
+{
+ int i;
+
+ for (i = 0; i < number; i++) {
+ if (attrs[i] > AVRCP_ATTRIBUTE_LAST ||
+ attrs[i] == AVRCP_ATTRIBUTE_ILEGAL)
+ return false;
+ }
+
+ return true;
+}
+
+static ssize_t get_attribute_text(struct avrcp *session, uint8_t transaction,
+ uint16_t params_len, uint8_t *params,
+ void *user_data)
+{
+ struct avrcp_player *player = user_data;
+ struct get_attribute_text_req *req;
+
+ DBG("");
+
+ if (!player->ind || !player->ind->get_attribute_text)
+ return -ENOSYS;
+
+ if (!params || params_len < sizeof(*req))
+ return -EINVAL;
+
+ req = (void *) params;
+ if (params_len != sizeof(*req) + req->number)
+ return -EINVAL;
+
+ if (!check_attributes(req->number, req->attrs))
+ return -EINVAL;
+
+ return player->ind->get_attribute_text(session, transaction,
+ req->number, req->attrs,
+ player->user_data);
+}
+
+static ssize_t list_values(struct avrcp *session, uint8_t transaction,
+ uint16_t params_len, uint8_t *params,
+ void *user_data)
+{
+ struct avrcp_player *player = user_data;
+ struct list_values_req *req;
+
+ DBG("");
+
+ if (!params || params_len != sizeof(*req))
+ return -EINVAL;
+
+ req = (void *) params;
+ if (req->attr > AVRCP_ATTRIBUTE_LAST ||
+ req->attr == AVRCP_ATTRIBUTE_ILEGAL)
+ return -EINVAL;
+
+ if (!player->ind || !player->ind->list_values)
+ return -ENOSYS;
+
+ return player->ind->list_values(session, transaction, req->attr,
+ player->user_data);
+}
+
+static bool check_value(uint8_t attr, uint8_t number, const uint8_t *values)
+{
+ int i;
+
+ for (i = 0; i < number; i++) {
+ /* Check for invalid value */
+ switch (attr) {
+ case AVRCP_ATTRIBUTE_EQUALIZER:
+ if (values[i] < AVRCP_EQUALIZER_OFF ||
+ values[i] > AVRCP_EQUALIZER_ON)
+ return false;
+ break;
+ case AVRCP_ATTRIBUTE_REPEAT_MODE:
+ if (values[i] < AVRCP_REPEAT_MODE_OFF ||
+ values[i] > AVRCP_REPEAT_MODE_GROUP)
+ return false;
+ break;
+ case AVRCP_ATTRIBUTE_SHUFFLE:
+ if (values[i] < AVRCP_SHUFFLE_OFF ||
+ values[i] > AVRCP_SHUFFLE_GROUP)
+ return false;
+ break;
+ case AVRCP_ATTRIBUTE_SCAN:
+ if (values[i] < AVRCP_SCAN_OFF ||
+ values[i] > AVRCP_SCAN_GROUP)
+ return false;
+ break;
+ }
+ }
+
+ return true;
+}
+
+static ssize_t get_value_text(struct avrcp *session, uint8_t transaction,
+ uint16_t params_len, uint8_t *params,
+ void *user_data)
+{
+ struct avrcp_player *player = user_data;
+ struct get_value_text_req *req;
+
+ DBG("");
+
+ if (!player->ind || !player->ind->get_value_text)
+ return -ENOSYS;
+
+ if (!params || params_len < sizeof(*req))
+ return -EINVAL;
+
+ req = (void *) params;
+ if (params_len != sizeof(*req) + req->number)
+ return -EINVAL;
+
+ if (req->number > AVRCP_ATTRIBUTE_LAST ||
+ req->number == AVRCP_ATTRIBUTE_ILEGAL)
+ return -EINVAL;
+
+ if (!check_value(req->attr, req->number, req->values))
+ return -EINVAL;
+
+ return player->ind->get_value_text(session, transaction, params[0],
+ params[1], ¶ms[2],
+ player->user_data);
+}
+
+static ssize_t get_value(struct avrcp *session, uint8_t transaction,
+ uint16_t params_len, uint8_t *params,
+ void *user_data)
+{
+ struct avrcp_player *player = user_data;
+ struct get_value_req *req;
+
+ DBG("");
+
+ if (!player->ind || !player->ind->get_value)
+ return -ENOSYS;
+
+ if (!params || params_len < sizeof(*req))
+ return -EINVAL;
+
+ req = (void *) params;
+ if (params_len < sizeof(*req) + req->number)
+ return -EINVAL;
+
+ if (!check_attributes(req->number, req->attrs))
+ return -EINVAL;
+
+ return player->ind->get_value(session, transaction, params[0],
+ ¶ms[1], player->user_data);
+}
+
+static ssize_t set_value(struct avrcp *session, uint8_t transaction,
+ uint16_t params_len, uint8_t *params,
+ void *user_data)
+{
+ struct avrcp_player *player = user_data;
+ struct set_value_req *req;
+ uint8_t attrs[AVRCP_ATTRIBUTE_LAST];
+ uint8_t values[AVRCP_ATTRIBUTE_LAST];
+ int i;
+
+ DBG("");
+
+ if (!player->ind || !player->ind->set_value)
+ return -ENOSYS;
+
+ if (!params || params_len < sizeof(*req))
+ return -EINVAL;
+
+ req = (void *) params;
+ if (params_len < sizeof(*req) + req->number * sizeof(*req->values))
+ return -EINVAL;
+
+ for (i = 0; i < req->number; i++) {
+ attrs[i] = req->values[i].attr;
+ values[i] = req->values[i].value;
+
+ if (!check_value(attrs[i], 1, &values[i]))
+ return -EINVAL;
+ }
+
+ return player->ind->set_value(session, transaction, req->number,
+ attrs, values, player->user_data);
+}
+
+static ssize_t get_play_status(struct avrcp *session, uint8_t transaction,
+ uint16_t params_len, uint8_t *params,
+ void *user_data)
+{
+ struct avrcp_player *player = user_data;
+
+ DBG("");
+
+ if (!player->ind || !player->ind->get_play_status)
+ return -ENOSYS;
+
+ return player->ind->get_play_status(session, transaction,
+ player->user_data);
+}
+
+static bool parse_attributes(uint32_t *params, uint16_t params_len,
+ uint8_t number, uint32_t *attrs)
+{
+ int i;
+
+ for (i = 0; i < number && params_len >= sizeof(*attrs); i++,
+ params_len -= sizeof(*attrs)) {
+ attrs[i] = be32_to_cpu(params[i]);
+
+ if (attrs[i] == AVRCP_MEDIA_ATTRIBUTE_ILLEGAL ||
+ attrs[i] > AVRCP_MEDIA_ATTRIBUTE_LAST)
+ return false;
+ }
+
+ return true;
+}
+
+static ssize_t get_element_attributes(struct avrcp *session,
+ uint8_t transaction,
+ uint16_t params_len,
+ uint8_t *params,
+ void *user_data)
+{
+ struct avrcp_player *player = user_data;
+ struct get_element_attributes_req *req;
+ uint64_t uid;
+ uint32_t attrs[AVRCP_MEDIA_ATTRIBUTE_LAST];
+
+ DBG("");
+
+ if (!player->ind || !player->ind->get_element_attributes)
+ return -ENOSYS;
+
+ req = (void *) params;
+ if (!params || params_len < sizeof(*req))
+ return -EINVAL;
+
+ if (!parse_attributes(req->attrs, params_len - sizeof(*req),
+ req->number, attrs))
+ return -EINVAL;
+
+ uid = get_be64(params);
+
+ return player->ind->get_element_attributes(session, transaction, uid,
+ req->number, attrs,
+ player->user_data);
+}
+
+static ssize_t register_notification(struct avrcp *session, uint8_t transaction,
+ uint16_t params_len, uint8_t *params,
+ void *user_data)
+{
+ struct avrcp_player *player = user_data;
+ struct register_notification_req *req;
+ uint32_t interval;
+
+ DBG("");
+
+ if (!player->ind || !player->ind->register_notification)
+ return -ENOSYS;
+
+ if (!params || params_len != sizeof(*req))
+ return -EINVAL;
+
+ req = (void *) params;
+
+ interval = be32_to_cpu(req->interval);
+
+ return player->ind->register_notification(session, transaction,
+ req->event, interval,
+ player->user_data);
+}
+
+static ssize_t set_volume(struct avrcp *session, uint8_t transaction,
+ uint16_t params_len, uint8_t *params,
+ void *user_data)
+{
+ struct avrcp_player *player = user_data;
+ struct set_volume_req *req;
+ uint8_t volume;
+
+ DBG("");
+
+ if (!player->ind || !player->ind->set_volume)
+ return -ENOSYS;
+
+ if (!params || params_len != sizeof(volume))
+ return -EINVAL;
+
+ req = (void *) params;
+
+ volume = req->value & 0x7f;
+
+ return player->ind->set_volume(session, transaction, volume,
+ player->user_data);
+}
+
+static ssize_t set_addressed(struct avrcp *session, uint8_t transaction,
+ uint16_t params_len, uint8_t *params,
+ void *user_data)
+{
+ struct avrcp_player *player = user_data;
+ struct set_addressed_req *req;
+ uint16_t id;
+
+ DBG("");
+
+ if (!player->ind || !player->ind->set_addressed)
+ return -ENOSYS;
+
+ if (!params || params_len != sizeof(*req))
+ return -EINVAL;
+
+ req = (void *) params;
+
+ id = be16_to_cpu(req->id);
+
+ return player->ind->set_addressed(session, transaction, id,
+ player->user_data);
+}
+
+static void continuing_new(struct avrcp *session, uint8_t pdu_id,
+ const struct iovec *iov, int iov_cnt,
+ size_t offset)
+{
+ struct avrcp_continuing *continuing;
+ int i;
+ size_t len = 0;
+
+ continuing = g_new0(struct avrcp_continuing, 1);
+ continuing->pdu_id = pdu_id;
+
+ for (i = 0; i < iov_cnt; i++) {
+ if (i == 0 && offset) {
+ len += iov[i].iov_len - offset;
+ continue;
+ }
+
+ len += iov[i].iov_len;
+ }
+
+ continuing->pdu.iov_base = g_malloc0(len);
+
+ DBG("len %zu", len);
+
+ for (i = 0; i < iov_cnt; i++) {
+ if (i == 0 && offset) {
+ memcpy(continuing->pdu.iov_base,
+ iov[i].iov_base + offset,
+ iov[i].iov_len - offset);
+ continuing->pdu.iov_len += iov[i].iov_len - offset;
+ continue;
+ }
+
+ memcpy(continuing->pdu.iov_base + continuing->pdu.iov_len,
+ iov[i].iov_base, iov[i].iov_len);
+ continuing->pdu.iov_len += iov[i].iov_len;
+ }
+
+ session->continuing = continuing;
+}
+
+static int avrcp_send_internal(struct avrcp *session, uint8_t transaction,
+ uint8_t code, uint8_t subunit,
+ uint8_t pdu_id, uint8_t type,
+ const struct iovec *iov, int iov_cnt)
+{
+ struct iovec pdu[iov_cnt + 1];
+ struct avrcp_header hdr;
+ int i;
+
+ /*
+ * If a receiver receives a start fragment or non-fragmented AVRCP
+ * Specific AV/C message when it already has an incomplete fragment
+ * from that sender then the receiver shall consider the first PDU
+ * aborted.
+ */
+ if (session->continuing) {
+ continuing_free(session->continuing);
+ session->continuing = NULL;
+ }
+
+ memset(&hdr, 0, sizeof(hdr));
+
+ pdu[0].iov_base = &hdr;
+ pdu[0].iov_len = sizeof(hdr);
+
+ hdr.packet_type = type;
+
+ for (i = 0; i < iov_cnt; i++) {
+ pdu[i + 1].iov_base = iov[i].iov_base;
+
+ if (pdu[0].iov_len + hdr.params_len + iov[i].iov_len <=
+ session->control_mtu) {
+ pdu[i + 1].iov_len = iov[i].iov_len;
+ hdr.params_len += iov[i].iov_len;
+ if (hdr.packet_type != AVRCP_PACKET_TYPE_SINGLE)
+ hdr.packet_type = AVRCP_PACKET_TYPE_END;
+ continue;
+ }
+
+ /*
+ * Only send what can fit and store the remaining in the
+ * continuing iovec
+ */
+ pdu[i + 1].iov_len = session->control_mtu -
+ (pdu[0].iov_len + hdr.params_len);
+ hdr.params_len += pdu[i + 1].iov_len;
+
+ continuing_new(session, pdu_id, &iov[i], iov_cnt - i,
+ pdu[i + 1].iov_len);
+
+ hdr.packet_type = hdr.packet_type != AVRCP_PACKET_TYPE_SINGLE ?
+ AVRCP_PACKET_TYPE_CONTINUING :
+ AVRCP_PACKET_TYPE_START;
+ break;
+ }
+
+ hton24(hdr.company_id, IEEEID_BTSIG);
+ hdr.pdu_id = pdu_id;
+ hdr.params_len = htons(hdr.params_len);
+
+ return avctp_send_vendor(session->conn, transaction, code, subunit,
+ pdu, iov_cnt + 1);
+}
+
+static ssize_t request_continuing(struct avrcp *session, uint8_t transaction,
+ uint16_t params_len, uint8_t *params,
+ void *user_data)
+{
+ struct iovec iov;
+ int err;
+
+ DBG("");
+
+ if (!params || params_len != 1 || !session->continuing ||
+ session->continuing->pdu_id != params[0])
+ return -EINVAL;
+
+ iov.iov_base = session->continuing->pdu.iov_base;
+ iov.iov_len = session->continuing->pdu.iov_len;
+
+ DBG("len %zu", iov.iov_len);
+
+ session->continuing->pdu.iov_base = NULL;
+
+ err = avrcp_send_internal(session, transaction, AVC_CTYPE_STABLE,
+ AVC_SUBUNIT_PANEL, params[0],
+ AVRCP_PACKET_TYPE_CONTINUING, &iov, 1);
+
+ g_free(iov.iov_base);
+
+ if (err < 0)
+ return -EINVAL;
+
+ return 0;
+}
+
+static ssize_t abort_continuing(struct avrcp *session, uint8_t transaction,
+ uint16_t params_len, uint8_t *params,
+ void *user_data)
+{
+ DBG("");
+
+ if (!params || params_len != 1 || !session->continuing)
+ return -EINVAL;
+
+ continuing_free(session->continuing);
+ session->continuing = NULL;
+
+ avrcp_send_internal(session, transaction, AVC_CTYPE_ACCEPTED,
+ AVC_SUBUNIT_PANEL, AVRCP_ABORT_CONTINUING,
+ AVRCP_PACKET_TYPE_SINGLE, NULL, 0);
+
+ return 0;
+}
+
+static const struct avrcp_control_handler player_handlers[] = {
+ { AVRCP_GET_CAPABILITIES,
+ AVC_CTYPE_STATUS, AVC_CTYPE_STABLE,
+ get_capabilities },
+ { AVRCP_LIST_PLAYER_ATTRIBUTES,
+ AVC_CTYPE_STATUS, AVC_CTYPE_STABLE,
+ list_attributes },
+ { AVRCP_GET_PLAYER_ATTRIBUTE_TEXT,
+ AVC_CTYPE_STATUS, AVC_CTYPE_STABLE,
+ get_attribute_text },
+ { AVRCP_LIST_PLAYER_VALUES,
+ AVC_CTYPE_STATUS, AVC_CTYPE_STABLE,
+ list_values },
+ { AVRCP_GET_PLAYER_VALUE_TEXT,
+ AVC_CTYPE_STATUS, AVC_CTYPE_STABLE,
+ get_value_text },
+ { AVRCP_GET_CURRENT_PLAYER_VALUE,
+ AVC_CTYPE_STATUS, AVC_CTYPE_STABLE,
+ get_value },
+ { AVRCP_SET_PLAYER_VALUE,
+ AVC_CTYPE_CONTROL, AVC_CTYPE_STABLE,
+ set_value },
+ { AVRCP_GET_PLAY_STATUS,
+ AVC_CTYPE_STATUS, AVC_CTYPE_STABLE,
+ get_play_status },
+ { AVRCP_GET_ELEMENT_ATTRIBUTES,
+ AVC_CTYPE_STATUS, AVC_CTYPE_STABLE,
+ get_element_attributes },
+ { AVRCP_REGISTER_NOTIFICATION,
+ AVC_CTYPE_NOTIFY, AVC_CTYPE_INTERIM,
+ register_notification },
+ { AVRCP_SET_ABSOLUTE_VOLUME,
+ AVC_CTYPE_CONTROL, AVC_CTYPE_STABLE,
+ set_volume },
+ { AVRCP_SET_ADDRESSED_PLAYER,
+ AVC_CTYPE_CONTROL, AVC_CTYPE_STABLE,
+ set_addressed },
+ { AVRCP_REQUEST_CONTINUING,
+ AVC_CTYPE_CONTROL, AVC_CTYPE_STABLE,
+ request_continuing },
+ { AVRCP_ABORT_CONTINUING,
+ AVC_CTYPE_CONTROL, AVC_CTYPE_ACCEPTED,
+ abort_continuing },
+ { },
+};
+
+static void avrcp_set_control_handlers(struct avrcp *session,
+ const struct avrcp_control_handler *handlers,
+ void *user_data)
+{
+ session->control_handlers = handlers;
+ session->control_data = user_data;
+}
+
+static ssize_t set_browsed(struct avrcp *session, uint8_t transaction,
+ uint16_t params_len, uint8_t *params,
+ void *user_data)
+{
+ struct avrcp_player *player = user_data;
+ struct set_browsed_req *req;
+ uint16_t id;
+
+ DBG("");
+
+ if (!player->ind || !player->ind->set_browsed)
+ return -ENOSYS;
+
+ if (!params || params_len != sizeof(*req))
+ return -EINVAL;
+
+ req = (void *) params;
+
+ id = be16_to_cpu(req->id);
+
+ return player->ind->set_browsed(session, transaction, id,
+ player->user_data);
+}
+
+static ssize_t get_folder_items(struct avrcp *session, uint8_t transaction,
+ uint16_t params_len, uint8_t *params,
+ void *user_data)
+{
+ struct avrcp_player *player = user_data;
+ struct get_folder_items_req *req;
+ uint32_t start, end;
+ uint16_t number;
+ uint32_t attrs[AVRCP_MEDIA_ATTRIBUTE_LAST];
+ int i;
+
+ DBG("");
+
+ if (!player->ind || !player->ind->get_folder_items)
+ return -ENOSYS;
+
+ if (!params || params_len < sizeof(*req))
+ return -EINVAL;
+
+ req = (void *) params;
+
+ if (req->scope > AVRCP_MEDIA_NOW_PLAYING)
+ return -EBADRQC;
+
+ start = be32_to_cpu(req->start);
+ end = be32_to_cpu(req->end);
+
+ if (start > end)
+ return -ERANGE;
+
+ number = be16_to_cpu(req->number);
+
+ for (i = 0; i < number; i++) {
+ attrs[i] = be32_to_cpu(req->attrs[i]);
+
+ if (attrs[i] == AVRCP_MEDIA_ATTRIBUTE_ILLEGAL ||
+ attrs[i] > AVRCP_MEDIA_ATTRIBUTE_LAST)
+ return -EINVAL;
+ }
+
+ return player->ind->get_folder_items(session, transaction, req->scope,
+ start, end, number, attrs,
+ player->user_data);
+}
+
+static ssize_t change_path(struct avrcp *session, uint8_t transaction,
+ uint16_t params_len, uint8_t *params,
+ void *user_data)
+{
+ struct avrcp_player *player = user_data;
+ struct change_path_req *req;
+ uint16_t counter;
+ uint64_t uid;
+
+ DBG("");
+
+ if (!player->ind || !player->ind->change_path)
+ return -ENOSYS;
+
+ if (!params || params_len < sizeof(*req))
+ return -EINVAL;
+
+ req = (void *) params;
+
+ counter = be16_to_cpu(req->counter);
+ uid = be64_to_cpu(req->uid);
+
+ return player->ind->change_path(session, transaction, counter,
+ req->direction, uid, player->user_data);
+}
+
+static ssize_t get_item_attributes(struct avrcp *session, uint8_t transaction,
+ uint16_t params_len, uint8_t *params,
+ void *user_data)
+{
+ struct avrcp_player *player = user_data;
+ struct get_item_attributes_req *req;
+ uint64_t uid;
+ uint16_t counter;
+ uint32_t attrs[AVRCP_MEDIA_ATTRIBUTE_LAST];
+ int i;
+
+ DBG("");
+
+ if (!player->ind || !player->ind->get_item_attributes)
+ return -ENOSYS;
+
+ if (!params || params_len < sizeof(*req))
+ return -EINVAL;
+
+ req = (void *) params;
+
+ if (req->scope > AVRCP_MEDIA_NOW_PLAYING)
+ return -EBADRQC;
+
+ uid = be64_to_cpu(req->uid);
+ counter = be16_to_cpu(req->counter);
+
+ for (i = 0; i < req->number; i++) {
+ attrs[i] = be32_to_cpu(req->attrs[i]);
+
+ if (attrs[i] == AVRCP_MEDIA_ATTRIBUTE_ILLEGAL ||
+ attrs[i] > AVRCP_MEDIA_ATTRIBUTE_LAST)
+ return -EINVAL;
+ }
+
+ return player->ind->get_item_attributes(session, transaction,
+ req->scope, uid, counter,
+ req->number, attrs,
+ player->user_data);
+}
+
+static ssize_t play_item(struct avrcp *session, uint8_t transaction,
+ uint16_t params_len, uint8_t *params,
+ void *user_data)
+{
+ struct avrcp_player *player = user_data;
+ struct play_item_req *req;
+ uint64_t uid;
+ uint16_t counter;
+
+ DBG("");
+
+ if (!player->ind || !player->ind->play_item)
+ return -ENOSYS;
+
+ if (!params || params_len < sizeof(*req))
+ return -EINVAL;
+
+ req = (void *) params;
+
+ if (req->scope > AVRCP_MEDIA_NOW_PLAYING)
+ return -EBADRQC;
+
+ uid = be64_to_cpu(req->uid);
+ counter = be16_to_cpu(req->counter);
+
+ return player->ind->play_item(session, transaction, req->scope, uid,
+ counter, player->user_data);
+}
+
+static ssize_t search(struct avrcp *session, uint8_t transaction,
+ uint16_t params_len, uint8_t *params,
+ void *user_data)
+{
+ struct avrcp_player *player = user_data;
+ struct search_req *req;
+ char *string;
+ uint16_t len;
+ int ret;
+
+ DBG("");
+
+ if (!player->ind || !player->ind->search)
+ return -ENOSYS;
+
+ if (!params || params_len < sizeof(*req))
+ return -EINVAL;
+
+ req = (void *) params;
+
+ len = be16_to_cpu(req->len);
+ if (!len)
+ return -EINVAL;
+
+ string = strndup(req->string, len);
+
+ ret = player->ind->search(session, transaction, string,
+ player->user_data);
+
+ free(string);
+
+ return ret;
+}
+
+static ssize_t add_to_now_playing(struct avrcp *session, uint8_t transaction,
+ uint16_t params_len, uint8_t *params,
+ void *user_data)
+{
+ struct avrcp_player *player = user_data;
+ struct add_to_now_playing_req *req;
+ uint64_t uid;
+ uint16_t counter;
+
+ DBG("");
+
+ if (!player->ind || !player->ind->add_to_now_playing)
+ return -ENOSYS;
+
+ if (!params || params_len < sizeof(*req))
+ return -EINVAL;
+
+ req = (void *) params;
+
+ if (req->scope > AVRCP_MEDIA_NOW_PLAYING)
+ return -EBADRQC;
+
+ uid = be64_to_cpu(req->uid);
+ counter = be16_to_cpu(req->counter);
+
+ return player->ind->add_to_now_playing(session, transaction, req->scope,
+ uid, counter,
+ player->user_data);
+}
+
+static const struct avrcp_browsing_handler browsing_handlers[] = {
+ { AVRCP_SET_BROWSED_PLAYER, set_browsed },
+ { AVRCP_GET_FOLDER_ITEMS, get_folder_items },
+ { AVRCP_CHANGE_PATH, change_path },
+ { AVRCP_GET_ITEM_ATTRIBUTES, get_item_attributes },
+ { AVRCP_PLAY_ITEM, play_item },
+ { AVRCP_SEARCH, search },
+ { AVRCP_ADD_TO_NOW_PLAYING, add_to_now_playing },
+ { },
+};
+
+static void avrcp_set_browsing_handlers(struct avrcp *session,
+ const struct avrcp_browsing_handler *handlers,
+ void *user_data)
+{
+ session->browsing_handlers = handlers;
+ session->browsing_data = user_data;
+}
+
+void avrcp_register_player(struct avrcp *session,
+ const struct avrcp_control_ind *ind,
+ const struct avrcp_control_cfm *cfm,
+ void *user_data)
+{
+ struct avrcp_player *player;
+
+ player = g_new0(struct avrcp_player, 1);
+ player->ind = ind;
+ player->cfm = cfm;
+ player->user_data = user_data;
+
+ avrcp_set_control_handlers(session, player_handlers, player);
+ avrcp_set_browsing_handlers(session, browsing_handlers, player);
+ session->player = player;
+}
+
+void avrcp_set_passthrough_handlers(struct avrcp *session,
+ const struct avrcp_passthrough_handler *handlers,
+ void *user_data)
+{
+ session->passthrough_handlers = handlers;
+ session->passthrough_data = user_data;
+}
+
+int avrcp_init_uinput(struct avrcp *session, const char *name,
+ const char *address)
+{
+ return avctp_init_uinput(session->conn, name, address);
+}
+
+int avrcp_send(struct avrcp *session, uint8_t transaction, uint8_t code,
+ uint8_t subunit, uint8_t pdu_id,
+ const struct iovec *iov, int iov_cnt)
+{
+ return avrcp_send_internal(session, transaction, code, subunit, pdu_id,
+ AVRCP_PACKET_TYPE_SINGLE, iov, iov_cnt);
+}
+
+static int status2errno(uint8_t status)
+{
+ switch (status) {
+ case AVRCP_STATUS_INVALID_COMMAND:
+ return -ENOSYS;
+ case AVRCP_STATUS_INVALID_PARAM:
+ return -EINVAL;
+ case AVRCP_STATUS_SUCCESS:
+ return 0;
+ case AVRCP_STATUS_NOT_DIRECTORY:
+ return -ENOTDIR;
+ case AVRCP_STATUS_INVALID_SCOPE:
+ return -EBADRQC;
+ case AVRCP_STATUS_OUT_OF_BOUNDS:
+ return -ERANGE;
+ case AVRCP_STATUS_INTERNAL_ERROR:
+ case AVRCP_STATUS_INVALID_PLAYER_ID:
+ case AVRCP_STATUS_PLAYER_NOT_BROWSABLE:
+ case AVRCP_STATUS_NO_AVAILABLE_PLAYERS:
+ case AVRCP_STATUS_ADDRESSED_PLAYER_CHANGED:
+ return -EPERM;
+ default:
+ return -EPROTO;
+ }
+}
+
+static int parse_status(struct avrcp_header *pdu)
+{
+ if (pdu->params_len < 1)
+ return -EPROTO;
+
+ return status2errno(pdu->params[0]);
+}
+
+static int parse_browsing_status(struct avrcp_browsing_header *pdu)
+{
+ if (pdu->params_len < 1)
+ return -EPROTO;
+
+ return status2errno(pdu->params[0]);
+}
+
+static int avrcp_send_req(struct avrcp *session, uint8_t code, uint8_t subunit,
+ uint8_t pdu_id, const struct iovec *iov,
+ int iov_cnt, avctp_rsp_cb func,
+ void *user_data)
+{
+ struct iovec pdu[iov_cnt + 1];
+ struct avrcp_header hdr;
+ int i;
+
+ memset(&hdr, 0, sizeof(hdr));
+
+ pdu[0].iov_base = &hdr;
+ pdu[0].iov_len = sizeof(hdr);
+
+ for (i = 0; i < iov_cnt; i++) {
+ pdu[i + 1].iov_base = iov[i].iov_base;
+ pdu[i + 1].iov_len = iov[i].iov_len;
+ hdr.params_len += iov[i].iov_len;
+ }
+
+ hton24(hdr.company_id, IEEEID_BTSIG);
+ hdr.pdu_id = pdu_id;
+ hdr.packet_type = AVRCP_PACKET_TYPE_SINGLE;
+ hdr.params_len = htons(hdr.params_len);
+
+ return avctp_send_vendor_req(session->conn, code, subunit, pdu,
+ iov_cnt + 1, func, user_data);
+}
+
+static int avrcp_send_browsing_req(struct avrcp *session, uint8_t pdu_id,
+ const struct iovec *iov, int iov_cnt,
+ avctp_browsing_rsp_cb func,
+ void *user_data)
+{
+ struct iovec pdu[iov_cnt + 1];
+ struct avrcp_browsing_header hdr;
+ int i;
+
+ memset(&hdr, 0, sizeof(hdr));
+
+ for (i = 0; i < iov_cnt; i++) {
+ pdu[i + 1].iov_base = iov[i].iov_base;
+ pdu[i + 1].iov_len = iov[i].iov_len;
+ hdr.params_len += iov[i].iov_len;
+ }
+
+ hdr.pdu_id = pdu_id;
+ hdr.params_len = htons(hdr.params_len);
+
+ pdu[0].iov_base = &hdr;
+ pdu[0].iov_len = sizeof(hdr);
+
+ return avctp_send_browsing_req(session->conn, pdu, iov_cnt + 1,
+ func, user_data);
+}
+
+static int avrcp_send_browsing(struct avrcp *session, uint8_t transaction,
+ uint8_t pdu_id, const struct iovec *iov,
+ int iov_cnt)
+{
+ struct iovec pdu[iov_cnt + 1];
+ struct avrcp_browsing_header hdr;
+ int i;
+
+ memset(&hdr, 0, sizeof(hdr));
+
+ for (i = 0; i < iov_cnt; i++) {
+ pdu[i + 1].iov_base = iov[i].iov_base;
+ pdu[i + 1].iov_len = iov[i].iov_len;
+ hdr.params_len += iov[i].iov_len;
+ }
+
+ hdr.pdu_id = pdu_id;
+ hdr.params_len = htons(hdr.params_len);
+
+ pdu[0].iov_base = &hdr;
+ pdu[0].iov_len = sizeof(hdr);
+
+ return avctp_send_browsing(session->conn, transaction, pdu,
+ iov_cnt + 1);
+}
+
+static gboolean get_capabilities_rsp(struct avctp *conn,
+ uint8_t code, uint8_t subunit,
+ uint8_t *operands, size_t operand_count,
+ void *user_data)
+{
+ struct avrcp *session = user_data;
+ struct avrcp_player *player = session->player;
+ struct avrcp_header *pdu;
+ struct get_capabilities_rsp *rsp;
+ uint8_t number = 0;
+ uint8_t *params = NULL;
+ int err;
+
+ DBG("");
+
+ if (!player || !player->cfm || !player->cfm->get_capabilities)
+ return FALSE;
+
+ pdu = parse_pdu(operands, operand_count);
+ if (!pdu) {
+ err = -EPROTO;
+ goto done;
+ }
+
+ if (code == AVC_CTYPE_REJECTED) {
+ err = parse_status(pdu);
+ goto done;
+ }
+
+ if (pdu->params_len < sizeof(*rsp)) {
+ err = -EPROTO;
+ goto done;
+ }
+
+ rsp = (void *) pdu->params;
+
+ switch (rsp->cap) {
+ case CAP_COMPANY_ID:
+ case CAP_EVENTS_SUPPORTED:
+ break;
+ default:
+ err = -EPROTO;
+ goto done;
+ }
+
+ if (rsp->number > 0) {
+ number = rsp->number;
+ params = rsp->params;
+ }
+
+ err = 0;
+
+done:
+ player->cfm->get_capabilities(session, err, number, params,
+ player->user_data);
+
+ return FALSE;
+}
+
+
+int avrcp_get_capabilities(struct avrcp *session, uint8_t param)
+{
+ struct iovec iov;
+ struct get_capabilities_req req;
+
+ req.cap = param;
+
+ iov.iov_base = &req;
+ iov.iov_len = sizeof(req);
+
+ return avrcp_send_req(session, AVC_CTYPE_STATUS, AVC_SUBUNIT_PANEL,
+ AVRCP_GET_CAPABILITIES, &iov, 1,
+ get_capabilities_rsp, session);
+}
+
+static gboolean register_notification_rsp(struct avctp *conn,
+ uint8_t code, uint8_t subunit,
+ uint8_t *operands, size_t operand_count,
+ void *user_data)
+{
+ struct avrcp *session = user_data;
+ struct avrcp_player *player = session->player;
+ struct avrcp_header *pdu;
+ struct register_notification_rsp *rsp;
+ uint8_t event = 0;
+ uint16_t value16, value16_2[2];
+ uint32_t value32;
+ uint64_t value64;
+ uint8_t *params = NULL;
+ int err;
+
+ DBG("");
+
+ if (!player || !player->cfm || !player->cfm->register_notification)
+ return FALSE;
+
+ pdu = parse_pdu(operands, operand_count);
+ if (!pdu) {
+ err = -EPROTO;
+ goto done;
+ }
+
+ if (code == AVC_CTYPE_REJECTED) {
+ err = parse_status(pdu);
+ goto done;
+ }
+
+ if (pdu->params_len < sizeof(*rsp)) {
+ err = -EPROTO;
+ goto done;
+ }
+
+ rsp = (void *) pdu->params;
+ event = rsp->event;
+
+ if (event > AVRCP_EVENT_LAST) {
+ err = -EPROTO;
+ goto done;
+ }
+
+ switch (event) {
+ case AVRCP_EVENT_STATUS_CHANGED:
+ if (pdu->params_len != sizeof(*rsp) + sizeof(uint8_t)) {
+ err = -EPROTO;
+ goto done;
+ }
+ params = rsp->data;
+ break;
+ case AVRCP_EVENT_VOLUME_CHANGED:
+ if (pdu->params_len != sizeof(*rsp) + sizeof(uint8_t)) {
+ err = -EPROTO;
+ goto done;
+ }
+ params = rsp->data;
+ params[0] &= 0x7f;
+ break;
+ case AVRCP_EVENT_TRACK_CHANGED:
+ if (pdu->params_len != sizeof(*rsp) + sizeof(value64)) {
+ err = -EPROTO;
+ goto done;
+ }
+ value64 = get_be64(rsp->data);
+ params = (uint8_t *) &value64;
+ break;
+ case AVRCP_EVENT_PLAYBACK_POS_CHANGED:
+ if (pdu->params_len != sizeof(*rsp) + sizeof(value32)) {
+ err = -EPROTO;
+ goto done;
+ }
+ value32 = get_be32(rsp->data);
+ params = (uint8_t *) &value32;
+ break;
+ case AVRCP_EVENT_ADDRESSED_PLAYER_CHANGED:
+ if (pdu->params_len < sizeof(*rsp) + sizeof(value16_2)) {
+ err = -EPROTO;
+ goto done;
+ }
+ value16_2[0] = get_be16(rsp->data);
+ value16_2[1] = get_be16(rsp->data + 2);
+ params = (uint8_t *) value16_2;
+ break;
+ case AVRCP_EVENT_SETTINGS_CHANGED:
+ if (pdu->params_len < sizeof(*rsp) + sizeof(uint8_t)) {
+ err = -EPROTO;
+ goto done;
+ }
+ params = rsp->data;
+ break;
+ case AVRCP_EVENT_UIDS_CHANGED:
+ if (pdu->params_len < sizeof(*rsp) + sizeof(value16)) {
+ err = -EPROTO;
+ goto done;
+ }
+ value16 = get_be16(rsp->data);
+ params = (uint8_t *) &value16;
+ break;
+ }
+
+ err = 0;
+
+done:
+ return player->cfm->register_notification(session, err, code, event,
+ params, player->user_data);
+}
+
+int avrcp_register_notification(struct avrcp *session, uint8_t event,
+ uint32_t interval)
+{
+ struct iovec iov;
+ struct register_notification_req req;
+
+ if (event > AVRCP_EVENT_LAST)
+ return -EINVAL;
+
+ req.event = event;
+ req.interval = cpu_to_be32(interval);
+
+ iov.iov_base = &req;
+ iov.iov_len = sizeof(req);
+
+ return avrcp_send_req(session, AVC_CTYPE_NOTIFY, AVC_SUBUNIT_PANEL,
+ AVRCP_REGISTER_NOTIFICATION, &iov, 1,
+ register_notification_rsp, session);
+}
+
+static gboolean list_attributes_rsp(struct avctp *conn,
+ uint8_t code, uint8_t subunit,
+ uint8_t *operands, size_t operand_count,
+ void *user_data)
+{
+ struct avrcp *session = user_data;
+ struct avrcp_player *player = session->player;
+ struct avrcp_header *pdu = (void *) operands;
+ struct list_attributes_rsp *rsp;
+ uint8_t number = 0;
+ uint8_t *attrs = NULL;
+ int err;
+
+ DBG("");
+
+ if (!player || !player->cfm || !player->cfm->list_attributes)
+ return FALSE;
+
+ pdu = parse_pdu(operands, operand_count);
+ if (!pdu) {
+ err = -EPROTO;
+ goto done;
+ }
+
+ if (code == AVC_CTYPE_REJECTED) {
+ err = parse_status(pdu);
+ goto done;
+ }
+
+ rsp = (void *) pdu->params;
+
+ if (pdu->params_len < sizeof(*rsp)) {
+ err = -EPROTO;
+ goto done;
+ }
+
+ number = rsp->number;
+ if (number > 0)
+ attrs = rsp->params;
+
+ err = 0;
+
+done:
+ player->cfm->list_attributes(session, err, number, attrs,
+ player->user_data);
+
+ return FALSE;
+}
+
+int avrcp_list_player_attributes(struct avrcp *session)
+{
+ return avrcp_send_req(session, AVC_CTYPE_STATUS, AVC_SUBUNIT_PANEL,
+ AVRCP_LIST_PLAYER_ATTRIBUTES, NULL, 0,
+ list_attributes_rsp, session);
+}
+
+static int parse_text_rsp(struct avrcp_header *pdu, uint8_t *number,
+ uint8_t *attrs, char **text)
+{
+ uint8_t *ptr;
+ uint16_t params_len;
+ int i;
+
+ if (pdu->params_len < 1)
+ return -EPROTO;
+
+ *number = pdu->params[0];
+ if (*number > AVRCP_ATTRIBUTE_LAST) {
+ *number = 0;
+ return -EPROTO;
+ }
+
+ params_len = pdu->params_len - 1;
+ for (i = 0, ptr = &pdu->params[1]; i < *number && params_len > 0; i++) {
+ struct text_value *val;
+
+ if (params_len < sizeof(*val))
+ goto fail;
+
+ val = (void *) ptr;
+
+ attrs[i] = val->attr;
+
+ params_len -= sizeof(*val);
+ ptr += sizeof(*val);
+
+ if (val->len > params_len)
+ goto fail;
+
+ if (val->len > 0) {
+ text[i] = g_strndup(val->data, val->len);
+ params_len -= val->len;
+ ptr += val->len;
+ }
+ }
+
+ if (i != *number)
+ goto fail;
+
+ return 0;
+
+fail:
+ for (i -= 1; i >= 0; i--)
+ g_free(text[i]);
+
+ *number = 0;
+
+ return -EPROTO;
+}
+
+static gboolean get_attribute_text_rsp(struct avctp *conn,
+ uint8_t code, uint8_t subunit,
+ uint8_t *operands, size_t operand_count,
+ void *user_data)
+{
+ struct avrcp *session = user_data;
+ struct avrcp_player *player = session->player;
+ struct avrcp_header *pdu;
+ uint8_t number = 0;
+ uint8_t attrs[AVRCP_ATTRIBUTE_LAST];
+ char *text[AVRCP_ATTRIBUTE_LAST];
+ int err;
+
+ DBG("");
+
+ if (!player || !player->cfm || !player->cfm->get_attribute_text)
+ return FALSE;
+
+ pdu = parse_pdu(operands, operand_count);
+ if (!pdu) {
+ err = -EPROTO;
+ goto done;
+ }
+
+ if (code == AVC_CTYPE_REJECTED) {
+ err = parse_status(pdu);
+ goto done;
+ }
+
+ err = parse_text_rsp(pdu, &number, attrs, text);
+
+done:
+ player->cfm->get_attribute_text(session, err, number, attrs, text,
+ player->user_data);
+
+ return FALSE;
+}
+
+int avrcp_get_player_attribute_text(struct avrcp *session, uint8_t number,
+ uint8_t *attrs)
+{
+ struct iovec iov[2];
+
+ if (!number || number > AVRCP_ATTRIBUTE_LAST)
+ return -EINVAL;
+
+ iov[0].iov_base = &number;
+ iov[0].iov_len = sizeof(number);
+
+ iov[1].iov_base = attrs;
+ iov[1].iov_len = number;
+
+ return avrcp_send_req(session, AVC_CTYPE_STATUS, AVC_SUBUNIT_PANEL,
+ AVRCP_GET_PLAYER_ATTRIBUTE_TEXT, iov, 2,
+ get_attribute_text_rsp, session);
+}
+
+static gboolean list_values_rsp(struct avctp *conn,
+ uint8_t code, uint8_t subunit,
+ uint8_t *operands, size_t operand_count,
+ void *user_data)
+{
+ struct avrcp *session = user_data;
+ struct avrcp_player *player = session->player;
+ struct avrcp_header *pdu;
+ struct list_values_rsp *rsp;
+ uint8_t number = 0;
+ uint8_t *values = NULL;
+ int err;
+
+ DBG("");
+
+ if (!player || !player->cfm || !player->cfm->list_values)
+ return FALSE;
+
+ pdu = parse_pdu(operands, operand_count);
+ if (!pdu) {
+ err = -EPROTO;
+ goto done;
+ }
+
+ if (code == AVC_CTYPE_REJECTED) {
+ err = parse_status(pdu);
+ goto done;
+ }
+
+ if (pdu->params_len < sizeof(*rsp)) {
+ err = -EPROTO;
+ goto done;
+ }
+
+ rsp = (void *) pdu->params;
+
+ if (rsp->number > 0) {
+ number = rsp->number;
+ values = rsp->params;
+ }
+
+ err = 0;
+
+done:
+ player->cfm->list_values(session, err, number, values,
+ player->user_data);
+
+ return FALSE;
+}
+
+int avrcp_list_player_values(struct avrcp *session, uint8_t attr)
+{
+ struct iovec iov;
+
+ iov.iov_base = &attr;
+ iov.iov_len = sizeof(attr);
+
+ return avrcp_send_req(session, AVC_CTYPE_STATUS, AVC_SUBUNIT_PANEL,
+ AVRCP_LIST_PLAYER_VALUES, &iov, 1,
+ list_values_rsp, session);
+}
+
+static gboolean get_value_text_rsp(struct avctp *conn,
+ uint8_t code, uint8_t subunit,
+ uint8_t *operands, size_t operand_count,
+ void *user_data)
+{
+ struct avrcp *session = user_data;
+ struct avrcp_player *player = session->player;
+ struct avrcp_header *pdu;
+ uint8_t number = 0;
+ uint8_t values[AVRCP_ATTRIBUTE_LAST];
+ char *text[AVRCP_ATTRIBUTE_LAST];
+ int err;
+
+ DBG("");
+
+ if (!player || !player->cfm || !player->cfm->get_value_text)
+ return FALSE;
+
+ pdu = parse_pdu(operands, operand_count);
+ if (!pdu) {
+ err = -EPROTO;
+ goto done;
+ }
+
+ if (code == AVC_CTYPE_REJECTED) {
+ err = parse_status(pdu);
+ goto done;
+ }
+
+ err = parse_text_rsp(pdu, &number, values, text);
+
+done:
+ player->cfm->get_value_text(session, err, number, values, text,
+ player->user_data);
+
+ return FALSE;
+}
+
+int avrcp_get_player_value_text(struct avrcp *session, uint8_t attr,
+ uint8_t number, uint8_t *values)
+{
+ struct iovec iov[2];
+ struct get_value_text_req req;
+
+ if (!number)
+ return -EINVAL;
+
+ req.attr = attr;
+ req.number = number;
+
+ iov[0].iov_base = &req;
+ iov[0].iov_len = sizeof(req);
+
+ iov[1].iov_base = values;
+ iov[1].iov_len = number;
+
+ return avrcp_send_req(session, AVC_CTYPE_STATUS, AVC_SUBUNIT_PANEL,
+ AVRCP_GET_PLAYER_VALUE_TEXT, iov, 2,
+ get_value_text_rsp, session);
+}
+
+static int parse_value(struct avrcp_header *pdu, uint8_t *number,
+ uint8_t *attrs, uint8_t *values)
+{
+ int i;
+ struct value_rsp *rsp;
+
+ if (pdu->params_len < sizeof(*rsp))
+ return -EPROTO;
+
+ rsp = (void *) pdu->params;
+
+ /*
+ * Check if PDU is big enough to hold the number of (attribute, value)
+ * tuples.
+ */
+ if (rsp->number > AVRCP_ATTRIBUTE_LAST ||
+ sizeof(*rsp) + rsp->number * 2 != pdu->params_len) {
+ *number = 0;
+ return -EPROTO;
+ }
+
+ for (i = 0; i < rsp->number; i++) {
+ attrs[i] = rsp->values[i].attr;
+ values[i] = rsp->values[i].value;
+ }
+
+ *number = rsp->number;
+
+ return 0;
+}
+
+static gboolean get_value_rsp(struct avctp *conn,
+ uint8_t code, uint8_t subunit,
+ uint8_t *operands, size_t operand_count,
+ void *user_data)
+{
+ struct avrcp *session = user_data;
+ struct avrcp_player *player = session->player;
+ struct avrcp_header *pdu;
+ uint8_t number = 0;
+ uint8_t attrs[AVRCP_ATTRIBUTE_LAST];
+ uint8_t values[AVRCP_ATTRIBUTE_LAST];
+ int err;
+
+ DBG("");
+
+ if (!player || !player->cfm || !player->cfm->get_value)
+ return FALSE;
+
+ pdu = parse_pdu(operands, operand_count);
+ if (!pdu) {
+ err = -EPROTO;
+ goto done;
+ }
+
+ if (code == AVC_CTYPE_REJECTED) {
+ err = parse_status(pdu);
+ goto done;
+ }
+
+ err = parse_value(pdu, &number, attrs, values);
+
+done:
+ player->cfm->get_value(session, err, number, attrs, values,
+ player->user_data);
+
+ return FALSE;
+}
+
+int avrcp_get_current_player_value(struct avrcp *session, uint8_t number,
+ uint8_t *attrs)
+
+{
+ struct iovec iov[2];
+
+ if (number > AVRCP_ATTRIBUTE_LAST)
+ return -EINVAL;
+
+ iov[0].iov_base = &number;
+ iov[0].iov_len = sizeof(number);
+
+ iov[1].iov_base = attrs;
+ iov[1].iov_len = number;
+
+ return avrcp_send_req(session, AVC_CTYPE_STATUS, AVC_SUBUNIT_PANEL,
+ AVRCP_GET_CURRENT_PLAYER_VALUE, iov, 2,
+ get_value_rsp, session);
+}
+
+static gboolean set_value_rsp(struct avctp *conn,
+ uint8_t code, uint8_t subunit,
+ uint8_t *operands, size_t operand_count,
+ void *user_data)
+{
+ struct avrcp *session = user_data;
+ struct avrcp_player *player = session->player;
+ struct avrcp_header *pdu;
+ int err;
+
+ DBG("");
+
+ if (!player || !player->cfm || !player->cfm->set_value)
+ return FALSE;
+
+ pdu = parse_pdu(operands, operand_count);
+ if (!pdu) {
+ err = -EPROTO;
+ goto done;
+ }
+
+ if (code == AVC_CTYPE_REJECTED) {
+ err = parse_status(pdu);
+ goto done;
+ }
+
+ err = 0;
+
+done:
+ player->cfm->set_value(session, err, player->user_data);
+
+ return FALSE;
+}
+
+int avrcp_set_player_value(struct avrcp *session, uint8_t number,
+ uint8_t *attrs, uint8_t *values)
+{
+ struct iovec iov[2];
+ struct attr_value val[AVRCP_ATTRIBUTE_LAST];
+ int i;
+
+ if (number > AVRCP_ATTRIBUTE_LAST)
+ return -EINVAL;
+
+ iov[0].iov_base = &number;
+ iov[0].iov_len = sizeof(number);
+
+ for (i = 0; i < number; i++) {
+ val[i].attr = attrs[i];
+ val[i].value = values[i];
+ }
+
+ iov[1].iov_base = val;
+ iov[1].iov_len = sizeof(*val) * number;
+
+ return avrcp_send_req(session, AVC_CTYPE_CONTROL, AVC_SUBUNIT_PANEL,
+ AVRCP_SET_PLAYER_VALUE, iov, 2,
+ set_value_rsp, session);
+}
+
+static gboolean get_play_status_rsp(struct avctp *conn,
+ uint8_t code, uint8_t subunit,
+ uint8_t *operands, size_t operand_count,
+ void *user_data)
+{
+ struct avrcp *session = user_data;
+ struct avrcp_player *player = session->player;
+ struct avrcp_header *pdu;
+ struct get_play_status_rsp *rsp;
+ uint8_t status = 0;
+ uint32_t position = 0;
+ uint32_t duration = 0;
+ int err;
+
+ DBG("");
+
+ if (!player || !player->cfm || !player->cfm->get_play_status)
+ return FALSE;
+
+ pdu = parse_pdu(operands, operand_count);
+ if (!pdu) {
+ err = -EPROTO;
+ goto done;
+ }
+
+ if (code == AVC_CTYPE_REJECTED) {
+ err = parse_status(pdu);
+ goto done;
+ }
+
+ if (pdu->params_len < sizeof(*rsp)) {
+ err = -EPROTO;
+ goto done;
+ }
+
+ rsp = (void *) pdu->params;
+
+ duration = be32_to_cpu(rsp->duration);
+ position = be32_to_cpu(rsp->position);
+ status = rsp->status;
+ err = 0;
+
+done:
+ player->cfm->get_play_status(session, err, status, position, duration,
+ player->user_data);
+
+ return FALSE;
+}
+
+int avrcp_get_play_status(struct avrcp *session)
+{
+ return avrcp_send_req(session, AVC_CTYPE_STATUS, AVC_SUBUNIT_PANEL,
+ AVRCP_GET_PLAY_STATUS, NULL, 0,
+ get_play_status_rsp, session);
+}
+
+static gboolean set_volume_rsp(struct avctp *conn,
+ uint8_t code, uint8_t subunit,
+ uint8_t *operands, size_t operand_count,
+ void *user_data)
+{
+ struct avrcp *session = user_data;
+ struct avrcp_player *player = session->player;
+ struct avrcp_header *pdu;
+ struct set_volume_rsp *rsp;
+ uint8_t value = 0;
+ int err;
+
+ DBG("");
+
+ if (!player || !player->cfm || !player->cfm->set_volume)
+ return FALSE;
+
+ pdu = parse_pdu(operands, operand_count);
+ if (!pdu) {
+ err = -EPROTO;
+ goto done;
+ }
+
+ if (code == AVC_CTYPE_REJECTED) {
+ err = parse_status(pdu);
+ goto done;
+ }
+
+ if (pdu->params_len < sizeof(*rsp)) {
+ err = -EPROTO;
+ goto done;
+ }
+
+ rsp = (void *) pdu->params;
+
+ value = rsp->value & 0x7f;
+ err = 0;
+
+done:
+ player->cfm->set_volume(session, err, value, player->user_data);
+
+ return FALSE;
+}
+
+int avrcp_set_volume(struct avrcp *session, uint8_t volume)
+{
+ struct iovec iov;
+
+ iov.iov_base = &volume;
+ iov.iov_len = sizeof(volume);
+
+ return avrcp_send_req(session, AVC_CTYPE_CONTROL, AVC_SUBUNIT_PANEL,
+ AVRCP_SET_ABSOLUTE_VOLUME, &iov, 1,
+ set_volume_rsp, session);
+}
+
+static int parse_attribute_list(uint8_t *params, uint16_t params_len,
+ uint8_t number, uint32_t *attrs, char **text)
+{
+ struct media_item *item;
+ int i;
+
+ if (number > AVRCP_MEDIA_ATTRIBUTE_LAST)
+ return -EPROTO;
+
+ for (i = 0; i < number && params_len >= sizeof(*item); i++) {
+ item = (void *) params;
+
+ item->attr = be32_to_cpu(item->attr);
+ item->charset = be16_to_cpu(item->charset);
+ item->len = be16_to_cpu(item->len);
+
+ params_len -= sizeof(*item);
+ params += sizeof(*item);
+ if (item->len > params_len)
+ goto fail;
+
+ if (item->len > 0) {
+ text[i] = g_strndup(item->data, item->len);
+ attrs[i] = item->attr;
+ params_len -= item->len;
+ params += item->len;
+ } else {
+ text[i] = NULL;
+ attrs[i] = 0;
+ }
+ }
+
+ return 0;
+
+fail:
+ for (i -= 1; i >= 0; i--)
+ g_free(text[i]);
+
+ return -EPROTO;
+}
+
+static void free_attribute_list(uint8_t number, char **text)
+{
+ while(number--)
+ g_free(text[number]);
+}
+
+static int parse_elements(struct avrcp_header *pdu, uint8_t *number,
+ uint32_t *attrs, char **text)
+{
+ struct get_element_attributes_rsp *rsp;
+
+ if (pdu->params_len < sizeof(*rsp))
+ return -EPROTO;
+
+ rsp = (void *) pdu->params;
+ if (rsp->number > AVRCP_MEDIA_ATTRIBUTE_LAST)
+ return -EPROTO;
+
+ *number = rsp->number;
+
+ return parse_attribute_list(pdu->params + sizeof(*rsp),
+ pdu->params_len - sizeof(*rsp),
+ *number, attrs, text);
+}
+
+static int parse_items(struct avrcp_browsing_header *pdu, uint8_t *number,
+ uint32_t *attrs, char **text)
+{
+ struct get_item_attributes_rsp *rsp;
+
+ if (pdu->params_len < sizeof(*rsp))
+ return -EPROTO;
+
+ rsp = (void *) pdu->params;
+
+ if (rsp->number > AVRCP_MEDIA_ATTRIBUTE_LAST)
+ return -EPROTO;
+
+ *number = rsp->number;
+
+ return parse_attribute_list(pdu->params + sizeof(*rsp),
+ pdu->params_len - sizeof(*rsp),
+ *number, attrs, text);
+}
+
+static gboolean get_element_attributes_rsp(struct avctp *conn,
+ uint8_t code, uint8_t subunit,
+ uint8_t *operands, size_t operand_count,
+ void *user_data)
+{
+ struct avrcp *session = user_data;
+ struct avrcp_player *player = session->player;
+ struct avrcp_header *pdu;
+ uint8_t number = 0;
+ uint32_t attrs[AVRCP_MEDIA_ATTRIBUTE_LAST];
+ char *text[AVRCP_MEDIA_ATTRIBUTE_LAST];
+ int err;
+
+ DBG("");
+
+ if (!player || !player->cfm || !player->cfm->get_element_attributes)
+ return FALSE;
+
+ pdu = parse_pdu(operands, operand_count);
+ if (!pdu) {
+ err = -EPROTO;
+ goto done;
+ }
+
+ if (code == AVC_CTYPE_REJECTED) {
+ err = parse_status(pdu);
+ goto done;
+ }
+
+ err = parse_elements(pdu, &number, attrs, text);
+
+done:
+ player->cfm->get_element_attributes(session, err, number, attrs, text,
+ player->user_data);
+
+ if (err == 0)
+ free_attribute_list(number, text);
+
+ return FALSE;
+}
+
+int avrcp_get_element_attributes(struct avrcp *session)
+{
+ struct iovec iov;
+ struct get_element_attributes_req req;
+
+ /* This returns all attributes */
+ memset(&req, 0, sizeof(req));
+
+ iov.iov_base = &req;
+ iov.iov_len = sizeof(req);
+
+ return avrcp_send_req(session, AVC_CTYPE_STATUS, AVC_SUBUNIT_PANEL,
+ AVRCP_GET_ELEMENT_ATTRIBUTES, &iov, 1,
+ get_element_attributes_rsp, session);
+}
+
+static gboolean set_addressed_rsp(struct avctp *conn,
+ uint8_t code, uint8_t subunit,
+ uint8_t *operands, size_t operand_count,
+ void *user_data)
+{
+ struct avrcp *session = user_data;
+ struct avrcp_player *player = session->player;
+ struct avrcp_header *pdu;
+ int err;
+
+ DBG("");
+
+ if (!player || !player->cfm || !player->cfm->set_addressed)
+ return FALSE;
+
+ pdu = parse_pdu(operands, operand_count);
+ if (!pdu) {
+ err = -EPROTO;
+ goto done;
+ }
+
+ err = parse_status(pdu);
+
+done:
+ player->cfm->set_addressed(session, err, player->user_data);
+
+ return FALSE;
+}
+
+int avrcp_set_addressed_player(struct avrcp *session, uint16_t player_id)
+{
+ struct iovec iov;
+ struct set_addressed_req req;
+
+ req.id = cpu_to_be16(player_id);
+
+ iov.iov_base = &req;
+ iov.iov_len = sizeof(req);
+
+ return avrcp_send_req(session, AVC_CTYPE_CONTROL, AVC_SUBUNIT_PANEL,
+ AVRCP_SET_ADDRESSED_PLAYER, &iov, 1,
+ set_addressed_rsp, session);
+}
+
+static char *parse_folder_list(uint8_t *params, uint16_t params_len,
+ uint8_t depth)
+{
+ char **folders, *path;
+ uint8_t count;
+ size_t i;
+
+ folders = g_new0(char *, depth + 2);
+ folders[0] = g_strdup("/Filesystem");
+
+ for (i = 0, count = 1; count <= depth && i < params_len; count++) {
+ uint8_t len;
+
+ len = params[i++];
+
+ if (i + len > params_len || len == 0) {
+ g_strfreev(folders);
+ return NULL;
+ }
+
+ folders[count] = g_memdup(¶ms[i], len);
+ i += len;
+ }
+
+ path = g_build_pathv("/", folders);
+ g_strfreev(folders);
+
+ return path;
+}
+
+static gboolean set_browsed_rsp(struct avctp *conn, uint8_t *operands,
+ size_t operand_count, void *user_data)
+{
+ struct avrcp *session = user_data;
+ struct avrcp_player *player = session->player;
+ struct avrcp_browsing_header *pdu;
+ struct set_browsed_rsp *rsp;
+ uint16_t counter = 0;
+ uint32_t items = 0;
+ char *path = NULL;
+ int err;
+
+ DBG("");
+
+ if (!player || !player->cfm || !player->cfm->set_browsed)
+ return FALSE;
+
+ pdu = parse_browsing_pdu(operands, operand_count);
+ if (!pdu) {
+ err = -EPROTO;
+ goto done;
+ }
+
+ err = parse_browsing_status(pdu);
+ if (err < 0)
+ goto done;
+
+ if (pdu->params_len < sizeof(*rsp)) {
+ err = -EPROTO;
+ goto done;
+ }
+
+ rsp = (void *) pdu->params;
+
+ counter = be16_to_cpu(rsp->counter);
+ items = be32_to_cpu(rsp->items);
+
+ path = parse_folder_list(rsp->data, pdu->params_len - sizeof(*rsp),
+ rsp->depth);
+ if (!path)
+ err = -EPROTO;
+
+done:
+ player->cfm->set_browsed(session, err, counter, items, path,
+ player->user_data);
+
+ return FALSE;
+}
+
+int avrcp_set_browsed_player(struct avrcp *session, uint16_t player_id)
+{
+ struct iovec iov;
+ struct set_browsed_req req;
+
+ req.id = cpu_to_be16(player_id);
+
+ iov.iov_base = &req;
+ iov.iov_len = sizeof(req);
+
+ return avrcp_send_browsing_req(session, AVRCP_SET_BROWSED_PLAYER,
+ &iov, 1, set_browsed_rsp, session);
+}
+
+static gboolean get_folder_items_rsp(struct avctp *conn,
+ uint8_t *operands, size_t operand_count,
+ void *user_data)
+{
+ struct avrcp *session = user_data;
+ struct avrcp_player *player = session->player;
+ struct avrcp_browsing_header *pdu;
+ struct get_folder_items_rsp *rsp;
+ uint16_t counter = 0, number = 0;
+ uint8_t *params = NULL;
+ int err;
+
+ DBG("");
+
+ if (!player || !player->cfm || !player->cfm->get_folder_items)
+ return FALSE;
+
+ pdu = parse_browsing_pdu(operands, operand_count);
+ if (!pdu) {
+ err = -EPROTO;
+ goto done;
+ }
+
+ err = parse_browsing_status(pdu);
+ if (err < 0)
+ goto done;
+
+ if (pdu->params_len < sizeof(*rsp)) {
+ err = -EPROTO;
+ goto done;
+ }
+
+ rsp = (void *) pdu->params;
+
+ counter = be16_to_cpu(rsp->counter);
+ number = be16_to_cpu(rsp->number);
+ params = rsp->data;
+
+ /* FIXME: Add proper parsing for each item type */
+
+done:
+ player->cfm->get_folder_items(session, err, counter, number, params,
+ player->user_data);
+
+ return FALSE;
+}
+
+int avrcp_get_folder_items(struct avrcp *session, uint8_t scope,
+ uint32_t start, uint32_t end, uint8_t number,
+ uint32_t *attrs)
+{
+
+ struct iovec iov[2];
+ struct get_folder_items_req req;
+ int i;
+
+ req.scope = scope;
+ req.start = cpu_to_be32(start);
+ req.end = cpu_to_be32(end);
+ req.number = number;
+
+ iov[0].iov_base = &req;
+ iov[0].iov_len = sizeof(req);
+
+ if (!number)
+ return avrcp_send_browsing_req(session, AVRCP_GET_FOLDER_ITEMS,
+ iov, 1, get_folder_items_rsp,
+ session);
+
+ for (i = 0; i < number; i++)
+ attrs[i] = cpu_to_be32(attrs[i]);
+
+ iov[1].iov_base = attrs;
+ iov[1].iov_len = number * sizeof(*attrs);
+
+ return avrcp_send_browsing_req(session, AVRCP_GET_FOLDER_ITEMS,
+ iov, 2, get_folder_items_rsp, session);
+}
+
+static gboolean change_path_rsp(struct avctp *conn, uint8_t *operands,
+ size_t operand_count, void *user_data)
+{
+ struct avrcp *session = user_data;
+ struct avrcp_player *player = session->player;
+ struct avrcp_browsing_header *pdu;
+ struct change_path_rsp *rsp;
+ uint32_t items = 0;
+ int err;
+
+ DBG("");
+
+ if (!player || !player->cfm || !player->cfm->change_path)
+ return FALSE;
+
+ pdu = parse_browsing_pdu(operands, operand_count);
+ if (!pdu) {
+ err = -EPROTO;
+ goto done;
+ }
+
+ err = parse_browsing_status(pdu);
+ if (err < 0)
+ goto done;
+
+ if (pdu->params_len < sizeof(*rsp)) {
+ err = -EPROTO;
+ goto done;
+ }
+
+ rsp = (void *) pdu->params;
+
+ items = be32_to_cpu(rsp->items);
+
+done:
+ player->cfm->change_path(session, err, items, player->user_data);
+
+ return FALSE;
+}
+
+int avrcp_change_path(struct avrcp *session, uint8_t direction, uint64_t uid,
+ uint16_t counter)
+{
+ struct iovec iov;
+ struct change_path_req req;
+
+ req.counter = cpu_to_be16(counter);
+ req.direction = direction;
+ req.uid = cpu_to_be64(uid);
+
+ iov.iov_base = &req;
+ iov.iov_len = sizeof(req);
+
+ return avrcp_send_browsing_req(session, AVRCP_CHANGE_PATH,
+ &iov, 1, change_path_rsp, session);
+}
+
+static gboolean get_item_attributes_rsp(struct avctp *conn, uint8_t *operands,
+ size_t operand_count, void *user_data)
+{
+ struct avrcp *session = user_data;
+ struct avrcp_player *player = session->player;
+ struct avrcp_browsing_header *pdu;
+ uint8_t number = 0;
+ uint32_t attrs[AVRCP_MEDIA_ATTRIBUTE_LAST];
+ char *text[AVRCP_MEDIA_ATTRIBUTE_LAST];
+ int err;
+
+ DBG("");
+
+ if (!player || !player->cfm || !player->cfm->get_item_attributes)
+ return FALSE;
+
+ pdu = parse_browsing_pdu(operands, operand_count);
+ if (!pdu) {
+ err = -EPROTO;
+ goto done;
+ }
+
+ err = parse_browsing_status(pdu);
+ if (err < 0)
+ goto done;
+
+ err = parse_items(pdu, &number, attrs, text);
+
+done:
+ player->cfm->get_item_attributes(session, err, number, attrs, text,
+ player->user_data);
+
+ if (err == 0)
+ free_attribute_list(number, text);
+
+ return FALSE;
+}
+
+int avrcp_get_item_attributes(struct avrcp *session, uint8_t scope,
+ uint64_t uid, uint16_t counter, uint8_t number,
+ uint32_t *attrs)
+{
+ struct iovec iov[2];
+ struct get_item_attributes_req req;
+ int i;
+
+ req.scope = scope;
+ req.uid = cpu_to_be64(uid);
+ req.counter = cpu_to_be16(counter);
+ req.number = number;
+
+ iov[0].iov_base = &req;
+ iov[0].iov_len = sizeof(req);
+
+ if (!number)
+ return avrcp_send_browsing_req(session,
+ AVRCP_GET_ITEM_ATTRIBUTES,
+ iov, 1, get_item_attributes_rsp,
+ session);
+
+ if (number > AVRCP_MEDIA_ATTRIBUTE_LAST)
+ return -EINVAL;
+
+ for (i = 0; i < number; i++) {
+ if (attrs[i] > AVRCP_MEDIA_ATTRIBUTE_LAST ||
+ attrs[i] == AVRCP_MEDIA_ATTRIBUTE_ILLEGAL)
+ return -EINVAL;
+ attrs[i] = cpu_to_be32(attrs[i]);
+ }
+
+ iov[1].iov_base = attrs;
+ iov[1].iov_len = number * sizeof(*attrs);
+
+ return avrcp_send_browsing_req(session, AVRCP_GET_ITEM_ATTRIBUTES,
+ iov, 2, get_item_attributes_rsp,
+ session);
+}
+
+static gboolean play_item_rsp(struct avctp *conn, uint8_t *operands,
+ size_t operand_count, void *user_data)
+{
+ struct avrcp *session = user_data;
+ struct avrcp_player *player = session->player;
+ struct avrcp_browsing_header *pdu;
+ int err;
+
+ DBG("");
+
+ if (!player || !player->cfm || !player->cfm->play_item)
+ return FALSE;
+
+ pdu = parse_browsing_pdu(operands, operand_count);
+ if (!pdu) {
+ err = -EPROTO;
+ goto done;
+ }
+
+ err = parse_browsing_status(pdu);
+
+done:
+ player->cfm->play_item(session, err, player->user_data);
+
+ return FALSE;
+}
+
+int avrcp_play_item(struct avrcp *session, uint8_t scope, uint64_t uid,
+ uint16_t counter)
+{
+ struct iovec iov;
+ struct play_item_req req;
+
+ if (scope > AVRCP_MEDIA_NOW_PLAYING)
+ return -EINVAL;
+
+ req.scope = scope;
+ req.uid = cpu_to_be64(uid);
+ req.counter = cpu_to_be16(counter);
+
+ iov.iov_base = &req;
+ iov.iov_len = sizeof(req);
+
+ return avrcp_send_browsing_req(session, AVRCP_PLAY_ITEM, &iov, 1,
+ play_item_rsp, session);
+}
+
+static gboolean search_rsp(struct avctp *conn, uint8_t *operands,
+ size_t operand_count, void *user_data)
+{
+ struct avrcp *session = user_data;
+ struct avrcp_player *player = session->player;
+ struct avrcp_browsing_header *pdu;
+ struct search_rsp *rsp;
+ uint16_t counter = 0;
+ uint32_t items = 0;
+ int err;
+
+ DBG("");
+
+ if (!player || !player->cfm || !player->cfm->search)
+ return FALSE;
+
+ pdu = parse_browsing_pdu(operands, operand_count);
+ if (!pdu) {
+ err = -EPROTO;
+ goto done;
+ }
+
+ err = parse_browsing_status(pdu);
+ if (err < 0)
+ goto done;
+
+ if (pdu->params_len < sizeof(*rsp)) {
+ err = -EPROTO;
+ goto done;
+ }
+
+ rsp = (void *) pdu->params;
+
+ counter = be16_to_cpu(rsp->counter);
+ items = be32_to_cpu(rsp->items);
+
+ err = 0;
+
+done:
+ player->cfm->search(session, err, counter, items, player->user_data);
+
+ return FALSE;
+}
+
+int avrcp_search(struct avrcp *session, const char *string)
+{
+ struct iovec iov[2];
+ struct search_req req;
+ size_t len;
+
+ if (!string)
+ return -EINVAL;
+
+ len = strnlen(string, UINT8_MAX);
+
+ req.charset = cpu_to_be16(AVRCP_CHARSET_UTF8);
+ req.len = cpu_to_be16(len);
+
+ iov[0].iov_base = &req;
+ iov[0].iov_len = sizeof(req);
+
+ iov[1].iov_base = (void *) string;
+ iov[1].iov_len = len;
+
+ return avrcp_send_browsing_req(session, AVRCP_SEARCH, iov, 2,
+ search_rsp, session);
+}
+
+static gboolean add_to_now_playing_rsp(struct avctp *conn, uint8_t *operands,
+ size_t operand_count, void *user_data)
+{
+ struct avrcp *session = user_data;
+ struct avrcp_player *player = session->player;
+ struct avrcp_browsing_header *pdu;
+ int err;
+
+ DBG("");
+
+ if (!player || !player->cfm || !player->cfm->add_to_now_playing)
+ return FALSE;
+
+ pdu = parse_browsing_pdu(operands, operand_count);
+ if (!pdu) {
+ err = -EPROTO;
+ goto done;
+ }
+
+ err = parse_browsing_status(pdu);
+
+done:
+ player->cfm->add_to_now_playing(session, err, player->user_data);
+
+ return FALSE;
+}
+
+int avrcp_add_to_now_playing(struct avrcp *session, uint8_t scope, uint64_t uid,
+ uint16_t counter)
+{
+ struct iovec iov;
+ struct add_to_now_playing_req req;
+
+ if (scope > AVRCP_MEDIA_NOW_PLAYING)
+ return -EINVAL;
+
+ req.scope = scope;
+ req.uid = cpu_to_be64(uid);
+ req.counter = cpu_to_be16(counter);
+
+ iov.iov_base = &req;
+ iov.iov_len = sizeof(req);
+
+ return avrcp_send_browsing_req(session, AVRCP_ADD_TO_NOW_PLAYING,
+ &iov, 1, add_to_now_playing_rsp,
+ session);
+}
+
+int avrcp_get_capabilities_rsp(struct avrcp *session, uint8_t transaction,
+ uint8_t number, uint8_t *events)
+{
+ struct iovec iov[2];
+ struct get_capabilities_rsp rsp;
+
+ if (number > AVRCP_EVENT_LAST)
+ return -EINVAL;
+
+ rsp.cap = CAP_EVENTS_SUPPORTED;
+ rsp.number = number;
+
+ iov[0].iov_base = &rsp;
+ iov[0].iov_len = sizeof(rsp);
+
+ iov[1].iov_base = events;
+ iov[1].iov_len = number;
+
+ return avrcp_send(session, transaction, AVC_CTYPE_STABLE,
+ AVC_SUBUNIT_PANEL, AVRCP_GET_CAPABILITIES,
+ iov, 2);
+}
+
+int avrcp_list_player_attributes_rsp(struct avrcp *session, uint8_t transaction,
+ uint8_t number, uint8_t *attrs)
+{
+ struct iovec iov[2];
+ struct list_attributes_rsp rsp;
+
+ if (number > AVRCP_ATTRIBUTE_LAST)
+ return -EINVAL;
+
+ rsp.number = number;
+
+ iov[0].iov_base = &rsp;
+ iov[0].iov_len = sizeof(rsp);
+
+ if (!number)
+ return avrcp_send(session, transaction, AVC_CTYPE_STABLE,
+ AVC_SUBUNIT_PANEL, AVRCP_LIST_PLAYER_ATTRIBUTES,
+ iov, 1);
+
+ iov[1].iov_base = attrs;
+ iov[1].iov_len = number;
+
+ return avrcp_send(session, transaction, AVC_CTYPE_STABLE,
+ AVC_SUBUNIT_PANEL, AVRCP_LIST_PLAYER_ATTRIBUTES,
+ iov, 2);
+}
+
+int avrcp_get_player_attribute_text_rsp(struct avrcp *session,
+ uint8_t transaction, uint8_t number,
+ uint8_t *attrs, const char **text)
+{
+ struct iovec iov[1 + AVRCP_ATTRIBUTE_LAST * 2];
+ struct text_value val[AVRCP_ATTRIBUTE_LAST];
+ int i;
+
+ if (number > AVRCP_ATTRIBUTE_LAST)
+ return -EINVAL;
+
+ iov[0].iov_base = &number;
+ iov[0].iov_len = sizeof(number);
+
+ for (i = 0; i < number; i++) {
+ uint8_t len = 0;
+
+ if (attrs[i] > AVRCP_ATTRIBUTE_LAST ||
+ attrs[i] == AVRCP_ATTRIBUTE_ILEGAL)
+ return -EINVAL;
+
+ if (text[i])
+ len = strlen(text[i]);
+
+ val[i].attr = attrs[i];
+ val[i].charset = cpu_to_be16(AVRCP_CHARSET_UTF8);
+ val[i].len = len;
+
+ iov[i + 1].iov_base = &val[i];
+ iov[i + 1].iov_len = sizeof(val[i]);
+
+ iov[i + 2].iov_base = (void *) text[i];
+ iov[i + 2].iov_len = len;
+ }
+
+ return avrcp_send(session, transaction, AVC_CTYPE_STABLE,
+ AVC_SUBUNIT_PANEL, AVRCP_GET_PLAYER_ATTRIBUTE_TEXT,
+ iov, 1 + i * 2);
+}
+
+int avrcp_list_player_values_rsp(struct avrcp *session, uint8_t transaction,
+ uint8_t number, uint8_t *values)
+{
+ struct iovec iov[2];
+
+ if (number > AVRCP_ATTRIBUTE_LAST)
+ return -EINVAL;
+
+ iov[0].iov_base = &number;
+ iov[0].iov_len = sizeof(number);
+
+ iov[1].iov_base = values;
+ iov[1].iov_len = number;
+
+ return avrcp_send(session, transaction, AVC_CTYPE_STABLE,
+ AVC_SUBUNIT_PANEL, AVRCP_LIST_PLAYER_VALUES,
+ iov, 2);
+}
+
+int avrcp_get_play_status_rsp(struct avrcp *session, uint8_t transaction,
+ uint32_t position, uint32_t duration,
+ uint8_t status)
+{
+ struct iovec iov;
+ struct get_play_status_rsp rsp;
+
+ rsp.duration = cpu_to_be32(duration);
+ rsp.position = cpu_to_be32(position);
+ rsp.status = status;
+
+ iov.iov_base = &rsp;
+ iov.iov_len = sizeof(rsp);
+
+ return avrcp_send(session, transaction, AVC_CTYPE_STABLE,
+ AVC_SUBUNIT_PANEL, AVRCP_GET_PLAY_STATUS,
+ &iov, 1);
+}
+
+int avrcp_get_player_values_text_rsp(struct avrcp *session,
+ uint8_t transaction, uint8_t number,
+ uint8_t *values, const char **text)
+{
+ struct iovec iov[1 + AVRCP_ATTRIBUTE_LAST * 2];
+ struct text_value val[AVRCP_ATTRIBUTE_LAST];
+ int i;
+
+ if (number > AVRCP_ATTRIBUTE_LAST)
+ return -EINVAL;
+
+ iov[0].iov_base = &number;
+ iov[0].iov_len = sizeof(number);
+
+ for (i = 0; i < number; i++) {
+ uint8_t len = 0;
+
+ if (text[i])
+ len = strlen(text[i]);
+
+ val[i].attr = values[i];
+ val[i].charset = cpu_to_be16(AVRCP_CHARSET_UTF8);
+ val[i].len = len;
+
+ iov[i + 1].iov_base = &val[i];
+ iov[i + 1].iov_len = sizeof(val[i]);
+
+ iov[i + 2].iov_base = (void *) text[i];
+ iov[i + 2].iov_len = len;
+ }
+
+ return avrcp_send(session, transaction, AVC_CTYPE_STABLE,
+ AVC_SUBUNIT_PANEL, AVRCP_GET_PLAYER_VALUE_TEXT,
+ iov, 1 + i * 2);
+}
+
+int avrcp_get_current_player_value_rsp(struct avrcp *session,
+ uint8_t transaction, uint8_t number,
+ uint8_t *attrs, uint8_t *values)
+{
+ struct iovec iov[1 + AVRCP_ATTRIBUTE_LAST];
+ struct attr_value val[AVRCP_ATTRIBUTE_LAST];
+ int i;
+
+ if (number > AVRCP_ATTRIBUTE_LAST)
+ return -EINVAL;
+
+ iov[0].iov_base = &number;
+ iov[0].iov_len = sizeof(number);
+
+ for (i = 0; i < number; i++) {
+ val[i].attr = attrs[i];
+ val[i].value = values[i];
+
+ iov[i + 1].iov_base = &val[i];
+ iov[i + 1].iov_len = sizeof(val[i]);
+ }
+
+ return avrcp_send(session, transaction, AVC_CTYPE_STABLE,
+ AVC_SUBUNIT_PANEL, AVRCP_GET_CURRENT_PLAYER_VALUE,
+ iov, 1 + i);
+}
+
+int avrcp_set_player_value_rsp(struct avrcp *session, uint8_t transaction)
+{
+ return avrcp_send(session, transaction, AVC_CTYPE_STABLE,
+ AVC_SUBUNIT_PANEL, AVRCP_SET_PLAYER_VALUE, NULL, 0);
+}
+
+int avrcp_get_element_attrs_rsp(struct avrcp *session, uint8_t transaction,
+ uint8_t *params, size_t params_len)
+{
+ struct iovec iov;
+
+ iov.iov_base = params;
+ iov.iov_len = params_len;
+
+ return avrcp_send(session, transaction, AVC_CTYPE_STABLE,
+ AVC_SUBUNIT_PANEL, AVRCP_GET_ELEMENT_ATTRIBUTES,
+ &iov, 1);
+}
+
+int avrcp_register_notification_rsp(struct avrcp *session, uint8_t transaction,
+ uint8_t code, uint8_t event,
+ void *data, size_t len)
+{
+ struct iovec iov[2];
+ uint16_t *player;
+ uint8_t *volume;
+
+ if (event > AVRCP_EVENT_LAST)
+ return -EINVAL;
+
+ iov[0].iov_base = &event;
+ iov[0].iov_len = sizeof(event);
+
+ switch (event) {
+ case AVRCP_EVENT_STATUS_CHANGED:
+ if (len != sizeof(uint8_t))
+ return -EINVAL;
+ break;
+ case AVRCP_EVENT_VOLUME_CHANGED:
+ if (len != sizeof(uint8_t))
+ return -EINVAL;
+ volume = data;
+ if (volume[0] > 127)
+ return -EINVAL;
+ break;
+ case AVRCP_EVENT_TRACK_CHANGED:
+ if (len != sizeof(uint64_t))
+ return -EINVAL;
+
+ put_be64(*(uint64_t *) data, data);
+ break;
+ case AVRCP_EVENT_PLAYBACK_POS_CHANGED:
+ if (len != sizeof(uint32_t))
+ return -EINVAL;
+
+ put_be32(*(uint32_t *) data, data);
+ break;
+ case AVRCP_EVENT_ADDRESSED_PLAYER_CHANGED:
+ if (len != 4)
+ return -EINVAL;
+
+ player = data;
+ player[0] = cpu_to_be16(player[0]);
+ player[1] = cpu_to_be16(player[1]);
+
+ break;
+ case AVRCP_EVENT_SETTINGS_CHANGED:
+ if (len < sizeof(uint8_t))
+ return -EINVAL;
+ break;
+ case AVRCP_EVENT_UIDS_CHANGED:
+ if (len != sizeof(uint16_t))
+ return -EINVAL;
+
+ put_be16(*(uint16_t *) data, data);
+ break;
+ default:
+ return avrcp_send(session, transaction, code, AVC_SUBUNIT_PANEL,
+ AVRCP_REGISTER_NOTIFICATION, iov, 1);
+ }
+
+ iov[1].iov_base = data;
+ iov[1].iov_len = len;
+
+ return avrcp_send(session, transaction, code, AVC_SUBUNIT_PANEL,
+ AVRCP_REGISTER_NOTIFICATION, iov, 2);
+}
+
+int avrcp_set_volume_rsp(struct avrcp *session, uint8_t transaction,
+ uint8_t volume)
+{
+ struct iovec iov;
+
+ if (volume > 127)
+ return -EINVAL;
+
+ iov.iov_base = &volume;
+ iov.iov_len = sizeof(volume);
+
+ return avrcp_send(session, transaction, AVC_CTYPE_STABLE,
+ AVC_SUBUNIT_PANEL, AVRCP_SET_ABSOLUTE_VOLUME,
+ &iov, 1);
+}
+
+int avrcp_set_addressed_player_rsp(struct avrcp *session, uint8_t transaction,
+ uint8_t status)
+{
+ struct iovec iov;
+
+ iov.iov_base = &status;
+ iov.iov_len = sizeof(status);
+
+ return avrcp_send(session, transaction, AVC_CTYPE_STABLE,
+ AVC_SUBUNIT_PANEL, AVRCP_SET_ADDRESSED_PLAYER,
+ &iov, 1);
+}
+
+static int avrcp_status_rsp(struct avrcp *session, uint8_t transaction,
+ uint8_t pdu_id, uint8_t status)
+{
+ struct iovec iov;
+
+ if (status > AVRCP_STATUS_ADDRESSED_PLAYER_CHANGED)
+ return -EINVAL;
+
+ iov.iov_base = &status;
+ iov.iov_len = sizeof(status);
+
+ return avrcp_send_browsing(session, transaction, pdu_id, &iov, 1);
+}
+
+int avrcp_set_browsed_player_rsp(struct avrcp *session, uint8_t transaction,
+ uint8_t status, uint16_t counter,
+ uint32_t items, uint8_t depth,
+ const char **folders)
+{
+ struct iovec iov[UINT8_MAX * 2 + 1];
+ struct set_browsed_rsp rsp;
+ uint16_t len[UINT8_MAX];
+ int i;
+
+ if (status != AVRCP_STATUS_SUCCESS)
+ return avrcp_status_rsp(session, transaction,
+ AVRCP_SET_BROWSED_PLAYER, status);
+
+ rsp.status = status;
+ rsp.counter = cpu_to_be16(counter);
+ rsp.items = cpu_to_be32(items);
+ rsp.charset = cpu_to_be16(AVRCP_CHARSET_UTF8);
+ rsp.depth = depth;
+
+ iov[0].iov_base = &rsp;
+ iov[0].iov_len = sizeof(rsp);
+
+ if (!depth)
+ return avrcp_send_browsing(session, transaction,
+ AVRCP_SET_BROWSED_PLAYER,
+ iov, 1);
+
+ for (i = 0; i < depth; i++) {
+ if (!folders[i])
+ return -EINVAL;
+
+ len[i] = strlen(folders[i]);
+
+ iov[i * 2 + 2].iov_base = (void *) folders[i];
+ iov[i * 2 + 2].iov_len = len[i];
+
+ len[i] = cpu_to_be16(len[i]);
+
+ iov[i * 2 + 1].iov_base = &len[i];
+ iov[i * 2 + 1].iov_len = sizeof(len[i]);
+ }
+
+ return avrcp_send_browsing(session, transaction,
+ AVRCP_SET_BROWSED_PLAYER, iov,
+ depth * 2 + 1);
+}
+
+int avrcp_get_folder_items_rsp(struct avrcp *session, uint8_t transaction,
+ uint8_t status, uint16_t counter,
+ uint8_t number, uint8_t *type,
+ uint16_t *len, uint8_t **params)
+{
+ struct iovec iov[UINT8_MAX * 2 + 1];
+ struct get_folder_items_rsp rsp;
+ uint8_t item[UINT8_MAX][3];
+ int i;
+
+ if (status != AVRCP_STATUS_SUCCESS)
+ return avrcp_status_rsp(session, transaction,
+ AVRCP_GET_FOLDER_ITEMS, status);
+
+ rsp.status = status;
+ rsp.counter = cpu_to_be16(counter);
+ rsp.number = cpu_to_be16(number);
+
+ iov[0].iov_base = &rsp;
+ iov[0].iov_len = sizeof(rsp);
+
+ for (i = 0; i < number; i++) {
+ item[i][0] = type[i];
+ put_be16(len[i], &item[i][1]);
+
+ iov[i * 2 + 1].iov_base = item[i];
+ iov[i * 2 + 1].iov_len = sizeof(item[i]);
+
+ iov[i * 2 + 2].iov_base = params[i];
+ iov[i * 2 + 2].iov_len = len[i];
+ }
+
+ return avrcp_send_browsing(session, transaction, AVRCP_GET_FOLDER_ITEMS,
+ iov, number * 2 + 1);
+}
+
+int avrcp_change_path_rsp(struct avrcp *session, uint8_t transaction,
+ uint8_t status, uint32_t items)
+{
+ struct iovec iov;
+ struct change_path_rsp rsp;
+
+ if (status != AVRCP_STATUS_SUCCESS)
+ return avrcp_status_rsp(session, transaction, AVRCP_CHANGE_PATH,
+ status);
+
+ rsp.status = status;
+ rsp.items = cpu_to_be32(items);
+
+ iov.iov_base = &rsp;
+ iov.iov_len = sizeof(rsp);
+
+ return avrcp_send_browsing(session, transaction, AVRCP_CHANGE_PATH,
+ &iov, 1);
+}
+
+static bool pack_attribute_list(struct iovec *iov, uint8_t number,
+ uint32_t *attrs, const char **text)
+{
+ int i;
+ struct media_item val[AVRCP_MEDIA_ATTRIBUTE_LAST];
+
+ for (i = 0; i < number; i++) {
+ uint16_t len = 0;
+
+ if (attrs[i] > AVRCP_MEDIA_ATTRIBUTE_LAST ||
+ attrs[i] == AVRCP_MEDIA_ATTRIBUTE_ILLEGAL)
+ return false;
+
+ if (text[i])
+ len = strlen(text[i]);
+
+ val[i].attr = cpu_to_be32(attrs[i]);
+ val[i].charset = cpu_to_be16(AVRCP_CHARSET_UTF8);
+ val[i].len = cpu_to_be16(len);
+
+ iov[i].iov_base = &val[i];
+ iov[i].iov_len = sizeof(val[i]);
+
+ iov[i + 1].iov_base = (void *) text[i];
+ iov[i + 1].iov_len = len;
+ }
+
+ return true;
+}
+
+int avrcp_get_item_attributes_rsp(struct avrcp *session, uint8_t transaction,
+ uint8_t status, uint8_t number,
+ uint32_t *attrs, const char **text)
+{
+ struct iovec iov[AVRCP_MEDIA_ATTRIBUTE_LAST * 2 + 1];
+ struct get_item_attributes_rsp rsp;
+
+ if (number > AVRCP_MEDIA_ATTRIBUTE_LAST)
+ return -EINVAL;
+
+ if (status != AVRCP_STATUS_SUCCESS)
+ return avrcp_status_rsp(session, transaction,
+ AVRCP_GET_ITEM_ATTRIBUTES, status);
+
+ rsp.status = status;
+ rsp.number = number;
+
+ iov[0].iov_base = &rsp;
+ iov[0].iov_len = sizeof(rsp);
+
+ if (!pack_attribute_list(&iov[1], number, attrs, text))
+ return -EINVAL;
+
+ return avrcp_send_browsing(session, transaction,
+ AVRCP_GET_ITEM_ATTRIBUTES, iov,
+ number * 2 + 1);
+}
+
+int avrcp_play_item_rsp(struct avrcp *session, uint8_t transaction,
+ uint8_t status)
+{
+ return avrcp_status_rsp(session, transaction, AVRCP_PLAY_ITEM,
+ status);
+}
+
+int avrcp_search_rsp(struct avrcp *session, uint8_t transaction, uint8_t status,
+ uint16_t counter, uint32_t items)
+{
+ struct iovec iov;
+ struct search_rsp rsp;
+
+ if (status != AVRCP_STATUS_SUCCESS)
+ return avrcp_status_rsp(session, transaction, AVRCP_SEARCH,
+ status);
+
+ rsp.status = status;
+ rsp.counter = cpu_to_be16(counter);
+ rsp.items = cpu_to_be32(items);
+
+ iov.iov_base = &rsp;
+ iov.iov_len = sizeof(rsp);
+
+ return avrcp_send_browsing(session, transaction, AVRCP_SEARCH,
+ &iov, 1);
+}
+
+int avrcp_add_to_now_playing_rsp(struct avrcp *session, uint8_t transaction,
+ uint8_t status)
+{
+ return avrcp_status_rsp(session, transaction, AVRCP_ADD_TO_NOW_PLAYING,
+ status);
+}
+
+int avrcp_send_passthrough(struct avrcp *session, uint32_t vendor, uint8_t op)
+{
+ uint8_t params[5];
+
+ if (!vendor)
+ return avctp_send_passthrough(session->conn, op, NULL, 0);
+
+ hton24(params, vendor);
+ put_be16(op, ¶ms[3]);
+
+ return avctp_send_passthrough(session->conn, AVC_VENDOR_UNIQUE, params,
+ sizeof(params));
+}
diff --git a/repo/android/avrcp-lib.h b/repo/android/avrcp-lib.h
new file mode 100644
index 0000000..6554b12
--- /dev/null
+++ b/repo/android/avrcp-lib.h
@@ -0,0 +1,356 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2014 Intel Corporation. All rights reserved.
+ *
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+/* Control PDU ids */
+#define AVRCP_GET_CAPABILITIES 0x10
+#define AVRCP_LIST_PLAYER_ATTRIBUTES 0X11
+#define AVRCP_LIST_PLAYER_VALUES 0x12
+#define AVRCP_GET_CURRENT_PLAYER_VALUE 0x13
+#define AVRCP_SET_PLAYER_VALUE 0x14
+#define AVRCP_GET_PLAYER_ATTRIBUTE_TEXT 0x15
+#define AVRCP_GET_PLAYER_VALUE_TEXT 0x16
+#define AVRCP_DISPLAYABLE_CHARSET 0x17
+#define AVRCP_CT_BATTERY_STATUS 0x18
+#define AVRCP_GET_ELEMENT_ATTRIBUTES 0x20
+#define AVRCP_GET_PLAY_STATUS 0x30
+#define AVRCP_REGISTER_NOTIFICATION 0x31
+#define AVRCP_REQUEST_CONTINUING 0x40
+#define AVRCP_ABORT_CONTINUING 0x41
+#define AVRCP_SET_ABSOLUTE_VOLUME 0x50
+#define AVRCP_SET_ADDRESSED_PLAYER 0x60
+#define AVRCP_SET_BROWSED_PLAYER 0x70
+#define AVRCP_GET_FOLDER_ITEMS 0x71
+#define AVRCP_CHANGE_PATH 0x72
+#define AVRCP_GET_ITEM_ATTRIBUTES 0x73
+#define AVRCP_PLAY_ITEM 0x74
+#define AVRCP_SEARCH 0x80
+#define AVRCP_ADD_TO_NOW_PLAYING 0x90
+#define AVRCP_GENERAL_REJECT 0xA0
+
+/* Notification events */
+#define AVRCP_EVENT_STATUS_CHANGED 0x01
+#define AVRCP_EVENT_TRACK_CHANGED 0x02
+#define AVRCP_EVENT_TRACK_REACHED_END 0x03
+#define AVRCP_EVENT_TRACK_REACHED_START 0x04
+#define AVRCP_EVENT_PLAYBACK_POS_CHANGED 0x05
+#define AVRCP_EVENT_SETTINGS_CHANGED 0x08
+#define AVRCP_EVENT_NOW_PLAYING_CONTENT_CHANGED 0x09
+#define AVRCP_EVENT_AVAILABLE_PLAYERS_CHANGED 0x0a
+#define AVRCP_EVENT_ADDRESSED_PLAYER_CHANGED 0x0b
+#define AVRCP_EVENT_UIDS_CHANGED 0x0c
+#define AVRCP_EVENT_VOLUME_CHANGED 0x0d
+#define AVRCP_EVENT_LAST AVRCP_EVENT_VOLUME_CHANGED
+
+/* Status codes */
+#define AVRCP_STATUS_INVALID_COMMAND 0x00
+#define AVRCP_STATUS_INVALID_PARAM 0x01
+#define AVRCP_STATUS_PARAM_NOT_FOUND 0x02
+#define AVRCP_STATUS_INTERNAL_ERROR 0x03
+#define AVRCP_STATUS_SUCCESS 0x04
+#define AVRCP_STATUS_UID_CHANGED 0x05
+#define AVRCP_STATUS_NOT_DIRECTORY 0x08
+#define AVRCP_STATUS_DOES_NOT_EXIST 0x09
+#define AVRCP_STATUS_INVALID_SCOPE 0x0a
+#define AVRCP_STATUS_OUT_OF_BOUNDS 0x0b
+#define AVRCP_STATUS_INVALID_PLAYER_ID 0x11
+#define AVRCP_STATUS_PLAYER_NOT_BROWSABLE 0x12
+#define AVRCP_STATUS_NO_AVAILABLE_PLAYERS 0x15
+#define AVRCP_STATUS_ADDRESSED_PLAYER_CHANGED 0x16
+
+/* Capabilities for AVRCP_GET_CAPABILITIES pdu */
+#define CAP_COMPANY_ID 0x02
+#define CAP_EVENTS_SUPPORTED 0x03
+
+/* Player Attributes */
+#define AVRCP_ATTRIBUTE_ILEGAL 0x00
+#define AVRCP_ATTRIBUTE_EQUALIZER 0x01
+#define AVRCP_ATTRIBUTE_REPEAT_MODE 0x02
+#define AVRCP_ATTRIBUTE_SHUFFLE 0x03
+#define AVRCP_ATTRIBUTE_SCAN 0x04
+#define AVRCP_ATTRIBUTE_LAST AVRCP_ATTRIBUTE_SCAN
+
+/* equalizer values */
+#define AVRCP_EQUALIZER_OFF 0x01
+#define AVRCP_EQUALIZER_ON 0x02
+
+/* repeat mode values */
+#define AVRCP_REPEAT_MODE_OFF 0x01
+#define AVRCP_REPEAT_MODE_SINGLE 0x02
+#define AVRCP_REPEAT_MODE_ALL 0x03
+#define AVRCP_REPEAT_MODE_GROUP 0x04
+
+/* shuffle values */
+#define AVRCP_SHUFFLE_OFF 0x01
+#define AVRCP_SHUFFLE_ALL 0x02
+#define AVRCP_SHUFFLE_GROUP 0x03
+
+/* scan values */
+#define AVRCP_SCAN_OFF 0x01
+#define AVRCP_SCAN_ALL 0x02
+#define AVRCP_SCAN_GROUP 0x03
+
+/* media attributes */
+#define AVRCP_MEDIA_ATTRIBUTE_ILLEGAL 0x00
+#define AVRCP_MEDIA_ATTRIBUTE_TITLE 0x01
+#define AVRCP_MEDIA_ATTRIBUTE_ARTIST 0x02
+#define AVRCP_MEDIA_ATTRIBUTE_ALBUM 0x03
+#define AVRCP_MEDIA_ATTRIBUTE_TRACK 0x04
+#define AVRCP_MEDIA_ATTRIBUTE_N_TRACKS 0x05
+#define AVRCP_MEDIA_ATTRIBUTE_GENRE 0x06
+#define AVRCP_MEDIA_ATTRIBUTE_DURATION 0x07
+#define AVRCP_MEDIA_ATTRIBUTE_LAST AVRCP_MEDIA_ATTRIBUTE_DURATION
+
+/* Media Scope */
+#define AVRCP_MEDIA_PLAYER_LIST 0x00
+#define AVRCP_MEDIA_PLAYER_VFS 0x01
+#define AVRCP_MEDIA_SEARCH 0x02
+#define AVRCP_MEDIA_NOW_PLAYING 0x03
+
+/* SDP features */
+#define AVRCP_FEATURE_CATEGORY_1 0x0001
+#define AVRCP_FEATURE_CATEGORY_2 0x0002
+#define AVRCP_FEATURE_CATEGORY_3 0x0004
+#define AVRCP_FEATURE_CATEGORY_4 0x0008
+#define AVRCP_FEATURE_PLAYER_SETTINGS 0x0010
+#define AVRCP_FEATURE_GROUP_NAVIGATION 0x0020
+#define AVRCP_FEATURE_BROWSING 0x0040
+#define AVRCP_FEATURE_MULTIPLE_PLAYERS 0x0080
+
+/* Company IDs for vendor dependent commands */
+#define IEEEID_BTSIG 0x001958
+
+struct avrcp;
+
+struct avrcp_control_ind {
+ int (*get_capabilities) (struct avrcp *session, uint8_t transaction,
+ void *user_data);
+ int (*list_attributes) (struct avrcp *session, uint8_t transaction,
+ void *user_data);
+ int (*get_attribute_text) (struct avrcp *session, uint8_t transaction,
+ uint8_t number, uint8_t *attrs,
+ void *user_data);
+ int (*list_values) (struct avrcp *session, uint8_t transaction,
+ uint8_t attr, void *user_data);
+ int (*get_value_text) (struct avrcp *session, uint8_t transaction,
+ uint8_t attr, uint8_t number,
+ uint8_t *values, void *user_data);
+ int (*get_value) (struct avrcp *session, uint8_t transaction,
+ uint8_t number, uint8_t *attrs,
+ void *user_data);
+ int (*set_value) (struct avrcp *session, uint8_t transaction,
+ uint8_t number, uint8_t *attrs,
+ uint8_t *values, void *user_data);
+ int (*get_play_status) (struct avrcp *session, uint8_t transaction,
+ void *user_data);
+ int (*get_element_attributes) (struct avrcp *session,
+ uint8_t transaction, uint64_t uid,
+ uint8_t number, uint32_t *attrs,
+ void *user_data);
+ int (*register_notification) (struct avrcp *session,
+ uint8_t transaction, uint8_t event,
+ uint32_t interval, void *user_data);
+ int (*set_volume) (struct avrcp *session, uint8_t transaction,
+ uint8_t volume, void *user_data);
+ int (*set_addressed) (struct avrcp *session, uint8_t transaction,
+ uint16_t id, void *user_data);
+ int (*set_browsed) (struct avrcp *session, uint8_t transaction,
+ uint16_t id, void *user_data);
+ int (*get_folder_items) (struct avrcp *session, uint8_t transaction,
+ uint8_t scope, uint32_t start,
+ uint32_t end, uint16_t number,
+ uint32_t *attrs, void *user_data);
+ int (*change_path) (struct avrcp *session, uint8_t transaction,
+ uint16_t counter, uint8_t direction,
+ uint64_t uid, void *user_data);
+ int (*get_item_attributes) (struct avrcp *session, uint8_t transaction,
+ uint8_t scope, uint64_t uid,
+ uint16_t counter, uint8_t number,
+ uint32_t *attrs, void *user_data);
+ int (*play_item) (struct avrcp *session, uint8_t transaction,
+ uint8_t scope, uint64_t uid,
+ uint16_t counter, void *user_data);
+ int (*search) (struct avrcp *session, uint8_t transaction,
+ const char *string, void *user_data);
+ int (*add_to_now_playing) (struct avrcp *session, uint8_t transaction,
+ uint8_t scope, uint64_t uid,
+ uint16_t counter, void *user_data);
+};
+
+struct avrcp_control_cfm {
+ void (*get_capabilities) (struct avrcp *session, int err,
+ uint8_t number, uint8_t *params,
+ void *user_data);
+ void (*list_attributes) (struct avrcp *session, int err,
+ uint8_t number, uint8_t *attrs,
+ void *user_data);
+ void (*get_attribute_text) (struct avrcp *session, int err,
+ uint8_t number, uint8_t *attrs,
+ char **text, void *user_data);
+ void (*list_values) (struct avrcp *session, int err,
+ uint8_t number, uint8_t *values,
+ void *user_data);
+ void (*get_value_text) (struct avrcp *session, int err,
+ uint8_t number, uint8_t *values,
+ char **text, void *user_data);
+ void (*get_value) (struct avrcp *session, int err,
+ uint8_t number, uint8_t *attrs,
+ uint8_t *values, void *user_data);
+ void (*set_value) (struct avrcp *session, int err, void *user_data);
+ void (*get_play_status) (struct avrcp *session, int err,
+ uint8_t status, uint32_t position,
+ uint32_t duration, void *user_data);
+ void (*get_element_attributes) (struct avrcp *session, int err,
+ uint8_t number, uint32_t *attrs,
+ char **text, void *user_data);
+ bool (*register_notification) (struct avrcp *session, int err,
+ uint8_t code, uint8_t event,
+ void *params, void *user_data);
+ void (*set_volume) (struct avrcp *session, int err, uint8_t volume,
+ void *user_data);
+ void (*set_addressed) (struct avrcp *session, int err,
+ void *user_data);
+ void (*set_browsed) (struct avrcp *session, int err,
+ uint16_t counter, uint32_t items,
+ char *path, void *user_data);
+ void (*get_folder_items) (struct avrcp *session, int err,
+ uint16_t counter, uint16_t number,
+ uint8_t *params, void *user_data);
+ void (*change_path) (struct avrcp *session, int err,
+ uint32_t items, void *user_data);
+ void (*get_item_attributes) (struct avrcp *session, int err,
+ uint8_t number, uint32_t *attrs,
+ char **text, void *user_data);
+ void (*play_item) (struct avrcp *session, int err, void *user_data);
+ void (*search) (struct avrcp *session, int err, uint16_t counter,
+ uint32_t items, void *user_data);
+ void (*add_to_now_playing) (struct avrcp *session, int err,
+ void *user_data);
+};
+
+struct avrcp_passthrough_handler {
+ uint8_t op;
+ bool (*func) (struct avrcp *session, bool pressed, void *user_data);
+};
+
+typedef void (*avrcp_destroy_cb_t) (void *user_data);
+
+struct avrcp *avrcp_new(int fd, size_t imtu, size_t omtu, uint16_t version);
+void avrcp_shutdown(struct avrcp *session);
+void avrcp_set_destroy_cb(struct avrcp *session, avrcp_destroy_cb_t cb,
+ void *user_data);
+int avrcp_connect_browsing(struct avrcp *session, int fd, size_t imtu,
+ size_t omtu);
+
+void avrcp_register_player(struct avrcp *session,
+ const struct avrcp_control_ind *ind,
+ const struct avrcp_control_cfm *cfm,
+ void *user_data);
+void avrcp_set_passthrough_handlers(struct avrcp *session,
+ const struct avrcp_passthrough_handler *handlers,
+ void *user_data);
+int avrcp_init_uinput(struct avrcp *session, const char *name,
+ const char *address);
+int avrcp_send(struct avrcp *session, uint8_t transaction, uint8_t code,
+ uint8_t subunit, uint8_t pdu_id,
+ const struct iovec *iov, int iov_cnt);
+int avrcp_get_capabilities(struct avrcp *session, uint8_t param);
+int avrcp_register_notification(struct avrcp *session, uint8_t event,
+ uint32_t interval);
+int avrcp_list_player_attributes(struct avrcp *session);
+int avrcp_get_player_attribute_text(struct avrcp *session, uint8_t number,
+ uint8_t *attrs);
+int avrcp_list_player_values(struct avrcp *session, uint8_t attr);
+int avrcp_get_player_value_text(struct avrcp *session, uint8_t attr,
+ uint8_t number, uint8_t *values);
+int avrcp_set_player_value(struct avrcp *session, uint8_t number,
+ uint8_t *attrs, uint8_t *values);
+int avrcp_get_current_player_value(struct avrcp *session, uint8_t number,
+ uint8_t *attrs);
+int avrcp_get_play_status(struct avrcp *session);
+int avrcp_set_volume(struct avrcp *session, uint8_t volume);
+int avrcp_get_element_attributes(struct avrcp *session);
+int avrcp_set_addressed_player(struct avrcp *session, uint16_t player_id);
+int avrcp_set_browsed_player(struct avrcp *session, uint16_t player_id);
+int avrcp_get_folder_items(struct avrcp *session, uint8_t scope,
+ uint32_t start, uint32_t end, uint8_t number,
+ uint32_t *attrs);
+int avrcp_change_path(struct avrcp *session, uint8_t direction, uint64_t uid,
+ uint16_t counter);
+int avrcp_get_item_attributes(struct avrcp *session, uint8_t scope,
+ uint64_t uid, uint16_t counter, uint8_t number,
+ uint32_t *attrs);
+int avrcp_play_item(struct avrcp *session, uint8_t scope, uint64_t uid,
+ uint16_t counter);
+int avrcp_search(struct avrcp *session, const char *string);
+int avrcp_add_to_now_playing(struct avrcp *session, uint8_t scope, uint64_t uid,
+ uint16_t counter);
+
+int avrcp_get_capabilities_rsp(struct avrcp *session, uint8_t transaction,
+ uint8_t number, uint8_t *events);
+int avrcp_list_player_attributes_rsp(struct avrcp *session, uint8_t transaction,
+ uint8_t number, uint8_t *attrs);
+int avrcp_get_player_attribute_text_rsp(struct avrcp *session,
+ uint8_t transaction, uint8_t number,
+ uint8_t *attrs, const char **text);
+int avrcp_list_player_values_rsp(struct avrcp *session, uint8_t transaction,
+ uint8_t number, uint8_t *values);
+int avrcp_get_play_status_rsp(struct avrcp *session, uint8_t transaction,
+ uint32_t position, uint32_t duration,
+ uint8_t status);
+int avrcp_get_player_values_text_rsp(struct avrcp *session,
+ uint8_t transaction, uint8_t number,
+ uint8_t *values, const char **text);
+int avrcp_get_current_player_value_rsp(struct avrcp *session,
+ uint8_t transaction, uint8_t number,
+ uint8_t *attrs, uint8_t *values);
+int avrcp_set_player_value_rsp(struct avrcp *session, uint8_t transaction);
+int avrcp_get_element_attrs_rsp(struct avrcp *session, uint8_t transaction,
+ uint8_t *params, size_t params_len);
+int avrcp_register_notification_rsp(struct avrcp *session, uint8_t transaction,
+ uint8_t code, uint8_t event,
+ void *data, size_t len);
+int avrcp_set_volume_rsp(struct avrcp *session, uint8_t transaction,
+ uint8_t volume);
+int avrcp_set_addressed_player_rsp(struct avrcp *session, uint8_t transaction,
+ uint8_t status);
+int avrcp_set_browsed_player_rsp(struct avrcp *session, uint8_t transaction,
+ uint8_t status, uint16_t counter,
+ uint32_t items, uint8_t depth,
+ const char **folders);
+int avrcp_get_folder_items_rsp(struct avrcp *session, uint8_t transaction,
+ uint8_t status, uint16_t counter,
+ uint8_t number, uint8_t *type,
+ uint16_t *len, uint8_t **params);
+int avrcp_change_path_rsp(struct avrcp *session, uint8_t transaction,
+ uint8_t status, uint32_t items);
+int avrcp_get_item_attributes_rsp(struct avrcp *session, uint8_t transaction,
+ uint8_t status, uint8_t number,
+ uint32_t *attrs, const char **text);
+int avrcp_play_item_rsp(struct avrcp *session, uint8_t transaction,
+ uint8_t status);
+int avrcp_search_rsp(struct avrcp *session, uint8_t transaction, uint8_t status,
+ uint16_t counter, uint32_t items);
+int avrcp_add_to_now_playing_rsp(struct avrcp *session, uint8_t transaction,
+ uint8_t status);
+
+int avrcp_send_passthrough(struct avrcp *session, uint32_t vendor, uint8_t op);
diff --git a/repo/android/avrcp.c b/repo/android/avrcp.c
new file mode 100644
index 0000000..8c3e25a
--- /dev/null
+++ b/repo/android/avrcp.c
@@ -0,0 +1,1169 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2013-2014 Intel Corporation. All rights reserved.
+ *
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdlib.h>
+#include <stdbool.h>
+#include <errno.h>
+#include <glib.h>
+
+#include "btio/btio.h"
+#include "lib/bluetooth.h"
+#include "lib/sdp.h"
+#include "lib/sdp_lib.h"
+#include "src/sdp-client.h"
+#include "src/shared/util.h"
+#include "src/log.h"
+
+#include "avctp.h"
+#include "avrcp-lib.h"
+#include "hal-msg.h"
+#include "ipc-common.h"
+#include "ipc.h"
+#include "bluetooth.h"
+#include "avrcp.h"
+#include "utils.h"
+
+#define L2CAP_PSM_AVCTP 0x17
+
+static bdaddr_t adapter_addr;
+static uint32_t record_tg_id = 0;
+static uint32_t record_ct_id = 0;
+static GSList *devices = NULL;
+static GIOChannel *server = NULL;
+static struct ipc *hal_ipc = NULL;
+
+struct avrcp_request {
+ struct avrcp_device *dev;
+ uint8_t pdu_id;
+ uint8_t event_id;
+ uint8_t transaction;
+};
+
+struct avrcp_device {
+ bdaddr_t dst;
+ uint16_t version;
+ uint16_t features;
+ struct avrcp *session;
+ GIOChannel *io;
+ GQueue *queue;
+};
+
+static struct avrcp_request *pop_request(uint8_t pdu_id, uint8_t event_id,
+ bool peek)
+{
+ GSList *l;
+
+ for (l = devices; l; l = g_slist_next(l)) {
+ struct avrcp_device *dev = l->data;
+ GList *reqs = g_queue_peek_head_link(dev->queue);
+ int i;
+
+ for (i = 0; reqs; reqs = g_list_next(reqs), i++) {
+ struct avrcp_request *req = reqs->data;
+
+ if (req->pdu_id != pdu_id || req->event_id != event_id)
+ continue;
+
+ if (!peek)
+ g_queue_pop_nth(dev->queue, i);
+
+ return req;
+ }
+ }
+
+ return NULL;
+}
+
+static void handle_get_play_status(const void *buf, uint16_t len)
+{
+ const struct hal_cmd_avrcp_get_play_status *cmd = buf;
+ uint8_t status;
+ struct avrcp_request *req;
+ int ret;
+
+ DBG("");
+
+ req = pop_request(AVRCP_GET_PLAY_STATUS, 0, false);
+ if (!req) {
+ status = HAL_STATUS_FAILED;
+ goto done;
+ }
+
+ ret = avrcp_get_play_status_rsp(req->dev->session, req->transaction,
+ cmd->position, cmd->duration,
+ cmd->status);
+ if (ret < 0) {
+ status = HAL_STATUS_FAILED;
+ g_free(req);
+ goto done;
+ }
+
+ status = HAL_STATUS_SUCCESS;
+ g_free(req);
+
+done:
+ ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_AVRCP,
+ HAL_OP_AVRCP_GET_PLAY_STATUS, status);
+}
+
+static void handle_list_player_attrs(const void *buf, uint16_t len)
+{
+ DBG("");
+
+ ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_AVRCP,
+ HAL_OP_AVRCP_LIST_PLAYER_ATTRS, HAL_STATUS_FAILED);
+}
+
+static void handle_list_player_values(const void *buf, uint16_t len)
+{
+ DBG("");
+
+ ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_AVRCP,
+ HAL_OP_AVRCP_LIST_PLAYER_VALUES, HAL_STATUS_FAILED);
+}
+
+static void handle_get_player_attrs(const void *buf, uint16_t len)
+{
+ DBG("");
+
+ ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_AVRCP,
+ HAL_OP_AVRCP_GET_PLAYER_ATTRS, HAL_STATUS_FAILED);
+}
+
+static void handle_get_player_attrs_text(const void *buf, uint16_t len)
+{
+ DBG("");
+
+ ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_AVRCP,
+ HAL_OP_AVRCP_GET_PLAYER_ATTRS_TEXT, HAL_STATUS_FAILED);
+}
+
+static void handle_get_player_values_text(const void *buf, uint16_t len)
+{
+ DBG("");
+
+ ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_AVRCP,
+ HAL_OP_AVRCP_GET_PLAYER_VALUES_TEXT, HAL_STATUS_FAILED);
+}
+
+static size_t write_element_text(uint8_t id, uint8_t text_len, uint8_t *text,
+ uint8_t *pdu)
+{
+ uint16_t charset = 106;
+ size_t len = 0;
+
+ put_be32(id, pdu);
+ pdu += 4;
+ len += 4;
+
+ put_be16(charset, pdu);
+ pdu += 2;
+ len += 2;
+
+ put_be16(text_len, pdu);
+ pdu += 2;
+ len += 2;
+
+ memcpy(pdu, text, text_len);
+ len += text_len;
+
+ return len;
+}
+
+static void write_element_attrs(uint8_t *ptr, uint8_t number, uint8_t *pdu,
+ size_t *len)
+{
+ int i;
+
+ *pdu = number;
+ pdu++;
+ *len += 1;
+
+ for (i = 0; i < number; i++) {
+ struct hal_avrcp_player_setting_text *text = (void *) ptr;
+ size_t ret;
+
+ ret = write_element_text(text->id, text->len, text->text, pdu);
+
+ ptr += sizeof(*text) + text->len;
+ pdu += ret;
+ *len += ret;
+ }
+}
+
+static void handle_get_element_attrs_text(const void *buf, uint16_t len)
+{
+ struct hal_cmd_avrcp_get_element_attrs_text *cmd = (void *) buf;
+ uint8_t status;
+ struct avrcp_request *req;
+ uint8_t pdu[IPC_MTU];
+ uint8_t *ptr;
+ size_t pdu_len;
+ int ret;
+
+ DBG("");
+
+ req = pop_request(AVRCP_GET_ELEMENT_ATTRIBUTES, 0, false);
+ if (!req) {
+ status = HAL_STATUS_FAILED;
+ goto done;
+ }
+
+ ptr = (uint8_t *) &cmd->values[0];
+ pdu_len = 0;
+ write_element_attrs(ptr, cmd->number, pdu, &pdu_len);
+
+ ret = avrcp_get_element_attrs_rsp(req->dev->session, req->transaction,
+ pdu, pdu_len);
+ if (ret < 0) {
+ status = HAL_STATUS_FAILED;
+ g_free(req);
+ goto done;
+ }
+
+ status = HAL_STATUS_SUCCESS;
+ g_free(req);
+
+done:
+ ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_AVRCP,
+ HAL_OP_AVRCP_GET_ELEMENT_ATTRS_TEXT, status);
+}
+
+static void handle_set_player_attrs_value(const void *buf, uint16_t len)
+{
+ DBG("");
+
+ ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_AVRCP,
+ HAL_OP_AVRCP_SET_PLAYER_ATTRS_VALUE, HAL_STATUS_FAILED);
+}
+
+static void handle_register_notification(const void *buf, uint16_t len)
+{
+ struct hal_cmd_avrcp_register_notification *cmd = (void *) buf;
+ uint8_t status;
+ struct avrcp_request *req;
+ uint8_t code;
+ bool peek = false;
+ int ret;
+
+ DBG("");
+
+ switch (cmd->type) {
+ case HAL_AVRCP_EVENT_TYPE_INTERIM:
+ code = AVC_CTYPE_INTERIM;
+ peek = true;
+ break;
+ case HAL_AVRCP_EVENT_TYPE_CHANGED:
+ code = AVC_CTYPE_CHANGED;
+ break;
+ default:
+ status = HAL_STATUS_FAILED;
+ goto done;
+ }
+
+ req = pop_request(AVRCP_REGISTER_NOTIFICATION, cmd->event, peek);
+ if (!req) {
+ status = HAL_STATUS_FAILED;
+ goto done;
+ }
+
+ ret = avrcp_register_notification_rsp(req->dev->session,
+ req->transaction, code,
+ cmd->event, cmd->data,
+ cmd->len);
+ if (ret < 0) {
+ status = HAL_STATUS_FAILED;
+ if (!peek)
+ g_free(req);
+ goto done;
+ }
+
+ status = HAL_STATUS_SUCCESS;
+ if (!peek)
+ g_free(req);
+
+done:
+ ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_AVRCP,
+ HAL_OP_AVRCP_REGISTER_NOTIFICATION, status);
+}
+
+static void handle_set_volume(const void *buf, uint16_t len)
+{
+ struct hal_cmd_avrcp_set_volume *cmd = (void *) buf;
+ struct avrcp_device *dev;
+ uint8_t status;
+ int ret;
+
+ DBG("");
+
+ if (!devices) {
+ error("AVRCP: No device found to set volume");
+ status = HAL_STATUS_FAILED;
+ goto done;
+ }
+
+ /*
+ * Peek the first device since the HAL cannot really address a specific
+ * device it might mean there could only be one connected.
+ */
+ dev = devices->data;
+
+ ret = avrcp_set_volume(dev->session, cmd->value & 0x7f);
+ if (ret < 0) {
+ status = HAL_STATUS_FAILED;
+ goto done;
+ }
+
+ status = HAL_STATUS_SUCCESS;
+
+done:
+ ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_AVRCP, HAL_OP_AVRCP_SET_VOLUME,
+ status);
+}
+
+static const struct ipc_handler cmd_handlers[] = {
+ /* HAL_OP_AVRCP_GET_PLAY_STATUS */
+ { handle_get_play_status, false,
+ sizeof(struct hal_cmd_avrcp_get_play_status) },
+ /* HAL_OP_AVRCP_LIST_PLAYER_ATTRS */
+ { handle_list_player_attrs, true,
+ sizeof(struct hal_cmd_avrcp_list_player_attrs) },
+ /* HAL_OP_AVRCP_LIST_PLAYER_VALUES */
+ { handle_list_player_values, true,
+ sizeof(struct hal_cmd_avrcp_list_player_values) },
+ /* HAL_OP_AVRCP_GET_PLAYER_ATTRS */
+ { handle_get_player_attrs, true,
+ sizeof(struct hal_cmd_avrcp_get_player_attrs) },
+ /* HAL_OP_AVRCP_GET_PLAYER_ATTRS_TEXT */
+ { handle_get_player_attrs_text, true,
+ sizeof(struct hal_cmd_avrcp_get_player_attrs_text) },
+ /* HAL_OP_AVRCP_GET_PLAYER_VALUES_TEXT */
+ { handle_get_player_values_text, true,
+ sizeof(struct hal_cmd_avrcp_get_player_values_text) },
+ /* HAL_OP_AVRCP_GET_ELEMENT_ATTRS_TEXT */
+ { handle_get_element_attrs_text, true,
+ sizeof(struct hal_cmd_avrcp_get_element_attrs_text) },
+ /* HAL_OP_AVRCP_SET_PLAYER_ATTRS_VALUE */
+ { handle_set_player_attrs_value, true,
+ sizeof(struct hal_cmd_avrcp_set_player_attrs_value) },
+ /* HAL_OP_AVRCP_REGISTER_NOTIFICATION */
+ { handle_register_notification, true,
+ sizeof(struct hal_cmd_avrcp_register_notification) },
+ /* HAL_OP_AVRCP_SET_VOLUME */
+ { handle_set_volume, false, sizeof(struct hal_cmd_avrcp_set_volume) },
+};
+
+static sdp_record_t *avrcp_tg_record(void)
+{
+ sdp_list_t *svclass_id, *pfseq, *apseq, *root;
+ uuid_t root_uuid, l2cap, avctp, avrtg;
+ sdp_profile_desc_t profile[1];
+ sdp_list_t *aproto_control, *proto_control[2];
+ sdp_record_t *record;
+ sdp_data_t *psm, *version, *features;
+ uint16_t lp = L2CAP_PSM_AVCTP;
+ uint16_t avrcp_ver = 0x0105, avctp_ver = 0x0104;
+ uint16_t feat = (AVRCP_FEATURE_CATEGORY_1 |
+ AVRCP_FEATURE_CATEGORY_2 |
+ AVRCP_FEATURE_CATEGORY_3 |
+ AVRCP_FEATURE_CATEGORY_4);
+
+ record = sdp_record_alloc();
+ if (!record)
+ return NULL;
+
+ sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
+ root = sdp_list_append(NULL, &root_uuid);
+ sdp_set_browse_groups(record, root);
+
+ /* Service Class ID List */
+ sdp_uuid16_create(&avrtg, AV_REMOTE_TARGET_SVCLASS_ID);
+ svclass_id = sdp_list_append(NULL, &avrtg);
+ sdp_set_service_classes(record, svclass_id);
+
+ /* Protocol Descriptor List */
+ sdp_uuid16_create(&l2cap, L2CAP_UUID);
+ proto_control[0] = sdp_list_append(NULL, &l2cap);
+ psm = sdp_data_alloc(SDP_UINT16, &lp);
+ proto_control[0] = sdp_list_append(proto_control[0], psm);
+ apseq = sdp_list_append(NULL, proto_control[0]);
+
+ sdp_uuid16_create(&avctp, AVCTP_UUID);
+ proto_control[1] = sdp_list_append(NULL, &avctp);
+ version = sdp_data_alloc(SDP_UINT16, &avctp_ver);
+ proto_control[1] = sdp_list_append(proto_control[1], version);
+ apseq = sdp_list_append(apseq, proto_control[1]);
+
+ aproto_control = sdp_list_append(NULL, apseq);
+ sdp_set_access_protos(record, aproto_control);
+
+ /* Bluetooth Profile Descriptor List */
+ sdp_uuid16_create(&profile[0].uuid, AV_REMOTE_PROFILE_ID);
+ profile[0].version = avrcp_ver;
+ pfseq = sdp_list_append(NULL, &profile[0]);
+ sdp_set_profile_descs(record, pfseq);
+
+ features = sdp_data_alloc(SDP_UINT16, &feat);
+ sdp_attr_add(record, SDP_ATTR_SUPPORTED_FEATURES, features);
+
+ sdp_set_info_attr(record, "AVRCP TG", NULL, NULL);
+
+ sdp_data_free(psm);
+ sdp_data_free(version);
+ sdp_list_free(proto_control[0], NULL);
+ sdp_list_free(proto_control[1], NULL);
+ sdp_list_free(apseq, NULL);
+ sdp_list_free(aproto_control, NULL);
+ sdp_list_free(pfseq, NULL);
+ sdp_list_free(root, NULL);
+ sdp_list_free(svclass_id, NULL);
+
+ return record;
+}
+
+static sdp_record_t *avrcp_ct_record(void)
+{
+ sdp_list_t *svclass_id, *pfseq, *apseq, *root;
+ uuid_t root_uuid, l2cap, avctp, avrct, avrctr;
+ sdp_profile_desc_t profile[1];
+ sdp_list_t *aproto, *proto[2];
+ sdp_record_t *record;
+ sdp_data_t *psm, *version, *features;
+ uint16_t lp = AVCTP_CONTROL_PSM;
+ uint16_t avrcp_ver = 0x0105, avctp_ver = 0x0104;
+ uint16_t feat = ( AVRCP_FEATURE_CATEGORY_1 |
+ AVRCP_FEATURE_CATEGORY_2 |
+ AVRCP_FEATURE_CATEGORY_3 |
+ AVRCP_FEATURE_CATEGORY_4);
+
+ record = sdp_record_alloc();
+ if (!record)
+ return NULL;
+
+ sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
+ root = sdp_list_append(NULL, &root_uuid);
+ sdp_set_browse_groups(record, root);
+
+ /* Service Class ID List */
+ sdp_uuid16_create(&avrct, AV_REMOTE_SVCLASS_ID);
+ svclass_id = sdp_list_append(NULL, &avrct);
+ sdp_uuid16_create(&avrctr, AV_REMOTE_CONTROLLER_SVCLASS_ID);
+ svclass_id = sdp_list_append(svclass_id, &avrctr);
+ sdp_set_service_classes(record, svclass_id);
+
+ /* Protocol Descriptor List */
+ sdp_uuid16_create(&l2cap, L2CAP_UUID);
+ proto[0] = sdp_list_append(NULL, &l2cap);
+ psm = sdp_data_alloc(SDP_UINT16, &lp);
+ proto[0] = sdp_list_append(proto[0], psm);
+ apseq = sdp_list_append(NULL, proto[0]);
+
+ sdp_uuid16_create(&avctp, AVCTP_UUID);
+ proto[1] = sdp_list_append(NULL, &avctp);
+ version = sdp_data_alloc(SDP_UINT16, &avctp_ver);
+ proto[1] = sdp_list_append(proto[1], version);
+ apseq = sdp_list_append(apseq, proto[1]);
+
+ aproto = sdp_list_append(NULL, apseq);
+ sdp_set_access_protos(record, aproto);
+
+ /* Bluetooth Profile Descriptor List */
+ sdp_uuid16_create(&profile[0].uuid, AV_REMOTE_PROFILE_ID);
+ profile[0].version = avrcp_ver;
+ pfseq = sdp_list_append(NULL, &profile[0]);
+ sdp_set_profile_descs(record, pfseq);
+
+ features = sdp_data_alloc(SDP_UINT16, &feat);
+ sdp_attr_add(record, SDP_ATTR_SUPPORTED_FEATURES, features);
+
+ sdp_set_info_attr(record, "AVRCP CT", NULL, NULL);
+
+ free(psm);
+ free(version);
+ sdp_list_free(proto[0], NULL);
+ sdp_list_free(proto[1], NULL);
+ sdp_list_free(apseq, NULL);
+ sdp_list_free(pfseq, NULL);
+ sdp_list_free(aproto, NULL);
+ sdp_list_free(root, NULL);
+ sdp_list_free(svclass_id, NULL);
+
+ return record;
+}
+
+static void avrcp_device_free(void *data)
+{
+ struct avrcp_device *dev = data;
+
+ if (dev->queue) {
+ g_queue_foreach(dev->queue, (GFunc) g_free, NULL);
+ g_queue_free(dev->queue);
+ }
+
+ if (dev->session)
+ avrcp_shutdown(dev->session);
+
+ if (dev->io) {
+ g_io_channel_shutdown(dev->io, FALSE, NULL);
+ g_io_channel_unref(dev->io);
+ }
+
+ g_free(dev);
+}
+
+static void avrcp_device_remove(struct avrcp_device *dev)
+{
+ devices = g_slist_remove(devices, dev);
+ avrcp_device_free(dev);
+}
+
+static struct avrcp_device *avrcp_device_new(const bdaddr_t *dst)
+{
+ struct avrcp_device *dev;
+
+ dev = g_new0(struct avrcp_device, 1);
+ bacpy(&dev->dst, dst);
+ devices = g_slist_prepend(devices, dev);
+
+ return dev;
+}
+
+static int device_cmp(gconstpointer s, gconstpointer user_data)
+{
+ const struct avrcp_device *dev = s;
+ const bdaddr_t *dst = user_data;
+
+ return bacmp(&dev->dst, dst);
+}
+
+static struct avrcp_device *avrcp_device_find(const bdaddr_t *dst)
+{
+ GSList *l;
+
+ l = g_slist_find_custom(devices, dst, device_cmp);
+ if (!l)
+ return NULL;
+
+ return l->data;
+}
+
+static void disconnect_cb(void *data)
+{
+ struct avrcp_device *dev = data;
+
+ DBG("");
+
+ dev->session = NULL;
+
+ avrcp_device_remove(dev);
+}
+
+static bool handle_fast_forward(struct avrcp *session, bool pressed,
+ void *user_data)
+{
+ struct hal_ev_avrcp_passthrough_cmd ev;
+
+ DBG("pressed %s", pressed ? "true" : "false");
+
+ ev.id = AVC_FAST_FORWARD;
+ ev.state = pressed;
+
+ ipc_send_notif(hal_ipc, HAL_SERVICE_ID_AVRCP,
+ HAL_EV_AVRCP_PASSTHROUGH_CMD, sizeof(ev), &ev);
+
+ return true;
+}
+
+static bool handle_rewind(struct avrcp *session, bool pressed,
+ void *user_data)
+{
+ struct hal_ev_avrcp_passthrough_cmd ev;
+
+ DBG("pressed %s", pressed ? "true" : "false");
+
+ ev.id = AVC_REWIND;
+ ev.state = pressed;
+
+ ipc_send_notif(hal_ipc, HAL_SERVICE_ID_AVRCP,
+ HAL_EV_AVRCP_PASSTHROUGH_CMD, sizeof(ev), &ev);
+
+ return true;
+}
+
+static const struct avrcp_passthrough_handler passthrough_handlers[] = {
+ { AVC_FAST_FORWARD, handle_fast_forward },
+ { AVC_REWIND, handle_rewind },
+ { },
+};
+
+static int handle_get_capabilities_cmd(struct avrcp *session,
+ uint8_t transaction, void *user_data)
+{
+ uint8_t events[] = { AVRCP_EVENT_STATUS_CHANGED,
+ AVRCP_EVENT_TRACK_CHANGED,
+ AVRCP_EVENT_PLAYBACK_POS_CHANGED };
+
+ DBG("");
+
+ /*
+ * Android do not provide this info via HAL so the list most
+ * be hardcoded according to what RegisterNotification can
+ * actually handle
+ */
+ avrcp_get_capabilities_rsp(session, transaction, sizeof(events),
+ events);
+
+ return 0;
+}
+
+static void push_request(struct avrcp_device *dev, uint8_t pdu_id,
+ uint8_t event_id, uint8_t transaction)
+{
+ struct avrcp_request *req;
+
+ req = g_new0(struct avrcp_request, 1);
+ req->dev = dev;
+ req->pdu_id = pdu_id;
+ req->event_id = event_id;
+ req->transaction = transaction;
+
+ g_queue_push_tail(dev->queue, req);
+}
+
+static int handle_get_play_status_cmd(struct avrcp *session,
+ uint8_t transaction, void *user_data)
+{
+ struct avrcp_device *dev = user_data;
+
+ DBG("");
+
+ ipc_send_notif(hal_ipc, HAL_SERVICE_ID_AVRCP,
+ HAL_EV_AVRCP_GET_PLAY_STATUS, 0, NULL);
+
+ push_request(dev, AVRCP_GET_PLAY_STATUS, 0, transaction);
+
+ return 0;
+}
+
+static int handle_get_element_attrs_cmd(struct avrcp *session,
+ uint8_t transaction, uint64_t uid,
+ uint8_t number, uint32_t *attrs,
+ void *user_data)
+{
+ struct avrcp_device *dev = user_data;
+ uint8_t buf[IPC_MTU];
+ struct hal_ev_avrcp_get_element_attrs *ev = (void *) buf;
+ int i;
+
+ DBG("");
+
+ ev->number = number;
+ /* Set everything in case of empty list */
+ if (ev->number == 0) {
+ for (i = 0; i < HAL_AVRCP_MEDIA_ATTR_DURATION; i++) {
+ /* Skip 0x00 as the attributes start with 0x01 */
+ ev->attrs[i] = i + 1;
+ }
+ ev->number = i;
+ goto done;
+ }
+
+ for (i = 0; i < number; i++)
+ ev->attrs[i] = attrs[i];
+
+done:
+ ipc_send_notif(hal_ipc, HAL_SERVICE_ID_AVRCP,
+ HAL_EV_AVRCP_GET_ELEMENT_ATTRS,
+ sizeof(*ev) + ev->number, ev);
+
+ push_request(dev, AVRCP_GET_ELEMENT_ATTRIBUTES, 0, transaction);
+
+ return 0;
+
+}
+
+static int handle_register_notification_cmd(struct avrcp *session,
+ uint8_t transaction,
+ uint8_t event,
+ uint32_t interval,
+ void *user_data)
+{
+ struct avrcp_device *dev = user_data;
+ struct hal_ev_avrcp_register_notification ev;
+
+ DBG("");
+
+ /* TODO: Add any missing events supported by Android */
+ switch (event) {
+ case AVRCP_EVENT_STATUS_CHANGED:
+ case AVRCP_EVENT_TRACK_CHANGED:
+ case AVRCP_EVENT_PLAYBACK_POS_CHANGED:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ ev.event = event;
+ ev.param = interval;
+
+ ipc_send_notif(hal_ipc, HAL_SERVICE_ID_AVRCP,
+ HAL_EV_AVRCP_REGISTER_NOTIFICATION,
+ sizeof(ev), &ev);
+
+ push_request(dev, AVRCP_REGISTER_NOTIFICATION, event, transaction);
+
+ return 0;
+}
+
+static const struct avrcp_control_ind control_ind = {
+ .get_capabilities = handle_get_capabilities_cmd,
+ .get_play_status = handle_get_play_status_cmd,
+ .get_element_attributes = handle_get_element_attrs_cmd,
+ .register_notification = handle_register_notification_cmd,
+};
+
+static bool handle_register_notification_rsp(struct avrcp *session, int err,
+ uint8_t code, uint8_t event,
+ void *params,
+ void *user_data)
+{
+ struct avrcp_device *dev = user_data;
+ struct hal_ev_avrcp_volume_changed ev;
+ uint8_t *volume = params;
+
+ if (err < 0) {
+ error("AVRCP: %s", strerror(-err));
+ return false;
+ }
+
+ if (code != AVC_CTYPE_INTERIM && code != AVC_CTYPE_CHANGED)
+ return false;
+
+ if (event != AVRCP_EVENT_VOLUME_CHANGED)
+ return false;
+
+ ev.type = code;
+ ev.volume = volume[0];
+
+ ipc_send_notif(hal_ipc, HAL_SERVICE_ID_AVRCP,
+ HAL_EV_AVRCP_VOLUME_CHANGED,
+ sizeof(ev), &ev);
+
+ if (code == AVC_CTYPE_INTERIM)
+ return true;
+
+ avrcp_register_notification(dev->session, event, 0);
+ return false;
+}
+
+static void handle_get_capabilities_rsp(struct avrcp *session, int err,
+ uint8_t number, uint8_t *events,
+ void *user_data)
+{
+ struct avrcp_device *dev = user_data;
+ int i;
+
+ if (err < 0) {
+ error("AVRCP: %s", strerror(-err));
+ return;
+ }
+
+ for (i = 0; i < number; i++) {
+ if (events[i] != AVRCP_EVENT_VOLUME_CHANGED)
+ continue;
+
+ avrcp_register_notification(dev->session, events[i], 0);
+ break;
+ }
+
+ return;
+}
+
+static void handle_set_volume_rsp(struct avrcp *session, int err,
+ uint8_t value, void *user_data)
+{
+ struct hal_ev_avrcp_volume_changed ev;
+
+ if (err < 0) {
+ ev.volume = 0;
+ ev.type = AVC_CTYPE_REJECTED;
+ goto done;
+ }
+
+ ev.volume = value;
+ ev.type = AVC_CTYPE_ACCEPTED;
+
+done:
+ ipc_send_notif(hal_ipc, HAL_SERVICE_ID_AVRCP,
+ HAL_EV_AVRCP_VOLUME_CHANGED,
+ sizeof(ev), &ev);
+}
+
+static const struct avrcp_control_cfm control_cfm = {
+ .get_capabilities = handle_get_capabilities_rsp,
+ .register_notification = handle_register_notification_rsp,
+ .set_volume = handle_set_volume_rsp,
+};
+
+static int avrcp_device_add_session(struct avrcp_device *dev, int fd,
+ uint16_t imtu, uint16_t omtu)
+{
+ struct hal_ev_avrcp_remote_features ev;
+ char address[18];
+
+ dev->session = avrcp_new(fd, imtu, omtu, dev->version);
+ if (!dev->session)
+ return -EINVAL;
+
+ avrcp_set_destroy_cb(dev->session, disconnect_cb, dev);
+ avrcp_set_passthrough_handlers(dev->session, passthrough_handlers,
+ dev);
+ avrcp_register_player(dev->session, &control_ind, &control_cfm, dev);
+
+ dev->queue = g_queue_new();
+
+ ba2str(&dev->dst, address);
+
+ /* FIXME: get the real name of the device */
+ avrcp_init_uinput(dev->session, "bluetooth", address);
+
+ bdaddr2android(&dev->dst, ev.bdaddr);
+ ev.features = HAL_AVRCP_FEATURE_NONE;
+
+ DBG("version 0x%02x", dev->version);
+
+ if (dev->version < 0x0103)
+ goto done;
+
+ ev.features |= HAL_AVRCP_FEATURE_METADATA;
+
+ if (dev->version < 0x0104)
+ goto done;
+
+ ev.features |= HAL_AVRCP_FEATURE_ABSOLUTE_VOLUME;
+
+ avrcp_get_capabilities(dev->session, CAP_EVENTS_SUPPORTED);
+
+done:
+ ipc_send_notif(hal_ipc, HAL_SERVICE_ID_AVRCP,
+ HAL_EV_AVRCP_REMOTE_FEATURES,
+ sizeof(ev), &ev);
+
+ return 0;
+}
+
+static void connect_cb(GIOChannel *chan, GError *err, gpointer user_data)
+{
+ struct avrcp_device *dev = user_data;
+ uint16_t imtu, omtu;
+ char address[18];
+ GError *gerr = NULL;
+ int fd;
+
+ if (err) {
+ error("%s", err->message);
+ return;
+ }
+
+ bt_io_get(chan, &gerr,
+ BT_IO_OPT_DEST, address,
+ BT_IO_OPT_IMTU, &imtu,
+ BT_IO_OPT_OMTU, &omtu,
+ BT_IO_OPT_INVALID);
+ if (gerr) {
+ error("%s", gerr->message);
+ g_error_free(gerr);
+ g_io_channel_shutdown(chan, TRUE, NULL);
+ return;
+ }
+
+ fd = g_io_channel_unix_get_fd(chan);
+ if (avrcp_device_add_session(dev, fd, imtu, omtu) < 0) {
+ avrcp_device_free(dev);
+ return;
+ }
+
+ g_io_channel_set_close_on_unref(chan, FALSE);
+
+ if (dev->io) {
+ g_io_channel_unref(dev->io);
+ dev->io = NULL;
+ }
+
+ DBG("%s connected", address);
+}
+
+static bool avrcp_device_connect(struct avrcp_device *dev, BtIOConnect cb)
+{
+ GError *err = NULL;
+
+ dev->io = bt_io_connect(cb, dev, NULL, &err,
+ BT_IO_OPT_SOURCE_BDADDR, &adapter_addr,
+ BT_IO_OPT_DEST_BDADDR, &dev->dst,
+ BT_IO_OPT_PSM, L2CAP_PSM_AVCTP,
+ BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_MEDIUM,
+ BT_IO_OPT_INVALID);
+ if (err) {
+ error("%s", err->message);
+ g_error_free(err);
+ return false;
+ }
+
+ return true;
+}
+
+static void search_cb(sdp_list_t *recs, int err, gpointer data)
+{
+ struct avrcp_device *dev = data;
+ sdp_list_t *list;
+
+ DBG("");
+
+ if (!g_slist_find(devices, dev))
+ return;
+
+ if (err < 0) {
+ error("Unable to get AV_REMOTE_SVCLASS_ID SDP record: %s",
+ strerror(-err));
+ goto fail;
+ }
+
+ if (!recs || !recs->data) {
+ error("No AVRCP records found");
+ goto fail;
+ }
+
+ for (list = recs; list; list = list->next) {
+ sdp_record_t *rec = list->data;
+ sdp_list_t *l;
+ sdp_profile_desc_t *desc;
+ int features;
+
+ if (sdp_get_profile_descs(rec, &l) < 0)
+ continue;
+
+ desc = l->data;
+ dev->version = desc->version;
+
+ if (sdp_get_int_attr(rec, SDP_ATTR_SUPPORTED_FEATURES,
+ &features) == 0)
+ dev->features = features;
+
+ sdp_list_free(l, free);
+ break;
+ }
+
+ if (dev->io) {
+ GError *gerr = NULL;
+ if (!bt_io_accept(dev->io, connect_cb, dev, NULL, &gerr)) {
+ error("bt_io_accept: %s", gerr->message);
+ g_error_free(gerr);
+ goto fail;
+ }
+ return;
+ }
+
+ if (!avrcp_device_connect(dev, connect_cb)) {
+ error("Unable to connect to AVRCP");
+ goto fail;
+ }
+
+ return;
+
+fail:
+ avrcp_device_remove(dev);
+}
+
+static int avrcp_device_search(struct avrcp_device *dev)
+{
+ uuid_t uuid;
+
+ sdp_uuid16_create(&uuid, AV_REMOTE_SVCLASS_ID);
+
+ return bt_search_service(&adapter_addr, &dev->dst, &uuid, search_cb,
+ dev, NULL, 0);
+}
+
+static void confirm_cb(GIOChannel *chan, gpointer data)
+{
+ struct avrcp_device *dev;
+ char address[18];
+ bdaddr_t dst;
+ GError *err = NULL;
+
+ bt_io_get(chan, &err,
+ BT_IO_OPT_DEST_BDADDR, &dst,
+ BT_IO_OPT_DEST, address,
+ BT_IO_OPT_INVALID);
+ if (err) {
+ error("%s", err->message);
+ g_error_free(err);
+ g_io_channel_shutdown(chan, TRUE, NULL);
+ return;
+ }
+
+ DBG("incoming connect from %s", address);
+
+ dev = avrcp_device_find(&dst);
+ if (dev && dev->session) {
+ error("AVRCP: Refusing unexpected connect");
+ g_io_channel_shutdown(chan, TRUE, NULL);
+ return;
+ }
+
+ dev = avrcp_device_new(&dst);
+ if (avrcp_device_search(dev) < 0) {
+ error("AVRCP: Failed to search SDP details");
+ avrcp_device_free(dev);
+ g_io_channel_shutdown(chan, TRUE, NULL);
+ }
+
+ dev->io = g_io_channel_ref(chan);
+}
+
+bool bt_avrcp_register(struct ipc *ipc, const bdaddr_t *addr, uint8_t mode)
+{
+ GError *err = NULL;
+ sdp_record_t *rec;
+
+ DBG("");
+
+ bacpy(&adapter_addr, addr);
+
+ server = bt_io_listen(NULL, confirm_cb, NULL, NULL, &err,
+ BT_IO_OPT_SOURCE_BDADDR, &adapter_addr,
+ BT_IO_OPT_PSM, L2CAP_PSM_AVCTP,
+ BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_MEDIUM,
+ BT_IO_OPT_INVALID);
+ if (!server) {
+ error("Failed to listen on AVDTP channel: %s", err->message);
+ g_error_free(err);
+ return false;
+ }
+
+ rec = avrcp_tg_record();
+ if (!rec) {
+ error("Failed to allocate AVRCP TG record");
+ goto fail;
+ }
+
+ if (bt_adapter_add_record(rec, 0) < 0) {
+ error("Failed to register AVRCP TG record");
+ sdp_record_free(rec);
+ goto fail;
+ }
+ record_tg_id = rec->handle;
+
+ rec = avrcp_ct_record();
+ if (!rec) {
+ error("Failed to allocate AVRCP CT record");
+ bt_adapter_remove_record(record_tg_id);
+ goto fail;
+ }
+
+ if (bt_adapter_add_record(rec, 0) < 0) {
+ error("Failed to register AVRCP CT record");
+ bt_adapter_remove_record(record_tg_id);
+ sdp_record_free(rec);
+ goto fail;
+ }
+ record_ct_id = rec->handle;
+
+ hal_ipc = ipc;
+
+ ipc_register(hal_ipc, HAL_SERVICE_ID_AVRCP, cmd_handlers,
+ G_N_ELEMENTS(cmd_handlers));
+
+ return true;
+fail:
+ g_io_channel_shutdown(server, TRUE, NULL);
+ g_io_channel_unref(server);
+ server = NULL;
+
+ return false;
+}
+
+void bt_avrcp_unregister(void)
+{
+ DBG("");
+
+ g_slist_free_full(devices, avrcp_device_free);
+ devices = NULL;
+
+ ipc_unregister(hal_ipc, HAL_SERVICE_ID_AVRCP);
+ hal_ipc = NULL;
+
+ bt_adapter_remove_record(record_tg_id);
+ record_tg_id = 0;
+
+ bt_adapter_remove_record(record_ct_id);
+ record_ct_id = 0;
+
+ if (server) {
+ g_io_channel_shutdown(server, TRUE, NULL);
+ g_io_channel_unref(server);
+ server = NULL;
+ }
+}
+
+void bt_avrcp_connect(const bdaddr_t *dst)
+{
+ struct avrcp_device *dev;
+ char addr[18];
+
+ DBG("");
+
+ if (avrcp_device_find(dst))
+ return;
+
+ dev = avrcp_device_new(dst);
+ if (avrcp_device_search(dev) < 0) {
+ error("AVRCP: Failed to search SDP details");
+ avrcp_device_free(dev);
+ }
+
+ ba2str(&dev->dst, addr);
+ DBG("connecting to %s", addr);
+}
+
+void bt_avrcp_disconnect(const bdaddr_t *dst)
+{
+ struct avrcp_device *dev;
+
+ DBG("");
+
+ dev = avrcp_device_find(dst);
+ if (!dev)
+ return;
+
+ if (dev->session) {
+ avrcp_shutdown(dev->session);
+ return;
+ }
+
+ avrcp_device_remove(dev);
+}
diff --git a/repo/android/avrcp.h b/repo/android/avrcp.h
new file mode 100644
index 0000000..11e79b7
--- /dev/null
+++ b/repo/android/avrcp.h
@@ -0,0 +1,28 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2013-2014 Intel Corporation. All rights reserved.
+ *
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+bool bt_avrcp_register(struct ipc *ipc, const bdaddr_t *addr, uint8_t mode);
+void bt_avrcp_unregister(void);
+
+void bt_avrcp_connect(const bdaddr_t *dst);
+void bt_avrcp_disconnect(const bdaddr_t *dst);
diff --git a/repo/android/bluetooth.c b/repo/android/bluetooth.c
new file mode 100644
index 0000000..51a31fe
--- /dev/null
+++ b/repo/android/bluetooth.c
@@ -0,0 +1,5343 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2013-2014 Intel Corporation. All rights reserved.
+ *
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <errno.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <inttypes.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+#include <glib.h>
+
+#include "lib/bluetooth.h"
+#include "lib/sdp.h"
+#include "lib/mgmt.h"
+#include "lib/uuid.h"
+#include "src/shared/util.h"
+#include "src/shared/mgmt.h"
+#include "src/shared/queue.h"
+#include "src/eir.h"
+#include "lib/sdp.h"
+#include "lib/sdp_lib.h"
+#include "src/sdp-client.h"
+#include "src/sdpd.h"
+#include "src/log.h"
+#include "hal-msg.h"
+#include "ipc-common.h"
+#include "ipc.h"
+#include "utils.h"
+#include "bluetooth.h"
+
+#define DUT_MODE_FILE "/sys/kernel/debug/bluetooth/hci%u/dut_mode"
+
+#define SETTINGS_FILE ANDROID_STORAGEDIR"/settings"
+#define DEVICES_FILE ANDROID_STORAGEDIR"/devices"
+#define CACHE_FILE ANDROID_STORAGEDIR"/cache"
+
+#define ADAPTER_MAJOR_CLASS 0x02 /* Phone */
+#define ADAPTER_MINOR_CLASS 0x03 /* Smartphone */
+
+/* Default to DisplayYesNo */
+#define DEFAULT_IO_CAPABILITY 0x01
+
+/* Default discoverable timeout 120sec as in Android */
+#define DEFAULT_DISCOVERABLE_TIMEOUT 120
+
+#define DEVICES_CACHE_MAX 300
+
+#define BASELEN_PROP_CHANGED (sizeof(struct hal_ev_adapter_props_changed) \
+ + sizeof(struct hal_property))
+
+#define BASELEN_REMOTE_DEV_PROP (sizeof(struct hal_ev_remote_device_props) \
+ + sizeof(struct hal_property))
+
+#define SCAN_TYPE_NONE 0
+#define SCAN_TYPE_BREDR (1 << BDADDR_BREDR)
+#define SCAN_TYPE_LE ((1 << BDADDR_LE_PUBLIC) | (1 << BDADDR_LE_RANDOM))
+#define SCAN_TYPE_DUAL (SCAN_TYPE_BREDR | SCAN_TYPE_LE)
+
+#define BDADDR_LE (BDADDR_LE_RANDOM | BDADDR_LE_PUBLIC)
+
+struct device {
+ bdaddr_t bdaddr;
+ uint8_t bdaddr_type;
+
+ bdaddr_t rpa;
+ uint8_t rpa_type;
+
+ bool le;
+ bool bredr;
+
+ bool pairing;
+
+ bool bredr_paired;
+ bool bredr_bonded;
+ bool le_paired;
+ bool le_bonded;
+
+ bool in_white_list;
+
+ bool connected;
+
+ char *name;
+ char *friendly_name;
+
+ uint32_t class;
+ int32_t rssi;
+
+ time_t bredr_seen;
+ time_t le_seen;
+
+ GSList *uuids;
+
+ bool found; /* if device is found in current discovery session */
+ unsigned int confirm_id; /* mgtm command id if command pending */
+
+ bool valid_remote_csrk;
+ bool remote_csrk_auth;
+ uint8_t remote_csrk[16];
+ uint32_t remote_sign_cnt;
+
+ bool valid_local_csrk;
+ bool local_csrk_auth;
+ uint8_t local_csrk[16];
+ uint32_t local_sign_cnt;
+ uint16_t gatt_ccc;
+};
+
+struct browse_req {
+ bdaddr_t bdaddr;
+ GSList *uuids;
+ int search_uuid;
+ int reconnect_attempt;
+};
+
+static struct {
+ uint16_t index;
+
+ bdaddr_t bdaddr;
+ uint32_t dev_class;
+
+ char *name;
+
+ uint8_t max_advert_instance;
+ uint8_t rpa_offload_supported;
+ uint8_t max_irk_list_size;
+ uint8_t max_scan_filters_supported;
+ uint16_t scan_result_storage_size;
+ uint8_t activity_energy_info_supported;
+
+ uint32_t current_settings;
+ uint32_t supported_settings;
+
+ bool le_scanning;
+ uint8_t cur_discovery_type;
+ uint8_t exp_discovery_type;
+ uint32_t discoverable_timeout;
+
+ GSList *uuids;
+} adapter = {
+ .index = MGMT_INDEX_NONE,
+ .dev_class = 0,
+ .name = NULL,
+ .max_advert_instance = 0,
+ .rpa_offload_supported = 0,
+ .max_irk_list_size = 0,
+ .max_scan_filters_supported = 0,
+ .scan_result_storage_size = 0,
+ .activity_energy_info_supported = 0,
+ .current_settings = 0,
+ .supported_settings = 0,
+ .cur_discovery_type = SCAN_TYPE_NONE,
+ .exp_discovery_type = SCAN_TYPE_NONE,
+ .discoverable_timeout = DEFAULT_DISCOVERABLE_TIMEOUT,
+ .uuids = NULL,
+};
+
+static const uint16_t uuid_list[] = {
+ L2CAP_UUID,
+ PNP_INFO_SVCLASS_ID,
+ PUBLIC_BROWSE_GROUP,
+ 0
+};
+
+static uint16_t option_index = MGMT_INDEX_NONE;
+static struct mgmt *mgmt_if = NULL;
+
+static GSList *bonded_devices = NULL;
+static GSList *cached_devices = NULL;
+
+static bt_le_device_found gatt_device_found_cb = NULL;
+static bt_le_discovery_stopped gatt_discovery_stopped_cb = NULL;
+
+/* This list contains addresses which are asked for records */
+static GSList *browse_reqs;
+
+static struct ipc *hal_ipc = NULL;
+
+static bool kernel_conn_control = false;
+
+static struct queue *unpaired_cb_list = NULL;
+static struct queue *paired_cb_list = NULL;
+
+static void get_device_android_addr(struct device *dev, uint8_t *addr)
+{
+ /*
+ * If RPA is set it means that IRK was received and ID address is being
+ * used. Android Framework is still using old RPA and it needs to be
+ * used in notifications.
+ */
+ if (bacmp(&dev->rpa, BDADDR_ANY))
+ bdaddr2android(&dev->rpa, addr);
+ else
+ bdaddr2android(&dev->bdaddr, addr);
+}
+
+static void mgmt_debug(const char *str, void *user_data)
+{
+ const char *prefix = user_data;
+ info("%s%s", prefix, str);
+}
+
+static void store_adapter_config(void)
+{
+ GKeyFile *key_file;
+ gsize length = 0;
+ char addr[18];
+ char *data;
+
+ key_file = g_key_file_new();
+
+ g_key_file_load_from_file(key_file, SETTINGS_FILE, 0, NULL);
+
+ ba2str(&adapter.bdaddr, addr);
+
+ g_key_file_set_string(key_file, "General", "Address", addr);
+
+ if (adapter.name)
+ g_key_file_set_string(key_file, "General", "Name",
+ adapter.name);
+
+ g_key_file_set_integer(key_file, "General", "DiscoverableTimeout",
+ adapter.discoverable_timeout);
+
+ data = g_key_file_to_data(key_file, &length, NULL);
+
+ g_file_set_contents(SETTINGS_FILE, data, length, NULL);
+
+ g_free(data);
+ g_key_file_free(key_file);
+}
+
+static void load_adapter_config(void)
+{
+ GError *gerr = NULL;
+ GKeyFile *key_file;
+ char *str;
+
+ key_file = g_key_file_new();
+ g_key_file_load_from_file(key_file, SETTINGS_FILE, 0, NULL);
+
+ str = g_key_file_get_string(key_file, "General", "Address", NULL);
+ if (!str) {
+ g_key_file_free(key_file);
+ return;
+ }
+
+ str2ba(str, &adapter.bdaddr);
+ g_free(str);
+
+ adapter.name = g_key_file_get_string(key_file, "General", "Name", NULL);
+
+ adapter.discoverable_timeout = g_key_file_get_integer(key_file,
+ "General", "DiscoverableTimeout", &gerr);
+ if (gerr) {
+ adapter.discoverable_timeout = DEFAULT_DISCOVERABLE_TIMEOUT;
+ g_clear_error(&gerr);
+ }
+
+ g_key_file_free(key_file);
+}
+
+static void store_device_info(struct device *dev, const char *path)
+{
+ GKeyFile *key_file;
+ char addr[18];
+ gsize length = 0;
+ char **uuids = NULL;
+ char *str;
+
+ ba2str(&dev->bdaddr, addr);
+
+ key_file = g_key_file_new();
+ g_key_file_load_from_file(key_file, path, 0, NULL);
+
+ g_key_file_set_boolean(key_file, addr, "BREDR", dev->bredr);
+
+ if (dev->le)
+ g_key_file_set_integer(key_file, addr, "AddressType",
+ dev->bdaddr_type);
+
+ g_key_file_set_string(key_file, addr, "Name", dev->name);
+
+ if (dev->friendly_name)
+ g_key_file_set_string(key_file, addr, "FriendlyName",
+ dev->friendly_name);
+ else
+ g_key_file_remove_key(key_file, addr, "FriendlyName", NULL);
+
+ if (dev->class)
+ g_key_file_set_integer(key_file, addr, "Class", dev->class);
+ else
+ g_key_file_remove_key(key_file, addr, "Class", NULL);
+
+ if (dev->bredr_seen > dev->le_seen)
+ g_key_file_set_integer(key_file, addr, "Timestamp",
+ dev->bredr_seen);
+ else
+ g_key_file_set_integer(key_file, addr, "Timestamp",
+ dev->le_seen);
+
+ if (dev->uuids) {
+ GSList *l;
+ int i;
+
+ uuids = g_new0(char *, g_slist_length(dev->uuids) + 1);
+
+ for (i = 0, l = dev->uuids; l; l = g_slist_next(l), i++) {
+ int j;
+ uint8_t *u = l->data;
+ char *uuid_str = g_malloc0(33);
+
+ for (j = 0; j < 16; j++)
+ sprintf(uuid_str + (j * 2), "%2.2X", u[j]);
+
+ uuids[i] = uuid_str;
+ }
+
+ g_key_file_set_string_list(key_file, addr, "Services",
+ (const char **)uuids, i);
+ } else {
+ g_key_file_remove_key(key_file, addr, "Services", NULL);
+ }
+
+ str = g_key_file_to_data(key_file, &length, NULL);
+ g_file_set_contents(path, str, length, NULL);
+ g_free(str);
+
+ g_key_file_free(key_file);
+ g_strfreev(uuids);
+}
+
+static void remove_device_info(struct device *dev, const char *path)
+{
+ GKeyFile *key_file;
+ gsize length = 0;
+ char addr[18];
+ char *str;
+
+ ba2str(&dev->bdaddr, addr);
+
+ key_file = g_key_file_new();
+ g_key_file_load_from_file(key_file, path, 0, NULL);
+
+ g_key_file_remove_group(key_file, addr, NULL);
+
+ str = g_key_file_to_data(key_file, &length, NULL);
+ g_file_set_contents(path, str, length, NULL);
+ g_free(str);
+
+ g_key_file_free(key_file);
+}
+
+static int device_match(gconstpointer a, gconstpointer b)
+{
+ const struct device *dev = a;
+ const bdaddr_t *bdaddr = b;
+
+ /* Android is using RPA even if IRK was received and ID addr resolved */
+ if (!bacmp(&dev->rpa, bdaddr))
+ return 0;
+
+ return bacmp(&dev->bdaddr, bdaddr);
+}
+
+static struct device *find_device(const bdaddr_t *bdaddr)
+{
+ GSList *l;
+
+ l = g_slist_find_custom(bonded_devices, bdaddr, device_match);
+ if (l)
+ return l->data;
+
+ l = g_slist_find_custom(cached_devices, bdaddr, device_match);
+ if (l)
+ return l->data;
+
+ return NULL;
+}
+
+static void free_device(struct device *dev)
+{
+ if (dev->confirm_id)
+ mgmt_cancel(mgmt_if, dev->confirm_id);
+
+ g_free(dev->name);
+ g_free(dev->friendly_name);
+ g_slist_free_full(dev->uuids, g_free);
+ g_free(dev);
+}
+
+static void cache_device(struct device *new_dev)
+{
+ struct device *dev;
+ GSList *l;
+
+ l = g_slist_find(cached_devices, new_dev);
+ if (l) {
+ cached_devices = g_slist_remove(cached_devices, new_dev);
+ goto cache;
+ }
+
+ if (g_slist_length(cached_devices) < DEVICES_CACHE_MAX)
+ goto cache;
+
+ l = g_slist_last(cached_devices);
+ dev = l->data;
+
+ cached_devices = g_slist_remove(cached_devices, dev);
+ remove_device_info(dev, CACHE_FILE);
+ free_device(dev);
+
+cache:
+ cached_devices = g_slist_prepend(cached_devices, new_dev);
+ store_device_info(new_dev, CACHE_FILE);
+}
+
+static struct device *create_device(const bdaddr_t *bdaddr, uint8_t bdaddr_type)
+{
+ struct device *dev;
+ char addr[18];
+
+ ba2str(bdaddr, addr);
+ DBG("%s", addr);
+
+ dev = g_new0(struct device, 1);
+
+ bacpy(&dev->bdaddr, bdaddr);
+
+ if (bdaddr_type == BDADDR_BREDR) {
+ dev->bredr = true;
+ dev->bredr_seen = time(NULL);
+ } else {
+ dev->le = true;
+ dev->bdaddr_type = bdaddr_type;
+ dev->le_seen = time(NULL);
+ }
+
+ /*
+ * Use address for name, will be change if one is present
+ * eg. in EIR or set by set_property.
+ */
+ dev->name = g_strdup(addr);
+
+ return dev;
+}
+
+static struct device *get_device(const bdaddr_t *bdaddr, uint8_t type)
+{
+ struct device *dev;
+
+ dev = find_device(bdaddr);
+ if (dev)
+ return dev;
+
+ dev = create_device(bdaddr, type);
+
+ cache_device(dev);
+
+ return dev;
+}
+
+static struct device *find_device_android(const uint8_t *addr)
+{
+ bdaddr_t bdaddr;
+
+ android2bdaddr(addr, &bdaddr);
+
+ return find_device(&bdaddr);
+}
+
+static struct device *get_device_android(const uint8_t *addr)
+{
+ bdaddr_t bdaddr;
+
+ android2bdaddr(addr, &bdaddr);
+
+ return get_device(&bdaddr, BDADDR_BREDR);
+}
+
+static void send_adapter_property(uint8_t type, uint16_t len, const void *val)
+{
+ uint8_t buf[BASELEN_PROP_CHANGED + len];
+ struct hal_ev_adapter_props_changed *ev = (void *) buf;
+
+ ev->status = HAL_STATUS_SUCCESS;
+ ev->num_props = 1;
+ ev->props[0].type = type;
+ ev->props[0].len = len;
+ memcpy(ev->props[0].val, val, len);
+
+ ipc_send_notif(hal_ipc, HAL_SERVICE_ID_BLUETOOTH,
+ HAL_EV_ADAPTER_PROPS_CHANGED, sizeof(buf), buf);
+}
+
+static void adapter_name_changed(const uint8_t *name)
+{
+ /* Android expects string value without NULL terminator */
+ send_adapter_property(HAL_PROP_ADAPTER_NAME,
+ strlen((const char *) name), name);
+}
+
+static void adapter_set_name(const uint8_t *name)
+{
+ if (!g_strcmp0(adapter.name, (const char *) name))
+ return;
+
+ DBG("%s", name);
+
+ g_free(adapter.name);
+ adapter.name = g_strdup((const char *) name);
+
+ store_adapter_config();
+
+ adapter_name_changed(name);
+}
+
+static void mgmt_local_name_changed_event(uint16_t index, uint16_t length,
+ const void *param, void *user_data)
+{
+ const struct mgmt_cp_set_local_name *rp = param;
+
+ if (length < sizeof(*rp)) {
+ error("Wrong size of local name changed parameters");
+ return;
+ }
+
+ adapter_set_name(rp->name);
+
+ /* TODO Update services if needed */
+}
+
+static void powered_changed(void)
+{
+ struct hal_ev_adapter_state_changed ev;
+
+ ev.state = (adapter.current_settings & MGMT_SETTING_POWERED) ?
+ HAL_POWER_ON : HAL_POWER_OFF;
+
+ DBG("%u", ev.state);
+
+ ipc_send_notif(hal_ipc, HAL_SERVICE_ID_BLUETOOTH,
+ HAL_EV_ADAPTER_STATE_CHANGED, sizeof(ev), &ev);
+}
+
+static uint8_t settings2scan_mode(void)
+{
+ bool connectable, discoverable;
+
+ connectable = adapter.current_settings & MGMT_SETTING_CONNECTABLE;
+ discoverable = adapter.current_settings & MGMT_SETTING_DISCOVERABLE;
+
+ if (connectable && discoverable)
+ return HAL_ADAPTER_SCAN_MODE_CONN_DISC;
+
+ if (connectable)
+ return HAL_ADAPTER_SCAN_MODE_CONN;
+
+ return HAL_ADAPTER_SCAN_MODE_NONE;
+}
+
+static void scan_mode_changed(void)
+{
+ uint8_t mode;
+
+ mode = settings2scan_mode();
+
+ DBG("mode %u", mode);
+
+ send_adapter_property(HAL_PROP_ADAPTER_SCAN_MODE, sizeof(mode), &mode);
+}
+
+static void adapter_class_changed(void)
+{
+ send_adapter_property(HAL_PROP_ADAPTER_CLASS, sizeof(adapter.dev_class),
+ &adapter.dev_class);
+}
+
+static void settings_changed(uint32_t settings)
+{
+ uint32_t changed_mask;
+ uint32_t scan_mode_mask;
+
+ changed_mask = adapter.current_settings ^ settings;
+
+ adapter.current_settings = settings;
+
+ DBG("0x%08x", changed_mask);
+
+ if (changed_mask & MGMT_SETTING_POWERED)
+ powered_changed();
+
+ scan_mode_mask = MGMT_SETTING_CONNECTABLE |
+ MGMT_SETTING_DISCOVERABLE;
+
+ /*
+ * Only when powered, the connectable and discoverable
+ * state changes should be communicated.
+ */
+ if (adapter.current_settings & MGMT_SETTING_POWERED)
+ if (changed_mask & scan_mode_mask)
+ scan_mode_changed();
+}
+
+static void new_settings_callback(uint16_t index, uint16_t length,
+ const void *param, void *user_data)
+{
+ uint32_t settings;
+
+ if (length < sizeof(settings)) {
+ error("Wrong size of new settings parameters");
+ return;
+ }
+
+ settings = get_le32(param);
+
+ DBG("settings: 0x%8.8x -> 0x%8.8x", adapter.current_settings,
+ settings);
+
+ if (settings == adapter.current_settings)
+ return;
+
+ settings_changed(settings);
+}
+
+static void mgmt_dev_class_changed_event(uint16_t index, uint16_t length,
+ const void *param, void *user_data)
+{
+ const struct mgmt_cod *rp = param;
+ uint32_t dev_class;
+
+ if (length < sizeof(*rp)) {
+ error("Wrong size of class of device changed parameters");
+ return;
+ }
+
+ dev_class = rp->val[0] | (rp->val[1] << 8) | (rp->val[2] << 16);
+
+ if (dev_class == adapter.dev_class)
+ return;
+
+ DBG("Class: 0x%06x", dev_class);
+
+ adapter.dev_class = dev_class;
+
+ adapter_class_changed();
+
+ /* TODO: Gatt attrib set*/
+}
+
+void bt_store_gatt_ccc(const bdaddr_t *dst, uint16_t value)
+{
+ struct device *dev;
+ GKeyFile *key_file;
+ gsize length = 0;
+ char addr[18];
+ char *data;
+
+ dev = find_device(dst);
+ if (!dev)
+ return;
+
+ key_file = g_key_file_new();
+
+ if (!g_key_file_load_from_file(key_file, DEVICES_FILE, 0, NULL)) {
+ g_key_file_free(key_file);
+ return;
+ }
+
+ ba2str(&dev->bdaddr, addr);
+
+ DBG("%s Gatt CCC %d", addr, value);
+
+ g_key_file_set_integer(key_file, addr, "GattCCC", value);
+
+ data = g_key_file_to_data(key_file, &length, NULL);
+ g_file_set_contents(DEVICES_FILE, data, length, NULL);
+ g_free(data);
+
+ g_key_file_free(key_file);
+
+ dev->gatt_ccc = value;
+}
+
+uint16_t bt_get_gatt_ccc(const bdaddr_t *addr)
+{
+ struct device *dev;
+
+ dev = find_device(addr);
+ if (!dev)
+ return 0;
+
+ return dev->gatt_ccc;
+}
+
+static void store_link_key(const bdaddr_t *dst, const uint8_t *key,
+ uint8_t type, uint8_t pin_length)
+{
+ GKeyFile *key_file;
+ char key_str[33];
+ gsize length = 0;
+ char addr[18];
+ char *data;
+ int i;
+
+ key_file = g_key_file_new();
+
+ if (!g_key_file_load_from_file(key_file, DEVICES_FILE, 0, NULL)) {
+ g_key_file_free(key_file);
+ return;
+ }
+
+ ba2str(dst, addr);
+
+ DBG("%s type %u pin_len %u", addr, type, pin_length);
+
+ for (i = 0; i < 16; i++)
+ sprintf(key_str + (i * 2), "%2.2X", key[i]);
+
+ g_key_file_set_string(key_file, addr, "LinkKey", key_str);
+ g_key_file_set_integer(key_file, addr, "LinkKeyType", type);
+ g_key_file_set_integer(key_file, addr, "LinkKeyPinLength", pin_length);
+
+ data = g_key_file_to_data(key_file, &length, NULL);
+ g_file_set_contents(DEVICES_FILE, data, length, NULL);
+ g_free(data);
+
+ g_key_file_free(key_file);
+}
+
+static void send_bond_state_change(struct device *dev, uint8_t status,
+ uint8_t state)
+{
+ struct hal_ev_bond_state_changed ev;
+
+ ev.status = status;
+ ev.state = state;
+ get_device_android_addr(dev, ev.bdaddr);
+
+ ipc_send_notif(hal_ipc, HAL_SERVICE_ID_BLUETOOTH,
+ HAL_EV_BOND_STATE_CHANGED, sizeof(ev), &ev);
+}
+
+static void update_bredr_state(struct device *dev, bool pairing, bool paired,
+ bool bonded)
+{
+ if (pairing == dev->pairing && paired == dev->bredr_paired &&
+ bonded == dev->bredr_bonded)
+ return;
+
+ /* avoid unpairing device on incoming pairing request */
+ if (pairing && dev->bredr_paired)
+ goto done;
+
+ /* avoid unpairing device if pairing failed */
+ if (!pairing && !paired && dev->pairing && dev->bredr_paired)
+ goto done;
+
+ if (paired && !dev->le_paired && !dev->bredr_paired) {
+ cached_devices = g_slist_remove(cached_devices, dev);
+ bonded_devices = g_slist_prepend(bonded_devices, dev);
+ remove_device_info(dev, CACHE_FILE);
+ store_device_info(dev, DEVICES_FILE);
+ } else if (!paired && !dev->le_paired) {
+ bonded_devices = g_slist_remove(bonded_devices, dev);
+ remove_device_info(dev, DEVICES_FILE);
+ cache_device(dev);
+ }
+
+ dev->bredr_paired = paired;
+
+ if (dev->bredr_paired)
+ dev->bredr_bonded = dev->bredr_bonded || bonded;
+ else
+ dev->bredr_bonded = false;
+
+done:
+ dev->pairing = pairing;
+}
+
+static void update_le_state(struct device *dev, bool pairing, bool paired,
+ bool bonded)
+{
+ if (pairing == dev->pairing && paired == dev->le_paired &&
+ bonded == dev->le_bonded)
+ return;
+
+ /* avoid unpairing device on incoming pairing request */
+ if (pairing && dev->le_paired)
+ goto done;
+
+ /* avoid unpairing device if pairing failed */
+ if (!pairing && !paired && dev->pairing && dev->le_paired)
+ goto done;
+
+ if (paired && !dev->bredr_paired && !dev->le_paired) {
+ cached_devices = g_slist_remove(cached_devices, dev);
+ bonded_devices = g_slist_prepend(bonded_devices, dev);
+ remove_device_info(dev, CACHE_FILE);
+ store_device_info(dev, DEVICES_FILE);
+ } else if (!paired && !dev->bredr_paired) {
+ bonded_devices = g_slist_remove(bonded_devices, dev);
+ remove_device_info(dev, DEVICES_FILE);
+ dev->valid_local_csrk = false;
+ dev->valid_remote_csrk = false;
+ dev->local_sign_cnt = 0;
+ dev->remote_sign_cnt = 0;
+ memset(dev->local_csrk, 0, sizeof(dev->local_csrk));
+ memset(dev->remote_csrk, 0, sizeof(dev->remote_csrk));
+ cache_device(dev);
+ }
+
+ dev->le_paired = paired;
+
+ if (dev->le_paired)
+ dev->le_bonded = dev->le_bonded || bonded;
+ else
+ dev->le_bonded = false;
+
+done:
+ dev->pairing = pairing;
+}
+
+static uint8_t device_bond_state(struct device *dev)
+{
+ if (dev->pairing)
+ return HAL_BOND_STATE_BONDING;
+
+ /*
+ * We are checking for paired here instead of bonded as HAL API is
+ * using BOND state also if there was no bonding pairing.
+ */
+ if (dev->bredr_paired || dev->le_paired)
+ return HAL_BOND_STATE_BONDED;
+
+ return HAL_BOND_STATE_NONE;
+}
+
+static void update_bond_state(struct device *dev, uint8_t status,
+ uint8_t old_bond, uint8_t new_bond)
+{
+ if (old_bond == new_bond)
+ return;
+
+ /*
+ * When internal bond state changes from bond to non-bond or other way,
+ * BfA needs to send bonding state to Android in the middle. Otherwise
+ * Android will not handle it correctly
+ */
+ if ((old_bond == HAL_BOND_STATE_NONE &&
+ new_bond == HAL_BOND_STATE_BONDED) ||
+ (old_bond == HAL_BOND_STATE_BONDED &&
+ new_bond == HAL_BOND_STATE_NONE))
+ send_bond_state_change(dev, HAL_STATUS_SUCCESS,
+ HAL_BOND_STATE_BONDING);
+
+ send_bond_state_change(dev, status, new_bond);
+}
+
+static void send_paired_notification(void *data, void *user_data)
+{
+ bt_paired_device_cb cb = data;
+ struct device *dev = user_data;
+
+ cb(&dev->bdaddr);
+}
+
+static void update_device_state(struct device *dev, uint8_t addr_type,
+ uint8_t status, bool pairing, bool paired,
+ bool bonded)
+{
+ uint8_t old_bond, new_bond;
+
+ old_bond = device_bond_state(dev);
+
+ if (addr_type == BDADDR_BREDR)
+ update_bredr_state(dev, pairing, paired, bonded);
+ else
+ update_le_state(dev, pairing, paired, bonded);
+
+ new_bond = device_bond_state(dev);
+
+ update_bond_state(dev, status, old_bond, new_bond);
+}
+
+static void send_device_property(struct device *dev, uint8_t type,
+ uint16_t len, const void *val)
+{
+ uint8_t buf[BASELEN_REMOTE_DEV_PROP + len];
+ struct hal_ev_remote_device_props *ev = (void *) buf;
+
+ ev->status = HAL_STATUS_SUCCESS;
+ get_device_android_addr(dev, ev->bdaddr);
+ ev->num_props = 1;
+ ev->props[0].type = type;
+ ev->props[0].len = len;
+ memcpy(ev->props[0].val, val, len);
+
+ ipc_send_notif(hal_ipc, HAL_SERVICE_ID_BLUETOOTH,
+ HAL_EV_REMOTE_DEVICE_PROPS, sizeof(buf), buf);
+}
+
+static void send_device_uuids_notif(struct device *dev)
+{
+ uint8_t buf[sizeof(uint128_t) * g_slist_length(dev->uuids)];
+ uint8_t *ptr = buf;
+ GSList *l;
+
+ for (l = dev->uuids; l; l = g_slist_next(l)) {
+ memcpy(ptr, l->data, sizeof(uint128_t));
+ ptr += sizeof(uint128_t);
+ }
+
+ send_device_property(dev, HAL_PROP_DEVICE_UUIDS, sizeof(buf), buf);
+}
+
+static void set_device_uuids(struct device *dev, GSList *uuids)
+{
+ g_slist_free_full(dev->uuids, g_free);
+ dev->uuids = uuids;
+
+ if (dev->le_paired || dev->bredr_paired)
+ store_device_info(dev, DEVICES_FILE);
+ else
+ store_device_info(dev, CACHE_FILE);
+
+ send_device_uuids_notif(dev);
+}
+
+static void browse_req_free(struct browse_req *req)
+{
+ g_slist_free_full(req->uuids, g_free);
+ g_free(req);
+}
+
+static int uuid_128_cmp(gconstpointer a, gconstpointer b)
+{
+ return memcmp(a, b, sizeof(uint128_t));
+}
+
+static void update_records(struct browse_req *req, sdp_list_t *recs)
+{
+ for (; recs; recs = recs->next) {
+ sdp_record_t *rec = (sdp_record_t *) recs->data;
+ sdp_list_t *svcclass = NULL;
+ uuid_t uuid128;
+ uuid_t *tmp;
+ uint8_t *new_uuid;
+
+ if (!rec)
+ break;
+
+ if (sdp_get_service_classes(rec, &svcclass) < 0)
+ continue;
+
+ if (!svcclass)
+ continue;
+
+ tmp = svcclass->data;
+
+ switch (tmp->type) {
+ case SDP_UUID16:
+ sdp_uuid16_to_uuid128(&uuid128, tmp);
+ break;
+ case SDP_UUID32:
+ sdp_uuid32_to_uuid128(&uuid128, tmp);
+ break;
+ case SDP_UUID128:
+ memcpy(&uuid128, tmp, sizeof(uuid_t));
+ break;
+ default:
+ sdp_list_free(svcclass, free);
+ continue;
+ }
+
+ new_uuid = g_malloc(16);/* size of 128 bit uuid */
+ memcpy(new_uuid, &uuid128.value.uuid128,
+ sizeof(uuid128.value.uuid128));
+
+ /* Check if uuid is already added */
+ if (g_slist_find_custom(req->uuids, new_uuid, uuid_128_cmp))
+ g_free(new_uuid);
+ else
+ req->uuids = g_slist_append(req->uuids, new_uuid);
+
+ sdp_list_free(svcclass, free);
+ }
+}
+
+static void browse_cb(sdp_list_t *recs, int err, gpointer user_data)
+{
+ struct browse_req *req = user_data;
+ struct device *dev;
+ uuid_t uuid;
+
+ /*
+ * If we have a valid response and req->search_uuid == 2, then L2CAP
+ * UUID & PNP searching was successful -- we are done
+ */
+ if (err < 0 || req->search_uuid == 2) {
+ if (err == -ECONNRESET && req->reconnect_attempt < 1) {
+ req->search_uuid--;
+ req->reconnect_attempt++;
+ } else {
+ goto done;
+ }
+ }
+
+ update_records(req, recs);
+
+ /* Search for mandatory uuids */
+ if (uuid_list[req->search_uuid]) {
+ sdp_uuid16_create(&uuid, uuid_list[req->search_uuid++]);
+ bt_search_service(&adapter.bdaddr, &req->bdaddr, &uuid,
+ browse_cb, user_data, NULL, 0);
+ return;
+ }
+
+done:
+ dev = find_device(&req->bdaddr);
+ if (dev) {
+ set_device_uuids(dev, req->uuids);
+ req->uuids = NULL;
+ }
+
+ browse_reqs = g_slist_remove(browse_reqs, req);
+ browse_req_free(req);
+}
+
+static int req_cmp(gconstpointer a, gconstpointer b)
+{
+ const struct browse_req *req = a;
+ const bdaddr_t *bdaddr = b;
+
+ return bacmp(&req->bdaddr, bdaddr);
+}
+
+static uint8_t browse_remote_sdp(const bdaddr_t *addr)
+{
+ struct browse_req *req;
+ uuid_t uuid;
+
+ if (g_slist_find_custom(browse_reqs, addr, req_cmp))
+ return HAL_STATUS_SUCCESS;
+
+ req = g_new0(struct browse_req, 1);
+ bacpy(&req->bdaddr, addr);
+ sdp_uuid16_create(&uuid, uuid_list[req->search_uuid++]);
+
+ if (bt_search_service(&adapter.bdaddr,
+ &req->bdaddr, &uuid, browse_cb, req, NULL , 0) < 0) {
+ browse_req_free(req);
+ return HAL_STATUS_FAILED;
+ }
+
+ browse_reqs = g_slist_append(browse_reqs, req);
+
+ return HAL_STATUS_SUCCESS;
+}
+
+static void send_remote_sdp_rec_notify(bt_uuid_t *uuid, int channel,
+ char *name, uint8_t name_len,
+ uint8_t status, bdaddr_t *bdaddr)
+{
+ struct hal_prop_device_service_rec *prop;
+ uint8_t buf[BASELEN_REMOTE_DEV_PROP + name_len + sizeof(*prop)];
+ struct hal_ev_remote_device_props *ev = (void *) buf;
+ size_t prop_len = sizeof(*prop) + name_len;
+
+ memset(buf, 0, sizeof(buf));
+
+ if (uuid && status == HAL_STATUS_SUCCESS) {
+ prop = (void *) &ev->props[0].val;
+ prop->name_len = name_len;
+ prop->channel = (uint16_t)channel;
+ memcpy(prop->name, name, name_len);
+ memcpy(prop->uuid, &uuid->value.u128, sizeof(prop->uuid));
+ }
+
+ ev->num_props = 1;
+ ev->status = status;
+ ev->props[0].len = prop_len;
+ bdaddr2android(bdaddr, ev->bdaddr);
+ ev->props[0].type = HAL_PROP_DEVICE_SERVICE_REC;
+
+ ipc_send_notif(hal_ipc, HAL_SERVICE_ID_BLUETOOTH,
+ HAL_EV_REMOTE_DEVICE_PROPS,
+ sizeof(buf), buf);
+}
+
+static void find_remote_sdp_rec_cb(sdp_list_t *recs, int err,
+ gpointer user_data)
+{
+ bdaddr_t *addr = user_data;
+ uint8_t name_len;
+ uint8_t status;
+ char name_buf[256];
+ int channel;
+ bt_uuid_t uuid;
+ uuid_t uuid128_sdp;
+ sdp_list_t *protos;
+ sdp_record_t *sdp_rec;
+
+ if (err < 0) {
+ error("error while search remote sdp records");
+ status = HAL_STATUS_FAILED;
+ send_remote_sdp_rec_notify(NULL, 0, NULL, 0, status, addr);
+ goto done;
+ }
+
+ if (!recs) {
+ info("No service records found on remote");
+ status = HAL_STATUS_SUCCESS;
+ send_remote_sdp_rec_notify(NULL, 0, NULL, 0, status, addr);
+ goto done;
+ }
+
+ for ( ; recs; recs = recs->next) {
+ sdp_rec = recs->data;
+
+ switch (sdp_rec->svclass.type) {
+ case SDP_UUID16:
+ sdp_uuid16_to_uuid128(&uuid128_sdp,
+ &sdp_rec->svclass);
+ break;
+ case SDP_UUID32:
+ sdp_uuid32_to_uuid128(&uuid128_sdp,
+ &sdp_rec->svclass);
+ break;
+ case SDP_UUID128:
+ break;
+ default:
+ error("wrong sdp uuid type");
+ goto done;
+ }
+
+ if (!sdp_get_access_protos(sdp_rec, &protos)) {
+ channel = sdp_get_proto_port(protos, RFCOMM_UUID);
+
+ sdp_list_foreach(protos,
+ (sdp_list_func_t) sdp_list_free,
+ NULL);
+ sdp_list_free(protos, NULL);
+ } else
+ channel = -1;
+
+ if (channel < 0) {
+ error("can't get channel for sdp record");
+ channel = 0;
+ }
+
+ if (!sdp_get_service_name(sdp_rec, name_buf, sizeof(name_buf)))
+ name_len = strlen(name_buf);
+ else
+ name_len = 0;
+
+ uuid.type = BT_UUID128;
+ memcpy(&uuid.value.u128, uuid128_sdp.value.uuid128.data,
+ sizeof(uuid.value.u128));
+ status = HAL_STATUS_SUCCESS;
+
+ send_remote_sdp_rec_notify(&uuid, channel, name_buf, name_len,
+ status, addr);
+ }
+
+done:
+ g_free(addr);
+}
+
+static uint8_t find_remote_sdp_rec(const bdaddr_t *addr,
+ const uint8_t *find_uuid)
+{
+ bdaddr_t *bdaddr;
+ uuid_t uuid;
+
+ /* from android we always get full 128bit length uuid */
+ sdp_uuid128_create(&uuid, find_uuid);
+
+ bdaddr = g_new(bdaddr_t, 1);
+ if (!bdaddr)
+ return HAL_STATUS_NOMEM;
+
+ memcpy(bdaddr, addr, sizeof(*bdaddr));
+
+ if (bt_search_service(&adapter.bdaddr, addr, &uuid,
+ find_remote_sdp_rec_cb, bdaddr, NULL, 0) < 0) {
+ g_free(bdaddr);
+ return HAL_STATUS_FAILED;
+ }
+
+ return HAL_STATUS_SUCCESS;
+}
+
+static void new_link_key_callback(uint16_t index, uint16_t length,
+ const void *param, void *user_data)
+{
+ const struct mgmt_ev_new_link_key *ev = param;
+ const struct mgmt_addr_info *addr = &ev->key.addr;
+ struct device *dev;
+ char dst[18];
+
+ if (length < sizeof(*ev)) {
+ error("Too small new link key event");
+ return;
+ }
+
+ ba2str(&addr->bdaddr, dst);
+
+ DBG("new key for %s type %u pin_len %u",
+ dst, ev->key.type, ev->key.pin_len);
+
+ if (ev->key.pin_len > 16) {
+ error("Invalid PIN length (%u) in new_key event",
+ ev->key.pin_len);
+ return;
+ }
+
+ dev = get_device(&ev->key.addr.bdaddr, ev->key.addr.type);
+ if (!dev)
+ return;
+
+ update_device_state(dev, ev->key.addr.type, HAL_STATUS_SUCCESS, false,
+ true, !!ev->store_hint);
+
+ if (ev->store_hint) {
+ const struct mgmt_link_key_info *key = &ev->key;
+
+ store_link_key(&addr->bdaddr, key->val, key->type,
+ key->pin_len);
+ }
+
+ browse_remote_sdp(&addr->bdaddr);
+}
+
+static uint8_t get_device_name(struct device *dev)
+{
+ send_device_property(dev, HAL_PROP_DEVICE_NAME,
+ strlen(dev->name), dev->name);
+
+ return HAL_STATUS_SUCCESS;
+}
+
+static void pin_code_request_callback(uint16_t index, uint16_t length,
+ const void *param, void *user_data)
+{
+ const struct mgmt_ev_pin_code_request *ev = param;
+ struct hal_ev_pin_request hal_ev;
+ struct device *dev;
+ char dst[18];
+
+ if (length < sizeof(*ev)) {
+ error("Too small PIN code request event");
+ return;
+ }
+
+ ba2str(&ev->addr.bdaddr, dst);
+
+ dev = get_device(&ev->addr.bdaddr, BDADDR_BREDR);
+
+ /*
+ * Workaround for Android Bluetooth.apk issue: send remote
+ * device property
+ */
+ get_device_name(dev);
+
+ update_device_state(dev, ev->addr.type, HAL_STATUS_SUCCESS, true,
+ false, false);
+
+ DBG("%s type %u secure %u", dst, ev->addr.type, ev->secure);
+
+ /* Name already sent in remote device prop */
+ memset(&hal_ev, 0, sizeof(hal_ev));
+ get_device_android_addr(dev, hal_ev.bdaddr);
+ hal_ev.class_of_dev = dev->class;
+
+ ipc_send_notif(hal_ipc, HAL_SERVICE_ID_BLUETOOTH, HAL_EV_PIN_REQUEST,
+ sizeof(hal_ev), &hal_ev);
+}
+
+static void send_ssp_request(struct device *dev, uint8_t variant,
+ uint32_t passkey)
+{
+ struct hal_ev_ssp_request ev;
+
+ memset(&ev, 0, sizeof(ev));
+
+ get_device_android_addr(dev, ev.bdaddr);
+ memcpy(ev.name, dev->name, strlen(dev->name));
+ ev.class_of_dev = dev->class;
+
+ ev.pairing_variant = variant;
+ ev.passkey = passkey;
+
+ ipc_send_notif(hal_ipc, HAL_SERVICE_ID_BLUETOOTH, HAL_EV_SSP_REQUEST,
+ sizeof(ev), &ev);
+}
+
+static void user_confirm_request_callback(uint16_t index, uint16_t length,
+ const void *param, void *user_data)
+{
+ const struct mgmt_ev_user_confirm_request *ev = param;
+ struct device *dev;
+ char dst[18];
+
+ if (length < sizeof(*ev)) {
+ error("Too small user confirm request event");
+ return;
+ }
+
+ ba2str(&ev->addr.bdaddr, dst);
+ DBG("%s confirm_hint %u", dst, ev->confirm_hint);
+
+ dev = get_device(&ev->addr.bdaddr, ev->addr.type);
+ if (!dev)
+ return;
+
+ update_device_state(dev, ev->addr.type, HAL_STATUS_SUCCESS, true,
+ false, false);
+
+ if (ev->confirm_hint)
+ send_ssp_request(dev, HAL_SSP_VARIANT_CONSENT, 0);
+ else
+ send_ssp_request(dev, HAL_SSP_VARIANT_CONFIRM, ev->value);
+}
+
+static void user_passkey_request_callback(uint16_t index, uint16_t length,
+ const void *param, void *user_data)
+{
+ const struct mgmt_ev_user_passkey_request *ev = param;
+ struct device *dev;
+ char dst[18];
+
+ if (length < sizeof(*ev)) {
+ error("Too small passkey request event");
+ return;
+ }
+
+ ba2str(&ev->addr.bdaddr, dst);
+ DBG("%s", dst);
+
+ dev = get_device(&ev->addr.bdaddr, ev->addr.type);
+ if (!dev)
+ return;
+
+ update_device_state(dev, ev->addr.type, HAL_STATUS_SUCCESS, true,
+ false, false);
+
+ send_ssp_request(dev, HAL_SSP_VARIANT_ENTRY, 0);
+}
+
+static void user_passkey_notify_callback(uint16_t index, uint16_t length,
+ const void *param,
+ void *user_data)
+{
+ const struct mgmt_ev_passkey_notify *ev = param;
+ struct device *dev;
+ char dst[18];
+
+ if (length < sizeof(*ev)) {
+ error("Too small passkey notify event");
+ return;
+ }
+
+ ba2str(&ev->addr.bdaddr, dst);
+ DBG("%s entered %u", dst, ev->entered);
+
+ /* HAL seems to not support entered characters */
+ if (ev->entered)
+ return;
+
+ dev = find_device(&ev->addr.bdaddr);
+ if (!dev)
+ return;
+
+ update_device_state(dev, ev->addr.type, HAL_STATUS_SUCCESS, true,
+ false, false);
+
+ send_ssp_request(dev, HAL_SSP_VARIANT_NOTIF, ev->passkey);
+}
+
+static void clear_device_found(gpointer data, gpointer user_data)
+{
+ struct device *dev = data;
+
+ dev->found = false;
+}
+
+static uint8_t get_supported_discovery_type(void)
+{
+ uint8_t type = SCAN_TYPE_NONE;
+
+ if (adapter.current_settings & MGMT_SETTING_BREDR)
+ type |= SCAN_TYPE_BREDR;
+
+ if (adapter.current_settings & MGMT_SETTING_LE)
+ type |= SCAN_TYPE_LE;
+
+ return type;
+}
+
+static bool start_discovery(uint8_t type)
+{
+ struct mgmt_cp_start_discovery cp;
+
+ cp.type = get_supported_discovery_type() & type;
+
+ DBG("type=0x%x", cp.type);
+
+ if (cp.type == SCAN_TYPE_NONE)
+ return false;
+
+ if (mgmt_send(mgmt_if, MGMT_OP_START_DISCOVERY, adapter.index,
+ sizeof(cp), &cp, NULL, NULL, NULL) > 0)
+ return true;
+
+ error("Failed to start discovery");
+
+ return false;
+}
+
+/*
+ * Send discovery state change event only if it is related to dual type
+ * discovery session (triggered by start/cancel discovery commands)
+ */
+static void check_discovery_state(uint8_t new_type, uint8_t old_type)
+{
+ struct hal_ev_discovery_state_changed ev;
+
+ DBG("%u %u", new_type, old_type);
+
+ if (new_type == get_supported_discovery_type()) {
+ g_slist_foreach(bonded_devices, clear_device_found, NULL);
+ g_slist_foreach(cached_devices, clear_device_found, NULL);
+ ev.state = HAL_DISCOVERY_STATE_STARTED;
+ goto done;
+ }
+
+ if (old_type != get_supported_discovery_type())
+ return;
+
+ ev.state = HAL_DISCOVERY_STATE_STOPPED;
+
+done:
+ ipc_send_notif(hal_ipc, HAL_SERVICE_ID_BLUETOOTH,
+ HAL_EV_DISCOVERY_STATE_CHANGED, sizeof(ev), &ev);
+}
+
+static void mgmt_discovering_event(uint16_t index, uint16_t length,
+ const void *param, void *user_data)
+{
+ const struct mgmt_ev_discovering *ev = param;
+ uint8_t type;
+
+ if (length < sizeof(*ev)) {
+ error("Too small discovering event");
+ return;
+ }
+
+ DBG("type %u discovering %u", ev->type, ev->discovering);
+
+ if (!!adapter.cur_discovery_type == !!ev->discovering)
+ return;
+
+ type = ev->discovering ? ev->type : SCAN_TYPE_NONE;
+
+ check_discovery_state(type, adapter.cur_discovery_type);
+
+ adapter.cur_discovery_type = type;
+
+ if (ev->discovering) {
+ adapter.exp_discovery_type = adapter.le_scanning ?
+ SCAN_TYPE_LE : SCAN_TYPE_NONE;
+ return;
+ }
+
+ /* One shot notification about discovery stopped */
+ if (gatt_discovery_stopped_cb) {
+ gatt_discovery_stopped_cb();
+ gatt_discovery_stopped_cb = NULL;
+ }
+
+ type = adapter.exp_discovery_type;
+ adapter.exp_discovery_type = adapter.le_scanning ? SCAN_TYPE_LE :
+ SCAN_TYPE_NONE;
+
+ if (type != SCAN_TYPE_NONE)
+ start_discovery(type);
+}
+
+static void confirm_device_name_cb(uint8_t status, uint16_t length,
+ const void *param, void *user_data)
+{
+ const struct mgmt_rp_confirm_name *rp = param;
+ struct device *dev;
+
+ DBG("Confirm name status: %s (0x%02x)", mgmt_errstr(status), status);
+
+ if (length < sizeof(*rp)) {
+ error("Wrong size of confirm name response");
+ return;
+ }
+
+ dev = find_device(&rp->addr.bdaddr);
+ if (!dev)
+ return;
+
+ dev->confirm_id = 0;
+}
+
+static unsigned int confirm_device_name(const bdaddr_t *addr, uint8_t addr_type,
+ bool resolve_name)
+{
+ struct mgmt_cp_confirm_name cp;
+ unsigned int res;
+
+ memset(&cp, 0, sizeof(cp));
+ bacpy(&cp.addr.bdaddr, addr);
+ cp.addr.type = addr_type;
+
+ if (!resolve_name)
+ cp.name_known = 1;
+
+ res = mgmt_send(mgmt_if, MGMT_OP_CONFIRM_NAME, adapter.index,
+ sizeof(cp), &cp, confirm_device_name_cb,
+ NULL, NULL);
+ if (!res)
+ error("Failed to send confirm name request");
+
+ return res;
+}
+
+static int fill_hal_prop(void *buf, uint8_t type, uint16_t len,
+ const void *val)
+{
+ struct hal_property *prop = buf;
+
+ prop->type = type;
+ prop->len = len;
+
+ if (len)
+ memcpy(prop->val, val, len);
+
+ return sizeof(*prop) + len;
+}
+
+static uint8_t get_device_android_type(struct device *dev)
+{
+ if (dev->bredr && dev->le)
+ return HAL_TYPE_DUAL;
+
+ if (dev->le)
+ return HAL_TYPE_LE;
+
+ return HAL_TYPE_BREDR;
+}
+
+uint8_t bt_get_device_android_type(const bdaddr_t *addr)
+{
+ struct device *dev;
+
+ dev = get_device(addr, BDADDR_BREDR);
+
+ return get_device_android_type(dev);
+}
+
+bool bt_is_device_le(const bdaddr_t *addr)
+{
+ struct device *dev;
+
+ dev = find_device(addr);
+ if (!dev)
+ return false;
+
+ return dev->le;
+}
+
+const bdaddr_t *bt_get_id_addr(const bdaddr_t *addr, uint8_t *type)
+{
+ struct device *dev;
+
+ dev = find_device(addr);
+ if (!dev)
+ return NULL;
+
+ if (type)
+ *type = dev->bdaddr_type;
+
+ return &dev->bdaddr;
+}
+
+const char *bt_get_adapter_name(void)
+{
+ return adapter.name;
+}
+
+bool bt_device_is_bonded(const bdaddr_t *bdaddr)
+{
+ if (g_slist_find_custom(bonded_devices, bdaddr, device_match))
+ return true;
+
+ return false;
+}
+
+bool bt_device_set_uuids(const bdaddr_t *addr, GSList *uuids)
+{
+ struct device *dev;
+
+ dev = find_device(addr);
+ if (!dev)
+ return false;
+
+ set_device_uuids(dev, uuids);
+
+ return true;
+}
+
+bool bt_kernel_conn_control(void)
+{
+ return kernel_conn_control;
+}
+
+bool bt_auto_connect_add(const bdaddr_t *addr)
+{
+ struct mgmt_cp_add_device cp;
+ struct device *dev;
+
+ if (!kernel_conn_control)
+ return false;
+
+ dev = find_device(addr);
+ if (!dev)
+ return false;
+
+ if (dev->bdaddr_type == BDADDR_BREDR) {
+ DBG("auto-connection feature is not available for BR/EDR");
+ return false;
+ }
+
+ if (dev->in_white_list) {
+ DBG("Device already in white list");
+ return true;
+ }
+
+ memset(&cp, 0, sizeof(cp));
+ bacpy(&cp.addr.bdaddr, addr);
+ cp.addr.type = dev->bdaddr_type;
+ cp.action = 0x02;
+
+ if (mgmt_send(mgmt_if, MGMT_OP_ADD_DEVICE, adapter.index, sizeof(cp),
+ &cp, NULL, NULL, NULL) > 0) {
+ dev->in_white_list = true;
+ return true;
+ }
+
+ error("Failed to add device");
+
+ return false;
+}
+
+void bt_auto_connect_remove(const bdaddr_t *addr)
+{
+ struct mgmt_cp_remove_device cp;
+ struct device *dev;
+
+ if (!kernel_conn_control)
+ return;
+
+ dev = find_device(addr);
+ if (!dev)
+ return;
+
+ if (dev->bdaddr_type == BDADDR_BREDR) {
+ DBG("auto-connection feature is not available for BR/EDR");
+ return;
+ }
+
+ if (!dev->in_white_list) {
+ DBG("Device already removed from white list");
+ return;
+ }
+
+ memset(&cp, 0, sizeof(cp));
+ bacpy(&cp.addr.bdaddr, addr);
+ cp.addr.type = dev->bdaddr_type;
+
+ if (mgmt_send(mgmt_if, MGMT_OP_REMOVE_DEVICE, adapter.index,
+ sizeof(cp), &cp, NULL, NULL, NULL) > 0) {
+ dev->in_white_list = false;
+ return;
+ }
+
+ error("Failed to remove device");
+}
+
+static bool match_by_value(const void *data, const void *user_data)
+{
+ return data == user_data;
+}
+
+bool bt_unpaired_register(bt_unpaired_device_cb cb)
+{
+ if (queue_find(unpaired_cb_list, match_by_value, cb))
+ return false;
+
+ return queue_push_head(unpaired_cb_list, cb);
+}
+
+void bt_unpaired_unregister(bt_unpaired_device_cb cb)
+{
+ queue_remove(unpaired_cb_list, cb);
+}
+
+bool bt_paired_register(bt_paired_device_cb cb)
+{
+ if (queue_find(paired_cb_list, match_by_value, cb))
+ return false;
+
+ return queue_push_head(paired_cb_list, cb);
+}
+
+void bt_paired_unregister(bt_paired_device_cb cb)
+{
+ queue_remove(paired_cb_list, cb);
+}
+
+bool bt_is_pairing(const bdaddr_t *addr)
+{
+ struct device *dev;
+
+ dev = find_device(addr);
+ if (!dev)
+ return false;
+
+ return dev->pairing;
+}
+
+static bool rssi_above_threshold(int old, int new)
+{
+ /* only 8 dBm or more */
+ return abs(old - new) >= 8;
+}
+
+static void update_new_device(struct device *dev, int8_t rssi,
+ const struct eir_data *eir)
+{
+ uint8_t buf[IPC_MTU];
+ struct hal_ev_device_found *ev = (void *) buf;
+ uint8_t android_bdaddr[6];
+ uint8_t android_type;
+ size_t size;
+
+ memset(buf, 0, sizeof(buf));
+
+ if (adapter.cur_discovery_type)
+ dev->found = true;
+
+ size = sizeof(*ev);
+
+ get_device_android_addr(dev, android_bdaddr);
+ size += fill_hal_prop(buf + size, HAL_PROP_DEVICE_ADDR,
+ sizeof(android_bdaddr), android_bdaddr);
+ ev->num_props++;
+
+ android_type = get_device_android_type(dev);
+ size += fill_hal_prop(buf + size, HAL_PROP_DEVICE_TYPE,
+ sizeof(android_type), &android_type);
+ ev->num_props++;
+
+ if (eir->class)
+ dev->class = eir->class;
+
+ if (dev->class) {
+ size += fill_hal_prop(buf + size, HAL_PROP_DEVICE_CLASS,
+ sizeof(dev->class), &dev->class);
+ ev->num_props++;
+ }
+
+ if (rssi && rssi_above_threshold(dev->rssi, rssi))
+ dev->rssi = rssi;
+
+ if (dev->rssi) {
+ size += fill_hal_prop(buf + size, HAL_PROP_DEVICE_RSSI,
+ sizeof(dev->rssi), &dev->rssi);
+ ev->num_props++;
+ }
+
+ if (eir->name && strlen(eir->name)) {
+ g_free(dev->name);
+ dev->name = g_strdup(eir->name);
+ }
+
+ if (dev->name) {
+ size += fill_hal_prop(buf + size, HAL_PROP_DEVICE_NAME,
+ strlen(dev->name), dev->name);
+ ev->num_props++;
+
+ /* when updating name also send stored friendly name */
+ if (dev->friendly_name) {
+ size += fill_hal_prop(buf + size,
+ HAL_PROP_DEVICE_FRIENDLY_NAME,
+ strlen(dev->friendly_name),
+ dev->friendly_name);
+ ev->num_props++;
+ }
+ }
+
+ ipc_send_notif(hal_ipc, HAL_SERVICE_ID_BLUETOOTH, HAL_EV_DEVICE_FOUND,
+ size, buf);
+}
+
+static void update_device(struct device *dev, int8_t rssi,
+ const struct eir_data *eir)
+{
+ uint8_t buf[IPC_MTU];
+ struct hal_ev_remote_device_props *ev = (void *) buf;
+ uint8_t old_type, new_type;
+ size_t size;
+
+ memset(buf, 0, sizeof(buf));
+
+ size = sizeof(*ev);
+
+ ev->status = HAL_STATUS_SUCCESS;
+ get_device_android_addr(dev, ev->bdaddr);
+
+ old_type = get_device_android_type(dev);
+
+ new_type = get_device_android_type(dev);
+
+ if (old_type != new_type) {
+ size += fill_hal_prop(buf + size, HAL_PROP_DEVICE_TYPE,
+ sizeof(new_type), &new_type);
+ ev->num_props++;
+ }
+
+ if (eir->class && dev->class != eir->class) {
+ dev->class = eir->class;
+ size += fill_hal_prop(buf + size, HAL_PROP_DEVICE_CLASS,
+ sizeof(dev->class), &dev->class);
+ ev->num_props++;
+ }
+
+ if (rssi && rssi_above_threshold(dev->rssi, rssi)) {
+ dev->rssi = rssi;
+ size += fill_hal_prop(buf + size, HAL_PROP_DEVICE_RSSI,
+ sizeof(dev->rssi), &dev->rssi);
+ ev->num_props++;
+ }
+
+ if (eir->name && strlen(eir->name) && strcmp(dev->name, eir->name)) {
+ g_free(dev->name);
+ dev->name = g_strdup(eir->name);
+ size += fill_hal_prop(buf + size, HAL_PROP_DEVICE_NAME,
+ strlen(dev->name), dev->name);
+ ev->num_props++;
+
+ /* when updating name also send stored friendly name */
+ if (dev->friendly_name) {
+ size += fill_hal_prop(buf + size,
+ HAL_PROP_DEVICE_FRIENDLY_NAME,
+ strlen(dev->friendly_name),
+ dev->friendly_name);
+ ev->num_props++;
+ }
+ }
+
+ if (ev->num_props)
+ ipc_send_notif(hal_ipc, HAL_SERVICE_ID_BLUETOOTH,
+ HAL_EV_REMOTE_DEVICE_PROPS, size, buf);
+}
+
+static bool is_new_device(const struct device *dev, unsigned int flags,
+ uint8_t bdaddr_type)
+{
+ if (dev->found)
+ return false;
+
+ if (dev->bredr_paired || dev->le_paired)
+ return false;
+
+ if (bdaddr_type != BDADDR_BREDR &&
+ !(flags & (EIR_LIM_DISC | EIR_GEN_DISC)))
+ return false;
+
+ return true;
+}
+
+static void update_found_device(const bdaddr_t *bdaddr, uint8_t bdaddr_type,
+ int8_t rssi, bool confirm,
+ bool connectable,
+ const uint8_t *data, uint8_t data_len)
+{
+ struct eir_data eir;
+ struct device *dev;
+
+ memset(&eir, 0, sizeof(eir));
+
+ eir_parse(&eir, data, data_len);
+
+ dev = get_device(bdaddr, bdaddr_type);
+
+ if (bdaddr_type == BDADDR_BREDR) {
+ dev->bredr = true;
+ dev->bredr_seen = time(NULL);
+ } else {
+ dev->le = true;
+ dev->bdaddr_type = bdaddr_type;
+ dev->le_seen = time(NULL);
+ }
+
+ /*
+ * Device found event needs to be send also for known device if this is
+ * new discovery session. Otherwise framework will ignore it.
+ */
+ if (is_new_device(dev, eir.flags, bdaddr_type))
+ update_new_device(dev, rssi, &eir);
+ else
+ update_device(dev, rssi, &eir);
+
+ eir_data_free(&eir);
+
+ /* Notify Gatt if its registered for LE events */
+ if (bdaddr_type != BDADDR_BREDR && gatt_device_found_cb) {
+ const bdaddr_t *addr;
+
+ /*
+ * If RPA is set it means that IRK was received and ID address
+ * is being used. Android Framework is still using old RPA and
+ * it needs to be used also in GATT notifications. Also GATT
+ * HAL implementation is using RPA for devices matching.
+ */
+ if (bacmp(&dev->rpa, BDADDR_ANY))
+ addr = &dev->rpa;
+ else
+ addr = &dev->bdaddr;
+
+ gatt_device_found_cb(addr, rssi, data_len, data, connectable,
+ dev->le_bonded);
+ }
+
+ if (!dev->bredr_paired && !dev->le_paired)
+ cache_device(dev);
+
+ if (confirm) {
+ char addr[18];
+ bool resolve_name = true;
+
+ ba2str(bdaddr, addr);
+
+ /*
+ * Don't need to confirm name if we have it already in cache
+ * Just check if device name is different than bdaddr
+ */
+ if (g_strcmp0(dev->name, addr)) {
+ get_device_name(dev);
+ resolve_name = false;
+ }
+
+ info("Device %s needs name confirmation (resolve_name=%d)",
+ addr, resolve_name);
+ dev->confirm_id = confirm_device_name(bdaddr, bdaddr_type,
+ resolve_name);
+ }
+}
+
+static void mgmt_device_found_event(uint16_t index, uint16_t length,
+ const void *param, void *user_data)
+{
+ const struct mgmt_ev_device_found *ev = param;
+ const uint8_t *eir;
+ uint16_t eir_len;
+ uint32_t flags;
+ bool confirm_name;
+ bool connectable;
+ char addr[18];
+
+ if (length < sizeof(*ev)) {
+ error("Too short device found event (%u bytes)", length);
+ return;
+ }
+
+ eir_len = le16_to_cpu(ev->eir_len);
+ if (length != sizeof(*ev) + eir_len) {
+ error("Device found event size mismatch (%u != %zu)",
+ length, sizeof(*ev) + eir_len);
+ return;
+ }
+
+ if (eir_len == 0)
+ eir = NULL;
+ else
+ eir = ev->eir;
+
+ flags = le32_to_cpu(ev->flags);
+
+ ba2str(&ev->addr.bdaddr, addr);
+ DBG("hci%u addr %s, rssi %d flags 0x%04x eir_len %u",
+ index, addr, ev->rssi, flags, eir_len);
+
+ confirm_name = flags & MGMT_DEV_FOUND_CONFIRM_NAME;
+ connectable = !(flags & MGMT_DEV_FOUND_NOT_CONNECTABLE);
+
+ update_found_device(&ev->addr.bdaddr, ev->addr.type, ev->rssi,
+ confirm_name, connectable, eir, eir_len);
+}
+
+static void mgmt_device_connected_event(uint16_t index, uint16_t length,
+ const void *param, void *user_data)
+{
+ const struct mgmt_ev_device_connected *ev = param;
+ struct hal_ev_acl_state_changed hal_ev;
+ struct device *dev;
+ char addr[18];
+
+ if (length < sizeof(*ev)) {
+ error("Too short device connected event (%u bytes)", length);
+ return;
+ }
+
+ ba2str(&ev->addr.bdaddr, addr);
+ DBG("%s type %u", addr, ev->addr.type);
+
+ update_found_device(&ev->addr.bdaddr, ev->addr.type, 0, false, false,
+ &ev->eir[0], le16_to_cpu(ev->eir_len));
+
+ hal_ev.status = HAL_STATUS_SUCCESS;
+ hal_ev.state = HAL_ACL_STATE_CONNECTED;
+
+ dev = find_device(&ev->addr.bdaddr);
+ if (!dev)
+ return;
+
+ dev->connected = true;
+
+ get_device_android_addr(dev, hal_ev.bdaddr);
+
+ ipc_send_notif(hal_ipc, HAL_SERVICE_ID_BLUETOOTH,
+ HAL_EV_ACL_STATE_CHANGED, sizeof(hal_ev), &hal_ev);
+}
+
+static bool device_is_paired(struct device *dev, uint8_t addr_type)
+{
+ if (addr_type == BDADDR_BREDR)
+ return dev->bredr_paired;
+
+ return dev->le_paired;
+}
+
+static bool device_is_bonded(struct device *dev)
+{
+ return dev->bredr_bonded || dev->le_bonded;
+}
+
+static void mgmt_device_disconnected_event(uint16_t index, uint16_t length,
+ const void *param,
+ void *user_data)
+{
+ const struct mgmt_ev_device_disconnected *ev = param;
+ struct hal_ev_acl_state_changed hal_ev;
+ struct device *dev;
+ uint8_t type = ev->addr.type;
+
+ if (length < sizeof(*ev)) {
+ error("Too short device disconnected event (%u bytes)", length);
+ return;
+ }
+
+ dev = find_device(&ev->addr.bdaddr);
+ if (!dev)
+ return;
+
+ hal_ev.status = HAL_STATUS_SUCCESS;
+ hal_ev.state = HAL_ACL_STATE_DISCONNECTED;
+ get_device_android_addr(dev, hal_ev.bdaddr);
+
+ ipc_send_notif(hal_ipc, HAL_SERVICE_ID_BLUETOOTH,
+ HAL_EV_ACL_STATE_CHANGED, sizeof(hal_ev), &hal_ev);
+
+ if (device_is_paired(dev, type) && !device_is_bonded(dev))
+ update_device_state(dev, type, HAL_STATUS_SUCCESS, false,
+ false, false);
+
+ dev->connected = false;
+}
+
+static uint8_t status_mgmt2hal(uint8_t mgmt)
+{
+ switch (mgmt) {
+ case MGMT_STATUS_SUCCESS:
+ return HAL_STATUS_SUCCESS;
+ case MGMT_STATUS_NO_RESOURCES:
+ return HAL_STATUS_NOMEM;
+ case MGMT_STATUS_BUSY:
+ return HAL_STATUS_BUSY;
+ case MGMT_STATUS_NOT_SUPPORTED:
+ return HAL_STATUS_UNSUPPORTED;
+ case MGMT_STATUS_INVALID_PARAMS:
+ return HAL_STATUS_INVALID;
+ case MGMT_STATUS_AUTH_FAILED:
+ return HAL_STATUS_AUTH_FAILURE;
+ case MGMT_STATUS_NOT_CONNECTED:
+ return HAL_STATUS_REMOTE_DEVICE_DOWN;
+ default:
+ return HAL_STATUS_FAILED;
+ }
+}
+
+static void mgmt_connect_failed_event(uint16_t index, uint16_t length,
+ const void *param, void *user_data)
+{
+ const struct mgmt_ev_connect_failed *ev = param;
+ struct device *dev;
+
+ if (length < sizeof(*ev)) {
+ error("Too short connect failed event (%u bytes)", length);
+ return;
+ }
+
+ DBG("");
+
+ dev = find_device(&ev->addr.bdaddr);
+ if (!dev)
+ return;
+
+ /*
+ * In case security mode 3 pairing we will get connect failed event
+ * in case e.g wrong PIN code entered. Let's check if device is
+ * bonding, if so update bond state
+ */
+
+ if (!dev->pairing)
+ return;
+
+ update_device_state(dev, ev->addr.type, status_mgmt2hal(ev->status),
+ false, false, false);
+}
+
+static void mgmt_auth_failed_event(uint16_t index, uint16_t length,
+ const void *param, void *user_data)
+{
+ const struct mgmt_ev_auth_failed *ev = param;
+ struct device *dev;
+
+ if (length < sizeof(*ev)) {
+ error("Too small auth failed mgmt event (%u bytes)", length);
+ return;
+ }
+
+ DBG("");
+
+ dev = find_device(&ev->addr.bdaddr);
+ if (!dev)
+ return;
+
+ if (!dev->pairing)
+ return;
+
+ update_device_state(dev, ev->addr.type, status_mgmt2hal(ev->status),
+ false, false, false);
+}
+
+static void mgmt_device_unpaired_event(uint16_t index, uint16_t length,
+ const void *param, void *user_data)
+{
+ const struct mgmt_ev_device_unpaired *ev = param;
+ struct device *dev;
+
+ if (length < sizeof(*ev)) {
+ error("Too small device unpaired event (%u bytes)", length);
+ return;
+ }
+
+ DBG("");
+
+ /* TODO should device be disconnected ? */
+
+ dev = find_device(&ev->addr.bdaddr);
+ if (!dev)
+ return;
+
+ update_device_state(dev, ev->addr.type, HAL_STATUS_SUCCESS, false,
+ false, false);
+
+ /* Unpaired device is removed from the white list */
+ dev->in_white_list = false;
+}
+
+static void store_ltk(const bdaddr_t *dst, uint8_t bdaddr_type, bool master,
+ const uint8_t *key, uint8_t key_type, uint8_t enc_size,
+ uint16_t ediv, uint64_t rand)
+{
+ const char *key_s, *keytype_s, *encsize_s, *ediv_s, *rand_s;
+ GKeyFile *key_file;
+ char key_str[33];
+ gsize length = 0;
+ char addr[18];
+ char *data;
+ int i;
+
+ key_file = g_key_file_new();
+ if (!g_key_file_load_from_file(key_file, DEVICES_FILE, 0, NULL)) {
+ g_key_file_free(key_file);
+ return;
+ }
+
+ ba2str(dst, addr);
+
+ key_s = master ? "LongTermKey" : "SlaveLongTermKey";
+ keytype_s = master ? "LongTermKeyType" : "SlaveLongTermKeyType";
+ encsize_s = master ? "LongTermKeyEncSize" : "SlaveLongTermKeyEncSize";
+ ediv_s = master ? "LongTermKeyEDiv" : "SlaveLongTermKeyEDiv";
+ rand_s = master ? "LongTermKeyRand" : "SlaveLongTermKeyRand";
+
+ for (i = 0; i < 16; i++)
+ sprintf(key_str + (i * 2), "%2.2X", key[i]);
+
+ g_key_file_set_string(key_file, addr, key_s, key_str);
+
+ g_key_file_set_integer(key_file, addr, keytype_s, key_type);
+
+ g_key_file_set_integer(key_file, addr, encsize_s, enc_size);
+
+ g_key_file_set_integer(key_file, addr, ediv_s, ediv);
+
+ g_key_file_set_uint64(key_file, addr, rand_s, rand);
+
+ data = g_key_file_to_data(key_file, &length, NULL);
+ g_file_set_contents(DEVICES_FILE, data, length, NULL);
+ g_free(data);
+
+ g_key_file_free(key_file);
+}
+
+static void new_long_term_key_event(uint16_t index, uint16_t length,
+ const void *param, void *user_data)
+{
+ const struct mgmt_ev_new_long_term_key *ev = param;
+ struct device *dev;
+ char dst[18];
+
+ if (length < sizeof(*ev)) {
+ error("Too small long term key event (%u bytes)", length);
+ return;
+ }
+
+ ba2str(&ev->key.addr.bdaddr, dst);
+
+ DBG("new LTK for %s type %u enc_size %u store_hint %u",
+ dst, ev->key.type, ev->key.enc_size, ev->store_hint);
+
+ dev = find_device(&ev->key.addr.bdaddr);
+ if (!dev)
+ return;
+
+ update_device_state(dev, ev->key.addr.type, HAL_STATUS_SUCCESS, false,
+ true, !!ev->store_hint);
+
+ if (ev->store_hint) {
+ const struct mgmt_ltk_info *key = &ev->key;
+ uint16_t ediv;
+ uint64_t rand;
+
+ ediv = le16_to_cpu(key->ediv);
+ rand = le64_to_cpu(key->rand);
+
+ store_ltk(&key->addr.bdaddr, key->addr.type, key->master,
+ key->val, key->type, key->enc_size, ediv, rand);
+ }
+
+ /* TODO browse services here? */
+}
+
+static void store_csrk(struct device *dev)
+{
+ GKeyFile *key_file;
+ char key_str[33];
+ char addr[18];
+ int i;
+ gsize length = 0;
+ char *data;
+
+ ba2str(&dev->bdaddr, addr);
+
+ key_file = g_key_file_new();
+ if (!g_key_file_load_from_file(key_file, DEVICES_FILE, 0, NULL)) {
+ g_key_file_free(key_file);
+ return;
+ }
+
+ if (dev->valid_local_csrk) {
+ for (i = 0; i < 16; i++)
+ sprintf(key_str + (i * 2), "%2.2X",
+ dev->local_csrk[i]);
+
+ g_key_file_set_string(key_file, addr, "LocalCSRK", key_str);
+
+ g_key_file_set_boolean(key_file, addr, "LocalCSRKAuthenticated",
+ dev->local_csrk_auth);
+ }
+
+ if (dev->valid_remote_csrk) {
+ for (i = 0; i < 16; i++)
+ sprintf(key_str + (i * 2), "%2.2X",
+ dev->remote_csrk[i]);
+
+ g_key_file_set_string(key_file, addr, "RemoteCSRK", key_str);
+
+ g_key_file_set_boolean(key_file, addr,
+ "RemoteCSRKAuthenticated",
+ dev->remote_csrk_auth);
+ }
+
+ data = g_key_file_to_data(key_file, &length, NULL);
+ g_file_set_contents(DEVICES_FILE, data, length, NULL);
+ g_free(data);
+
+ g_key_file_free(key_file);
+}
+
+static void new_csrk_callback(uint16_t index, uint16_t length,
+ const void *param, void *user_data)
+{
+ const struct mgmt_ev_new_csrk *ev = param;
+ struct device *dev;
+ char dst[18];
+
+ if (length < sizeof(*ev)) {
+ error("Too small csrk event (%u bytes)", length);
+ return;
+ }
+
+ ba2str(&ev->key.addr.bdaddr, dst);
+ dev = get_device(&ev->key.addr.bdaddr, ev->key.addr.type);
+ if (!dev)
+ return;
+
+ switch (ev->key.type) {
+ case 0x00:
+ case 0x02:
+ memcpy(dev->local_csrk, ev->key.val, 16);
+ dev->local_sign_cnt = 0;
+ dev->valid_local_csrk = true;
+ dev->local_csrk_auth = ev->key.type == 0x02;
+ break;
+ case 0x01:
+ case 0x03:
+ memcpy(dev->remote_csrk, ev->key.val, 16);
+ dev->remote_sign_cnt = 0;
+ dev->valid_remote_csrk = true;
+ dev->remote_csrk_auth = ev->key.type == 0x03;
+ break;
+ default:
+ error("Unknown CSRK key type 02%02x", ev->key.type);
+ return;
+ }
+
+ update_device_state(dev, ev->key.addr.type, HAL_STATUS_SUCCESS, false,
+ true, !!ev->store_hint);
+
+ if (ev->store_hint)
+ store_csrk(dev);
+}
+
+static void store_irk(struct device *dev, const uint8_t *val)
+{
+ GKeyFile *key_file;
+ char key_str[33];
+ char addr[18];
+ int i;
+ gsize length = 0;
+ char *data;
+
+ ba2str(&dev->bdaddr, addr);
+
+ key_file = g_key_file_new();
+ if (!g_key_file_load_from_file(key_file, DEVICES_FILE, 0, NULL)) {
+ g_key_file_free(key_file);
+ return;
+ }
+
+ for (i = 0; i < 16; i++)
+ sprintf(key_str + (i * 2), "%2.2X", val[i]);
+
+ g_key_file_set_string(key_file, addr, "IdentityResolvingKey", key_str);
+
+ data = g_key_file_to_data(key_file, &length, NULL);
+ g_file_set_contents(DEVICES_FILE, data, length, NULL);
+ g_free(data);
+
+ g_key_file_free(key_file);
+}
+
+static void new_irk_callback(uint16_t index, uint16_t length,
+ const void *param, void *user_data)
+{
+ const struct mgmt_ev_new_irk *ev = param;
+ const struct mgmt_addr_info *addr = &ev->key.addr;
+ struct device *dev;
+ char dst[18], rpa[18];
+
+ if (length < sizeof(*ev)) {
+ error("To small New Irk Event (%u bytes)", length);
+ return;
+ }
+
+ ba2str(&ev->key.addr.bdaddr, dst);
+ ba2str(&ev->rpa, rpa);
+
+ DBG("new IRK for %s, RPA %s", dst, rpa);
+
+ if (!bacmp(&ev->rpa, BDADDR_ANY)) {
+ dev = get_device(&addr->bdaddr, addr->type);
+ if (!dev)
+ return;
+ } else {
+ dev = find_device(&addr->bdaddr);
+
+ if (dev && dev->bredr_paired) {
+ bacpy(&dev->rpa, &addr->bdaddr);
+ dev->rpa_type = addr->type;
+
+ /* TODO merge properties ie. UUIDs */
+ } else {
+ dev = find_device(&ev->rpa);
+ if (!dev)
+ return;
+
+ /* don't leave garbage in cache file */
+ remove_device_info(dev, CACHE_FILE);
+
+ /*
+ * RPA resolution is transparent for Android Framework
+ * ie. device is still access by RPA so it need to be
+ * keep. After bluetoothd restart device is advertised
+ * to Android with IDA and RPA is not set.
+ */
+ bacpy(&dev->rpa, &dev->bdaddr);
+ dev->rpa_type = dev->bdaddr_type;
+
+ bacpy(&dev->bdaddr, &addr->bdaddr);
+ dev->bdaddr_type = addr->type;
+ }
+ }
+
+ update_device_state(dev, ev->key.addr.type, HAL_STATUS_SUCCESS, false,
+ true, !!ev->store_hint);
+
+ if (ev->store_hint)
+ store_irk(dev, ev->key.val);
+}
+
+static void register_mgmt_handlers(void)
+{
+ mgmt_register(mgmt_if, MGMT_EV_NEW_SETTINGS, adapter.index,
+ new_settings_callback, NULL, NULL);
+
+ mgmt_register(mgmt_if, MGMT_EV_CLASS_OF_DEV_CHANGED, adapter.index,
+ mgmt_dev_class_changed_event, NULL, NULL);
+
+ mgmt_register(mgmt_if, MGMT_EV_LOCAL_NAME_CHANGED, adapter.index,
+ mgmt_local_name_changed_event, NULL, NULL);
+
+ mgmt_register(mgmt_if, MGMT_EV_NEW_LINK_KEY, adapter.index,
+ new_link_key_callback, NULL, NULL);
+
+ mgmt_register(mgmt_if, MGMT_EV_PIN_CODE_REQUEST, adapter.index,
+ pin_code_request_callback, NULL, NULL);
+
+ mgmt_register(mgmt_if, MGMT_EV_USER_CONFIRM_REQUEST, adapter.index,
+ user_confirm_request_callback, NULL, NULL);
+
+ mgmt_register(mgmt_if, MGMT_EV_USER_PASSKEY_REQUEST, adapter.index,
+ user_passkey_request_callback, NULL, NULL);
+
+ mgmt_register(mgmt_if, MGMT_EV_PASSKEY_NOTIFY, adapter.index,
+ user_passkey_notify_callback, NULL, NULL);
+
+ mgmt_register(mgmt_if, MGMT_EV_DISCOVERING, adapter.index,
+ mgmt_discovering_event, NULL, NULL);
+
+ mgmt_register(mgmt_if, MGMT_EV_DEVICE_FOUND, adapter.index,
+ mgmt_device_found_event, NULL, NULL);
+
+ mgmt_register(mgmt_if, MGMT_EV_DEVICE_CONNECTED, adapter.index,
+ mgmt_device_connected_event, NULL, NULL);
+
+ mgmt_register(mgmt_if, MGMT_EV_DEVICE_DISCONNECTED, adapter.index,
+ mgmt_device_disconnected_event, NULL, NULL);
+
+ mgmt_register(mgmt_if, MGMT_EV_CONNECT_FAILED, adapter.index,
+ mgmt_connect_failed_event, NULL, NULL);
+
+ mgmt_register(mgmt_if, MGMT_EV_AUTH_FAILED, adapter.index,
+ mgmt_auth_failed_event, NULL, NULL);
+
+ mgmt_register(mgmt_if, MGMT_EV_DEVICE_UNPAIRED, adapter.index,
+ mgmt_device_unpaired_event, NULL, NULL);
+
+ mgmt_register(mgmt_if, MGMT_EV_NEW_LONG_TERM_KEY, adapter.index,
+ new_long_term_key_event, NULL, NULL);
+
+ mgmt_register(mgmt_if, MGMT_EV_NEW_CSRK, adapter.index,
+ new_csrk_callback, NULL, NULL);
+
+ mgmt_register(mgmt_if, MGMT_EV_NEW_IRK, adapter.index, new_irk_callback,
+ NULL, NULL);
+}
+
+static void load_link_keys_complete(uint8_t status, uint16_t length,
+ const void *param, void *user_data)
+{
+ bt_bluetooth_ready cb = user_data;
+ int err;
+
+ if (status) {
+ error("Failed to load link keys for index %u: %s (0x%02x)",
+ adapter.index, mgmt_errstr(status), status);
+ err = -EIO;
+ goto failed;
+ }
+
+ DBG("status %u", status);
+
+ cb(0, &adapter.bdaddr);
+ return;
+
+failed:
+ cb(err, NULL);
+}
+
+static void load_link_keys(GSList *keys, bt_bluetooth_ready cb)
+{
+ struct mgmt_cp_load_link_keys *cp;
+ struct mgmt_link_key_info *key;
+ size_t key_count, cp_size;
+ unsigned int id;
+
+ key_count = g_slist_length(keys);
+
+ DBG("keys %zu ", key_count);
+
+ cp_size = sizeof(*cp) + (key_count * sizeof(*key));
+
+ cp = g_malloc0(cp_size);
+
+ /*
+ * Even if the list of stored keys is empty, it is important to
+ * load an empty list into the kernel. That way it is ensured
+ * that no old keys from a previous daemon are present.
+ */
+ cp->key_count = cpu_to_le16(key_count);
+
+ for (key = cp->keys; keys != NULL; keys = g_slist_next(keys), key++)
+ memcpy(key, keys->data, sizeof(*key));
+
+ id = mgmt_send(mgmt_if, MGMT_OP_LOAD_LINK_KEYS, adapter.index,
+ cp_size, cp, load_link_keys_complete, cb, NULL);
+
+ g_free(cp);
+
+ if (id == 0) {
+ error("Failed to load link keys");
+ cb(-EIO, NULL);
+ }
+}
+
+static void load_ltk_complete(uint8_t status, uint16_t length,
+ const void *param, void *user_data)
+{
+ if (status == MGMT_STATUS_SUCCESS)
+ return;
+
+ info("Failed to load LTKs: %s (0x%02x)", mgmt_errstr(status), status);
+}
+
+static void load_ltks(GSList *ltks)
+{
+ struct mgmt_cp_load_long_term_keys *cp;
+ struct mgmt_ltk_info *ltk;
+ size_t ltk_count, cp_size;
+ GSList *l;
+
+ ltk_count = g_slist_length(ltks);
+
+ DBG("ltks %zu", ltk_count);
+
+ cp_size = sizeof(*cp) + (ltk_count * sizeof(*ltk));
+
+ cp = g_malloc0(cp_size);
+
+ /*
+ * Even if the list of stored keys is empty, it is important to load
+ * an empty list into the kernel. That way it is ensured that no old
+ * keys from a previous daemon are present.
+ */
+ cp->key_count = cpu_to_le16(ltk_count);
+
+ for (l = ltks, ltk = cp->keys; l != NULL; l = g_slist_next(l), ltk++)
+ memcpy(ltk, l->data, sizeof(*ltk));
+
+ if (mgmt_send(mgmt_if, MGMT_OP_LOAD_LONG_TERM_KEYS, adapter.index,
+ cp_size, cp, load_ltk_complete, NULL, NULL) == 0)
+ error("Failed to load LTKs");
+
+ g_free(cp);
+}
+
+static void load_irk_complete(uint8_t status, uint16_t length,
+ const void *param, void *user_data)
+{
+ if (status == MGMT_STATUS_SUCCESS)
+ return;
+
+ info("Failed to load IRKs: %s (0x%02x)", mgmt_errstr(status), status);
+}
+
+static void load_irks(GSList *irks)
+{
+ struct mgmt_cp_load_irks *cp;
+ struct mgmt_irk_info *irk;
+ size_t irk_count, cp_size;
+ GSList *l;
+
+ irk_count = g_slist_length(irks);
+
+ DBG("irks %zu", irk_count);
+
+ cp_size = sizeof(*cp) + (irk_count * sizeof(*irk));
+
+ cp = g_malloc0(cp_size);
+
+ cp->irk_count = cpu_to_le16(irk_count);
+
+ for (l = irks, irk = cp->irks; l != NULL; l = g_slist_next(l), irk++)
+ memcpy(irk, irks->data, sizeof(*irk));
+
+ if (mgmt_send(mgmt_if, MGMT_OP_LOAD_IRKS, adapter.index, cp_size, cp,
+ load_irk_complete, NULL, NULL) == 0)
+ error("Failed to load IRKs");
+
+ g_free(cp);
+}
+
+static uint8_t get_adapter_uuids(void)
+{
+ struct hal_ev_adapter_props_changed *ev;
+ GSList *list = adapter.uuids;
+ size_t uuid_count = g_slist_length(list);
+ size_t len = uuid_count * sizeof(uint128_t);
+ uint8_t buf[BASELEN_PROP_CHANGED + len];
+ uint8_t *p;
+
+ memset(buf, 0, sizeof(buf));
+ ev = (void *) buf;
+
+ ev->num_props = 1;
+ ev->status = HAL_STATUS_SUCCESS;
+
+ ev->props[0].type = HAL_PROP_ADAPTER_UUIDS;
+ ev->props[0].len = len;
+ p = ev->props->val;
+
+ for (; list; list = g_slist_next(list)) {
+ uuid_t *uuid = list->data;
+
+ memcpy(p, &uuid->value.uuid128, sizeof(uint128_t));
+
+ p += sizeof(uint128_t);
+ }
+
+ ipc_send_notif(hal_ipc, HAL_SERVICE_ID_BLUETOOTH,
+ HAL_EV_ADAPTER_PROPS_CHANGED, sizeof(buf), ev);
+
+ return HAL_STATUS_SUCCESS;
+}
+
+static void remove_uuid_complete(uint8_t status, uint16_t length,
+ const void *param, void *user_data)
+{
+ if (status != MGMT_STATUS_SUCCESS) {
+ error("Failed to remove UUID: %s (0x%02x)", mgmt_errstr(status),
+ status);
+ return;
+ }
+
+ mgmt_dev_class_changed_event(adapter.index, length, param, NULL);
+
+ get_adapter_uuids();
+}
+
+static void remove_uuid(uuid_t *uuid)
+{
+ uint128_t uint128;
+ struct mgmt_cp_remove_uuid cp;
+
+ ntoh128((uint128_t *) uuid->value.uuid128.data, &uint128);
+ htob128(&uint128, (uint128_t *) cp.uuid);
+
+ mgmt_send(mgmt_if, MGMT_OP_REMOVE_UUID, adapter.index, sizeof(cp), &cp,
+ remove_uuid_complete, NULL, NULL);
+}
+
+static void add_uuid_complete(uint8_t status, uint16_t length,
+ const void *param, void *user_data)
+{
+ if (status != MGMT_STATUS_SUCCESS) {
+ error("Failed to add UUID: %s (0x%02x)", mgmt_errstr(status),
+ status);
+ return;
+ }
+
+ mgmt_dev_class_changed_event(adapter.index, length, param, NULL);
+
+ get_adapter_uuids();
+}
+
+static void add_uuid(uint8_t svc_hint, uuid_t *uuid)
+{
+ uint128_t uint128;
+ struct mgmt_cp_add_uuid cp;
+
+ ntoh128((uint128_t *) uuid->value.uuid128.data, &uint128);
+ htob128(&uint128, (uint128_t *) cp.uuid);
+
+ cp.svc_hint = svc_hint;
+
+ mgmt_send(mgmt_if, MGMT_OP_ADD_UUID, adapter.index, sizeof(cp), &cp,
+ add_uuid_complete, NULL, NULL);
+}
+
+int bt_adapter_add_record(sdp_record_t *rec, uint8_t svc_hint)
+{
+ uuid_t *uuid;
+
+ uuid = sdp_uuid_to_uuid128(&rec->svclass);
+
+ if (g_slist_find_custom(adapter.uuids, uuid, sdp_uuid_cmp)) {
+ char uuid_str[32];
+
+ sdp_uuid2strn(uuid, uuid_str, sizeof(uuid_str));
+ DBG("UUID %s already added", uuid_str);
+
+ bt_free(uuid);
+ return -EALREADY;
+ }
+
+ adapter.uuids = g_slist_prepend(adapter.uuids, uuid);
+
+ add_uuid(svc_hint, uuid);
+
+ return add_record_to_server(&adapter.bdaddr, rec);
+}
+
+void bt_adapter_remove_record(uint32_t handle)
+{
+ sdp_record_t *rec;
+ GSList *uuid_found;
+
+ rec = sdp_record_find(handle);
+ if (!rec)
+ return;
+
+ uuid_found = g_slist_find_custom(adapter.uuids, &rec->svclass,
+ sdp_uuid_cmp);
+ if (uuid_found) {
+ uuid_t *uuid = uuid_found->data;
+
+ remove_uuid(uuid);
+
+ adapter.uuids = g_slist_remove(adapter.uuids, uuid);
+
+ free(uuid);
+ }
+
+ remove_record_from_server(handle);
+}
+
+static void set_mode_complete(uint8_t status, uint16_t length,
+ const void *param, void *user_data)
+{
+ if (status != MGMT_STATUS_SUCCESS) {
+ error("Failed to set mode: %s (0x%02x)",
+ mgmt_errstr(status), status);
+ return;
+ }
+
+ /*
+ * The parameters are identical and also the task that is
+ * required in both cases. So it is safe to just call the
+ * event handling functions here.
+ */
+ new_settings_callback(adapter.index, length, param, NULL);
+}
+
+static bool set_mode(uint16_t opcode, uint8_t mode)
+{
+ struct mgmt_mode cp;
+
+ memset(&cp, 0, sizeof(cp));
+ cp.val = mode;
+
+ DBG("opcode=0x%x mode=0x%x", opcode, mode);
+
+ if (mgmt_send(mgmt_if, opcode, adapter.index, sizeof(cp), &cp,
+ set_mode_complete, NULL, NULL) > 0)
+ return true;
+
+ error("Failed to set mode");
+
+ return false;
+}
+
+static void set_io_capability(void)
+{
+ struct mgmt_cp_set_io_capability cp;
+
+ memset(&cp, 0, sizeof(cp));
+ cp.io_capability = DEFAULT_IO_CAPABILITY;
+
+ if (mgmt_send(mgmt_if, MGMT_OP_SET_IO_CAPABILITY, adapter.index,
+ sizeof(cp), &cp, NULL, NULL, NULL) == 0)
+ error("Failed to set IO capability");
+}
+
+static void set_device_id(void)
+{
+ struct mgmt_cp_set_device_id cp;
+
+ memset(&cp, 0, sizeof(cp));
+ cp.source = cpu_to_le16(bt_config_get_pnp_source());
+ cp.vendor = cpu_to_le16(bt_config_get_pnp_vendor());
+ cp.product = cpu_to_le16(bt_config_get_pnp_product());
+ cp.version = cpu_to_le16(bt_config_get_pnp_version());
+
+ if (mgmt_send(mgmt_if, MGMT_OP_SET_DEVICE_ID, adapter.index,
+ sizeof(cp), &cp, NULL, NULL, NULL) == 0)
+ error("Failed to set device id");
+
+ register_device_id(bt_config_get_pnp_source(),
+ bt_config_get_pnp_vendor(),
+ bt_config_get_pnp_product(),
+ bt_config_get_pnp_version());
+
+ bt_adapter_add_record(sdp_record_find(0x10000), 0x00);
+}
+
+static void set_adapter_name_complete(uint8_t status, uint16_t length,
+ const void *param, void *user_data)
+{
+ const struct mgmt_cp_set_local_name *rp = param;
+
+ if (status != MGMT_STATUS_SUCCESS) {
+ error("Failed to set name: %s (0x%02x)", mgmt_errstr(status),
+ status);
+ return;
+ }
+
+ adapter_set_name(rp->name);
+}
+
+static uint8_t set_adapter_name(const uint8_t *name, uint16_t len)
+{
+ struct mgmt_cp_set_local_name cp;
+
+ memset(&cp, 0, sizeof(cp));
+ memcpy(cp.name, name, len);
+
+ if (mgmt_send(mgmt_if, MGMT_OP_SET_LOCAL_NAME, adapter.index,
+ sizeof(cp), &cp, set_adapter_name_complete,
+ NULL, NULL) > 0)
+ return HAL_STATUS_SUCCESS;
+
+ error("Failed to set name");
+
+ return HAL_STATUS_FAILED;
+}
+
+static uint8_t set_adapter_discoverable_timeout(const void *buf, uint16_t len)
+{
+ const uint32_t *timeout = buf;
+
+ if (len != sizeof(*timeout)) {
+ error("Invalid set disc timeout size (%u bytes), terminating",
+ len);
+ raise(SIGTERM);
+ return HAL_STATUS_FAILED;
+ }
+
+ /*
+ * Android handles discoverable timeout in Settings app.
+ * There is no need to use kernel feature for that.
+ * Just need to store this value here
+ */
+
+ memcpy(&adapter.discoverable_timeout, timeout, sizeof(uint32_t));
+
+ store_adapter_config();
+
+ send_adapter_property(HAL_PROP_ADAPTER_DISC_TIMEOUT,
+ sizeof(adapter.discoverable_timeout),
+ &adapter.discoverable_timeout);
+
+ return HAL_STATUS_SUCCESS;
+}
+
+static void clear_uuids(void)
+{
+ struct mgmt_cp_remove_uuid cp;
+
+ memset(&cp, 0, sizeof(cp));
+
+ mgmt_send(mgmt_if, MGMT_OP_REMOVE_UUID, adapter.index, sizeof(cp),
+ &cp, NULL, NULL, NULL);
+}
+
+static struct device *create_device_from_info(GKeyFile *key_file,
+ const char *peer)
+{
+ struct device *dev;
+ uint8_t type;
+ bdaddr_t bdaddr;
+ char **uuids;
+ char *str;
+
+ /* BREDR if not present */
+ type = g_key_file_get_integer(key_file, peer, "AddressType", NULL);
+
+ str2ba(peer, &bdaddr);
+ dev = create_device(&bdaddr, type);
+
+ if (type != BDADDR_BREDR)
+ dev->bredr = g_key_file_get_boolean(key_file, peer, "BREDR",
+ NULL);
+
+ str = g_key_file_get_string(key_file, peer, "LocalCSRK", NULL);
+ if (str) {
+ int i;
+
+ dev->valid_local_csrk = true;
+ for (i = 0; i < 16; i++)
+ sscanf(str + (i * 2), "%02hhX", &dev->local_csrk[i]);
+
+ g_free(str);
+
+ dev->local_sign_cnt = g_key_file_get_integer(key_file, peer,
+ "LocalCSRKSignCounter", NULL);
+
+ dev->local_csrk_auth = g_key_file_get_boolean(key_file, peer,
+ "LocalCSRKAuthenticated", NULL);
+ }
+
+ str = g_key_file_get_string(key_file, peer, "RemoteCSRK", NULL);
+ if (str) {
+ int i;
+
+ dev->valid_remote_csrk = true;
+ for (i = 0; i < 16; i++)
+ sscanf(str + (i * 2), "%02hhX", &dev->remote_csrk[i]);
+
+ g_free(str);
+
+ dev->remote_sign_cnt = g_key_file_get_integer(key_file, peer,
+ "RemoteCSRKSignCounter", NULL);
+
+ dev->remote_csrk_auth = g_key_file_get_boolean(key_file, peer,
+ "RemoteCSRKAuthenticated",
+ NULL);
+ }
+
+ str = g_key_file_get_string(key_file, peer, "GattCCC", NULL);
+ if (str) {
+ dev->gatt_ccc = atoi(str);
+ g_free(str);
+ }
+
+ str = g_key_file_get_string(key_file, peer, "Name", NULL);
+ if (str) {
+ g_free(dev->name);
+ dev->name = str;
+ }
+
+ str = g_key_file_get_string(key_file, peer, "FriendlyName", NULL);
+ if (str) {
+ g_free(dev->friendly_name);
+ dev->friendly_name = str;
+ }
+
+ dev->class = g_key_file_get_integer(key_file, peer, "Class", NULL);
+
+ if (dev->bredr)
+ dev->bredr_seen = g_key_file_get_integer(key_file, peer,
+ "Timestamp",
+ NULL);
+ else
+ dev->le_seen = g_key_file_get_integer(key_file, peer,
+ "Timestamp", NULL);
+
+ uuids = g_key_file_get_string_list(key_file, peer, "Services", NULL,
+ NULL);
+ if (uuids) {
+ char **uuid;
+
+ for (uuid = uuids; *uuid; uuid++) {
+ uint8_t *u = g_malloc0(16);
+ int i;
+
+ for (i = 0; i < 16; i++)
+ sscanf((*uuid) + (i * 2), "%02hhX", &u[i]);
+
+ dev->uuids = g_slist_append(dev->uuids, u);
+ }
+
+ g_strfreev(uuids);
+ }
+
+ return dev;
+}
+
+static struct mgmt_link_key_info *get_key_info(GKeyFile *key_file,
+ const char *peer)
+{
+ struct mgmt_link_key_info *info = NULL;
+ char *str;
+ unsigned int i;
+
+ str = g_key_file_get_string(key_file, peer, "LinkKey", NULL);
+ if (!str || strlen(str) != 32)
+ goto failed;
+
+ info = g_new0(struct mgmt_link_key_info, 1);
+
+ str2ba(peer, &info->addr.bdaddr);
+
+ for (i = 0; i < sizeof(info->val); i++)
+ sscanf(str + (i * 2), "%02hhX", &info->val[i]);
+
+ info->type = g_key_file_get_integer(key_file, peer, "LinkKeyType",
+ NULL);
+ info->pin_len = g_key_file_get_integer(key_file, peer,
+ "LinkKeyPinLength", NULL);
+
+failed:
+ g_free(str);
+
+ return info;
+}
+
+static struct mgmt_ltk_info *get_ltk_info(GKeyFile *key_file, const char *peer,
+ bool master)
+{
+ const char *key_s, *keytype_s, *encsize_s, *ediv_s, *rand_s;
+ struct mgmt_ltk_info *info = NULL;
+ char *key;
+ unsigned int i;
+
+ key_s = master ? "LongTermKey" : "SlaveLongTermKey";
+ keytype_s = master ? "LongTermKeyType" : "SlaveLongTermKeyType";
+ encsize_s = master ? "LongTermKeyEncSize" : "SlaveLongTermKeyEncSize";
+ ediv_s = master ? "LongTermKeyEDiv" : "SlaveLongTermKeyEDiv";
+ rand_s = master ? "LongTermKeyRand" : "SlaveLongTermKeyRand";
+
+ key = g_key_file_get_string(key_file, peer, key_s, NULL);
+ if (!key || strlen(key) != 32)
+ goto failed;
+
+ info = g_new0(struct mgmt_ltk_info, 1);
+
+ str2ba(peer, &info->addr.bdaddr);
+
+ info->addr.type = g_key_file_get_integer(key_file, peer, "AddressType",
+ NULL);
+
+ for (i = 0; i < sizeof(info->val); i++)
+ sscanf(key + (i * 2), "%02hhX", &info->val[i]);
+
+ info->type = g_key_file_get_integer(key_file, peer, keytype_s, NULL);
+
+ info->enc_size = g_key_file_get_integer(key_file, peer, encsize_s,
+ NULL);
+
+ info->rand = g_key_file_get_uint64(key_file, peer, rand_s, NULL);
+ info->rand = cpu_to_le64(info->rand);
+
+ info->ediv = g_key_file_get_integer(key_file, peer, ediv_s, NULL);
+ info->ediv = cpu_to_le16(info->ediv);
+
+ info->master = master;
+
+failed:
+ g_free(key);
+
+ return info;
+}
+
+static struct mgmt_irk_info *get_irk_info(GKeyFile *key_file, const char *peer)
+{
+ struct mgmt_irk_info *info = NULL;
+ unsigned int i;
+ char *str;
+
+ str = g_key_file_get_string(key_file, peer, "IdentityResolvingKey",
+ NULL);
+ if (!str || strlen(str) != 32)
+ goto failed;
+
+ info = g_new0(struct mgmt_irk_info, 1);
+
+ str2ba(peer, &info->addr.bdaddr);
+
+ info->addr.type = g_key_file_get_integer(key_file, peer, "AddressType",
+ NULL);
+
+ for (i = 0; i < sizeof(info->val); i++)
+ sscanf(str + (i * 2), "%02hhX", &info->val[i]);
+
+failed:
+ g_free(str);
+
+ return info;
+}
+
+static time_t device_timestamp(const struct device *dev)
+{
+ if (dev->bredr && dev->le) {
+ if (dev->le_seen > dev->bredr_seen)
+ return dev->le_seen;
+
+ return dev->bredr_seen;
+ }
+
+ if (dev->bredr)
+ return dev->bredr_seen;
+
+ return dev->le_seen;
+}
+
+static int device_timestamp_cmp(gconstpointer a, gconstpointer b)
+{
+ const struct device *deva = a;
+ const struct device *devb = b;
+
+ return device_timestamp(deva) < device_timestamp(devb);
+}
+
+static void load_devices_cache(void)
+{
+ GKeyFile *key_file;
+ gchar **devs;
+ gsize len = 0;
+ unsigned int i;
+
+ key_file = g_key_file_new();
+
+ g_key_file_load_from_file(key_file, CACHE_FILE, 0, NULL);
+
+ devs = g_key_file_get_groups(key_file, &len);
+
+ for (i = 0; i < len; i++) {
+ struct device *dev;
+
+ dev = create_device_from_info(key_file, devs[i]);
+ cached_devices = g_slist_prepend(cached_devices, dev);
+ }
+
+ cached_devices = g_slist_sort(cached_devices, device_timestamp_cmp);
+
+ g_strfreev(devs);
+ g_key_file_free(key_file);
+}
+
+static void load_devices_info(bt_bluetooth_ready cb)
+{
+ GKeyFile *key_file;
+ gchar **devs;
+ gsize len = 0;
+ unsigned int i;
+ GSList *keys = NULL;
+ GSList *ltks = NULL;
+ GSList *irks = NULL;
+
+ key_file = g_key_file_new();
+
+ g_key_file_load_from_file(key_file, DEVICES_FILE, 0, NULL);
+
+ devs = g_key_file_get_groups(key_file, &len);
+
+ for (i = 0; i < len; i++) {
+ struct mgmt_link_key_info *key_info;
+ struct mgmt_ltk_info *ltk_info;
+ struct mgmt_irk_info *irk_info;
+ struct mgmt_ltk_info *slave_ltk_info;
+ struct device *dev;
+
+ dev = create_device_from_info(key_file, devs[i]);
+
+ key_info = get_key_info(key_file, devs[i]);
+ irk_info = get_irk_info(key_file, devs[i]);
+ ltk_info = get_ltk_info(key_file, devs[i], true);
+ slave_ltk_info = get_ltk_info(key_file, devs[i], false);
+
+ /*
+ * Skip devices that have no permanent keys
+ * (CSRKs are loaded by create_device_from_info())
+ */
+ if (!dev->valid_local_csrk && !dev->valid_remote_csrk &&
+ !key_info && !ltk_info &&
+ !slave_ltk_info && !irk_info) {
+ error("Failed to load keys for %s, skipping", devs[i]);
+ free_device(dev);
+ continue;
+ }
+
+ if (key_info) {
+ keys = g_slist_prepend(keys, key_info);
+ dev->bredr_paired = true;
+ dev->bredr_bonded = true;
+ }
+
+ if (irk_info)
+ irks = g_slist_prepend(irks, irk_info);
+
+ if (ltk_info)
+ ltks = g_slist_prepend(ltks, ltk_info);
+
+ if (slave_ltk_info)
+ ltks = g_slist_prepend(ltks, slave_ltk_info);
+
+ if (dev->valid_local_csrk || dev->valid_remote_csrk ||
+ irk_info || ltk_info || slave_ltk_info) {
+ dev->le_paired = true;
+ dev->le_bonded = true;
+ }
+
+ bonded_devices = g_slist_prepend(bonded_devices, dev);
+ }
+
+ load_ltks(ltks);
+ g_slist_free_full(ltks, g_free);
+
+ load_irks(irks);
+ g_slist_free_full(irks, g_free);
+
+ load_link_keys(keys, cb);
+ g_slist_free_full(keys, g_free);
+
+ g_strfreev(devs);
+ g_key_file_free(key_file);
+}
+
+static void set_adapter_class(void)
+{
+ struct mgmt_cp_set_dev_class cp;
+
+ memset(&cp, 0, sizeof(cp));
+
+ /*
+ * kernel assign the major and minor numbers straight to dev_class[0]
+ * and dev_class[1] without considering the proper bit shifting.
+ */
+ cp.major = ADAPTER_MAJOR_CLASS & 0x1f;
+ cp.minor = ADAPTER_MINOR_CLASS << 2;
+
+ if (mgmt_send(mgmt_if, MGMT_OP_SET_DEV_CLASS, adapter.index, sizeof(cp),
+ &cp, NULL, NULL, NULL) > 0)
+ return;
+
+ error("Failed to set class of device");
+}
+
+static void enable_mps(void)
+{
+ uuid_t uuid, *uuid128;
+
+ sdp_uuid16_create(&uuid, MPS_SVCLASS_ID);
+ uuid128 = sdp_uuid_to_uuid128(&uuid);
+ if (!uuid128)
+ return;
+
+ register_mps(true);
+ adapter.uuids = g_slist_prepend(adapter.uuids, uuid128);
+ add_uuid(0, uuid128);
+}
+
+static void clear_auto_connect_list_complete(uint8_t status,
+ uint16_t length,
+ const void *param,
+ void *user_data)
+{
+ if (status != MGMT_STATUS_SUCCESS)
+ error("Failed to clear auto connect list: %s (0x%02x)",
+ mgmt_errstr(status), status);
+}
+
+static void clear_auto_connect_list(void)
+{
+ struct mgmt_cp_remove_device cp;
+
+ if (!kernel_conn_control)
+ return;
+
+ memset(&cp, 0, sizeof(cp));
+
+ if (mgmt_send(mgmt_if, MGMT_OP_REMOVE_DEVICE, adapter.index, sizeof(cp),
+ &cp, clear_auto_connect_list_complete, NULL, NULL) > 0)
+ return;
+
+ error("Could not clear auto connect list");
+}
+
+static void read_info_complete(uint8_t status, uint16_t length,
+ const void *param, void *user_data)
+{
+ const struct mgmt_rp_read_info *rp = param;
+ bt_bluetooth_ready cb = user_data;
+ uint32_t missing_settings;
+ int err;
+
+ DBG("");
+
+ if (status) {
+ error("Failed to read info for index %u: %s (0x%02x)",
+ adapter.index, mgmt_errstr(status), status);
+ err = -EIO;
+ goto failed;
+ }
+
+ if (length < sizeof(*rp)) {
+ error("Too small read info complete response");
+ err = -EIO;
+ goto failed;
+ }
+
+ if (!bacmp(&rp->bdaddr, BDADDR_ANY)) {
+ error("No Bluetooth address");
+ err = -ENODEV;
+ goto failed;
+ }
+
+ load_adapter_config();
+
+ if (!bacmp(&adapter.bdaddr, BDADDR_ANY)) {
+ bacpy(&adapter.bdaddr, &rp->bdaddr);
+ store_adapter_config();
+ } else if (bacmp(&adapter.bdaddr, &rp->bdaddr)) {
+ error("Bluetooth address mismatch");
+ err = -ENODEV;
+ goto failed;
+ }
+
+ if (adapter.name && g_strcmp0(adapter.name, (const char *) rp->name))
+ set_adapter_name((uint8_t *)adapter.name, strlen(adapter.name));
+
+ set_adapter_class();
+
+ /* Store adapter information */
+ adapter.dev_class = rp->dev_class[0] | (rp->dev_class[1] << 8) |
+ (rp->dev_class[2] << 16);
+
+ adapter.supported_settings = le32_to_cpu(rp->supported_settings);
+ adapter.current_settings = le32_to_cpu(rp->current_settings);
+
+ /* TODO: Register all event notification handlers */
+ register_mgmt_handlers();
+
+ clear_uuids();
+ clear_auto_connect_list();
+
+ set_io_capability();
+ set_device_id();
+ enable_mps();
+
+ missing_settings = adapter.current_settings ^
+ adapter.supported_settings;
+
+ if (missing_settings & MGMT_SETTING_SSP)
+ set_mode(MGMT_OP_SET_SSP, 0x01);
+
+ if (missing_settings & MGMT_SETTING_BONDABLE)
+ set_mode(MGMT_OP_SET_BONDABLE, 0x01);
+
+ load_devices_info(cb);
+ load_devices_cache();
+
+ return;
+
+failed:
+ cb(err, NULL);
+}
+
+static void mgmt_index_added_event(uint16_t index, uint16_t length,
+ const void *param, void *user_data)
+{
+ bt_bluetooth_ready cb = user_data;
+
+ DBG("index %u", index);
+
+ if (adapter.index != MGMT_INDEX_NONE) {
+ DBG("skip event for index %u", index);
+ return;
+ }
+
+ if (option_index != MGMT_INDEX_NONE && option_index != index) {
+ DBG("skip event for index %u (option %u)", index, option_index);
+ return;
+ }
+
+ adapter.index = index;
+
+ if (mgmt_send(mgmt_if, MGMT_OP_READ_INFO, index, 0, NULL,
+ read_info_complete, cb, NULL) == 0) {
+ cb(-EIO, NULL);
+ return;
+ }
+}
+
+static void mgmt_index_removed_event(uint16_t index, uint16_t length,
+ const void *param, void *user_data)
+{
+ DBG("index %u", index);
+
+ if (index != adapter.index)
+ return;
+
+ error("Adapter was removed. Exiting.");
+ raise(SIGTERM);
+}
+
+static void read_index_list_complete(uint8_t status, uint16_t length,
+ const void *param, void *user_data)
+{
+ const struct mgmt_rp_read_index_list *rp = param;
+ bt_bluetooth_ready cb = user_data;
+ uint16_t num;
+ int i;
+
+ DBG("");
+
+ if (status) {
+ error("%s: Failed to read index list: %s (0x%02x)", __func__,
+ mgmt_errstr(status), status);
+ goto failed;
+ }
+
+ if (length < sizeof(*rp)) {
+ error("%s: Wrong size of read index list response", __func__);
+ goto failed;
+ }
+
+ num = le16_to_cpu(rp->num_controllers);
+
+ DBG("Number of controllers: %u", num);
+
+ if (num * sizeof(uint16_t) + sizeof(*rp) != length) {
+ error("%s: Incorrect pkt size for index list rsp", __func__);
+ goto failed;
+ }
+
+ if (adapter.index != MGMT_INDEX_NONE)
+ return;
+
+ for (i = 0; i < num; i++) {
+ uint16_t index = le16_to_cpu(rp->index[i]);
+
+ if (option_index != MGMT_INDEX_NONE && option_index != index)
+ continue;
+
+ if (mgmt_send(mgmt_if, MGMT_OP_READ_INFO, index, 0, NULL,
+ read_info_complete, cb, NULL) == 0)
+ goto failed;
+
+ adapter.index = index;
+ return;
+ }
+
+ return;
+
+failed:
+ cb(-EIO, NULL);
+}
+
+static void read_version_complete(uint8_t status, uint16_t length,
+ const void *param, void *user_data)
+{
+ const struct mgmt_rp_read_version *rp = param;
+ uint8_t mgmt_version, mgmt_revision;
+ bt_bluetooth_ready cb = user_data;
+
+ DBG("");
+
+ if (status) {
+ error("Failed to read version information: %s (0x%02x)",
+ mgmt_errstr(status), status);
+ goto failed;
+ }
+
+ if (length < sizeof(*rp)) {
+ error("Wrong size response");
+ goto failed;
+ }
+
+ mgmt_version = rp->version;
+ mgmt_revision = le16_to_cpu(rp->revision);
+
+ info("Bluetooth management interface %u.%u initialized",
+ mgmt_version, mgmt_revision);
+
+ if (MGMT_VERSION(mgmt_version, mgmt_revision) < MGMT_VERSION(1, 3)) {
+ error("Version 1.3 or later of management interface required");
+ goto failed;
+ }
+
+ /* Starting from mgmt 1.7, kernel can handle connection control */
+ if (MGMT_VERSION(mgmt_version, mgmt_revision) >= MGMT_VERSION(1, 7)) {
+ info("Kernel connection control will be used");
+ kernel_conn_control = true;
+ }
+
+ mgmt_register(mgmt_if, MGMT_EV_INDEX_ADDED, MGMT_INDEX_NONE,
+ mgmt_index_added_event, cb, NULL);
+ mgmt_register(mgmt_if, MGMT_EV_INDEX_REMOVED, MGMT_INDEX_NONE,
+ mgmt_index_removed_event, NULL, NULL);
+
+ if (mgmt_send(mgmt_if, MGMT_OP_READ_INDEX_LIST, MGMT_INDEX_NONE, 0,
+ NULL, read_index_list_complete, cb, NULL) > 0)
+ return;
+
+ error("Failed to read controller index list");
+
+failed:
+ cb(-EIO, NULL);
+}
+
+bool bt_bluetooth_start(int index, bool mgmt_dbg, bt_bluetooth_ready cb)
+{
+ DBG("index %d", index);
+
+ mgmt_if = mgmt_new_default();
+ if (!mgmt_if) {
+ error("Failed to access management interface");
+ return false;
+ }
+
+ if (mgmt_dbg)
+ mgmt_set_debug(mgmt_if, mgmt_debug, "mgmt_if: ", NULL);
+
+ if (mgmt_send(mgmt_if, MGMT_OP_READ_VERSION, MGMT_INDEX_NONE, 0, NULL,
+ read_version_complete, cb, NULL) == 0) {
+ error("Error sending READ_VERSION mgmt command");
+
+ mgmt_unref(mgmt_if);
+ mgmt_if = NULL;
+
+ return false;
+ }
+
+ if (index >= 0)
+ option_index = index;
+
+ return true;
+}
+
+static void shutdown_complete(uint8_t status, uint16_t length,
+ const void *param, void *user_data)
+{
+ bt_bluetooth_stopped cb = user_data;
+
+ if (status != MGMT_STATUS_SUCCESS)
+ error("Clean controller shutdown failed");
+
+ cb();
+}
+
+bool bt_bluetooth_stop(bt_bluetooth_stopped cb)
+{
+ struct mgmt_mode cp;
+
+ if (adapter.index == MGMT_INDEX_NONE)
+ return false;
+
+ info("Switching controller off");
+
+ memset(&cp, 0, sizeof(cp));
+
+ return mgmt_send(mgmt_if, MGMT_OP_SET_POWERED, adapter.index,
+ sizeof(cp), &cp, shutdown_complete, (void *)cb,
+ NULL) > 0;
+}
+
+void bt_bluetooth_cleanup(void)
+{
+ g_free(adapter.name);
+ adapter.name = NULL;
+
+ mgmt_unref(mgmt_if);
+ mgmt_if = NULL;
+}
+
+static bool set_discoverable(uint8_t mode, uint16_t timeout)
+{
+ struct mgmt_cp_set_discoverable cp;
+
+ memset(&cp, 0, sizeof(cp));
+ cp.val = mode;
+ cp.timeout = cpu_to_le16(timeout);
+
+ DBG("mode %u timeout %u", mode, timeout);
+
+ if (mgmt_send(mgmt_if, MGMT_OP_SET_DISCOVERABLE, adapter.index,
+ sizeof(cp), &cp, set_mode_complete, NULL, NULL) > 0)
+ return true;
+
+ error("Failed to set mode discoverable");
+
+ return false;
+}
+
+static uint8_t get_adapter_address(void)
+{
+ uint8_t buf[6];
+
+ bdaddr2android(&adapter.bdaddr, buf);
+
+ send_adapter_property(HAL_PROP_ADAPTER_ADDR, sizeof(buf), buf);
+
+ return HAL_STATUS_SUCCESS;
+}
+
+static uint8_t get_adapter_name(void)
+{
+ if (!adapter.name)
+ return HAL_STATUS_FAILED;
+
+ adapter_name_changed((uint8_t *) adapter.name);
+
+ return HAL_STATUS_SUCCESS;
+}
+
+static uint8_t get_adapter_class(void)
+{
+ DBG("");
+
+ adapter_class_changed();
+
+ return HAL_STATUS_SUCCESS;
+}
+
+static uint8_t settings2type(void)
+{
+ bool bredr, le;
+
+ bredr = adapter.current_settings & MGMT_SETTING_BREDR;
+ le = adapter.current_settings & MGMT_SETTING_LE;
+
+ if (bredr && le)
+ return HAL_TYPE_DUAL;
+
+ if (bredr && !le)
+ return HAL_TYPE_BREDR;
+
+ if (!bredr && le)
+ return HAL_TYPE_LE;
+
+ return 0;
+}
+
+static uint8_t get_adapter_type(void)
+{
+ uint8_t type;
+
+ DBG("");
+
+ type = settings2type();
+
+ if (!type)
+ return HAL_STATUS_FAILED;
+
+ send_adapter_property(HAL_PROP_ADAPTER_TYPE, sizeof(type), &type);
+
+ return HAL_STATUS_SUCCESS;
+}
+
+static uint8_t get_adapter_service_rec(void)
+{
+ DBG("Not implemented");
+
+ /* TODO: Add implementation */
+
+ return HAL_STATUS_FAILED;
+}
+
+static uint8_t get_adapter_scan_mode(void)
+{
+ DBG("");
+
+ scan_mode_changed();
+
+ return HAL_STATUS_SUCCESS;
+}
+
+static uint8_t get_adapter_bonded_devices(void)
+{
+ uint8_t buf[sizeof(bdaddr_t) * g_slist_length(bonded_devices)];
+ int i = 0;
+ GSList *l;
+
+ DBG("");
+
+ for (l = bonded_devices; l; l = g_slist_next(l)) {
+ struct device *dev = l->data;
+
+ get_device_android_addr(dev, buf + (i * sizeof(bdaddr_t)));
+ i++;
+ }
+
+ send_adapter_property(HAL_PROP_ADAPTER_BONDED_DEVICES,
+ i * sizeof(bdaddr_t), buf);
+
+ return HAL_STATUS_SUCCESS;
+}
+
+static uint8_t get_adapter_discoverable_timeout(void)
+{
+ send_adapter_property(HAL_PROP_ADAPTER_DISC_TIMEOUT,
+ sizeof(adapter.discoverable_timeout),
+ &adapter.discoverable_timeout);
+
+ return HAL_STATUS_SUCCESS;
+}
+
+static void prepare_le_features(uint8_t *le_features)
+{
+ le_features[0] = !!(adapter.current_settings & MGMT_SETTING_PRIVACY);
+ le_features[1] = adapter.max_advert_instance;
+ le_features[2] = adapter.rpa_offload_supported;
+ le_features[3] = adapter.max_irk_list_size;
+ le_features[4] = adapter.max_scan_filters_supported;
+ /* lo byte */
+ le_features[5] = adapter.scan_result_storage_size;
+ /* hi byte */
+ le_features[6] = adapter.scan_result_storage_size >> 8;
+ le_features[7] = adapter.activity_energy_info_supported;
+}
+
+static uint8_t get_adapter_le_features(void)
+{
+ uint8_t le_features[8];
+
+ prepare_le_features(le_features);
+
+ send_adapter_property(HAL_PROP_ADAPTER_LOCAL_LE_FEAT,
+ sizeof(le_features), le_features);
+ return HAL_STATUS_SUCCESS;
+}
+
+static void handle_get_adapter_prop_cmd(const void *buf, uint16_t len)
+{
+ const struct hal_cmd_get_adapter_prop *cmd = buf;
+ uint8_t status;
+
+ switch (cmd->type) {
+ case HAL_PROP_ADAPTER_ADDR:
+ status = get_adapter_address();
+ break;
+ case HAL_PROP_ADAPTER_NAME:
+ status = get_adapter_name();
+ break;
+ case HAL_PROP_ADAPTER_UUIDS:
+ status = get_adapter_uuids();
+ break;
+ case HAL_PROP_ADAPTER_CLASS:
+ status = get_adapter_class();
+ break;
+ case HAL_PROP_ADAPTER_TYPE:
+ status = get_adapter_type();
+ break;
+ case HAL_PROP_ADAPTER_SERVICE_REC:
+ status = get_adapter_service_rec();
+ break;
+ case HAL_PROP_ADAPTER_SCAN_MODE:
+ status = get_adapter_scan_mode();
+ break;
+ case HAL_PROP_ADAPTER_BONDED_DEVICES:
+ status = get_adapter_bonded_devices();
+ break;
+ case HAL_PROP_ADAPTER_DISC_TIMEOUT:
+ status = get_adapter_discoverable_timeout();
+ break;
+ case HAL_PROP_ADAPTER_LOCAL_LE_FEAT:
+ status = get_adapter_le_features();
+ break;
+ default:
+ status = HAL_STATUS_FAILED;
+ break;
+ }
+
+ if (status != HAL_STATUS_SUCCESS)
+ error("Failed to get adapter property (type %u status %u)",
+ cmd->type, status);
+
+ ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_BLUETOOTH, HAL_OP_GET_ADAPTER_PROP,
+ status);
+}
+
+static void get_adapter_properties(void)
+{
+ uint8_t buf[IPC_MTU];
+ struct hal_ev_adapter_props_changed *ev = (void *) buf;
+ uint8_t bonded[g_slist_length(bonded_devices) * sizeof(bdaddr_t)];
+ uint128_t uuids[g_slist_length(adapter.uuids)];
+ uint8_t android_bdaddr[6];
+ uint8_t le_features[8];
+ uint8_t type, mode;
+ size_t size, i;
+ GSList *l;
+
+ size = sizeof(*ev);
+
+ ev->status = HAL_STATUS_SUCCESS;
+ ev->num_props = 0;
+
+ bdaddr2android(&adapter.bdaddr, &android_bdaddr);
+ size += fill_hal_prop(buf + size, HAL_PROP_ADAPTER_ADDR,
+ sizeof(android_bdaddr), android_bdaddr);
+ ev->num_props++;
+
+ if (adapter.name) {
+ size += fill_hal_prop(buf + size, HAL_PROP_ADAPTER_NAME,
+ strlen(adapter.name), adapter.name);
+ ev->num_props++;
+ }
+
+ size += fill_hal_prop(buf + size, HAL_PROP_ADAPTER_CLASS,
+ sizeof(adapter.dev_class), &adapter.dev_class);
+ ev->num_props++;
+
+ type = settings2type();
+ if (type) {
+ size += fill_hal_prop(buf + size, HAL_PROP_ADAPTER_TYPE,
+ sizeof(type), &type);
+ ev->num_props++;
+ }
+
+ mode = settings2scan_mode();
+ size += fill_hal_prop(buf + size, HAL_PROP_ADAPTER_SCAN_MODE,
+ sizeof(mode), &mode);
+ ev->num_props++;
+
+ size += fill_hal_prop(buf + size, HAL_PROP_ADAPTER_DISC_TIMEOUT,
+ sizeof(adapter.discoverable_timeout),
+ &adapter.discoverable_timeout);
+ ev->num_props++;
+
+ for (i = 0, l = bonded_devices; l; l = g_slist_next(l), i++) {
+ struct device *dev = l->data;
+
+ get_device_android_addr(dev, bonded + (i * sizeof(bdaddr_t)));
+ }
+
+ size += fill_hal_prop(buf + size, HAL_PROP_ADAPTER_BONDED_DEVICES,
+ sizeof(bonded), bonded);
+ ev->num_props++;
+
+ for (i = 0, l = adapter.uuids; l; l = g_slist_next(l), i++) {
+ uuid_t *uuid = l->data;
+
+ memcpy(&uuids[i], &uuid->value.uuid128, sizeof(uint128_t));
+ }
+
+ size += fill_hal_prop(buf + size, HAL_PROP_ADAPTER_UUIDS, sizeof(uuids),
+ uuids);
+ ev->num_props++;
+
+ prepare_le_features(le_features);
+ size += fill_hal_prop(buf + size, HAL_PROP_ADAPTER_LOCAL_LE_FEAT,
+ sizeof(le_features), le_features);
+
+ ev->num_props++;
+
+ ipc_send_notif(hal_ipc, HAL_SERVICE_ID_BLUETOOTH,
+ HAL_EV_ADAPTER_PROPS_CHANGED, size, buf);
+}
+
+static void cancel_pending_confirm_name(gpointer data, gpointer user_data)
+{
+ struct device *dev = data;
+
+ mgmt_cancel(mgmt_if, dev->confirm_id);
+ dev->confirm_id = 0;
+}
+
+static bool stop_discovery(uint8_t type)
+{
+ struct mgmt_cp_stop_discovery cp;
+
+ cp.type = get_supported_discovery_type() & type;
+
+ DBG("type=0x%x", cp.type);
+
+ if (cp.type == SCAN_TYPE_NONE)
+ return false;
+
+ /* Lets drop all confirm name request as we don't need it anymore */
+ g_slist_foreach(cached_devices, cancel_pending_confirm_name, NULL);
+
+ if (mgmt_send(mgmt_if, MGMT_OP_STOP_DISCOVERY, adapter.index,
+ sizeof(cp), &cp, NULL, NULL, NULL) > 0)
+ return true;
+
+ error("Failed to stop discovery");
+ return false;
+}
+
+struct adv_user_data {
+ bt_le_set_advertising_done cb;
+ void *user_data;
+};
+
+static void set_advertising_cb(uint8_t status, uint16_t length,
+ const void *param, void *user_data)
+{
+ struct adv_user_data *data = user_data;
+
+ DBG("");
+
+ if (status)
+ error("Failed to set adverising %s (0x%02x))",
+ mgmt_errstr(status), status);
+
+ data->cb(status, data->user_data);
+}
+
+bool bt_le_set_advertising(bool advertising, bt_le_set_advertising_done cb,
+ void *user_data)
+{
+ struct adv_user_data *data;
+ uint8_t adv = advertising ? 0x01 : 0x00;
+
+ data = new0(struct adv_user_data, 1);
+ data->cb = cb;
+ data->user_data = user_data;
+
+ if (mgmt_send(mgmt_if, MGMT_OP_SET_ADVERTISING, adapter.index,
+ sizeof(adv), &adv, set_advertising_cb, data, free) > 0)
+ return true;
+
+ error("Failed to set advertising");
+ free(data);
+ return false;
+}
+
+bool bt_le_register(bt_le_device_found cb)
+{
+ if (gatt_device_found_cb)
+ return false;
+
+ gatt_device_found_cb = cb;
+
+ return true;
+}
+
+void bt_le_unregister(void)
+{
+ gatt_device_found_cb = NULL;
+}
+
+bool bt_le_discovery_stop(bt_le_discovery_stopped cb)
+{
+ if (!(adapter.current_settings & MGMT_SETTING_POWERED))
+ return false;
+
+ adapter.le_scanning = false;
+
+ if (adapter.cur_discovery_type != SCAN_TYPE_LE) {
+ if (cb)
+ cb();
+
+ return true;
+ }
+
+ if (!stop_discovery(SCAN_TYPE_LE))
+ return false;
+
+ gatt_discovery_stopped_cb = cb;
+ adapter.exp_discovery_type = SCAN_TYPE_NONE;
+
+ return true;
+}
+
+bool bt_le_discovery_start(void)
+{
+ if (!(adapter.current_settings & MGMT_SETTING_POWERED))
+ return false;
+
+ adapter.le_scanning = true;
+
+ /*
+ * If core is discovering - just set expected next scan type.
+ * It will be triggered in case current scan session is almost done
+ * i.e. we missed LE phase in interleaved scan, or we're trying to
+ * connect to device that was already discovered.
+ */
+ if (adapter.cur_discovery_type != SCAN_TYPE_NONE) {
+ adapter.exp_discovery_type = SCAN_TYPE_LE;
+ return true;
+ }
+
+ if (start_discovery(SCAN_TYPE_LE))
+ return true;
+
+ return false;
+}
+
+struct read_rssi_user_data {
+ bt_read_device_rssi_done cb;
+ void *user_data;
+};
+
+static void read_device_rssi_cb(uint8_t status, uint16_t length,
+ const void *param, void *user_data)
+{
+ const struct mgmt_rp_get_conn_info *rp = param;
+ struct read_rssi_user_data *data = user_data;
+
+ DBG("");
+
+ if (status)
+ error("Failed to get conn info: %s (0x%02x))",
+ mgmt_errstr(status), status);
+
+ if (length < sizeof(*rp)) {
+ error("Wrong size of get conn info response");
+ return;
+ }
+
+ data->cb(status, &rp->addr.bdaddr, rp->rssi, data->user_data);
+}
+
+bool bt_read_device_rssi(const bdaddr_t *addr, bt_read_device_rssi_done cb,
+ void *user_data)
+{
+ struct device *dev;
+ struct read_rssi_user_data *data;
+ struct mgmt_cp_get_conn_info cp;
+
+ dev = find_device(addr);
+ if (!dev)
+ return false;
+
+ memcpy(&cp.addr.bdaddr, addr, sizeof(cp.addr.bdaddr));
+ cp.addr.type = dev->bredr ? BDADDR_BREDR : dev->bdaddr_type;
+
+ data = new0(struct read_rssi_user_data, 1);
+ data->cb = cb;
+ data->user_data = user_data;
+
+ if (!mgmt_send(mgmt_if, MGMT_OP_GET_CONN_INFO, adapter.index,
+ sizeof(cp), &cp, read_device_rssi_cb, data, free)) {
+ free(data);
+ error("Failed to get conn info");
+ return false;
+ }
+
+ return true;
+}
+
+bool bt_get_csrk(const bdaddr_t *addr, bool local, uint8_t key[16],
+ uint32_t *sign_cnt, bool *authenticated)
+{
+ struct device *dev;
+
+ dev = find_device(addr);
+ if (!dev)
+ return false;
+
+ if (local && dev->valid_local_csrk) {
+ if (key)
+ memcpy(key, dev->local_csrk, 16);
+
+ if (sign_cnt)
+ *sign_cnt = dev->local_sign_cnt;
+
+ if (authenticated)
+ *authenticated = dev->local_csrk_auth;
+ } else if (!local && dev->valid_remote_csrk) {
+ if (key)
+ memcpy(key, dev->remote_csrk, 16);
+
+ if (sign_cnt)
+ *sign_cnt = dev->remote_sign_cnt;
+
+ if (authenticated)
+ *authenticated = dev->remote_csrk_auth;
+ } else {
+ return false;
+ }
+
+ return true;
+}
+
+static void store_sign_counter(struct device *dev, bool local)
+{
+ const char *sign_cnt_s;
+ uint32_t sign_cnt;
+ GKeyFile *key_file;
+
+ gsize length = 0;
+ char addr[18];
+ char *data;
+
+ key_file = g_key_file_new();
+ if (!g_key_file_load_from_file(key_file, DEVICES_FILE, 0, NULL)) {
+ g_key_file_free(key_file);
+ return;
+ }
+
+ ba2str(&dev->bdaddr, addr);
+
+ sign_cnt_s = local ? "LocalCSRKSignCounter" : "RemoteCSRKSignCounter";
+ sign_cnt = local ? dev->local_sign_cnt : dev->remote_sign_cnt;
+
+ g_key_file_set_integer(key_file, addr, sign_cnt_s, sign_cnt);
+
+ data = g_key_file_to_data(key_file, &length, NULL);
+ g_file_set_contents(DEVICES_FILE, data, length, NULL);
+ g_free(data);
+
+ g_key_file_free(key_file);
+}
+
+void bt_update_sign_counter(const bdaddr_t *addr, bool local, uint32_t val)
+{
+ struct device *dev;
+
+ dev = find_device(addr);
+ if (!dev)
+ return;
+
+ if (local)
+ dev->local_sign_cnt = val;
+ else
+ dev->remote_sign_cnt = val;
+
+ store_sign_counter(dev, local);
+}
+
+static uint8_t set_adapter_scan_mode(const void *buf, uint16_t len)
+{
+ const uint8_t *mode = buf;
+ bool conn, disc, cur_conn, cur_disc;
+
+ if (len != sizeof(*mode)) {
+ error("Invalid set scan mode size (%u bytes), terminating",
+ len);
+ raise(SIGTERM);
+ return HAL_STATUS_FAILED;
+ }
+
+ cur_conn = adapter.current_settings & MGMT_SETTING_CONNECTABLE;
+ cur_disc = adapter.current_settings & MGMT_SETTING_DISCOVERABLE;
+
+ DBG("connectable %u discoverable %d mode %u", cur_conn, cur_disc,
+ *mode);
+
+ switch (*mode) {
+ case HAL_ADAPTER_SCAN_MODE_NONE:
+ if (!cur_conn && !cur_disc)
+ goto done;
+
+ conn = false;
+ disc = false;
+ break;
+ case HAL_ADAPTER_SCAN_MODE_CONN:
+ if (cur_conn && !cur_disc)
+ goto done;
+
+ conn = true;
+ disc = false;
+ break;
+ case HAL_ADAPTER_SCAN_MODE_CONN_DISC:
+ if (cur_conn && cur_disc)
+ goto done;
+
+ conn = true;
+ disc = true;
+ break;
+ default:
+ return HAL_STATUS_FAILED;
+ }
+
+ if (cur_conn != conn) {
+ if (!set_mode(MGMT_OP_SET_CONNECTABLE, conn ? 0x01 : 0x00))
+ return HAL_STATUS_FAILED;
+ }
+
+ if (cur_disc != disc && conn) {
+ if (!set_discoverable(disc ? 0x01 : 0x00, 0))
+ return HAL_STATUS_FAILED;
+ }
+
+ return HAL_STATUS_SUCCESS;
+
+done:
+ /* Android expects property changed callback */
+ scan_mode_changed();
+
+ return HAL_STATUS_SUCCESS;
+}
+
+static void handle_set_adapter_prop_cmd(const void *buf, uint16_t len)
+{
+ const struct hal_cmd_set_adapter_prop *cmd = buf;
+ uint8_t status;
+
+ if (len != sizeof(*cmd) + cmd->len) {
+ error("Invalid set adapter prop cmd (0x%x), terminating",
+ cmd->type);
+ raise(SIGTERM);
+ return;
+ }
+
+ switch (cmd->type) {
+ case HAL_PROP_ADAPTER_SCAN_MODE:
+ status = set_adapter_scan_mode(cmd->val, cmd->len);
+ break;
+ case HAL_PROP_ADAPTER_NAME:
+ status = set_adapter_name(cmd->val, cmd->len);
+ break;
+ case HAL_PROP_ADAPTER_DISC_TIMEOUT:
+ status = set_adapter_discoverable_timeout(cmd->val, cmd->len);
+ break;
+ default:
+ DBG("Unhandled property type 0x%x", cmd->type);
+ status = HAL_STATUS_FAILED;
+ break;
+ }
+
+ if (status != HAL_STATUS_SUCCESS)
+ error("Failed to set adapter property (type %u status %u)",
+ cmd->type, status);
+
+ ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_BLUETOOTH, HAL_OP_SET_ADAPTER_PROP,
+ status);
+}
+
+static void pair_device_complete(uint8_t status, uint16_t length,
+ const void *param, void *user_data)
+{
+ const struct mgmt_rp_pair_device *rp = param;
+ struct device *dev;
+
+ DBG("status %u", status);
+
+ dev = find_device(&rp->addr.bdaddr);
+ if (!dev)
+ return;
+
+ /*
+ * Update pairing and paired status. Bonded status will be updated once
+ * any link key come
+ */
+ update_device_state(dev, rp->addr.type, status_mgmt2hal(status), false,
+ !status, false);
+
+ if (status == MGMT_STATUS_SUCCESS)
+ queue_foreach(paired_cb_list, send_paired_notification, dev);
+}
+
+static uint8_t select_device_bearer(struct device *dev)
+{
+ uint8_t res;
+
+ if (dev->bredr && dev->le) {
+ if (dev->le_seen > dev->bredr_seen)
+ res = dev->bdaddr_type;
+ else
+ res = BDADDR_BREDR;
+ } else {
+ res = dev->bredr ? BDADDR_BREDR : dev->bdaddr_type;
+ }
+
+ DBG("Selected bearer %d", res);
+
+ return res;
+}
+
+uint8_t bt_device_last_seen_bearer(const bdaddr_t *bdaddr)
+{
+ struct device *dev;
+
+ dev = find_device(bdaddr);
+ if (!dev)
+ return BDADDR_BREDR;
+
+ return select_device_bearer(dev);
+}
+
+static void handle_create_bond_cmd(const void *buf, uint16_t len)
+{
+ const struct hal_cmd_create_bond *cmd = buf;
+ struct device *dev;
+ uint8_t status;
+ struct mgmt_cp_pair_device cp;
+
+ dev = get_device_android(cmd->bdaddr);
+
+ cp.io_cap = DEFAULT_IO_CAPABILITY;
+ cp.addr.type = select_device_bearer(dev);
+ bacpy(&cp.addr.bdaddr, &dev->bdaddr);
+
+ /* TODO: Handle transport parameter */
+ if (cmd->transport > BT_TRANSPORT_LE) {
+ status = HAL_STATUS_INVALID;
+ goto fail;
+ }
+
+ if (device_is_paired(dev, cp.addr.type)) {
+ status = HAL_STATUS_FAILED;
+ goto fail;
+ }
+
+ if (mgmt_send(mgmt_if, MGMT_OP_PAIR_DEVICE, adapter.index, sizeof(cp),
+ &cp, pair_device_complete, NULL, NULL) == 0) {
+ status = HAL_STATUS_FAILED;
+ goto fail;
+ }
+
+ status = HAL_STATUS_SUCCESS;
+
+ update_device_state(dev, cp.addr.type, HAL_STATUS_SUCCESS, true, false,
+ false);
+
+fail:
+ ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_BLUETOOTH, HAL_OP_CREATE_BOND,
+ status);
+}
+
+static void handle_cancel_bond_cmd(const void *buf, uint16_t len)
+{
+ const struct hal_cmd_cancel_bond *cmd = buf;
+ struct mgmt_addr_info cp;
+ struct device *dev;
+ uint8_t status;
+
+ dev = find_device_android(cmd->bdaddr);
+ if (!dev) {
+ status = HAL_STATUS_FAILED;
+ goto failed;
+ }
+
+ cp.type = select_device_bearer(dev);
+ bacpy(&cp.bdaddr, &dev->bdaddr);
+
+ if (mgmt_reply(mgmt_if, MGMT_OP_CANCEL_PAIR_DEVICE, adapter.index,
+ sizeof(cp), &cp, NULL, NULL, NULL) == 0) {
+ status = HAL_STATUS_FAILED;
+ goto failed;
+ }
+
+ status = HAL_STATUS_SUCCESS;
+
+failed:
+ ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_BLUETOOTH, HAL_OP_CANCEL_BOND,
+ status);
+}
+
+static void send_unpaired_notification(void *data, void *user_data)
+{
+ bt_unpaired_device_cb cb = data;
+ struct mgmt_addr_info *addr = user_data;
+
+ cb(&addr->bdaddr);
+}
+
+static void unpair_device_complete(uint8_t status, uint16_t length,
+ const void *param, void *user_data)
+{
+ const struct mgmt_rp_unpair_device *rp = param;
+ struct device *dev;
+
+ DBG("status %u", status);
+
+ if (status != MGMT_STATUS_SUCCESS && status != MGMT_STATUS_NOT_PAIRED)
+ return;
+
+ dev = find_device(&rp->addr.bdaddr);
+ if (!dev)
+ return;
+
+ update_device_state(dev, rp->addr.type, HAL_STATUS_SUCCESS, false,
+ false, false);
+
+ /* Cast rp->addr to (void *) since queue_foreach don't take const */
+
+ if (!dev->le_paired && !dev->bredr_paired)
+ queue_foreach(unpaired_cb_list, send_unpaired_notification,
+ (void *)&rp->addr);
+}
+
+static void handle_remove_bond_cmd(const void *buf, uint16_t len)
+{
+ const struct hal_cmd_remove_bond *cmd = buf;
+ struct mgmt_cp_unpair_device cp;
+ struct device *dev;
+ uint8_t status;
+
+ dev = find_device_android(cmd->bdaddr);
+ if (!dev) {
+ status = HAL_STATUS_FAILED;
+ goto failed;
+ }
+
+ cp.disconnect = 1;
+ bacpy(&cp.addr.bdaddr, &dev->bdaddr);
+
+ if (dev->le_paired) {
+ cp.addr.type = dev->bdaddr_type;
+
+ if (mgmt_send(mgmt_if, MGMT_OP_UNPAIR_DEVICE, adapter.index,
+ sizeof(cp), &cp, unpair_device_complete,
+ NULL, NULL) == 0) {
+ status = HAL_STATUS_FAILED;
+ goto failed;
+ }
+ }
+
+ if (dev->bredr_paired) {
+ cp.addr.type = BDADDR_BREDR;
+
+ if (mgmt_send(mgmt_if, MGMT_OP_UNPAIR_DEVICE, adapter.index,
+ sizeof(cp), &cp, unpair_device_complete,
+ NULL, NULL) == 0) {
+ status = HAL_STATUS_FAILED;
+ goto failed;
+ }
+ }
+
+ status = HAL_STATUS_SUCCESS;
+
+failed:
+ ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_BLUETOOTH, HAL_OP_REMOVE_BOND,
+ status);
+}
+
+static void handle_pin_reply_cmd(const void *buf, uint16_t len)
+{
+ const struct hal_cmd_pin_reply *cmd = buf;
+ uint8_t status;
+ bdaddr_t bdaddr;
+ char addr[18];
+
+ android2bdaddr(cmd->bdaddr, &bdaddr);
+ ba2str(&bdaddr, addr);
+
+ DBG("%s accept %u pin_len %u", addr, cmd->accept, cmd->pin_len);
+
+ if (!cmd->accept && cmd->pin_len) {
+ status = HAL_STATUS_INVALID;
+ goto failed;
+ }
+
+ if (cmd->accept) {
+ struct mgmt_cp_pin_code_reply rp;
+
+ memset(&rp, 0, sizeof(rp));
+
+ bacpy(&rp.addr.bdaddr, &bdaddr);
+ rp.addr.type = BDADDR_BREDR;
+ rp.pin_len = cmd->pin_len;
+ memcpy(rp.pin_code, cmd->pin_code, rp.pin_len);
+
+ if (mgmt_reply(mgmt_if, MGMT_OP_PIN_CODE_REPLY, adapter.index,
+ sizeof(rp), &rp, NULL, NULL, NULL) == 0) {
+ status = HAL_STATUS_FAILED;
+ goto failed;
+ }
+ } else {
+ struct mgmt_cp_pin_code_neg_reply rp;
+
+ bacpy(&rp.addr.bdaddr, &bdaddr);
+ rp.addr.type = BDADDR_BREDR;
+
+ if (mgmt_reply(mgmt_if, MGMT_OP_PIN_CODE_NEG_REPLY,
+ adapter.index, sizeof(rp), &rp,
+ NULL, NULL, NULL) == 0) {
+ status = HAL_STATUS_FAILED;
+ goto failed;
+ }
+ }
+
+ status = HAL_STATUS_SUCCESS;
+failed:
+ ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_BLUETOOTH, HAL_OP_PIN_REPLY,
+ status);
+}
+
+static uint8_t user_confirm_reply(const bdaddr_t *bdaddr, uint8_t type,
+ bool accept)
+{
+ struct mgmt_addr_info cp;
+ uint16_t opcode;
+
+ if (accept)
+ opcode = MGMT_OP_USER_CONFIRM_REPLY;
+ else
+ opcode = MGMT_OP_USER_CONFIRM_NEG_REPLY;
+
+ bacpy(&cp.bdaddr, bdaddr);
+ cp.type = type;
+
+ if (mgmt_reply(mgmt_if, opcode, adapter.index, sizeof(cp), &cp,
+ NULL, NULL, NULL) > 0)
+ return HAL_STATUS_SUCCESS;
+
+ return HAL_STATUS_FAILED;
+}
+
+static uint8_t user_passkey_reply(const bdaddr_t *bdaddr, uint8_t type,
+ bool accept, uint32_t passkey)
+{
+ unsigned int id;
+
+ if (accept) {
+ struct mgmt_cp_user_passkey_reply cp;
+
+ memset(&cp, 0, sizeof(cp));
+ bacpy(&cp.addr.bdaddr, bdaddr);
+ cp.addr.type = type;
+ cp.passkey = cpu_to_le32(passkey);
+
+ id = mgmt_reply(mgmt_if, MGMT_OP_USER_PASSKEY_REPLY,
+ adapter.index, sizeof(cp), &cp,
+ NULL, NULL, NULL);
+ } else {
+ struct mgmt_cp_user_passkey_neg_reply cp;
+
+ memset(&cp, 0, sizeof(cp));
+ bacpy(&cp.addr.bdaddr, bdaddr);
+ cp.addr.type = type;
+
+ id = mgmt_reply(mgmt_if, MGMT_OP_USER_PASSKEY_NEG_REPLY,
+ adapter.index, sizeof(cp), &cp,
+ NULL, NULL, NULL);
+ }
+
+ if (id == 0)
+ return HAL_STATUS_FAILED;
+
+ return HAL_STATUS_SUCCESS;
+}
+
+static void handle_ssp_reply_cmd(const void *buf, uint16_t len)
+{
+ const struct hal_cmd_ssp_reply *cmd = buf;
+ struct device *dev;
+ uint8_t status;
+ char addr[18];
+
+ /* TODO should parameters sanity be verified here? */
+
+ dev = find_device_android(cmd->bdaddr);
+ if (!dev)
+ return;
+
+ ba2str(&dev->bdaddr, addr);
+
+ DBG("%s variant %u accept %u", addr, cmd->ssp_variant, cmd->accept);
+
+ switch (cmd->ssp_variant) {
+ case HAL_SSP_VARIANT_CONFIRM:
+ case HAL_SSP_VARIANT_CONSENT:
+ status = user_confirm_reply(&dev->bdaddr,
+ select_device_bearer(dev),
+ cmd->accept);
+ break;
+ case HAL_SSP_VARIANT_ENTRY:
+ status = user_passkey_reply(&dev->bdaddr,
+ select_device_bearer(dev),
+ cmd->accept, cmd->passkey);
+ break;
+ case HAL_SSP_VARIANT_NOTIF:
+ status = HAL_STATUS_SUCCESS;
+ break;
+ default:
+ status = HAL_STATUS_INVALID;
+ break;
+ }
+
+ ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_BLUETOOTH, HAL_OP_SSP_REPLY,
+ status);
+}
+
+static void handle_get_remote_services_cmd(const void *buf, uint16_t len)
+{
+ const struct hal_cmd_get_remote_services *cmd = buf;
+ uint8_t status;
+ bdaddr_t addr;
+
+ android2bdaddr(&cmd->bdaddr, &addr);
+
+ status = browse_remote_sdp(&addr);
+
+ ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_BLUETOOTH,
+ HAL_OP_GET_REMOTE_SERVICES, status);
+}
+
+static uint8_t get_device_uuids(struct device *dev)
+{
+ send_device_uuids_notif(dev);
+
+ return HAL_STATUS_SUCCESS;
+}
+
+static uint8_t get_device_class(struct device *dev)
+{
+ send_device_property(dev, HAL_PROP_DEVICE_CLASS, sizeof(dev->class),
+ &dev->class);
+
+ return HAL_STATUS_SUCCESS;
+}
+
+static uint8_t get_device_type(struct device *dev)
+{
+ uint8_t type = get_device_android_type(dev);
+
+ send_device_property(dev, HAL_PROP_DEVICE_TYPE, sizeof(type), &type);
+
+ return HAL_STATUS_SUCCESS;
+}
+
+static uint8_t get_device_service_rec(struct device *dev)
+{
+ DBG("Not implemented");
+
+ /* TODO */
+
+ return HAL_STATUS_FAILED;
+}
+
+static uint8_t get_device_friendly_name(struct device *dev)
+{
+ if (!dev->friendly_name)
+ return HAL_STATUS_FAILED;
+
+ send_device_property(dev, HAL_PROP_DEVICE_FRIENDLY_NAME,
+ strlen(dev->friendly_name), dev->friendly_name);
+
+ return HAL_STATUS_SUCCESS;
+}
+
+static uint8_t get_device_rssi(struct device *dev)
+{
+ if (!dev->rssi)
+ return HAL_STATUS_FAILED;
+
+ send_device_property(dev, HAL_PROP_DEVICE_RSSI, sizeof(dev->rssi),
+ &dev->rssi);
+
+ return HAL_STATUS_SUCCESS;
+}
+
+static uint8_t get_device_version_info(struct device *dev)
+{
+ DBG("Not implemented");
+
+ /* TODO */
+
+ return HAL_STATUS_FAILED;
+}
+
+static uint8_t get_device_timestamp(struct device *dev)
+{
+ uint32_t timestamp;
+
+ timestamp = device_timestamp(dev);
+
+ send_device_property(dev, HAL_PROP_DEVICE_TIMESTAMP, sizeof(timestamp),
+ ×tamp);
+
+ return HAL_STATUS_SUCCESS;
+}
+
+static void get_remote_device_props(struct device *dev)
+{
+ uint8_t buf[IPC_MTU];
+ struct hal_ev_remote_device_props *ev = (void *) buf;
+ uint128_t uuids[g_slist_length(dev->uuids)];
+ uint8_t android_type;
+ uint32_t timestamp;
+ size_t size, i;
+ GSList *l;
+
+ memset(buf, 0, sizeof(buf));
+
+ size = sizeof(*ev);
+
+ ev->status = HAL_STATUS_SUCCESS;
+ get_device_android_addr(dev, ev->bdaddr);
+
+ android_type = get_device_android_type(dev);
+ size += fill_hal_prop(buf + size, HAL_PROP_DEVICE_TYPE,
+ sizeof(android_type), &android_type);
+ ev->num_props++;
+
+ size += fill_hal_prop(buf + size, HAL_PROP_DEVICE_CLASS,
+ sizeof(dev->class), &dev->class);
+ ev->num_props++;
+
+ if (dev->rssi) {
+ size += fill_hal_prop(buf + size, HAL_PROP_DEVICE_RSSI,
+ sizeof(dev->rssi), &dev->rssi);
+ ev->num_props++;
+ }
+
+ size += fill_hal_prop(buf + size, HAL_PROP_DEVICE_NAME,
+ strlen(dev->name), dev->name);
+ ev->num_props++;
+
+ if (dev->friendly_name) {
+ size += fill_hal_prop(buf + size,
+ HAL_PROP_DEVICE_FRIENDLY_NAME,
+ strlen(dev->friendly_name),
+ dev->friendly_name);
+ ev->num_props++;
+ }
+
+ for (i = 0, l = dev->uuids; l; l = g_slist_next(l), i++)
+ memcpy(&uuids[i], l->data, sizeof(uint128_t));
+
+ size += fill_hal_prop(buf + size, HAL_PROP_DEVICE_UUIDS, sizeof(uuids),
+ uuids);
+ ev->num_props++;
+
+ timestamp = get_device_timestamp(dev);
+
+ size += fill_hal_prop(buf + size, HAL_PROP_DEVICE_TIMESTAMP,
+ sizeof(timestamp), ×tamp);
+ ev->num_props++;
+
+ ipc_send_notif(hal_ipc, HAL_SERVICE_ID_BLUETOOTH,
+ HAL_EV_REMOTE_DEVICE_PROPS, size, buf);
+}
+
+static void send_bonded_devices_props(void)
+{
+ GSList *l;
+
+ for (l = bonded_devices; l; l = g_slist_next(l)) {
+ struct device *dev = l->data;
+
+ get_remote_device_props(dev);
+ }
+}
+
+static void handle_enable_cmd(const void *buf, uint16_t len)
+{
+ uint8_t status;
+
+ /*
+ * Framework expects all properties to be emitted while enabling
+ * adapter
+ */
+ get_adapter_properties();
+
+ /* Sent also properties of bonded devices */
+ send_bonded_devices_props();
+
+ if (adapter.current_settings & MGMT_SETTING_POWERED) {
+ status = HAL_STATUS_SUCCESS;
+ goto reply;
+ }
+
+ if (!set_mode(MGMT_OP_SET_POWERED, 0x01)) {
+ status = HAL_STATUS_FAILED;
+ goto reply;
+ }
+
+ status = HAL_STATUS_SUCCESS;
+reply:
+ ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_BLUETOOTH, HAL_OP_ENABLE, status);
+}
+
+static void handle_disable_cmd(const void *buf, uint16_t len)
+{
+ uint8_t status;
+
+ if (!(adapter.current_settings & MGMT_SETTING_POWERED)) {
+ status = HAL_STATUS_SUCCESS;
+ goto reply;
+ }
+
+ /* Cancel all pending requests. Need it in case of ongoing paring */
+ mgmt_cancel_index(mgmt_if, adapter.index);
+
+ if (!set_mode(MGMT_OP_SET_POWERED, 0x00)) {
+ status = HAL_STATUS_FAILED;
+ goto reply;
+ }
+
+ status = HAL_STATUS_SUCCESS;
+reply:
+ ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_BLUETOOTH, HAL_OP_DISABLE, status);
+}
+
+static void handle_get_adapter_props_cmd(const void *buf, uint16_t len)
+{
+ get_adapter_properties();
+
+ ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_BLUETOOTH,
+ HAL_OP_GET_ADAPTER_PROPS, HAL_STATUS_SUCCESS);
+}
+
+static void handle_get_remote_device_props_cmd(const void *buf, uint16_t len)
+{
+ const struct hal_cmd_get_remote_device_props *cmd = buf;
+ struct device *dev;
+ uint8_t status;
+
+ dev = find_device_android(cmd->bdaddr);
+ if (!dev) {
+ status = HAL_STATUS_INVALID;
+ goto failed;
+ }
+
+ get_remote_device_props(dev);
+
+ status = HAL_STATUS_SUCCESS;
+
+failed:
+ ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_BLUETOOTH,
+ HAL_OP_GET_REMOTE_DEVICE_PROPS, status);
+}
+
+static void handle_get_remote_device_prop_cmd(const void *buf, uint16_t len)
+{
+ const struct hal_cmd_get_remote_device_prop *cmd = buf;
+ struct device *dev;
+ uint8_t status;
+
+ dev = find_device_android(cmd->bdaddr);
+ if (!dev) {
+ status = HAL_STATUS_INVALID;
+ goto failed;
+ }
+
+ switch (cmd->type) {
+ case HAL_PROP_DEVICE_NAME:
+ status = get_device_name(dev);
+ break;
+ case HAL_PROP_DEVICE_UUIDS:
+ status = get_device_uuids(dev);
+ break;
+ case HAL_PROP_DEVICE_CLASS:
+ status = get_device_class(dev);
+ break;
+ case HAL_PROP_DEVICE_TYPE:
+ status = get_device_type(dev);
+ break;
+ case HAL_PROP_DEVICE_SERVICE_REC:
+ status = get_device_service_rec(dev);
+ break;
+ case HAL_PROP_DEVICE_FRIENDLY_NAME:
+ status = get_device_friendly_name(dev);
+ break;
+ case HAL_PROP_DEVICE_RSSI:
+ status = get_device_rssi(dev);
+ break;
+ case HAL_PROP_DEVICE_VERSION_INFO:
+ status = get_device_version_info(dev);
+ break;
+ case HAL_PROP_DEVICE_TIMESTAMP:
+ status = get_device_timestamp(dev);
+ break;
+ default:
+ status = HAL_STATUS_FAILED;
+ break;
+ }
+
+ if (status != HAL_STATUS_SUCCESS)
+ error("Failed to get device property (type %u status %u)",
+ cmd->type, status);
+
+failed:
+ ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_BLUETOOTH,
+ HAL_OP_GET_REMOTE_DEVICE_PROP, status);
+}
+
+static uint8_t set_device_friendly_name(struct device *dev, const uint8_t *val,
+ uint16_t len)
+{
+ DBG("");
+
+ g_free(dev->friendly_name);
+ dev->friendly_name = g_strndup((const char *) val, len);
+
+ if (dev->bredr_paired || dev->le_paired)
+ store_device_info(dev, DEVICES_FILE);
+ else
+ store_device_info(dev, CACHE_FILE);
+
+ return HAL_STATUS_SUCCESS;
+}
+
+static uint8_t set_device_version_info(struct device *dev)
+{
+ DBG("Not implemented");
+
+ /* TODO */
+
+ return HAL_STATUS_FAILED;
+}
+
+static void handle_set_remote_device_prop_cmd(const void *buf, uint16_t len)
+{
+ const struct hal_cmd_set_remote_device_prop *cmd = buf;
+ struct device *dev;
+ uint8_t status;
+
+ if (len != sizeof(*cmd) + cmd->len) {
+ error("Invalid set remote device prop cmd (0x%x), terminating",
+ cmd->type);
+ raise(SIGTERM);
+ return;
+ }
+
+ dev = find_device_android(cmd->bdaddr);
+ if (!dev) {
+ status = HAL_STATUS_INVALID;
+ goto failed;
+ }
+
+ switch (cmd->type) {
+ case HAL_PROP_DEVICE_FRIENDLY_NAME:
+ status = set_device_friendly_name(dev, cmd->val, cmd->len);
+ break;
+ case HAL_PROP_DEVICE_VERSION_INFO:
+ status = set_device_version_info(dev);
+ break;
+ default:
+ status = HAL_STATUS_FAILED;
+ break;
+ }
+
+ if (status != HAL_STATUS_SUCCESS)
+ error("Failed to set device property (type %u status %u)",
+ cmd->type, status);
+
+failed:
+ ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_BLUETOOTH,
+ HAL_OP_SET_REMOTE_DEVICE_PROP, status);
+}
+
+static void handle_get_remote_service_rec_cmd(const void *buf, uint16_t len)
+{
+ const struct hal_cmd_get_remote_service_rec *cmd = buf;
+ uint8_t status;
+ bdaddr_t addr;
+
+ android2bdaddr(&cmd->bdaddr, &addr);
+
+ status = find_remote_sdp_rec(&addr, cmd->uuid);
+
+ ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_BLUETOOTH,
+ HAL_OP_GET_REMOTE_SERVICE_REC, status);
+}
+
+static void handle_start_discovery_cmd(const void *buf, uint16_t len)
+{
+ uint8_t status;
+
+ if (!(adapter.current_settings & MGMT_SETTING_POWERED)) {
+ status = HAL_STATUS_NOT_READY;
+ goto failed;
+ }
+
+ switch (adapter.cur_discovery_type) {
+ case SCAN_TYPE_DUAL:
+ case SCAN_TYPE_BREDR:
+ break;
+ case SCAN_TYPE_NONE:
+ if (!start_discovery(SCAN_TYPE_DUAL)) {
+ status = HAL_STATUS_FAILED;
+ goto failed;
+ }
+
+ break;
+ case SCAN_TYPE_LE:
+ if (get_supported_discovery_type() == SCAN_TYPE_LE)
+ break;
+
+ if (!stop_discovery(SCAN_TYPE_LE)) {
+ status = HAL_STATUS_FAILED;
+ goto failed;
+ }
+
+ adapter.exp_discovery_type = SCAN_TYPE_DUAL;
+ break;
+ }
+
+ status = HAL_STATUS_SUCCESS;
+
+failed:
+ ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_BLUETOOTH, HAL_OP_START_DISCOVERY,
+ status);
+}
+
+static void handle_cancel_discovery_cmd(const void *buf, uint16_t len)
+{
+ uint8_t status;
+
+ if (!(adapter.current_settings & MGMT_SETTING_POWERED)) {
+ status = HAL_STATUS_NOT_READY;
+ goto failed;
+ }
+
+ switch (adapter.cur_discovery_type) {
+ case SCAN_TYPE_NONE:
+ break;
+ case SCAN_TYPE_LE:
+ if (get_supported_discovery_type() != SCAN_TYPE_LE)
+ break;
+
+ if (adapter.exp_discovery_type == SCAN_TYPE_LE) {
+ status = HAL_STATUS_BUSY;
+ goto failed;
+ }
+
+ if (!stop_discovery(SCAN_TYPE_LE)) {
+ status = HAL_STATUS_FAILED;
+ goto failed;
+ }
+
+ break;
+ case SCAN_TYPE_DUAL:
+ case SCAN_TYPE_BREDR:
+ if (!stop_discovery(SCAN_TYPE_DUAL)) {
+ status = HAL_STATUS_FAILED;
+ goto failed;
+ }
+
+ if (adapter.exp_discovery_type != SCAN_TYPE_LE)
+ adapter.exp_discovery_type = SCAN_TYPE_NONE;
+
+ break;
+ }
+
+ status = HAL_STATUS_SUCCESS;
+
+failed:
+ ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_BLUETOOTH, HAL_OP_CANCEL_DISCOVERY,
+ status);
+}
+
+static void handle_dut_mode_conf_cmd(const void *buf, uint16_t len)
+{
+ const struct hal_cmd_dut_mode_conf *cmd = buf;
+ char path[FILENAME_MAX];
+ uint8_t status;
+ int fd, ret;
+
+ DBG("enable %u", cmd->enable);
+
+ snprintf(path, sizeof(path), DUT_MODE_FILE, adapter.index);
+
+ fd = open(path, O_WRONLY);
+ if (fd < 0) {
+ status = HAL_STATUS_FAILED;
+ goto failed;
+ }
+
+ if (cmd->enable)
+ ret = write(fd, "1", sizeof("1"));
+ else
+ ret = write(fd, "0", sizeof("0"));
+
+ if (ret < 0)
+ status = HAL_STATUS_FAILED;
+ else
+ status = HAL_STATUS_SUCCESS;
+
+ close(fd);
+
+failed:
+ ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_BLUETOOTH, HAL_OP_DUT_MODE_CONF,
+ status);
+}
+
+static void handle_dut_mode_send_cmd(const void *buf, uint16_t len)
+{
+ const struct hal_cmd_dut_mode_send *cmd = buf;
+
+ if (len != sizeof(*cmd) + cmd->len) {
+ error("Invalid dut mode send cmd, terminating");
+ raise(SIGTERM);
+ return;
+ }
+
+ error("dut_mode_send not supported (cmd opcode %u)", cmd->opcode);
+
+ /* TODO */
+
+ ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_BLUETOOTH, HAL_OP_DUT_MODE_SEND,
+ HAL_STATUS_FAILED);
+}
+
+static void handle_le_test_mode_cmd(const void *buf, uint16_t len)
+{
+ const struct hal_cmd_le_test_mode *cmd = buf;
+
+ if (len != sizeof(*cmd) + cmd->len) {
+ error("Invalid le test mode cmd, terminating");
+ raise(SIGTERM);
+ return;
+ }
+
+ error("le_test_mode not supported (cmd opcode %u)", cmd->opcode);
+
+ /* TODO */
+
+ ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_BLUETOOTH, HAL_OP_LE_TEST_MODE,
+ HAL_STATUS_FAILED);
+}
+
+static void handle_get_connection_state(const void *buf, uint16_t len)
+{
+ const struct hal_cmd_get_connection_state *cmd = buf;
+ struct hal_rsp_get_connection_state rsp;
+ struct device *dev;
+ char address[18];
+ bdaddr_t bdaddr;
+
+ android2bdaddr(cmd->bdaddr, &bdaddr);
+ ba2str(&bdaddr, address);
+
+ dev = find_device_android(cmd->bdaddr);
+ if (dev && dev->connected)
+ rsp.connection_state = 1;
+ else
+ rsp.connection_state = 0;
+
+ DBG("%s %u", address, rsp.connection_state);
+
+ ipc_send_rsp_full(hal_ipc, HAL_SERVICE_ID_BLUETOOTH,
+ HAL_OP_GET_CONNECTION_STATE, sizeof(rsp), &rsp,
+ -1);
+}
+
+static void handle_read_energy_info(const void *buf, uint16_t len)
+{
+ DBG("");
+
+ /* TODO */
+
+ ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_BLUETOOTH, HAL_OP_READ_ENERGY_INFO,
+ HAL_STATUS_UNSUPPORTED);
+}
+
+static const struct ipc_handler cmd_handlers[] = {
+ /* HAL_OP_ENABLE */
+ { handle_enable_cmd, false, 0 },
+ /* HAL_OP_DISABLE */
+ { handle_disable_cmd, false, 0 },
+ /* HAL_OP_GET_ADAPTER_PROPS */
+ { handle_get_adapter_props_cmd, false, 0 },
+ /* HAL_OP_GET_ADAPTER_PROP */
+ { handle_get_adapter_prop_cmd, false,
+ sizeof(struct hal_cmd_get_adapter_prop) },
+ /* HAL_OP_SET_ADAPTER_PROP */
+ { handle_set_adapter_prop_cmd, true,
+ sizeof(struct hal_cmd_set_adapter_prop) },
+ /* HAL_OP_GET_REMOTE_DEVICE_PROPS */
+ { handle_get_remote_device_props_cmd, false,
+ sizeof(struct hal_cmd_get_remote_device_props) },
+ /* HAL_OP_GET_REMOTE_DEVICE_PROP */
+ { handle_get_remote_device_prop_cmd, false,
+ sizeof(struct hal_cmd_get_remote_device_prop) },
+ /* HAL_OP_SET_REMOTE_DEVICE_PROP */
+ { handle_set_remote_device_prop_cmd, true,
+ sizeof(struct hal_cmd_set_remote_device_prop) },
+ /* HAL_OP_GET_REMOTE_SERVICE_REC */
+ { handle_get_remote_service_rec_cmd, false,
+ sizeof(struct hal_cmd_get_remote_service_rec) },
+ /* HAL_OP_GET_REMOTE_SERVICES */
+ { handle_get_remote_services_cmd, false,
+ sizeof(struct hal_cmd_get_remote_services) },
+ /* HAL_OP_START_DISCOVERY */
+ { handle_start_discovery_cmd, false, 0 },
+ /* HAL_OP_CANCEL_DISCOVERY */
+ { handle_cancel_discovery_cmd, false, 0 },
+ /* HAL_OP_CREATE_BOND */
+ { handle_create_bond_cmd, false, sizeof(struct hal_cmd_create_bond) },
+ /* HAL_OP_REMOVE_BOND */
+ { handle_remove_bond_cmd, false, sizeof(struct hal_cmd_remove_bond) },
+ /* HAL_OP_CANCEL_BOND */
+ {handle_cancel_bond_cmd, false, sizeof(struct hal_cmd_cancel_bond) },
+ /* HAL_OP_PIN_REPLY */
+ { handle_pin_reply_cmd, false, sizeof(struct hal_cmd_pin_reply) },
+ /* HAL_OP_SSP_REPLY */
+ { handle_ssp_reply_cmd, false, sizeof(struct hal_cmd_ssp_reply) },
+ /* HAL_OP_DUT_MODE_CONF */
+ { handle_dut_mode_conf_cmd, false,
+ sizeof(struct hal_cmd_dut_mode_conf) },
+ /* HAL_OP_DUT_MODE_SEND */
+ { handle_dut_mode_send_cmd, true,
+ sizeof(struct hal_cmd_dut_mode_send) },
+ /* HAL_OP_LE_TEST_MODE */
+ { handle_le_test_mode_cmd, true, sizeof(struct hal_cmd_le_test_mode) },
+ /* HAL_OP_GET_CONNECTION_STATE */
+ { handle_get_connection_state, false,
+ sizeof(struct hal_cmd_get_connection_state) },
+ /* HAL_OP_READ_ENERGY_INFO */
+ { handle_read_energy_info, false, 0 },
+};
+
+bool bt_bluetooth_register(struct ipc *ipc, uint8_t mode)
+{
+ uint32_t missing_settings;
+
+ DBG("mode 0x%x", mode);
+
+ unpaired_cb_list = queue_new();
+ paired_cb_list = queue_new();
+
+ missing_settings = adapter.current_settings ^
+ adapter.supported_settings;
+
+ switch (mode) {
+ case HAL_MODE_DEFAULT:
+ if (missing_settings & MGMT_SETTING_BREDR)
+ set_mode(MGMT_OP_SET_BREDR, 0x01);
+
+ if (missing_settings & MGMT_SETTING_LE)
+ set_mode(MGMT_OP_SET_LE, 0x01);
+ break;
+ case HAL_MODE_LE:
+ /* Fail if controller does not support LE */
+ if (!(adapter.supported_settings & MGMT_SETTING_LE)) {
+ error("LE Mode not supported by controller");
+ goto failed;
+ }
+
+ /* If LE it is not yet enabled then enable it */
+ if (!(adapter.current_settings & MGMT_SETTING_LE))
+ set_mode(MGMT_OP_SET_LE, 0x01);
+
+ /* Disable BR/EDR if it is enabled */
+ if (adapter.current_settings & MGMT_SETTING_BREDR)
+ set_mode(MGMT_OP_SET_BREDR, 0x00);
+ break;
+ case HAL_MODE_BREDR:
+ /* Fail if controller does not support BR/EDR */
+ if (!(adapter.supported_settings & MGMT_SETTING_BREDR)) {
+ error("BR/EDR Mode not supported");
+ goto failed;
+ }
+
+ /* Enable BR/EDR if it is not enabled */
+ if (missing_settings & MGMT_SETTING_BREDR)
+ set_mode(MGMT_OP_SET_BREDR, 0x01);
+
+ /*
+ * According to Core Spec 4.0 host should not disable LE in
+ * controller if it was enabled (Vol 2. Part E. 7.3.79).
+ * Core Spec 4.1 removed this limitation and chips seem to be
+ * handling this just fine anyway.
+ */
+ if (adapter.current_settings & MGMT_SETTING_LE)
+ set_mode(MGMT_OP_SET_LE, 0x00);
+ break;
+ default:
+ error("Unknown mode 0x%x", mode);
+ goto failed;
+ }
+
+ /* Requested mode is set now, let's enable secure connection */
+ if (missing_settings & MGMT_SETTING_SECURE_CONN)
+ set_mode(MGMT_OP_SET_SECURE_CONN, 0x01);
+
+ /* Set initial default name */
+ if (!adapter.name) {
+ adapter.name = g_strdup(bt_config_get_model());
+ set_adapter_name((uint8_t *)adapter.name, strlen(adapter.name));
+ }
+
+ hal_ipc = ipc;
+
+ ipc_register(hal_ipc, HAL_SERVICE_ID_BLUETOOTH, cmd_handlers,
+ G_N_ELEMENTS(cmd_handlers));
+
+ return true;
+
+failed:
+ queue_destroy(unpaired_cb_list, NULL);
+ unpaired_cb_list = NULL;
+ queue_destroy(paired_cb_list, NULL);
+ paired_cb_list = NULL;
+
+ return false;
+}
+
+void bt_bluetooth_unregister(void)
+{
+ DBG("");
+
+ g_slist_free_full(bonded_devices, (GDestroyNotify) free_device);
+ bonded_devices = NULL;
+
+ g_slist_free_full(cached_devices, (GDestroyNotify) free_device);
+ cached_devices = NULL;
+
+ ipc_unregister(hal_ipc, HAL_SERVICE_ID_CORE);
+ hal_ipc = NULL;
+
+ queue_destroy(unpaired_cb_list, NULL);
+ unpaired_cb_list = NULL;
+
+ queue_destroy(paired_cb_list, NULL);
+ paired_cb_list = NULL;
+}
diff --git a/repo/android/bluetooth.h b/repo/android/bluetooth.h
new file mode 100644
index 0000000..4b17209
--- /dev/null
+++ b/repo/android/bluetooth.h
@@ -0,0 +1,90 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2013-2014 Intel Corporation. All rights reserved.
+ *
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+typedef void (*bt_bluetooth_ready)(int err, const bdaddr_t *addr);
+bool bt_bluetooth_start(int index, bool mgmt_dbg, bt_bluetooth_ready cb);
+
+typedef void (*bt_bluetooth_stopped)(void);
+bool bt_bluetooth_stop(bt_bluetooth_stopped cb);
+
+void bt_bluetooth_cleanup(void);
+
+bool bt_bluetooth_register(struct ipc *ipc, uint8_t mode);
+void bt_bluetooth_unregister(void);
+
+int bt_adapter_add_record(sdp_record_t *rec, uint8_t svc_hint);
+void bt_adapter_remove_record(uint32_t handle);
+
+typedef void (*bt_le_device_found)(const bdaddr_t *addr, int rssi,
+ uint16_t eir_len, const void *eir,
+ bool connectable, bool bonded);
+bool bt_le_register(bt_le_device_found cb);
+void bt_le_unregister(void);
+
+bool bt_le_discovery_start(void);
+
+typedef void (*bt_le_discovery_stopped)(void);
+bool bt_le_discovery_stop(bt_le_discovery_stopped cb);
+
+typedef void (*bt_le_set_advertising_done)(uint8_t status, void *user_data);
+bool bt_le_set_advertising(bool advertising, bt_le_set_advertising_done cb,
+ void *user_data);
+
+uint8_t bt_get_device_android_type(const bdaddr_t *addr);
+bool bt_is_device_le(const bdaddr_t *addr);
+uint8_t bt_device_last_seen_bearer(const bdaddr_t *bdaddr);
+
+const char *bt_get_adapter_name(void);
+bool bt_device_is_bonded(const bdaddr_t *bdaddr);
+bool bt_device_set_uuids(const bdaddr_t *bdaddr, GSList *uuids);
+
+typedef void (*bt_read_device_rssi_done)(uint8_t status, const bdaddr_t *addr,
+ int8_t rssi, void *user_data);
+bool bt_read_device_rssi(const bdaddr_t *addr, bt_read_device_rssi_done cb,
+ void *user_data);
+
+bool bt_get_csrk(const bdaddr_t *addr, bool local, uint8_t key[16],
+ uint32_t *sign_cnt, bool *authenticated);
+
+void bt_update_sign_counter(const bdaddr_t *addr, bool local, uint32_t val);
+
+void bt_store_gatt_ccc(const bdaddr_t *addr, uint16_t value);
+
+uint16_t bt_get_gatt_ccc(const bdaddr_t *addr);
+
+const bdaddr_t *bt_get_id_addr(const bdaddr_t *addr, uint8_t *type);
+
+bool bt_kernel_conn_control(void);
+
+bool bt_auto_connect_add(const bdaddr_t *addr);
+
+void bt_auto_connect_remove(const bdaddr_t *addr);
+
+typedef void (*bt_unpaired_device_cb)(const bdaddr_t *addr);
+bool bt_unpaired_register(bt_unpaired_device_cb cb);
+void bt_unpaired_unregister(bt_unpaired_device_cb cb);
+
+typedef void (*bt_paired_device_cb)(const bdaddr_t *addr);
+bool bt_paired_register(bt_paired_device_cb cb);
+void bt_paired_unregister(bt_paired_device_cb cb);
+bool bt_is_pairing(const bdaddr_t *addr);
diff --git a/repo/android/bluetoothd-snoop.c b/repo/android/bluetoothd-snoop.c
new file mode 100644
index 0000000..7526782
--- /dev/null
+++ b/repo/android/bluetoothd-snoop.c
@@ -0,0 +1,262 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2013-2014 Intel Corporation. All rights reserved.
+ *
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <ctype.h>
+#include <stdlib.h>
+#include <unistd.h>
+#if defined(ANDROID)
+#include <sys/capability.h>
+#endif
+
+#include "lib/bluetooth.h"
+#include "lib/hci.h"
+#include "lib/mgmt.h"
+
+#include "src/shared/mainloop.h"
+#include "src/shared/btsnoop.h"
+#include "src/log.h"
+
+#define DEFAULT_SNOOP_FILE "/sdcard/btsnoop_hci.log"
+
+static struct btsnoop *snoop = NULL;
+static uint8_t monitor_buf[BTSNOOP_MAX_PACKET_SIZE];
+static int monitor_fd = -1;
+
+static void signal_callback(int signum, void *user_data)
+{
+ switch (signum) {
+ case SIGINT:
+ case SIGTERM:
+ mainloop_quit();
+ break;
+ }
+}
+
+static uint32_t get_flags_from_opcode(uint16_t opcode)
+{
+ switch (opcode) {
+ case BTSNOOP_OPCODE_NEW_INDEX:
+ case BTSNOOP_OPCODE_DEL_INDEX:
+ break;
+ case BTSNOOP_OPCODE_COMMAND_PKT:
+ return 0x02;
+ case BTSNOOP_OPCODE_EVENT_PKT:
+ return 0x03;
+ case BTSNOOP_OPCODE_ACL_TX_PKT:
+ return 0x00;
+ case BTSNOOP_OPCODE_ACL_RX_PKT:
+ return 0x01;
+ case BTSNOOP_OPCODE_SCO_TX_PKT:
+ case BTSNOOP_OPCODE_SCO_RX_PKT:
+ break;
+ case BTSNOOP_OPCODE_OPEN_INDEX:
+ case BTSNOOP_OPCODE_CLOSE_INDEX:
+ break;
+ }
+
+ return 0xff;
+}
+
+static void data_callback(int fd, uint32_t events, void *user_data)
+{
+ unsigned char control[32];
+ struct mgmt_hdr hdr;
+ struct msghdr msg;
+ struct iovec iov[2];
+
+ if (events & (EPOLLERR | EPOLLHUP)) {
+ mainloop_remove_fd(monitor_fd);
+ return;
+ }
+
+ iov[0].iov_base = &hdr;
+ iov[0].iov_len = MGMT_HDR_SIZE;
+ iov[1].iov_base = monitor_buf;
+ iov[1].iov_len = sizeof(monitor_buf);
+
+ memset(&msg, 0, sizeof(msg));
+ msg.msg_iov = iov;
+ msg.msg_iovlen = 2;
+ msg.msg_control = control;
+ msg.msg_controllen = sizeof(control);
+
+ while (true) {
+ struct cmsghdr *cmsg;
+ struct timeval *tv = NULL;
+ struct timeval ctv;
+ uint16_t opcode, index, pktlen;
+ uint32_t flags;
+ ssize_t len;
+
+ len = recvmsg(monitor_fd, &msg, MSG_DONTWAIT);
+ if (len < 0)
+ break;
+
+ if (len < MGMT_HDR_SIZE)
+ break;
+
+ for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL;
+ cmsg = CMSG_NXTHDR(&msg, cmsg)) {
+ if (cmsg->cmsg_level != SOL_SOCKET)
+ continue;
+
+ if (cmsg->cmsg_type == SCM_TIMESTAMP) {
+ memcpy(&ctv, CMSG_DATA(cmsg), sizeof(ctv));
+ tv = &ctv;
+ }
+ }
+
+ opcode = btohs(hdr.opcode);
+ index = btohs(hdr.index);
+ pktlen = btohs(hdr.len);
+
+ if (index)
+ continue;
+
+ flags = get_flags_from_opcode(opcode);
+ if (flags != 0xff)
+ btsnoop_write(snoop, tv, flags, monitor_buf, pktlen);
+ }
+}
+
+static int open_monitor(const char *path)
+{
+ struct sockaddr_hci addr;
+ int opt = 1;
+
+ snoop = btsnoop_create(path, BTSNOOP_FORMAT_HCI);
+ if (!snoop)
+ return -1;
+
+ monitor_fd = socket(AF_BLUETOOTH, SOCK_RAW | SOCK_CLOEXEC, BTPROTO_HCI);
+ if (monitor_fd < 0)
+ goto failed;
+
+ memset(&addr, 0, sizeof(addr));
+ addr.hci_family = AF_BLUETOOTH;
+ addr.hci_dev = HCI_DEV_NONE;
+ addr.hci_channel = HCI_CHANNEL_MONITOR;
+
+ if (bind(monitor_fd, (struct sockaddr *) &addr, sizeof(addr)) < 0)
+ goto failed_close;
+
+ if (setsockopt(monitor_fd, SOL_SOCKET, SO_TIMESTAMP, &opt, sizeof(opt))
+ < 0)
+ goto failed_close;
+
+ mainloop_add_fd(monitor_fd, EPOLLIN, data_callback, NULL, NULL);
+
+ return 0;
+
+failed_close:
+ close(monitor_fd);
+ monitor_fd = -1;
+
+failed:
+ btsnoop_unref(snoop);
+ snoop = NULL;
+
+ return -1;
+}
+
+static void close_monitor(void)
+{
+ btsnoop_unref(snoop);
+ snoop = NULL;
+
+ close(monitor_fd);
+ monitor_fd = -1;
+}
+
+static void set_capabilities(void)
+{
+#if defined(ANDROID)
+ struct __user_cap_header_struct header;
+ struct __user_cap_data_struct cap;
+
+ header.version = _LINUX_CAPABILITY_VERSION;
+ header.pid = 0;
+
+ /*
+ * CAP_NET_RAW: for snooping
+ * CAP_DAC_READ_SEARCH: override path search permissions
+ */
+ cap.effective = cap.permitted =
+ CAP_TO_MASK(CAP_NET_RAW) |
+ CAP_TO_MASK(CAP_DAC_READ_SEARCH);
+ cap.inheritable = 0;
+
+ /* TODO: Move to cap_set_proc once bionic support it */
+ if (capset(&header, &cap) < 0)
+ exit(EXIT_FAILURE);
+#endif
+}
+
+int main(int argc, char *argv[])
+{
+ const char *path;
+ sigset_t mask;
+
+ __btd_log_init(NULL, 0);
+
+ DBG("");
+
+ set_capabilities();
+
+ if (argc > 1)
+ path = argv[1];
+ else
+ path = DEFAULT_SNOOP_FILE;
+
+ mainloop_init();
+
+ sigemptyset(&mask);
+ sigaddset(&mask, SIGINT);
+ sigaddset(&mask, SIGTERM);
+
+ mainloop_set_signal(&mask, signal_callback, NULL, NULL);
+
+ if (!strcmp(DEFAULT_SNOOP_FILE, path))
+ rename(DEFAULT_SNOOP_FILE, DEFAULT_SNOOP_FILE ".old");
+
+ if (open_monitor(path) < 0) {
+ error("bluetoothd_snoop: start failed");
+ return EXIT_FAILURE;
+ }
+
+ info("bluetoothd_snoop: started");
+
+ mainloop_run();
+
+ close_monitor();
+
+ info("bluetoothd_snoop: stopped");
+
+ __btd_log_cleanup();
+
+ return EXIT_SUCCESS;
+}
diff --git a/repo/android/bluetoothd-wrapper.c b/repo/android/bluetoothd-wrapper.c
new file mode 100644
index 0000000..7f668da
--- /dev/null
+++ b/repo/android/bluetoothd-wrapper.c
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2014 Intel Corporation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <stdbool.h>
+
+#include <cutils/properties.h>
+
+#include "hal-utils.h"
+
+#define VALGRIND_BIN "/system/bin/valgrind"
+
+#define BLUETOOTHD_BIN "/system/bin/bluetoothd-main"
+
+static void run_valgrind(int debug, int mgmt_dbg)
+{
+ char *prg_argv[7];
+ char *prg_envp[3];
+
+ prg_argv[0] = VALGRIND_BIN;
+ prg_argv[1] = "--leak-check=full";
+ prg_argv[2] = "--track-origins=yes";
+ prg_argv[3] = BLUETOOTHD_BIN;
+ prg_argv[4] = debug ? "-d" : NULL;
+ prg_argv[5] = mgmt_dbg ? "--mgmt-debug" : NULL;
+ prg_argv[6] = NULL;
+
+ prg_envp[0] = "G_SLICE=always-malloc";
+ prg_envp[1] = "G_DEBUG=gc-friendly";
+ prg_envp[2] = NULL;
+
+ execve(prg_argv[0], prg_argv, prg_envp);
+}
+
+static void run_bluetoothd(int debug, int mgmt_dbg)
+{
+ char *prg_argv[4];
+ char *prg_envp[1];
+
+ prg_argv[0] = BLUETOOTHD_BIN;
+ prg_argv[1] = debug ? "-d" : NULL;
+ prg_argv[2] = mgmt_dbg ? "--mgmt-debug" : NULL;
+ prg_argv[3] = NULL;
+
+ prg_envp[0] = NULL;
+
+ execve(prg_argv[0], prg_argv, prg_envp);
+}
+
+int main(int argc, char *argv[])
+{
+ char value[PROPERTY_VALUE_MAX];
+ int debug = 0;
+ int mgmt_dbg = 0;
+
+ if (get_config("debug", value, NULL) > 0 &&
+ (!strcasecmp(value, "true") || atoi(value) > 0))
+ debug = 1;
+
+ if (get_config("mgmtdbg", value, NULL) > 0 &&
+ (!strcasecmp(value, "true") || atoi(value) > 0)) {
+ debug = 1;
+ mgmt_dbg = 1;
+ }
+
+ if (get_config("valgrind", value, NULL) > 0 &&
+ (!strcasecmp(value, "true") || atoi(value) > 0))
+ run_valgrind(debug, mgmt_dbg);
+
+ /*
+ * In case we failed to execute Valgrind, try to run bluetoothd
+ * without it
+ */
+ run_bluetoothd(debug, mgmt_dbg);
+
+ return 0;
+}
diff --git a/repo/android/bluetoothd.te b/repo/android/bluetoothd.te
new file mode 100644
index 0000000..532bfbb
--- /dev/null
+++ b/repo/android/bluetoothd.te
@@ -0,0 +1,47 @@
+type bluetoothd, domain;
+type bluetoothd_exec, exec_type, file_type;
+type bluetoothd_main_exec, exec_type, file_type;
+
+# Start bluetoothd from init
+init_daemon_domain(bluetoothd)
+
+# Data file accesses
+allow bluetoothd bluetooth_data_file:dir w_dir_perms;
+allow bluetoothd bluetooth_data_file:notdevfile_class_set create_file_perms;
+
+allow bluetoothd self:capability { setuid net_admin net_bind_service net_raw };
+allow bluetoothd kernel:system module_request;
+
+# TODO: this may be romoved for userbuild where we don't use bluetoothd_wrapper
+allow bluetoothd bluetoothd_main_exec:file { execute execute_no_trans read open };
+
+# IPC socket communication
+allow bluetoothd self:socket { create_socket_perms accept listen setopt getopt };
+
+# Allow clients to use a socket provided by the bluetooth app.
+allow bluetoothd { bluetooth mediaserver }:unix_stream_socket connectto;
+
+# Allow system app to use sockets and fds
+allow bluetooth bluetoothd:fd use;
+allow bluetooth bluetoothd:unix_stream_socket rw_socket_perms;
+
+# Allow user bluetooth apps to use sockets and fds
+allow bluetoothdomain bluetoothd:fd use;
+allow bluetoothdomain bluetoothd:unix_stream_socket { getopt setopt getattr read write ioctl shutdown };
+
+# Other domains that can create and use bluetooth sockets.
+allow bluetoothdomain self:socket create_socket_perms;
+
+#This we might should put to mediaserver.te ?
+allow mediaserver bluetoothd:fd use;
+allow mediaserver bluetoothd:socket rw_socket_perms;
+
+# needs /system/bin/log access
+allow bluetoothd devpts:chr_file rw_file_perms;
+
+# access to uhid device
+allow bluetoothd uhid_device:chr_file rw_file_perms;
+
+# tethering
+allow bluetoothd self:udp_socket create_socket_perms;
+allow bluetoothd self:tcp_socket { create ioctl };
diff --git a/repo/android/bluetoothd_snoop.te b/repo/android/bluetoothd_snoop.te
new file mode 100644
index 0000000..ef817b5
--- /dev/null
+++ b/repo/android/bluetoothd_snoop.te
@@ -0,0 +1,17 @@
+type bluetoothd_snoop, domain;
+type bluetoothd_snoop_exec, exec_type, file_type;
+
+# Start bluetoothd_snoop from init
+init_daemon_domain(bluetoothd_snoop)
+
+# directory search and read caps
+allow bluetoothd_snoop self:capability dac_read_search;
+# use raw and packet sockets caps
+allow bluetoothd_snoop self:capability net_raw;
+
+# monitor socket access
+allow bluetoothd_snoop self:socket { create bind setopt read };
+
+# sdcard access
+allow bluetoothd_snoop fuse:dir w_dir_perms;
+allow bluetoothd_snoop fuse:file create_file_perms;
diff --git a/repo/android/client/haltest.c b/repo/android/client/haltest.c
new file mode 100644
index 0000000..92cd811
--- /dev/null
+++ b/repo/android/client/haltest.c
@@ -0,0 +1,477 @@
+/*
+ * Copyright (C) 2013 Intel Corporation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdlib.h>
+#include <stdbool.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <unistd.h>
+#include <poll.h>
+#include <unistd.h>
+#include <getopt.h>
+
+#include "if-main.h"
+#include "terminal.h"
+#include "pollhandler.h"
+#include "history.h"
+
+static void process_line(char *line_buffer);
+
+const struct interface *interfaces[] = {
+ &audio_if,
+ &sco_if,
+ &bluetooth_if,
+ &av_if,
+ &rc_if,
+ &gatt_if,
+ &gatt_client_if,
+ &gatt_server_if,
+ &hf_if,
+ &hh_if,
+ &pan_if,
+ &hl_if,
+ &sock_if,
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+ &hf_client_if,
+ &mce_if,
+ &ctrl_rc_if,
+ &av_sink_if,
+#endif
+ NULL
+};
+
+static struct method commands[];
+
+struct method *get_method(struct method *methods, const char *name)
+{
+ while (strcmp(methods->name, "") != 0) {
+ if (strcmp(methods->name, name) == 0)
+ return methods;
+ methods++;
+ }
+
+ return NULL;
+}
+
+/* function returns interface of given name or NULL if not found */
+const struct interface *get_interface(const char *name)
+{
+ int i;
+
+ for (i = 0; interfaces[i] != NULL; ++i) {
+ if (strcmp(interfaces[i]->name, name) == 0)
+ break;
+ }
+
+ return interfaces[i];
+}
+
+int haltest_error(const char *format, ...)
+{
+ va_list args;
+ int ret;
+ va_start(args, format);
+ ret = terminal_vprint(format, args);
+ va_end(args);
+ return ret;
+}
+
+int haltest_info(const char *format, ...)
+{
+ va_list args;
+ int ret;
+ va_start(args, format);
+ ret = terminal_vprint(format, args);
+ va_end(args);
+ return ret;
+}
+
+int haltest_warn(const char *format, ...)
+{
+ va_list args;
+ int ret;
+ va_start(args, format);
+ ret = terminal_vprint(format, args);
+ va_end(args);
+ return ret;
+}
+
+static void help_print_interface(const struct interface *i)
+{
+ struct method *m;
+
+ for (m = i->methods; strcmp(m->name, "") != 0; m++)
+ haltest_info("%s %s %s\n", i->name, m->name,
+ (m->help ? m->help : ""));
+}
+
+/* Help completion */
+static void help_c(int argc, const char **argv, enum_func *enum_func,
+ void **user)
+{
+ if (argc == 2)
+ *enum_func = interface_name;
+}
+
+/* Help execution */
+static void help_p(int argc, const char **argv)
+{
+ const struct method *m = commands;
+ const struct interface **ip = interfaces;
+ const struct interface *i;
+
+ if (argc == 1) {
+ terminal_print("haltest allows to call Android HAL methods.\n");
+ terminal_print("\nAvailable commands:\n");
+ while (0 != strcmp(m->name, "")) {
+ terminal_print("\t%s %s\n", m->name,
+ (m->help ? m->help : ""));
+ m++;
+ }
+
+ terminal_print("\nAvailable interfaces to use:\n");
+ while (NULL != *ip) {
+ terminal_print("\t%s\n", (*ip)->name);
+ ip++;
+ }
+
+ terminal_print("\nTo get help on methods for each interface type:\n");
+ terminal_print("\n\thelp <inerface>\n");
+ terminal_print("\nBasic scenario:\n\tbluetooth init\n");
+ terminal_print("\tbluetooth enable\n\tbluetooth start_discovery\n");
+ terminal_print("\tbluetooth get_profile_interface handsfree\n");
+ terminal_print("\thandsfree init\n\n");
+ return;
+ }
+
+ i = get_interface(argv[1]);
+ if (i == NULL) {
+ haltest_error("No such interface\n");
+ return;
+ }
+
+ help_print_interface(i);
+}
+
+/* quit/exit execution */
+static void quit_p(int argc, const char **argv)
+{
+ char cleanup_audio[] = "audio cleanup";
+
+ close_hw_bt_dev();
+ process_line(cleanup_audio);
+
+ exit(0);
+}
+
+static int fd_stack[10];
+static int fd_stack_pointer = 0;
+
+static void stdin_handler(struct pollfd *pollfd);
+
+static void process_file(const char *name)
+{
+ int fd = open(name, O_RDONLY);
+
+ if (fd < 0) {
+ haltest_error("Can't open file: %s for reading\n", name);
+ return;
+ }
+
+ if (fd_stack_pointer >= 10) {
+ haltest_error("To many open files\n");
+ close(fd);
+ return;
+ }
+
+ fd_stack[fd_stack_pointer++] = fd;
+ poll_unregister_fd(fd_stack[fd_stack_pointer - 2], stdin_handler);
+ poll_register_fd(fd_stack[fd_stack_pointer - 1], POLLIN, stdin_handler);
+}
+
+static void source_p(int argc, const char **argv)
+{
+ if (argc < 2) {
+ haltest_error("No file specified");
+ return;
+ }
+
+ process_file(argv[1]);
+}
+
+/* Commands available without interface */
+static struct method commands[] = {
+ STD_METHODCH(help, "[<interface>]"),
+ STD_METHOD(quit),
+ METHOD("exit", quit_p, NULL, NULL),
+ STD_METHODH(source, "<file>"),
+ END_METHOD
+};
+
+/* Gets comman by name */
+struct method *get_command(const char *name)
+{
+ return get_method(commands, name);
+}
+
+/* Function to enumerate interface names */
+const char *interface_name(void *v, int i)
+{
+ return interfaces[i] ? interfaces[i]->name : NULL;
+}
+
+/* Function to enumerate command and interface names */
+const char *command_name(void *v, int i)
+{
+ int cmd_cnt = NELEM(commands);
+
+ if (i >= cmd_cnt)
+ return interface_name(v, i - cmd_cnt);
+ else
+ return commands[i].name;
+}
+
+/*
+ * This function changes input parameter line_buffer so it has
+ * null termination after each token (due to strtok)
+ * Output argv is filled with pointers to arguments
+ * returns number of tokens parsed - argc
+ */
+static int command_line_to_argv(char *line_buffer, char *argv[], int argv_size)
+{
+ static const char *token_breaks = "\r\n\t ";
+ char *token;
+ int argc = 0;
+
+ token = strtok(line_buffer, token_breaks);
+ while (token != NULL && argc < (int) argv_size) {
+ argv[argc++] = token;
+ token = strtok(NULL, token_breaks);
+ }
+
+ return argc;
+}
+
+static void process_line(char *line_buffer)
+{
+ char *argv[50];
+ int argc;
+ int i = 0;
+ struct method *m;
+
+ argc = command_line_to_argv(line_buffer, argv, 50);
+ if (argc < 1)
+ return;
+
+ while (interfaces[i] != NULL) {
+ if (strcmp(interfaces[i]->name, argv[0])) {
+ i++;
+ continue;
+ }
+
+ if (argc < 2 || strcmp(argv[1], "?") == 0) {
+ help_print_interface(interfaces[i]);
+ return;
+ }
+
+ m = get_method(interfaces[i]->methods, argv[1]);
+ if (m != NULL) {
+ m->func(argc, (const char **) argv);
+ return;
+ }
+
+ haltest_error("No function %s found\n", argv[1]);
+ return;
+ }
+ /* No interface, try commands */
+ m = get_command(argv[0]);
+ if (m == NULL)
+ haltest_error("No such command %s\n", argv[0]);
+ else
+ m->func(argc, (const char **) argv);
+}
+
+/* called when there is something on stdin */
+static void stdin_handler(struct pollfd *pollfd)
+{
+ char buf[10];
+
+ if (pollfd->revents & POLLIN) {
+ int count = read(fd_stack[fd_stack_pointer - 1], buf, 10);
+
+ if (count > 0) {
+ int i;
+
+ for (i = 0; i < count; ++i)
+ terminal_process_char(buf[i], process_line);
+ return;
+ }
+ }
+
+ if (fd_stack_pointer > 1)
+ poll_register_fd(fd_stack[fd_stack_pointer - 2], POLLIN,
+ stdin_handler);
+ if (fd_stack_pointer > 0) {
+ poll_unregister_fd(fd_stack[--fd_stack_pointer], stdin_handler);
+
+ if (fd_stack[fd_stack_pointer])
+ close(fd_stack[fd_stack_pointer]);
+ }
+}
+
+static void usage(void)
+{
+ printf("haltest Android Bluetooth HAL testing tool\n"
+ "Usage:\n");
+ printf("\thaltest [options]\n");
+ printf("options:\n"
+ "\t-i --ivi Initialize only IVI interfaces\n"
+ "\t-n, --no-init Don't initialize any interfaces\n"
+ "\t-v --version Print version\n"
+ "\t-h, --help Show help options\n");
+}
+
+static void print_version(void)
+{
+ printf("haltest version %s\n", VERSION);
+}
+
+static const struct option main_options[] = {
+ { "no-init", no_argument, NULL, 'n' },
+ { "ivi", no_argument, NULL, 'i' },
+ { "help", no_argument, NULL, 'h' },
+ { "version", no_argument, NULL, 'v' },
+ { NULL }
+};
+
+static bool no_init = false;
+static bool ivi_only = false;
+
+static void parse_command_line(int argc, char *argv[])
+{
+ for (;;) {
+ int opt;
+
+ opt = getopt_long(argc, argv, "inhv", main_options, NULL);
+ if (opt < 0)
+ break;
+
+ switch (opt) {
+ case 'n':
+ no_init = true;
+ break;
+ case 'i':
+ ivi_only = true;
+ break;
+ case 'h':
+ usage();
+ exit(0);
+ case 'v':
+ print_version();
+ exit(0);
+ default:
+ putchar('\n');
+ exit(-1);
+ break;
+ }
+ }
+}
+
+static const char * const interface_names[] = {
+ BT_PROFILE_HANDSFREE_ID,
+ BT_PROFILE_ADVANCED_AUDIO_ID,
+ BT_PROFILE_AV_RC_ID,
+ BT_PROFILE_HEALTH_ID,
+ BT_PROFILE_HIDHOST_ID,
+ BT_PROFILE_PAN_ID,
+ BT_PROFILE_GATT_ID,
+ BT_PROFILE_SOCKETS_ID,
+ NULL
+};
+
+static const char * const ivi_interface_inames[] = {
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+ BT_PROFILE_HANDSFREE_CLIENT_ID,
+ BT_PROFILE_MAP_CLIENT_ID,
+ BT_PROFILE_AV_RC_CTRL_ID,
+ BT_PROFILE_ADVANCED_AUDIO_SINK_ID,
+#endif
+ NULL
+};
+
+static void init(const char * const *inames)
+{
+ const struct method *m;
+ const char *argv[4];
+ char init_audio[] = "audio init";
+ char init_sco[] = "sco init";
+ char init_bt[] = "bluetooth init";
+ uint32_t i;
+
+ process_line(init_audio);
+ process_line(init_sco);
+ process_line(init_bt);
+
+ m = get_interface_method("bluetooth", "get_profile_interface");
+
+ while (*inames) {
+ argv[2] = *inames;
+ m->func(3, argv);
+ inames++;
+ }
+
+ /* Init what is available to init */
+ for (i = 3; i < NELEM(interfaces) - 1; ++i) {
+ m = get_interface_method(interfaces[i]->name, "init");
+ if (m != NULL)
+ m->func(2, argv);
+ }
+}
+
+int main(int argc, char **argv)
+{
+ struct stat rcstat;
+
+ parse_command_line(argc, argv);
+
+ terminal_setup();
+
+ if (!no_init) {
+ if (ivi_only)
+ init(ivi_interface_inames);
+ else
+ init(interface_names);
+ }
+
+ history_restore(".haltest_history");
+
+ fd_stack[fd_stack_pointer++] = 0;
+ /* Register command line handler */
+ poll_register_fd(0, POLLIN, stdin_handler);
+
+ if (stat(".haltestrc", &rcstat) == 0 && (rcstat.st_mode & S_IFREG) != 0)
+ process_file(".haltestrc");
+
+ poll_dispatch_loop();
+
+ return 0;
+}
diff --git a/repo/android/client/history.c b/repo/android/client/history.c
new file mode 100644
index 0000000..ca4664c
--- /dev/null
+++ b/repo/android/client/history.c
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2013 Intel Corporation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <ctype.h>
+
+#include "history.h"
+
+/* Very simple history storage for easy usage of tool */
+
+#define HISTORY_DEPTH 40
+#define LINE_SIZE 200
+static char lines[HISTORY_DEPTH][LINE_SIZE];
+static int last_line = 0;
+static int history_size = 0;
+
+/* TODO: Storing history not implemented yet */
+void history_store(const char *filename)
+{
+}
+
+/* Restoring history from file */
+void history_restore(const char *filename)
+{
+ char line[1000];
+ FILE *f = fopen(filename, "rt");
+
+ if (f == NULL)
+ return;
+
+ for (;;) {
+ if (fgets(line, 1000, f) != NULL) {
+ int l = strlen(line);
+
+ while (l > 0 && isspace(line[--l]))
+ line[l] = 0;
+
+ if (l > 0)
+ history_add_line(line);
+ } else
+ break;
+ }
+
+ fclose(f);
+}
+
+/* Add new line to history buffer */
+void history_add_line(const char *line)
+{
+ if (line == NULL || strlen(line) == 0)
+ return;
+
+ if (strcmp(line, lines[last_line]) == 0)
+ return;
+
+ last_line = (last_line + 1) % HISTORY_DEPTH;
+ strncpy(&lines[last_line][0], line, LINE_SIZE - 1);
+ if (history_size < HISTORY_DEPTH)
+ history_size++;
+}
+
+/*
+ * Get n-th line from history
+ * 0 - means latest
+ * -1 - means oldest
+ * return -1 if there is no such line
+ */
+int history_get_line(int n, char *buf, int buf_size)
+{
+ if (n == -1)
+ n = history_size - 1;
+
+ if (n >= history_size || buf_size == 0 || n < 0)
+ return -1;
+
+ strncpy(buf,
+ &lines[(HISTORY_DEPTH + last_line - n) % HISTORY_DEPTH][0],
+ buf_size - 1);
+ buf[buf_size - 1] = 0;
+
+ return n;
+}
diff --git a/repo/android/client/history.h b/repo/android/client/history.h
new file mode 100644
index 0000000..26085b5
--- /dev/null
+++ b/repo/android/client/history.h
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2013 Intel Corporation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+void history_store(const char *filename);
+void history_restore(const char *filename);
+void history_add_line(const char *line);
+int history_get_line(int n, char *buf, int buf_size);
diff --git a/repo/android/client/if-audio.c b/repo/android/client/if-audio.c
new file mode 100644
index 0000000..65e2f2f
--- /dev/null
+++ b/repo/android/client/if-audio.c
@@ -0,0 +1,535 @@
+/*
+ * Copyright (C) 2014 Intel Corporation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include <pthread.h>
+#include <unistd.h>
+#include <math.h>
+
+#include "if-main.h"
+#include "../hal-utils.h"
+
+audio_hw_device_t *if_audio = NULL;
+static struct audio_stream_out *stream_out = NULL;
+
+static size_t buffer_size = 0;
+static pthread_t play_thread = 0;
+static pthread_mutex_t outstream_mutex = PTHREAD_MUTEX_INITIALIZER;
+static pthread_mutex_t state_mutex = PTHREAD_MUTEX_INITIALIZER;
+
+enum state {
+ STATE_STOPPED,
+ STATE_STOPPING,
+ STATE_PLAYING,
+ STATE_SUSPENDED,
+ STATE_MAX
+};
+
+SINTMAP(audio_channel_mask_t, -1, "(AUDIO_CHANNEL_INVALID)")
+ DELEMENT(AUDIO_CHANNEL_OUT_FRONT_LEFT),
+ DELEMENT(AUDIO_CHANNEL_OUT_FRONT_RIGHT),
+ DELEMENT(AUDIO_CHANNEL_OUT_FRONT_CENTER),
+ DELEMENT(AUDIO_CHANNEL_OUT_LOW_FREQUENCY),
+ DELEMENT(AUDIO_CHANNEL_OUT_BACK_LEFT),
+ DELEMENT(AUDIO_CHANNEL_OUT_BACK_RIGHT),
+ DELEMENT(AUDIO_CHANNEL_OUT_FRONT_LEFT_OF_CENTER),
+ DELEMENT(AUDIO_CHANNEL_OUT_FRONT_RIGHT_OF_CENTER),
+ DELEMENT(AUDIO_CHANNEL_OUT_BACK_CENTER),
+ DELEMENT(AUDIO_CHANNEL_OUT_SIDE_LEFT),
+ DELEMENT(AUDIO_CHANNEL_OUT_SIDE_RIGHT),
+ DELEMENT(AUDIO_CHANNEL_OUT_TOP_CENTER),
+ DELEMENT(AUDIO_CHANNEL_OUT_TOP_FRONT_LEFT),
+ DELEMENT(AUDIO_CHANNEL_OUT_TOP_FRONT_CENTER),
+ DELEMENT(AUDIO_CHANNEL_OUT_TOP_FRONT_RIGHT),
+ DELEMENT(AUDIO_CHANNEL_OUT_TOP_BACK_LEFT),
+ DELEMENT(AUDIO_CHANNEL_OUT_TOP_BACK_CENTER),
+ DELEMENT(AUDIO_CHANNEL_OUT_TOP_BACK_RIGHT),
+ DELEMENT(AUDIO_CHANNEL_OUT_MONO),
+ DELEMENT(AUDIO_CHANNEL_OUT_STEREO),
+ DELEMENT(AUDIO_CHANNEL_OUT_QUAD),
+#if ANDROID_VERSION < PLATFORM_VER(5, 0, 0)
+ DELEMENT(AUDIO_CHANNEL_OUT_SURROUND),
+#else
+ DELEMENT(AUDIO_CHANNEL_OUT_QUAD_BACK),
+ DELEMENT(AUDIO_CHANNEL_OUT_QUAD_SIDE),
+ DELEMENT(AUDIO_CHANNEL_OUT_5POINT1_BACK),
+ DELEMENT(AUDIO_CHANNEL_OUT_5POINT1_SIDE),
+#endif
+ DELEMENT(AUDIO_CHANNEL_OUT_5POINT1),
+ DELEMENT(AUDIO_CHANNEL_OUT_7POINT1),
+ DELEMENT(AUDIO_CHANNEL_OUT_ALL),
+ DELEMENT(AUDIO_CHANNEL_OUT_FRONT_LEFT),
+ DELEMENT(AUDIO_CHANNEL_OUT_FRONT_LEFT),
+ DELEMENT(AUDIO_CHANNEL_OUT_FRONT_LEFT),
+ DELEMENT(AUDIO_CHANNEL_OUT_FRONT_LEFT),
+ DELEMENT(AUDIO_CHANNEL_OUT_FRONT_LEFT),
+ENDMAP
+
+SINTMAP(audio_format_t, -1, "(AUDIO_FORMAT_INVALID)")
+ DELEMENT(AUDIO_FORMAT_DEFAULT),
+ DELEMENT(AUDIO_FORMAT_PCM),
+ DELEMENT(AUDIO_FORMAT_MP3),
+ DELEMENT(AUDIO_FORMAT_AMR_NB),
+ DELEMENT(AUDIO_FORMAT_AMR_WB),
+ DELEMENT(AUDIO_FORMAT_AAC),
+ DELEMENT(AUDIO_FORMAT_HE_AAC_V1),
+ DELEMENT(AUDIO_FORMAT_HE_AAC_V2),
+ DELEMENT(AUDIO_FORMAT_VORBIS),
+ DELEMENT(AUDIO_FORMAT_MAIN_MASK),
+ DELEMENT(AUDIO_FORMAT_SUB_MASK),
+ DELEMENT(AUDIO_FORMAT_PCM_16_BIT),
+ DELEMENT(AUDIO_FORMAT_PCM_8_BIT),
+ DELEMENT(AUDIO_FORMAT_PCM_32_BIT),
+ DELEMENT(AUDIO_FORMAT_PCM_8_24_BIT),
+ENDMAP
+
+static int current_state = STATE_STOPPED;
+
+#define SAMPLERATE 44100
+static short sample[SAMPLERATE];
+static uint16_t sample_pos;
+
+static void init_p(int argc, const char **argv)
+{
+ int err;
+ const hw_module_t *module;
+ audio_hw_device_t *device;
+
+ err = hw_get_module_by_class(AUDIO_HARDWARE_MODULE_ID,
+ AUDIO_HARDWARE_MODULE_ID_A2DP, &module);
+ if (err) {
+ haltest_error("hw_get_module_by_class returned %d\n", err);
+ return;
+ }
+
+ err = audio_hw_device_open(module, &device);
+ if (err) {
+ haltest_error("audio_hw_device_open returned %d\n", err);
+ return;
+ }
+
+ if_audio = device;
+}
+
+static int feed_from_file(short *buffer, void *data)
+{
+ FILE *in = data;
+ return fread(buffer, buffer_size, 1, in);
+}
+
+static int feed_from_generator(short *buffer, void *data)
+{
+ size_t i = 0;
+ float volume = 0.5;
+ float *freq = data;
+ float f = 1;
+
+ if (freq)
+ f = *freq;
+
+ /* buffer_size is in bytes but we are using buffer of shorts (2 bytes)*/
+ for (i = 0; i < buffer_size / sizeof(*buffer) - 1;) {
+ if (sample_pos >= SAMPLERATE)
+ sample_pos = sample_pos % SAMPLERATE;
+
+ /* Use the same sample for both channels */
+ buffer[i++] = sample[sample_pos] * volume;
+ buffer[i++] = sample[sample_pos] * volume;
+
+ sample_pos += f;
+ }
+
+ return buffer_size;
+}
+
+static void prepare_sample(void)
+{
+ int x;
+ double s;
+
+ haltest_info("Preparing audio sample...\n");
+
+ for (x = 0; x < SAMPLERATE; x++) {
+ /* prepare sinusoidal 1Hz sample */
+ s = (2.0 * 3.14159) * ((double)x / SAMPLERATE);
+ s = sin(s);
+
+ /* remap <-1, 1> to signed 16bit PCM range */
+ sample[x] = s * 32767;
+ }
+
+ sample_pos = 0;
+}
+
+static void *playback_thread(void *data)
+{
+ int (*filbuff_cb) (short*, void*);
+ short buffer[buffer_size / sizeof(short)];
+ size_t len = 0;
+ ssize_t w_len = 0;
+ FILE *in = data;
+ void *cb_data = NULL;
+ float freq = 440.0;
+
+ /* Use file or fall back to generator */
+ if (in) {
+ filbuff_cb = feed_from_file;
+ cb_data = in;
+ } else {
+ prepare_sample();
+ filbuff_cb = feed_from_generator;
+ cb_data = &freq;
+ }
+
+ pthread_mutex_lock(&state_mutex);
+ current_state = STATE_PLAYING;
+ pthread_mutex_unlock(&state_mutex);
+
+ do {
+ pthread_mutex_lock(&state_mutex);
+
+ if (current_state == STATE_STOPPING) {
+ pthread_mutex_unlock(&state_mutex);
+ break;
+ } else if (current_state == STATE_SUSPENDED) {
+ pthread_mutex_unlock(&state_mutex);
+ usleep(500);
+ continue;
+ }
+
+ pthread_mutex_unlock(&state_mutex);
+
+ len = filbuff_cb(buffer, cb_data);
+
+ pthread_mutex_lock(&outstream_mutex);
+ if (!stream_out) {
+ pthread_mutex_unlock(&outstream_mutex);
+ break;
+ }
+
+ w_len = stream_out->write(stream_out, buffer, buffer_size);
+ pthread_mutex_unlock(&outstream_mutex);
+ } while (len && w_len > 0);
+
+ if (in)
+ fclose(in);
+
+ pthread_mutex_lock(&state_mutex);
+ current_state = STATE_STOPPED;
+ pthread_mutex_unlock(&state_mutex);
+
+ haltest_info("Done playing.\n");
+
+ return NULL;
+}
+
+static void play_p(int argc, const char **argv)
+{
+ const char *fname = NULL;
+ FILE *in = NULL;
+
+ RETURN_IF_NULL(if_audio);
+ RETURN_IF_NULL(stream_out);
+
+ if (argc < 3) {
+ haltest_error("Invalid audio file path.\n");
+ haltest_info("Using sound generator.\n");
+ } else {
+ fname = argv[2];
+ in = fopen(fname, "r");
+
+ if (in == NULL) {
+ haltest_error("Cannot open file: %s\n", fname);
+ return;
+ }
+ haltest_info("Playing file: %s\n", fname);
+ }
+
+ if (buffer_size == 0) {
+ haltest_error("Invalid buffer size. Was stream_out opened?\n");
+ goto fail;
+ }
+
+ pthread_mutex_lock(&state_mutex);
+ if (current_state != STATE_STOPPED) {
+ haltest_error("Already playing or stream suspended!\n");
+ pthread_mutex_unlock(&state_mutex);
+ goto fail;
+ }
+ pthread_mutex_unlock(&state_mutex);
+
+ if (pthread_create(&play_thread, NULL, playback_thread, in) != 0) {
+ haltest_error("Cannot create playback thread!\n");
+ goto fail;
+ }
+
+ return;
+fail:
+ if (in)
+ fclose(in);
+}
+
+static void stop_p(int argc, const char **argv)
+{
+ pthread_mutex_lock(&state_mutex);
+ if (current_state == STATE_STOPPED || current_state == STATE_STOPPING) {
+ pthread_mutex_unlock(&state_mutex);
+ return;
+ }
+
+ current_state = STATE_STOPPING;
+ pthread_mutex_unlock(&state_mutex);
+
+ pthread_mutex_lock(&outstream_mutex);
+ stream_out->common.standby(&stream_out->common);
+ pthread_mutex_unlock(&outstream_mutex);
+}
+
+static void open_output_stream_p(int argc, const char **argv)
+{
+ int err;
+
+ RETURN_IF_NULL(if_audio);
+
+ pthread_mutex_lock(&state_mutex);
+ if (current_state == STATE_PLAYING) {
+ haltest_error("Already playing!\n");
+ pthread_mutex_unlock(&state_mutex);
+ return;
+ }
+ pthread_mutex_unlock(&state_mutex);
+
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+ err = if_audio->open_output_stream(if_audio,
+ 0,
+ AUDIO_DEVICE_OUT_ALL_A2DP,
+ AUDIO_OUTPUT_FLAG_NONE,
+ NULL,
+ &stream_out, NULL);
+#else
+ err = if_audio->open_output_stream(if_audio,
+ 0,
+ AUDIO_DEVICE_OUT_ALL_A2DP,
+ AUDIO_OUTPUT_FLAG_NONE,
+ NULL,
+ &stream_out);
+#endif
+ if (err < 0) {
+ haltest_error("open output stream returned %d\n", err);
+ return;
+ }
+
+ buffer_size = stream_out->common.get_buffer_size(&stream_out->common);
+ if (buffer_size == 0)
+ haltest_error("Invalid buffer size received!\n");
+ else
+ haltest_info("Using buffer size: %zu\n", buffer_size);
+}
+
+static void close_output_stream_p(int argc, const char **argv)
+{
+ RETURN_IF_NULL(if_audio);
+ RETURN_IF_NULL(stream_out);
+
+ stop_p(argc, argv);
+
+ haltest_info("Waiting for playback thread...\n");
+ pthread_join(play_thread, NULL);
+
+ if_audio->close_output_stream(if_audio, stream_out);
+
+ stream_out = NULL;
+ buffer_size = 0;
+}
+
+static void cleanup_p(int argc, const char **argv)
+{
+ int err;
+
+ RETURN_IF_NULL(if_audio);
+
+ pthread_mutex_lock(&state_mutex);
+ if (current_state != STATE_STOPPED) {
+ pthread_mutex_unlock(&state_mutex);
+ close_output_stream_p(0, NULL);
+ } else {
+ pthread_mutex_unlock(&state_mutex);
+ }
+
+ err = audio_hw_device_close(if_audio);
+ if (err < 0) {
+ haltest_error("audio_hw_device_close returned %d\n", err);
+ return;
+ }
+
+ if_audio = NULL;
+}
+
+static void suspend_p(int argc, const char **argv)
+{
+ RETURN_IF_NULL(if_audio);
+ RETURN_IF_NULL(stream_out);
+
+ pthread_mutex_lock(&state_mutex);
+ if (current_state != STATE_PLAYING) {
+ pthread_mutex_unlock(&state_mutex);
+ return;
+ }
+ current_state = STATE_SUSPENDED;
+ pthread_mutex_unlock(&state_mutex);
+
+ pthread_mutex_lock(&outstream_mutex);
+ stream_out->common.standby(&stream_out->common);
+ pthread_mutex_unlock(&outstream_mutex);
+}
+
+static void resume_p(int argc, const char **argv)
+{
+ RETURN_IF_NULL(if_audio);
+ RETURN_IF_NULL(stream_out);
+
+ pthread_mutex_lock(&state_mutex);
+ if (current_state == STATE_SUSPENDED)
+ current_state = STATE_PLAYING;
+ pthread_mutex_unlock(&state_mutex);
+}
+
+static void get_latency_p(int argc, const char **argv)
+{
+ RETURN_IF_NULL(if_audio);
+ RETURN_IF_NULL(stream_out);
+
+ haltest_info("Output audio stream latency: %d\n",
+ stream_out->get_latency(stream_out));
+}
+
+static void get_buffer_size_p(int argc, const char **argv)
+{
+ RETURN_IF_NULL(if_audio);
+ RETURN_IF_NULL(stream_out);
+
+ haltest_info("Current output buffer size: %zu\n",
+ stream_out->common.get_buffer_size(&stream_out->common));
+}
+
+static void get_channels_p(int argc, const char **argv)
+{
+ audio_channel_mask_t channels;
+
+ RETURN_IF_NULL(if_audio);
+ RETURN_IF_NULL(stream_out);
+
+ channels = stream_out->common.get_channels(&stream_out->common);
+
+ haltest_info("Channels: %s\n", audio_channel_mask_t2str(channels));
+}
+
+static void get_format_p(int argc, const char **argv)
+{
+ audio_format_t format;
+
+ RETURN_IF_NULL(if_audio);
+ RETURN_IF_NULL(stream_out);
+
+ format = stream_out->common.get_format(&stream_out->common);
+
+ haltest_info("Format: %s\n", audio_format_t2str(format));
+}
+
+static void get_sample_rate_p(int argc, const char **argv)
+{
+ RETURN_IF_NULL(if_audio);
+ RETURN_IF_NULL(stream_out);
+
+ haltest_info("Current sample rate: %d\n",
+ stream_out->common.get_sample_rate(&stream_out->common));
+}
+
+static void get_parameters_p(int argc, const char **argv)
+{
+ const char *keystr;
+
+ RETURN_IF_NULL(if_audio);
+ RETURN_IF_NULL(stream_out);
+
+ if (argc < 3) {
+ haltest_info("No keys given.\n");
+ keystr = "";
+ } else {
+ keystr = argv[2];
+ }
+
+ haltest_info("Current parameters: %s\n",
+ stream_out->common.get_parameters(&stream_out->common,
+ keystr));
+}
+
+static void set_parameters_p(int argc, const char **argv)
+{
+ RETURN_IF_NULL(if_audio);
+ RETURN_IF_NULL(stream_out);
+
+ if (argc < 3) {
+ haltest_error("No key=value; pairs given.\n");
+ return;
+ }
+
+ stream_out->common.set_parameters(&stream_out->common, argv[2]);
+}
+
+static void set_sample_rate_p(int argc, const char **argv)
+{
+ RETURN_IF_NULL(if_audio);
+ RETURN_IF_NULL(stream_out);
+
+ if (argc < 3)
+ return;
+
+ stream_out->common.set_sample_rate(&stream_out->common, atoi(argv[2]));
+}
+
+static void init_check_p(int argc, const char **argv)
+{
+ RETURN_IF_NULL(if_audio);
+
+ haltest_info("Init check result: %d\n", if_audio->init_check(if_audio));
+}
+
+static struct method methods[] = {
+ STD_METHOD(init),
+ STD_METHOD(cleanup),
+ STD_METHOD(open_output_stream),
+ STD_METHOD(close_output_stream),
+ STD_METHODH(play, "<path to pcm file>"),
+ STD_METHOD(stop),
+ STD_METHOD(suspend),
+ STD_METHOD(resume),
+ STD_METHOD(get_latency),
+ STD_METHOD(get_buffer_size),
+ STD_METHOD(get_channels),
+ STD_METHOD(get_format),
+ STD_METHOD(get_sample_rate),
+ STD_METHODH(get_parameters, "<A2dpSuspended;closing>"),
+ STD_METHODH(set_parameters, "<A2dpSuspended=value;closing=value>"),
+ STD_METHODH(set_sample_rate, "<sample rate>"),
+ STD_METHOD(init_check),
+ END_METHOD
+};
+
+const struct interface audio_if = {
+ .name = "audio",
+ .methods = methods
+};
diff --git a/repo/android/client/if-av-sink.c b/repo/android/client/if-av-sink.c
new file mode 100644
index 0000000..3087dcf
--- /dev/null
+++ b/repo/android/client/if-av-sink.c
@@ -0,0 +1,139 @@
+/*
+ * Copyright (C) 2014 Intel Corporation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include "if-main.h"
+#include "../hal-utils.h"
+
+const btav_interface_t *if_av_sink = NULL;
+
+SINTMAP(btav_connection_state_t, -1, "(unknown)")
+ DELEMENT(BTAV_CONNECTION_STATE_DISCONNECTED),
+ DELEMENT(BTAV_CONNECTION_STATE_CONNECTING),
+ DELEMENT(BTAV_CONNECTION_STATE_CONNECTED),
+ DELEMENT(BTAV_CONNECTION_STATE_DISCONNECTING),
+ENDMAP
+
+SINTMAP(btav_audio_state_t, -1, "(unknown)")
+ DELEMENT(BTAV_AUDIO_STATE_REMOTE_SUSPEND),
+ DELEMENT(BTAV_AUDIO_STATE_STOPPED),
+ DELEMENT(BTAV_AUDIO_STATE_STARTED),
+ENDMAP
+
+static char last_addr[MAX_ADDR_STR_LEN];
+
+static void connection_state(btav_connection_state_t state,
+ bt_bdaddr_t *bd_addr)
+{
+ haltest_info("(sink) %s: connection_state=%s remote_bd_addr=%s\n",
+ __func__, btav_connection_state_t2str(state),
+ bt_bdaddr_t2str(bd_addr, last_addr));
+}
+
+static void audio_state(btav_audio_state_t state, bt_bdaddr_t *bd_addr)
+{
+ haltest_info("(sink) %s: audio_state=%s remote_bd_addr=%s\n", __func__,
+ btav_audio_state_t2str(state),
+ bt_bdaddr_t2str(bd_addr, last_addr));
+}
+
+static void audio_config(bt_bdaddr_t *bd_addr, uint32_t sample_rate,
+ uint8_t channel_count) {
+ haltest_info("(sink) %s: addr=%s\n sample_rate=%d\n channel_count=%d\n",
+ __func__, bt_bdaddr_t2str(bd_addr, last_addr),
+ sample_rate, channel_count);
+}
+
+static btav_callbacks_t av_cbacks = {
+ .size = sizeof(av_cbacks),
+ .connection_state_cb = connection_state,
+ .audio_state_cb = audio_state,
+ .audio_config_cb = audio_config,
+};
+
+/* init */
+
+static void init_p(int argc, const char **argv)
+{
+ RETURN_IF_NULL(if_av_sink);
+
+ EXEC(if_av_sink->init, &av_cbacks);
+}
+
+/* connect */
+
+static void connect_c(int argc, const char **argv, enum_func *enum_func,
+ void **user)
+{
+ if (argc == 3) {
+ *user = NULL;
+ *enum_func = enum_devices;
+ }
+}
+
+static void connect_p(int argc, const char **argv)
+{
+ bt_bdaddr_t addr;
+
+ RETURN_IF_NULL(if_av_sink);
+ VERIFY_ADDR_ARG(2, &addr);
+
+ EXEC(if_av_sink->connect, &addr);
+}
+
+/* disconnect */
+
+static void disconnect_c(int argc, const char **argv, enum_func *enum_func,
+ void **user)
+{
+ if (argc == 3) {
+ *user = last_addr;
+ *enum_func = enum_one_string;
+ }
+}
+
+static void disconnect_p(int argc, const char **argv)
+{
+ bt_bdaddr_t addr;
+
+ RETURN_IF_NULL(if_av_sink);
+ VERIFY_ADDR_ARG(2, &addr);
+
+ EXEC(if_av_sink->disconnect, &addr);
+}
+
+/* cleanup */
+
+static void cleanup_p(int argc, const char **argv)
+{
+ RETURN_IF_NULL(if_av_sink);
+
+ EXECV(if_av_sink->cleanup);
+ if_av_sink = NULL;
+}
+
+static struct method methods[] = {
+ STD_METHOD(init),
+ STD_METHODCH(connect, "<addr>"),
+ STD_METHODCH(disconnect, "<addr>"),
+ STD_METHOD(cleanup),
+ END_METHOD
+};
+
+const struct interface av_sink_if = {
+ .name = "av-sink",
+ .methods = methods
+};
diff --git a/repo/android/client/if-av.c b/repo/android/client/if-av.c
new file mode 100644
index 0000000..85c641b
--- /dev/null
+++ b/repo/android/client/if-av.c
@@ -0,0 +1,143 @@
+/*
+ * Copyright (C) 2013 Intel Corporation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include "if-main.h"
+#include "../hal-utils.h"
+
+const btav_interface_t *if_av = NULL;
+
+SINTMAP(btav_connection_state_t, -1, "(unknown)")
+ DELEMENT(BTAV_CONNECTION_STATE_DISCONNECTED),
+ DELEMENT(BTAV_CONNECTION_STATE_CONNECTING),
+ DELEMENT(BTAV_CONNECTION_STATE_CONNECTED),
+ DELEMENT(BTAV_CONNECTION_STATE_DISCONNECTING),
+ENDMAP
+
+SINTMAP(btav_audio_state_t, -1, "(unknown)")
+ DELEMENT(BTAV_AUDIO_STATE_REMOTE_SUSPEND),
+ DELEMENT(BTAV_AUDIO_STATE_STOPPED),
+ DELEMENT(BTAV_AUDIO_STATE_STARTED),
+ENDMAP
+
+static char last_addr[MAX_ADDR_STR_LEN];
+
+static void connection_state(btav_connection_state_t state,
+ bt_bdaddr_t *bd_addr)
+{
+ haltest_info("%s: connection_state=%s remote_bd_addr=%s\n", __func__,
+ btav_connection_state_t2str(state),
+ bt_bdaddr_t2str(bd_addr, last_addr));
+}
+
+static void audio_state(btav_audio_state_t state, bt_bdaddr_t *bd_addr)
+{
+ haltest_info("%s: audio_state=%s remote_bd_addr=%s\n", __func__,
+ btav_audio_state_t2str(state),
+ bt_bdaddr_t2str(bd_addr, last_addr));
+}
+
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+static void audio_config(bt_bdaddr_t *bd_addr, uint32_t sample_rate,
+ uint8_t channel_count) {
+ haltest_info("%s: remote_addr=%s\n sample_rate=%d\n channel_count=%d\n",
+ __func__, bt_bdaddr_t2str(bd_addr, last_addr),
+ sample_rate, channel_count);
+}
+#endif
+
+static btav_callbacks_t av_cbacks = {
+ .size = sizeof(av_cbacks),
+ .connection_state_cb = connection_state,
+ .audio_state_cb = audio_state,
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+ .audio_config_cb = audio_config,
+#endif
+};
+
+/* init */
+
+static void init_p(int argc, const char **argv)
+{
+ RETURN_IF_NULL(if_av);
+
+ EXEC(if_av->init, &av_cbacks);
+}
+
+/* connect */
+
+static void connect_c(int argc, const char **argv, enum_func *enum_func,
+ void **user)
+{
+ if (argc == 3) {
+ *user = NULL;
+ *enum_func = enum_devices;
+ }
+}
+
+static void connect_p(int argc, const char **argv)
+{
+ bt_bdaddr_t addr;
+
+ RETURN_IF_NULL(if_av);
+ VERIFY_ADDR_ARG(2, &addr);
+
+ EXEC(if_av->connect, &addr);
+}
+
+/* disconnect */
+
+static void disconnect_c(int argc, const char **argv, enum_func *enum_func,
+ void **user)
+{
+ if (argc == 3) {
+ *user = last_addr;
+ *enum_func = enum_one_string;
+ }
+}
+
+static void disconnect_p(int argc, const char **argv)
+{
+ bt_bdaddr_t addr;
+
+ RETURN_IF_NULL(if_av);
+ VERIFY_ADDR_ARG(2, &addr);
+
+ EXEC(if_av->disconnect, &addr);
+}
+
+/* cleanup */
+
+static void cleanup_p(int argc, const char **argv)
+{
+ RETURN_IF_NULL(if_av);
+
+ EXECV(if_av->cleanup);
+ if_av = NULL;
+}
+
+static struct method methods[] = {
+ STD_METHOD(init),
+ STD_METHODCH(connect, "<addr>"),
+ STD_METHODCH(disconnect, "<addr>"),
+ STD_METHOD(cleanup),
+ END_METHOD
+};
+
+const struct interface av_if = {
+ .name = "av",
+ .methods = methods
+};
diff --git a/repo/android/client/if-bt.c b/repo/android/client/if-bt.c
new file mode 100644
index 0000000..c9acf6c
--- /dev/null
+++ b/repo/android/client/if-bt.c
@@ -0,0 +1,1023 @@
+/*
+ * Copyright (C) 2013 Intel Corporation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include <string.h>
+#include <inttypes.h>
+
+#include "if-main.h"
+#include "terminal.h"
+#include "../hal-msg.h"
+#include "../hal-utils.h"
+
+static hw_device_t *bt_device;
+const bt_interface_t *if_bluetooth;
+
+#define VERIFY_PROP_TYPE_ARG(n, typ) \
+ do { \
+ if (n < argc) \
+ typ = str2btpropertytype(argv[n]); \
+ else { \
+ haltest_error("No property type specified\n"); \
+ return;\
+ } \
+ } while (0)
+
+static bt_scan_mode_t str2btscanmode(const char *str)
+{
+ bt_scan_mode_t v = str2bt_scan_mode_t(str);
+
+ if ((int) v != -1)
+ return v;
+
+ haltest_warn("WARN: %s cannot convert %s\n", __func__, str);
+ return (bt_scan_mode_t) atoi(str);
+}
+
+static bt_ssp_variant_t str2btsspvariant(const char *str)
+{
+ bt_ssp_variant_t v = str2bt_ssp_variant_t(str);
+
+ if ((int) v != -1)
+ return v;
+
+ haltest_warn("WARN: %s cannot convert %s\n", __func__, str);
+ return (bt_ssp_variant_t) atoi(str);
+}
+
+static bt_property_type_t str2btpropertytype(const char *str)
+{
+ bt_property_type_t v = str2bt_property_type_t(str);
+
+ if ((int) v != -1)
+ return v;
+
+ haltest_warn("WARN: %s cannot convert %s\n", __func__, str);
+ return (bt_property_type_t) atoi(str);
+}
+
+static void dump_properties(int num_properties, bt_property_t *properties)
+{
+ int i;
+
+ for (i = 0; i < num_properties; i++) {
+ /*
+ * properities sometimes come unaligned hence memcp to
+ * aligned buffer
+ */
+ bt_property_t prop;
+ memcpy(&prop, properties + i, sizeof(prop));
+
+ haltest_info("prop: %s\n", btproperty2str(&prop));
+ }
+}
+
+/* Cache for remote devices, stored in sorted array */
+static bt_bdaddr_t *remote_devices = NULL;
+static int remote_devices_cnt = 0;
+static int remote_devices_capacity = 0;
+
+/* Adds address to remote device set so it can be used in tab completion */
+void add_remote_device(const bt_bdaddr_t *addr)
+{
+ int i;
+
+ if (remote_devices == NULL) {
+ remote_devices = malloc(4 * sizeof(bt_bdaddr_t));
+ remote_devices_cnt = 0;
+ if (remote_devices == NULL) {
+ remote_devices_capacity = 0;
+ return;
+ }
+
+ remote_devices_capacity = 4;
+ }
+
+ /* Array is sorted, search for right place */
+ for (i = 0; i < remote_devices_cnt; ++i) {
+ int res = memcmp(&remote_devices[i], addr, sizeof(*addr));
+
+ if (res == 0)
+ return; /* Already added */
+ else if (res > 0)
+ break;
+ }
+
+ /* Realloc space if needed */
+ if (remote_devices_cnt >= remote_devices_capacity) {
+ bt_bdaddr_t *tmp;
+
+ remote_devices_capacity *= 2;
+ /*
+ * Save reference to previously allocated memory block so that
+ * it can be freed in case realloc fails.
+ */
+ tmp = remote_devices;
+
+ remote_devices = realloc(remote_devices, sizeof(bt_bdaddr_t) *
+ remote_devices_capacity);
+ if (remote_devices == NULL) {
+ free(tmp);
+ remote_devices_capacity = 0;
+ remote_devices_cnt = 0;
+ return;
+ }
+ }
+
+ if (i < remote_devices_cnt)
+ memmove(remote_devices + i + 1, remote_devices + i,
+ (remote_devices_cnt - i) * sizeof(bt_bdaddr_t));
+ remote_devices[i] = *addr;
+ remote_devices_cnt++;
+}
+
+const char *enum_devices(void *v, int i)
+{
+ static char buf[MAX_ADDR_STR_LEN];
+
+ if (i >= remote_devices_cnt)
+ return NULL;
+
+ bt_bdaddr_t2str(&remote_devices[i], buf);
+ return buf;
+}
+
+static void add_remote_device_from_props(int num_properties,
+ const bt_property_t *properties)
+{
+ int i;
+
+ for (i = 0; i < num_properties; i++) {
+ /*
+ * properities sometimes come unaligned hence memcp to
+ * aligned buffer
+ */
+ bt_property_t property;
+
+ memcpy(&property, properties + i, sizeof(property));
+ if (property.type == BT_PROPERTY_BDADDR)
+ add_remote_device((bt_bdaddr_t *) property.val);
+ }
+}
+
+bool close_hw_bt_dev(void)
+{
+ if (!bt_device)
+ return false;
+
+ bt_device->close(bt_device);
+ return true;
+}
+
+static void adapter_state_changed_cb(bt_state_t state)
+{
+ haltest_info("%s: state=%s\n", __func__, bt_state_t2str(state));
+}
+
+static void adapter_properties_cb(bt_status_t status, int num_properties,
+ bt_property_t *properties)
+{
+ haltest_info("%s: status=%s num_properties=%d\n", __func__,
+ bt_status_t2str(status), num_properties);
+
+ dump_properties(num_properties, properties);
+}
+
+static void remote_device_properties_cb(bt_status_t status,
+ bt_bdaddr_t *bd_addr,
+ int num_properties,
+ bt_property_t *properties)
+{
+ haltest_info("%s: status=%s bd_addr=%s num_properties=%d\n", __func__,
+ bt_status_t2str(status), bdaddr2str(bd_addr),
+ num_properties);
+
+ add_remote_device(bd_addr);
+
+ dump_properties(num_properties, properties);
+}
+
+static void device_found_cb(int num_properties, bt_property_t *properties)
+{
+ haltest_info("%s: num_properties=%d\n", __func__, num_properties);
+
+ add_remote_device_from_props(num_properties, properties);
+
+ dump_properties(num_properties, properties);
+}
+
+static void discovery_state_changed_cb(bt_discovery_state_t state)
+{
+ haltest_info("%s: state=%s\n", __func__,
+ bt_discovery_state_t2str(state));
+}
+
+/*
+ * Buffer for remote addres that came from one of bind request.
+ * It's stored for command completion.
+ */
+static char last_remote_addr[MAX_ADDR_STR_LEN];
+static bt_ssp_variant_t last_ssp_variant = (bt_ssp_variant_t) -1;
+
+static bt_bdaddr_t pin_request_addr;
+static void pin_request_answer(char *reply)
+{
+ bt_pin_code_t pin;
+ int accept = 0;
+ int pin_len = strlen(reply);
+
+ if (pin_len > 0) {
+ accept = 1;
+ if (pin_len > 16)
+ pin_len = 16;
+ memcpy(&pin.pin, reply, pin_len);
+ }
+
+ EXEC(if_bluetooth->pin_reply, &pin_request_addr, accept, pin_len, &pin);
+}
+
+static void pin_request_cb(bt_bdaddr_t *remote_bd_addr, bt_bdname_t *bd_name,
+ uint32_t cod)
+{
+ /* Store for command completion */
+ bt_bdaddr_t2str(remote_bd_addr, last_remote_addr);
+ pin_request_addr = *remote_bd_addr;
+
+ haltest_info("%s: remote_bd_addr=%s bd_name=%s cod=%06x\n", __func__,
+ last_remote_addr, bd_name->name, cod);
+ terminal_prompt_for("Enter pin: ", pin_request_answer);
+}
+
+/* Variables to store information from ssp_request_cb used for ssp_reply */
+static bt_bdaddr_t ssp_request_addr;
+static bt_ssp_variant_t ssp_request_variant;
+static uint32_t ssp_request_pask_key;
+
+/* Called when user hit enter on prompt for confirmation */
+static void ssp_request_yes_no_answer(char *reply)
+{
+ int accept = *reply == 0 || *reply == 'y' || *reply == 'Y';
+
+ if_bluetooth->ssp_reply(&ssp_request_addr, ssp_request_variant, accept,
+ ssp_request_pask_key);
+}
+
+static void ssp_request_cb(bt_bdaddr_t *remote_bd_addr, bt_bdname_t *bd_name,
+ uint32_t cod, bt_ssp_variant_t pairing_variant,
+ uint32_t pass_key)
+{
+ static char prompt[50];
+
+ /* Store for command completion */
+ bt_bdaddr_t2str(remote_bd_addr, last_remote_addr);
+ last_ssp_variant = pairing_variant;
+
+ haltest_info("%s: remote_bd_addr=%s bd_name=%s cod=%06x pairing_variant=%s pass_key=%d\n",
+ __func__, last_remote_addr, bd_name->name, cod,
+ bt_ssp_variant_t2str(pairing_variant), pass_key);
+
+ switch (pairing_variant) {
+ case BT_SSP_VARIANT_PASSKEY_CONFIRMATION:
+ sprintf(prompt, "Does other device show %d [Y/n] ?", pass_key);
+
+ ssp_request_addr = *remote_bd_addr;
+ ssp_request_variant = pairing_variant;
+ ssp_request_pask_key = pass_key;
+
+ terminal_prompt_for(prompt, ssp_request_yes_no_answer);
+ break;
+ case BT_SSP_VARIANT_CONSENT:
+ sprintf(prompt, "Consent pairing [Y/n] ?");
+
+ ssp_request_addr = *remote_bd_addr;
+ ssp_request_variant = pairing_variant;
+ ssp_request_pask_key = 0;
+
+ terminal_prompt_for(prompt, ssp_request_yes_no_answer);
+ break;
+ case BT_SSP_VARIANT_PASSKEY_ENTRY:
+ case BT_SSP_VARIANT_PASSKEY_NOTIFICATION:
+ default:
+ haltest_info("Not automatically handled\n");
+ break;
+ }
+}
+
+static void bond_state_changed_cb(bt_status_t status,
+ bt_bdaddr_t *remote_bd_addr,
+ bt_bond_state_t state)
+{
+ haltest_info("%s: status=%s remote_bd_addr=%s state=%s\n", __func__,
+ bt_status_t2str(status), bdaddr2str(remote_bd_addr),
+ bt_bond_state_t2str(state));
+}
+
+static void acl_state_changed_cb(bt_status_t status,
+ bt_bdaddr_t *remote_bd_addr,
+ bt_acl_state_t state)
+{
+ haltest_info("%s: status=%s remote_bd_addr=%s state=%s\n", __func__,
+ bt_status_t2str(status), bdaddr2str(remote_bd_addr),
+ bt_acl_state_t2str(state));
+}
+
+static void thread_evt_cb(bt_cb_thread_evt evt)
+{
+ haltest_info("%s: evt=%s\n", __func__, bt_cb_thread_evt2str(evt));
+}
+
+static void dut_mode_recv_cb(uint16_t opcode, uint8_t *buf, uint8_t len)
+{
+ haltest_info("%s\n", __func__);
+}
+
+static void le_test_mode_cb(bt_status_t status, uint16_t num_packets)
+{
+ haltest_info("%s %s %d\n", __func__, bt_status_t2str(status),
+ num_packets);
+}
+
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+static void energy_info_cb(bt_activity_energy_info *energy_info)
+{
+ haltest_info("%s status=%s, ctrl_state=0x%02X, tx_time=0x%jx,"
+ "rx_time=0x%jx, idle_time=0x%jx, energu_used=0x%jx\n",
+ __func__, bt_status_t2str(energy_info->status),
+ energy_info->ctrl_state, energy_info->tx_time,
+ energy_info->rx_time, energy_info->idle_time,
+ energy_info->energy_used);
+}
+#endif
+
+static bt_callbacks_t bt_callbacks = {
+ .size = sizeof(bt_callbacks),
+ .adapter_state_changed_cb = adapter_state_changed_cb,
+ .adapter_properties_cb = adapter_properties_cb,
+ .remote_device_properties_cb = remote_device_properties_cb,
+ .device_found_cb = device_found_cb,
+ .discovery_state_changed_cb = discovery_state_changed_cb,
+ .pin_request_cb = pin_request_cb,
+ .ssp_request_cb = ssp_request_cb,
+ .bond_state_changed_cb = bond_state_changed_cb,
+ .acl_state_changed_cb = acl_state_changed_cb,
+ .thread_evt_cb = thread_evt_cb,
+ .dut_mode_recv_cb = dut_mode_recv_cb,
+ .le_test_mode_cb = le_test_mode_cb,
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+ .energy_info_cb = energy_info_cb,
+#endif
+};
+
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+static alarm_cb alarm_cb_p = NULL;
+static void *alarm_cb_p_data = NULL;
+
+static bool set_wake_alarm(uint64_t delay_millis, bool should_wake, alarm_cb cb,
+ void *data)
+{
+ haltest_info("%s: delay %"PRIu64" should_wake %u cb %p data %p\n",
+ __func__, delay_millis, should_wake, cb, data);
+
+ /* TODO call alarm callback after specified delay */
+ alarm_cb_p = cb;
+ alarm_cb_p_data = data;
+
+ return true;
+}
+
+static int acquire_wake_lock(const char *lock_name)
+{
+ haltest_info("%s: %s\n", __func__, lock_name);
+
+ return BT_STATUS_SUCCESS;
+}
+
+static int release_wake_lock(const char *lock_name)
+{
+ haltest_info("%s: %s\n", __func__, lock_name);
+
+ return BT_STATUS_SUCCESS;
+}
+
+static bt_os_callouts_t bt_os_callouts = {
+ .size = sizeof(bt_os_callouts),
+ .set_wake_alarm = set_wake_alarm,
+ .acquire_wake_lock = acquire_wake_lock,
+ .release_wake_lock = release_wake_lock,
+};
+#endif
+
+static void init_p(int argc, const char **argv)
+{
+ int err;
+ const hw_module_t *module;
+
+ err = hw_get_module(BT_HARDWARE_MODULE_ID, &module);
+ if (err) {
+ haltest_error("he_get_module returned %d\n", err);
+ return;
+ }
+
+ err = module->methods->open(module, BT_HARDWARE_MODULE_ID, &bt_device);
+ if (err) {
+ haltest_error("module->methods->open returned %d\n", err);
+ return;
+ }
+
+ if_bluetooth =
+ ((bluetooth_device_t *) bt_device)->get_bluetooth_interface();
+ if (!if_bluetooth) {
+ haltest_error("get_bluetooth_interface returned NULL\n");
+ return;
+ }
+
+ EXEC(if_bluetooth->init, &bt_callbacks);
+
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+ EXEC(if_bluetooth->set_os_callouts, &bt_os_callouts);
+#endif
+}
+
+static void cleanup_p(int argc, const char **argv)
+{
+ RETURN_IF_NULL(if_bluetooth);
+
+ EXECV(if_bluetooth->cleanup);
+
+ if_bluetooth = NULL;
+}
+
+static void enable_p(int argc, const char **argv)
+{
+ RETURN_IF_NULL(if_bluetooth);
+
+ EXEC(if_bluetooth->enable);
+}
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+static void read_energy_info_p(int argc, const char **argv)
+{
+ RETURN_IF_NULL(if_bluetooth);
+
+ EXEC(if_bluetooth->read_energy_info);
+}
+
+#define get_connection_state_c complete_addr_c
+
+static void get_connection_state_p(int argc, const char **argv)
+{
+ bt_bdaddr_t addr;
+
+ RETURN_IF_NULL(if_bluetooth);
+
+ VERIFY_ADDR_ARG(2, &addr);
+
+ haltest_info("if_bluetooth->get_connection_state : %d\n",
+ if_bluetooth->get_connection_state(&addr));
+}
+#endif
+
+static void disable_p(int argc, const char **argv)
+{
+ RETURN_IF_NULL(if_bluetooth);
+
+ EXEC(if_bluetooth->disable);
+}
+
+static void get_adapter_properties_p(int argc, const char **argv)
+{
+ RETURN_IF_NULL(if_bluetooth);
+
+ EXEC(if_bluetooth->get_adapter_properties);
+}
+
+static void get_adapter_property_c(int argc, const char **argv,
+ enum_func *enum_func, void **user)
+{
+ if (argc == 3) {
+ *user = TYPE_ENUM(bt_property_type_t);
+ *enum_func = enum_defines;
+ }
+}
+
+static void get_adapter_property_p(int argc, const char **argv)
+{
+ int type;
+
+ RETURN_IF_NULL(if_bluetooth);
+ VERIFY_PROP_TYPE_ARG(2, type);
+
+ EXEC(if_bluetooth->get_adapter_property, type);
+}
+
+static const char * const names[] = {
+ "BT_PROPERTY_BDNAME",
+ "BT_PROPERTY_ADAPTER_SCAN_MODE",
+ "BT_PROPERTY_ADAPTER_DISCOVERY_TIMEOUT",
+ NULL
+};
+
+static void set_adapter_property_c(int argc, const char **argv,
+ enum_func *enum_func, void **user)
+{
+ if (argc == 3) {
+ *user = (void *) names;
+ *enum_func = enum_strings;
+ } else if (argc == 4) {
+ if (0 == strcmp(argv[2], "BT_PROPERTY_ADAPTER_SCAN_MODE")) {
+ *user = TYPE_ENUM(bt_scan_mode_t);
+ *enum_func = enum_defines;
+ }
+ }
+}
+
+static void set_adapter_property_p(int argc, const char **argv)
+{
+ bt_property_t property;
+ bt_scan_mode_t mode;
+ int timeout;
+
+ RETURN_IF_NULL(if_bluetooth);
+ VERIFY_PROP_TYPE_ARG(2, property.type);
+
+ if (argc <= 3) {
+ haltest_error("No property value specified\n");
+ return;
+ }
+ switch (property.type) {
+ case BT_PROPERTY_BDNAME:
+ property.len = strlen(argv[3]) + 1;
+ property.val = (char *) argv[3];
+ break;
+
+ case BT_PROPERTY_ADAPTER_SCAN_MODE:
+ mode = str2btscanmode(argv[3]);
+ property.len = sizeof(bt_scan_mode_t);
+ property.val = &mode;
+ break;
+
+ case BT_PROPERTY_ADAPTER_DISCOVERY_TIMEOUT:
+ timeout = atoi(argv[3]);
+ property.val = &timeout;
+ property.len = sizeof(timeout);
+ break;
+
+ case BT_PROPERTY_BDADDR:
+ case BT_PROPERTY_UUIDS:
+ case BT_PROPERTY_CLASS_OF_DEVICE:
+ case BT_PROPERTY_TYPE_OF_DEVICE:
+ case BT_PROPERTY_SERVICE_RECORD:
+ case BT_PROPERTY_ADAPTER_BONDED_DEVICES:
+ case BT_PROPERTY_REMOTE_FRIENDLY_NAME:
+ case BT_PROPERTY_REMOTE_RSSI:
+ case BT_PROPERTY_REMOTE_VERSION_INFO:
+ case BT_PROPERTY_REMOTE_DEVICE_TIMESTAMP:
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+ case BT_PROPERTY_LOCAL_LE_FEATURES:
+#endif
+ default:
+ haltest_error("Invalid property %s\n", argv[3]);
+ return;
+ }
+
+ EXEC(if_bluetooth->set_adapter_property, &property);
+}
+
+/* This function is to be used for completion methods that need only address */
+static void complete_addr_c(int argc, const char **argv, enum_func *enum_func,
+ void **user)
+{
+ if (argc == 3) {
+ *user = NULL;
+ *enum_func = enum_devices;
+ }
+}
+
+/* Just addres to complete, use complete_addr_c */
+#define get_remote_device_properties_c complete_addr_c
+
+static void get_remote_device_properties_p(int argc, const char **argv)
+{
+ bt_bdaddr_t addr;
+
+ RETURN_IF_NULL(if_bluetooth);
+ VERIFY_ADDR_ARG(2, &addr);
+
+ EXEC(if_bluetooth->get_remote_device_properties, &addr);
+}
+
+static void get_remote_device_property_c(int argc, const char **argv,
+ enum_func *enum_func,
+ void **user)
+{
+ if (argc == 3) {
+ *user = NULL;
+ *enum_func = enum_devices;
+ } else if (argc == 4) {
+ *user = TYPE_ENUM(bt_property_type_t);
+ *enum_func = enum_defines;
+ }
+}
+
+static void get_remote_device_property_p(int argc, const char **argv)
+{
+ bt_property_type_t type;
+ bt_bdaddr_t addr;
+
+ RETURN_IF_NULL(if_bluetooth);
+ VERIFY_ADDR_ARG(2, &addr);
+ VERIFY_PROP_TYPE_ARG(3, type);
+
+ EXEC(if_bluetooth->get_remote_device_property, &addr, type);
+}
+
+/*
+ * Same completion as for get_remote_device_property_c can be used for
+ * set_remote_device_property_c. No need to create separate function.
+ */
+#define set_remote_device_property_c get_remote_device_property_c
+
+static void set_remote_device_property_p(int argc, const char **argv)
+{
+ bt_property_t property;
+ bt_bdaddr_t addr;
+
+ RETURN_IF_NULL(if_bluetooth);
+ VERIFY_ADDR_ARG(2, &addr);
+ VERIFY_PROP_TYPE_ARG(3, property.type);
+
+ switch (property.type) {
+ case BT_PROPERTY_REMOTE_FRIENDLY_NAME:
+ property.len = strlen(argv[4]);
+ property.val = (char *) argv[4];
+ break;
+ case BT_PROPERTY_BDNAME:
+ case BT_PROPERTY_BDADDR:
+ case BT_PROPERTY_UUIDS:
+ case BT_PROPERTY_CLASS_OF_DEVICE:
+ case BT_PROPERTY_TYPE_OF_DEVICE:
+ case BT_PROPERTY_SERVICE_RECORD:
+ case BT_PROPERTY_ADAPTER_SCAN_MODE:
+ case BT_PROPERTY_ADAPTER_BONDED_DEVICES:
+ case BT_PROPERTY_ADAPTER_DISCOVERY_TIMEOUT:
+ case BT_PROPERTY_REMOTE_RSSI:
+ case BT_PROPERTY_REMOTE_VERSION_INFO:
+ case BT_PROPERTY_REMOTE_DEVICE_TIMESTAMP:
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+ case BT_PROPERTY_LOCAL_LE_FEATURES:
+#endif
+ default:
+ return;
+ }
+
+ EXEC(if_bluetooth->set_remote_device_property, &addr, &property);
+}
+
+/* For now uuid is not autocompleted. Use routine for complete_addr_c */
+#define get_remote_service_record_c complete_addr_c
+
+static void get_remote_service_record_p(int argc, const char **argv)
+{
+ bt_bdaddr_t addr;
+ bt_uuid_t uuid;
+
+ RETURN_IF_NULL(if_bluetooth);
+ VERIFY_ADDR_ARG(2, &addr);
+
+ if (argc <= 3) {
+ haltest_error("No uuid specified\n");
+ return;
+ }
+
+ str2bt_uuid_t(argv[3], &uuid);
+
+ EXEC(if_bluetooth->get_remote_service_record, &addr, &uuid);
+}
+
+/* Just addres to complete, use complete_addr_c */
+#define get_remote_services_c complete_addr_c
+
+static void get_remote_services_p(int argc, const char **argv)
+{
+ bt_bdaddr_t addr;
+
+ RETURN_IF_NULL(if_bluetooth);
+ VERIFY_ADDR_ARG(2, &addr);
+
+ EXEC(if_bluetooth->get_remote_services, &addr);
+}
+
+static void start_discovery_p(int argc, const char **argv)
+{
+ RETURN_IF_NULL(if_bluetooth);
+
+ EXEC(if_bluetooth->start_discovery);
+}
+
+static void cancel_discovery_p(int argc, const char **argv)
+{
+ RETURN_IF_NULL(if_bluetooth);
+
+ EXEC(if_bluetooth->cancel_discovery);
+}
+
+/* Just addres to complete, use complete_addr_c */
+#define create_bond_c complete_addr_c
+
+static void create_bond_p(int argc, const char **argv)
+{
+ bt_bdaddr_t addr;
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+ int transport;
+#endif
+
+ RETURN_IF_NULL(if_bluetooth);
+ VERIFY_ADDR_ARG(2, &addr);
+
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+ if (argc < 4)
+ transport = BT_TRANSPORT_UNKNOWN;
+ else
+ transport = atoi(argv[3]);
+
+ EXEC(if_bluetooth->create_bond, &addr, transport);
+#else
+ EXEC(if_bluetooth->create_bond, &addr);
+#endif
+}
+
+/* Just addres to complete, use complete_addr_c */
+#define remove_bond_c complete_addr_c
+
+static void remove_bond_p(int argc, const char **argv)
+{
+ bt_bdaddr_t addr;
+
+ RETURN_IF_NULL(if_bluetooth);
+ VERIFY_ADDR_ARG(2, &addr);
+
+ EXEC(if_bluetooth->remove_bond, &addr);
+}
+
+/* Just addres to complete, use complete_addr_c */
+#define cancel_bond_c complete_addr_c
+
+static void cancel_bond_p(int argc, const char **argv)
+{
+ bt_bdaddr_t addr;
+
+ RETURN_IF_NULL(if_bluetooth);
+ VERIFY_ADDR_ARG(2, &addr);
+
+ EXEC(if_bluetooth->cancel_bond, &addr);
+}
+
+static void pin_reply_c(int argc, const char **argv, enum_func *enum_func,
+ void **user)
+{
+ static const char *const completions[] = { last_remote_addr, NULL };
+
+ if (argc == 3) {
+ *user = (void *) completions;
+ *enum_func = enum_strings;
+ }
+}
+
+static void pin_reply_p(int argc, const char **argv)
+{
+ bt_bdaddr_t addr;
+ bt_pin_code_t pin;
+ int pin_len = 0;
+ int accept = 0;
+
+ RETURN_IF_NULL(if_bluetooth);
+ VERIFY_ADDR_ARG(2, &addr);
+
+ if (argc > 3) {
+ accept = 1;
+ pin_len = strlen(argv[3]);
+ memcpy(pin.pin, argv[3], pin_len);
+ }
+
+ EXEC(if_bluetooth->pin_reply, &addr, accept, pin_len, &pin);
+}
+
+static void ssp_reply_c(int argc, const char **argv, enum_func *enum_func,
+ void **user)
+{
+ if (argc == 3) {
+ *user = last_remote_addr;
+ *enum_func = enum_one_string;
+ } else if (argc == 5) {
+ *user = "1";
+ *enum_func = enum_one_string;
+ } else if (argc == 4) {
+ if (-1 != (int) last_ssp_variant) {
+ *user = (void *) bt_ssp_variant_t2str(last_ssp_variant);
+ *enum_func = enum_one_string;
+ } else {
+ *user = TYPE_ENUM(bt_ssp_variant_t);
+ *enum_func = enum_defines;
+ }
+ }
+}
+
+static void ssp_reply_p(int argc, const char **argv)
+{
+ bt_bdaddr_t addr;
+ bt_ssp_variant_t var;
+ int accept;
+ int passkey;
+
+ RETURN_IF_NULL(if_bluetooth);
+ VERIFY_ADDR_ARG(2, &addr);
+
+ if (argc < 4) {
+ haltest_error("No ssp variant specified\n");
+ return;
+ }
+
+ var = str2btsspvariant(argv[3]);
+ if (argc < 5) {
+ haltest_error("No accept value specified\n");
+ return;
+ }
+
+ accept = atoi(argv[4]);
+ passkey = 0;
+
+ if (accept && var == BT_SSP_VARIANT_PASSKEY_ENTRY && argc >= 5)
+ passkey = atoi(argv[4]);
+
+ EXEC(if_bluetooth->ssp_reply, &addr, var, accept, passkey);
+}
+
+static void get_profile_interface_c(int argc, const char **argv,
+ enum_func *enum_func, void **user)
+{
+ static const char *const profile_ids[] = {
+ BT_PROFILE_HANDSFREE_ID,
+ BT_PROFILE_ADVANCED_AUDIO_ID,
+ BT_PROFILE_HEALTH_ID,
+ BT_PROFILE_SOCKETS_ID,
+ BT_PROFILE_HIDHOST_ID,
+ BT_PROFILE_PAN_ID,
+ BT_PROFILE_GATT_ID,
+ BT_PROFILE_AV_RC_ID,
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+ BT_PROFILE_HANDSFREE_CLIENT_ID,
+ BT_PROFILE_MAP_CLIENT_ID,
+ BT_PROFILE_AV_RC_CTRL_ID,
+ BT_PROFILE_ADVANCED_AUDIO_SINK_ID,
+#endif
+ NULL
+ };
+
+ if (argc == 3) {
+ *user = (void *) profile_ids;
+ *enum_func = enum_strings;
+ }
+}
+
+static void get_profile_interface_p(int argc, const char **argv)
+{
+ const char *id;
+ const void **pif = NULL;
+
+ RETURN_IF_NULL(if_bluetooth);
+ if (argc <= 2) {
+ haltest_error("No interface specified\n");
+ return;
+ }
+
+ id = argv[2];
+
+ if (strcmp(BT_PROFILE_HANDSFREE_ID, id) == 0)
+ pif = (const void **) &if_hf;
+ else if (strcmp(BT_PROFILE_ADVANCED_AUDIO_ID, id) == 0)
+ pif = (const void **) &if_av;
+ else if (strcmp(BT_PROFILE_HEALTH_ID, id) == 0)
+ pif = (const void **) &if_hl;
+ else if (strcmp(BT_PROFILE_SOCKETS_ID, id) == 0)
+ pif = (const void **) &if_sock;
+ else if (strcmp(BT_PROFILE_HIDHOST_ID, id) == 0)
+ pif = (const void **) &if_hh;
+ else if (strcmp(BT_PROFILE_PAN_ID, id) == 0)
+ pif = (const void **) &if_pan;
+ else if (strcmp(BT_PROFILE_AV_RC_ID, id) == 0)
+ pif = (const void **) &if_rc;
+ else if (strcmp(BT_PROFILE_GATT_ID, id) == 0)
+ pif = (const void **) &if_gatt;
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+ else if (strcmp(BT_PROFILE_AV_RC_CTRL_ID, id) == 0)
+ pif = (const void **) &if_rc_ctrl;
+ else if (strcmp(BT_PROFILE_HANDSFREE_CLIENT_ID, id) == 0)
+ pif = (const void **) &if_hf_client;
+ else if (strcmp(BT_PROFILE_MAP_CLIENT_ID, id) == 0)
+ pif = (const void **) &if_mce;
+ else if (strcmp(BT_PROFILE_ADVANCED_AUDIO_SINK_ID, id) == 0)
+ pif = (const void **) &if_av_sink;
+#endif
+ else
+ haltest_error("%s is not correct for get_profile_interface\n",
+ id);
+
+ if (pif != NULL) {
+ *pif = if_bluetooth->get_profile_interface(id);
+ haltest_info("get_profile_interface(%s) : %p\n", id, *pif);
+ }
+}
+
+static void dut_mode_configure_p(int argc, const char **argv)
+{
+ uint8_t mode;
+
+ RETURN_IF_NULL(if_bluetooth);
+
+ if (argc <= 2) {
+ haltest_error("No dut mode specified\n");
+ return;
+ }
+
+ mode = strtol(argv[2], NULL, 0);
+
+ EXEC(if_bluetooth->dut_mode_configure, mode);
+}
+
+static void dut_mode_send_p(int argc, const char **argv)
+{
+ haltest_error("not implemented\n");
+}
+
+static void le_test_mode_p(int argc, const char **argv)
+{
+ haltest_error("not implemented\n");
+}
+
+static void config_hci_snoop_log_p(int argc, const char **argv)
+{
+ uint8_t mode;
+
+ RETURN_IF_NULL(if_bluetooth);
+
+ if (argc <= 2) {
+ haltest_error("No mode specified\n");
+ return;
+ }
+
+ mode = strtol(argv[2], NULL, 0);
+
+ EXEC(if_bluetooth->config_hci_snoop_log, mode);
+}
+
+static struct method methods[] = {
+ STD_METHOD(init),
+ STD_METHOD(cleanup),
+ STD_METHOD(enable),
+ STD_METHOD(disable),
+ STD_METHOD(get_adapter_properties),
+ STD_METHODCH(get_adapter_property, "<prop_type>"),
+ STD_METHODCH(set_adapter_property, "<prop_type> <prop_value>"),
+ STD_METHODCH(get_remote_device_properties, "<addr>"),
+ STD_METHODCH(get_remote_device_property, "<addr> <property_type>"),
+ STD_METHODCH(set_remote_device_property,
+ "<addr> <property_type> <value>"),
+ STD_METHODCH(get_remote_service_record, "<addr> <uuid>"),
+ STD_METHODCH(get_remote_services, "<addr>"),
+ STD_METHOD(start_discovery),
+ STD_METHOD(cancel_discovery),
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+ STD_METHODCH(create_bond, "<addr> [<transport>]"),
+ STD_METHOD(read_energy_info),
+ STD_METHODCH(get_connection_state, "<addr>"),
+#else
+ STD_METHODCH(create_bond, "<addr>"),
+#endif
+ STD_METHODCH(remove_bond, "<addr>"),
+ STD_METHODCH(cancel_bond, "<addr>"),
+ STD_METHODCH(pin_reply, "<address> [<pin>]"),
+ STD_METHODCH(ssp_reply, "<address> <ssp_veriant> 1|0 [<passkey>]"),
+ STD_METHODCH(get_profile_interface, "<profile id>"),
+ STD_METHODH(dut_mode_configure, "<dut mode>"),
+ STD_METHOD(dut_mode_send),
+ STD_METHOD(le_test_mode),
+ STD_METHODH(config_hci_snoop_log, "<mode>"),
+ END_METHOD
+};
+
+const struct interface bluetooth_if = {
+ .name = "bluetooth",
+ .methods = methods
+};
diff --git a/repo/android/client/if-gatt.c b/repo/android/client/if-gatt.c
new file mode 100644
index 0000000..3526783
--- /dev/null
+++ b/repo/android/client/if-gatt.c
@@ -0,0 +1,2675 @@
+/*
+ * Copyright (C) 2013 Intel Corporation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include <stdbool.h>
+
+#include <string.h>
+
+#include <hardware/bluetooth.h>
+
+#include "../hal-utils.h"
+#include "if-main.h"
+
+const btgatt_interface_t *if_gatt = NULL;
+
+/*
+ * In version 19 some callback were changed.
+ * btgatt_char_id_t -> btgatt_gatt_id_t
+ * bt_uuid_t -> btgatt_gatt_id_t
+ */
+#define str2btgatt_descr_id_t str2btgatt_gatt_id_t
+#define btgatt_descr_id_t2str btgatt_gatt_id_t2str
+#define btgatt_descr_id_t btgatt_gatt_id_t
+
+#define MAX_CHAR_ID_STR_LEN (MAX_UUID_STR_LEN + 3 + 11)
+#define MAX_SRVC_ID_STR_LEN (MAX_UUID_STR_LEN + 3 + 11 + 1 + 11)
+/* How man characters print from binary objects (arbitrary) */
+#define MAX_HEX_VAL_STR_LEN 100
+#define MAX_NOTIFY_PARAMS_STR_LEN (MAX_SRVC_ID_STR_LEN + MAX_CHAR_ID_STR_LEN \
+ + MAX_ADDR_STR_LEN + MAX_HEX_VAL_STR_LEN + 60)
+#define MAX_READ_PARAMS_STR_LEN (MAX_SRVC_ID_STR_LEN + MAX_CHAR_ID_STR_LEN \
+ + MAX_UUID_STR_LEN + MAX_HEX_VAL_STR_LEN + 80)
+
+/* Hex arguments must have "0x" or "0X" prefix */
+#define VERIFY_INT_ARG(n, v, err) \
+ do { \
+ if (n < argc) \
+ v = strtol(argv[n], NULL, 0); \
+ else { \
+ haltest_error(err); \
+ return;\
+ } \
+ } while (0)
+
+#define VERIFY_HEX_ARG(n, v, err) \
+ do { \
+ if (n < argc) \
+ v = strtol(argv[n], NULL, 16); \
+ else { \
+ haltest_error(err); \
+ return;\
+ } \
+ } while (0)
+
+/* Helper macros to verify arguments of methods */
+#define VERIFY_CLIENT_IF(n, v) VERIFY_INT_ARG(n, v, "No client_if specified\n")
+#define VERIFY_SERVER_IF(n, v) VERIFY_INT_ARG(n, v, "No server_if specified\n")
+#define VERIFY_CONN_ID(n, v) VERIFY_INT_ARG(n, v, "No conn_if specified\n")
+#define VERIFY_TRANS_ID(n, v) VERIFY_INT_ARG(n, v, "No trans_id specified\n")
+#define VERIFY_STATUS(n, v) VERIFY_INT_ARG(n, v, "No status specified\n")
+#define VERIFY_OFFSET(n, v) VERIFY_INT_ARG(n, v, "No offset specified\n")
+#define VERIFY_TEST_ARG(n, v) VERIFY_INT_ARG(n, v, "No test arg specified\n")
+#define VERIFY_ACTION(n, v) VERIFY_INT_ARG(n, v, "No action specified\n")
+#define VERIFY_FILT_TYPE(n, v) VERIFY_INT_ARG(n, v, "No filter specified\n")
+#define VERIFY_FILT_INDEX(n, v) VERIFY_INT_ARG(n, v, \
+ "No filter index specified\n")
+#define VERIFY_FEAT_SELN(n, v) VERIFY_INT_ARG(n, v, "No feat seln specified\n")
+#define VERIFY_LIST_LOGIC_TYPE(n, v) VERIFY_INT_ARG(n, v, \
+ "No list logic type specified\n")
+#define VERIFY_FILT_LOGIC_TYPE(n, v) VERIFY_INT_ARG(n, v, \
+ "No filt logic type specified\n")
+#define VERIFY_RSSI_HI_THR(n, v) VERIFY_INT_ARG(n, v, \
+ "No rssi hi threshold specified\n")
+#define VERIFY_RSSI_LOW_THR(n, v) VERIFY_INT_ARG(n, v, \
+ "No low threshold specified\n")
+#define VERIFY_DELY_MODE(n, v) VERIFY_INT_ARG(n, v, "No delay mode specified\n")
+#define VERIFY_FND_TIME(n, v) VERIFY_INT_ARG(n, v, "No found time specified\n")
+#define VERIFY_LOST_TIME(n, v) VERIFY_INT_ARG(n, v, "No lost time specified\n")
+#define VERIFY_FND_TIME_CNT(n, v) VERIFY_INT_ARG(n, v, \
+ "No found timer counter specified\n")
+#define VERIFY_COMP_ID(n, v) VERIFY_INT_ARG(n, v, "No company id specified\n")
+#define VERIFY_COMP_ID_MASK(n, v) VERIFY_INT_ARG(n, v, \
+ "No comp. id mask specified\n")
+#define VERIFY_DATA_LEN(n, v) VERIFY_INT_ARG(n, v, "No data len specified\n")
+#define VERIFY_MASK_LEN(n, v) VERIFY_INT_ARG(n, v, "No mask len specified\n")
+#define VERIFY_MIN_INTERVAL(n, v) VERIFY_INT_ARG(n, v, \
+ "No min interval specified\n")
+#define VERIFY_MAX_INTERVAL(n, v) VERIFY_INT_ARG(n, v, \
+ "No max interval specified\n")
+#define VERIFY_APPEARANCE(n, v) VERIFY_INT_ARG(n, v, "No apperance specified\n")
+#define VERIFY_MANUFACTURER_LEN(n, v) VERIFY_INT_ARG(n, v, \
+ "No manufacturer len specified\n")
+#define VERIFY_SERVICE_DATA_LEN(n, v) VERIFY_INT_ARG(n, v, \
+ "No service data len specified\n")
+#define VERIFY_SERVICE_UUID_LEN(n, v) VERIFY_INT_ARG(n, v, \
+ "No service uuid len specified\n")
+#define VERIFY_MTU(n, v) VERIFY_INT_ARG(n, v, "No mtu specified\n")
+#define VERIFY_LATENCY(n, v) VERIFY_INT_ARG(n, v, \
+ "No latency specified\n")
+#define VERIFY_TIMEOUT(n, v) VERIFY_INT_ARG(n, v, "No timeout specified\n")
+#define VERIFY_SCAN_INTERVAL(n, v) VERIFY_INT_ARG(n, v, \
+ "No scan interval specified\n")
+#define VERIFY_SCAN_WINDOW(n, v) VERIFY_INT_ARG(n, v, \
+ "No scan window specified\n")
+#define VERIFY_ADV_TYPE(n, v) VERIFY_INT_ARG(n, v, \
+ "No advertising type specified\n")
+#define VERIFY_CHNL_MAP(n, v) VERIFY_INT_ARG(n, v, \
+ "No channel map specified\n")
+#define VERIFY_TX_POWER(n, v) VERIFY_INT_ARG(n, v, \
+ "No tx power specified\n")
+#define VERIFY_TIMEOUT_S(n, v) VERIFY_INT_ARG(n, v, \
+ "No timeout in sec specified\n")
+#define VERIFY_BATCH_SCAN_FULL_MAX(n, v) VERIFY_INT_ARG(n, v, \
+ "No batch scan full max specified\n")
+#define VERIFY_BATCH_SCAN_TRUNC_MAX(n, v) VERIFY_INT_ARG(n, v, \
+ "No batch scan trunc max specified\n")
+#define VERIFY_BATCH_SCAN_NOTIFY_THR(n, v) VERIFY_INT_ARG(n, v, \
+ "No batch scan notify threshold specified\n")
+#define VERIFY_SCAN_MODE(n, v) VERIFY_INT_ARG(n, v, \
+ "No scan mode specified\n")
+#define VERIFY_ADDR_TYPE(n, v) VERIFY_INT_ARG(n, v, \
+ "No address type specified\n")
+#define VERIFY_DISCARD_RULE(n, v) VERIFY_INT_ARG(n, v, \
+ "No discard rule specified\n")
+#define VERIFY_HANDLE(n, v) VERIFY_HEX_ARG(n, v, "No "#v" specified\n")
+#define VERIFY_SERVICE_HANDLE(n, v) VERIFY_HANDLE(n, v)
+
+#define VERIFY_UUID(n, v) \
+ do { \
+ if (n < argc) \
+ gatt_str2bt_uuid_t(argv[n], -1, v); \
+ else { \
+ haltest_error("No uuid specified\n"); \
+ return;\
+ } \
+ } while (0)
+
+#define VERIFY_SRVC_ID(n, v) \
+ do { \
+ if (n < argc) \
+ str2btgatt_srvc_id_t(argv[n], v); \
+ else { \
+ haltest_error("No srvc_id specified\n"); \
+ return;\
+ } \
+ } while (0)
+
+#define VERIFY_CHAR_ID(n, v) \
+ do { \
+ if (n < argc) \
+ str2btgatt_gatt_id_t(argv[n], v); \
+ else { \
+ haltest_error("No char_id specified\n"); \
+ return;\
+ } \
+ } while (0)
+
+#define VERIFY_DESCR_ID(n, v) \
+ do { \
+ if (n < argc) \
+ str2btgatt_descr_id_t(argv[n], v); \
+ else { \
+ haltest_error("No descr_id specified\n"); \
+ return;\
+ } \
+ } while (0)
+
+#define GET_VERIFY_HEX_STRING(i, v, l) \
+ do { \
+ int ll;\
+ const char *n = argv[i]; \
+ if (argc <= i) { \
+ haltest_error("No value specified\n"); \
+ return; \
+ } \
+ if (n[0] != '0' || (n[1] != 'X' && n[1] != 'x')) { \
+ haltest_error("Value must be hex string\n"); \
+ return; \
+ } \
+ ll = fill_buffer(n + 2, (uint8_t *) v, sizeof(v)); \
+ if (ll < 0) { \
+ haltest_error("Value must be byte hex string\n"); \
+ return; \
+ } \
+ l = ll; \
+ } while (0)
+
+/* Gatt uses little endian uuid */
+static const char GATT_BASE_UUID[] = {
+ 0xfb, 0x34, 0x9b, 0x5f, 0x80, 0x00, 0x00, 0x80,
+ 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+};
+
+/*
+ * converts gatt uuid to string
+ * buf should be at least 39 bytes
+ *
+ * This function formats 16, 32 and 128 bits uuid
+ *
+ * returns string representation of uuid
+ */
+static char *gatt_uuid_t2str(const bt_uuid_t *uuid, char *buf)
+{
+ int shift = 0;
+ int i = 16;
+ int limit = 0;
+ int j = 0;
+
+ /* for bluetooth uuid only 32 bits */
+ if (0 == memcmp(&uuid->uu, &GATT_BASE_UUID,
+ sizeof(bt_uuid_t) - 4)) {
+ limit = 12;
+ /* make it 16 bits */
+ if (uuid->uu[15] == 0 && uuid->uu[14] == 0)
+ i = 14;
+ }
+
+ while (i-- > limit) {
+ if (i == 11 || i == 9 || i == 7 || i == 5) {
+ buf[j * 2 + shift] = '-';
+ shift++;
+ }
+
+ sprintf(buf + j * 2 + shift, "%02x", uuid->uu[i]);
+ ++j;
+ }
+
+ return buf;
+}
+
+/*
+ * Tries to convert hex string of given size into out buffer.
+ * Output buffer is little endian.
+ */
+static void scan_field(const char *str, int len, uint8_t *out, int out_size)
+{
+ int i;
+
+ memset(out, 0, out_size);
+ if (out_size * 2 > len + 1)
+ out_size = (len + 1) / 2;
+
+ for (i = 0; i < out_size && len > 0; ++i) {
+ len -= 2;
+ if (len >= 0)
+ sscanf(str + len, "%02hhx", &out[i]);
+ else
+ sscanf(str, "%1hhx", &out[i]);
+ }
+}
+
+/* Like strchr but with upper limit instead of 0 terminated string */
+static const char *strchrlimit(const char *p, const char *e, int c)
+{
+ while (p < e && *p != (char) c)
+ ++p;
+
+ return p < e ? p : NULL;
+}
+
+/*
+ * converts string to uuid
+ * it accepts uuid in following forms:
+ * 123
+ * 0000123
+ * 0000123-0014-1234-0000-000056789abc
+ * 0000123001412340000000056789abc
+ * 123-14-1234-0-56789abc
+ */
+static void gatt_str2bt_uuid_t(const char *str, int len, bt_uuid_t *uuid)
+{
+ int dash_cnt = 0;
+ int dashes[6] = {-1}; /* indexes of '-' or \0 */
+ static uint8_t filed_offset[] = { 16, 12, 10, 8, 6, 0 };
+ const char *p = str;
+ const char *e;
+ int i;
+
+ e = str + ((len >= 0) ? len : (int) strlen(str));
+
+ while (p != NULL && dash_cnt < 5) {
+ const char *f = strchrlimit(p, e, '-');
+
+ if (f != NULL)
+ dashes[++dash_cnt] = f++ - str;
+ p = f;
+ }
+
+ /* get index of \0 to dashes table */
+ if (dash_cnt < 5)
+ dashes[++dash_cnt] = e - str;
+
+ memcpy(uuid, GATT_BASE_UUID, sizeof(bt_uuid_t));
+
+ /* whole uuid in one string without dashes */
+ if (dash_cnt == 1 && dashes[1] > 8) {
+ if (dashes[1] > 32)
+ dashes[1] = 32;
+ scan_field(str, dashes[1],
+ &uuid->uu[16 - (dashes[1] + 1) / 2],
+ (dashes[1] + 1) / 2);
+ } else {
+ for (i = 0; i < dash_cnt; ++i) {
+ scan_field(str + dashes[i] + 1,
+ dashes[i + 1] - dashes[i] - 1,
+ &uuid->uu[filed_offset[i + 1]],
+ filed_offset[i] - filed_offset[i + 1]);
+ }
+ }
+}
+
+/* char_id formating function */
+static char *btgatt_gatt_id_t2str(const btgatt_gatt_id_t *char_id, char *buf)
+{
+ char uuid_buf[MAX_UUID_STR_LEN];
+
+ sprintf(buf, "{%s,%d}", gatt_uuid_t2str(&char_id->uuid, uuid_buf),
+ char_id->inst_id);
+ return buf;
+}
+
+/* Parse btgatt_gatt_id_t */
+static void str2btgatt_gatt_id_t(const char *buf, btgatt_gatt_id_t *char_id)
+{
+ const char *e;
+
+ memcpy(&char_id->uuid, &GATT_BASE_UUID, sizeof(bt_uuid_t));
+ char_id->inst_id = 0;
+
+ if (*buf == '{')
+ buf++;
+ e = strpbrk(buf, " ,}");
+ if (e == NULL)
+ e = buf + strlen(buf);
+
+ gatt_str2bt_uuid_t(buf, e - buf, &char_id->uuid);
+ if (*e == ',') {
+ buf = e + 1;
+ e = strpbrk(buf, " ,}");
+ if (e == NULL)
+ e = buf + strlen(buf);
+ if (buf < e)
+ char_id->inst_id = atoi(buf);
+ }
+}
+
+/* service_id formating function */
+static char *btgatt_srvc_id_t2str(const btgatt_srvc_id_t *srvc_id, char *buf)
+{
+ char uuid_buf[MAX_UUID_STR_LEN];
+
+ sprintf(buf, "{%s,%d,%d}", gatt_uuid_t2str(&srvc_id->id.uuid, uuid_buf),
+ srvc_id->id.inst_id, srvc_id->is_primary);
+ return buf;
+}
+
+/* Parse btgatt_srvc_id_t */
+static void str2btgatt_srvc_id_t(const char *buf, btgatt_srvc_id_t *srvc_id)
+{
+ const char *e;
+
+ memcpy(&srvc_id->id.uuid, &GATT_BASE_UUID, sizeof(bt_uuid_t));
+ srvc_id->id.inst_id = 0;
+ srvc_id->is_primary = 1;
+
+ if (*buf == '{')
+ buf++;
+ e = strpbrk(buf, " ,}");
+ if (e == NULL)
+ e = buf + strlen(buf);
+
+ gatt_str2bt_uuid_t(buf, e - buf, &srvc_id->id.uuid);
+ if (*e == ',') {
+ buf = e + 1;
+ e = strpbrk(buf, " ,}");
+ if (e == NULL)
+ e = buf + strlen(buf);
+ if (buf < e)
+ srvc_id->id.inst_id = atoi(buf);
+ }
+
+ if (*e == ',') {
+ buf = e + 1;
+ e = strpbrk(buf, " ,}");
+ if (e == NULL)
+ e = buf + strlen(buf);
+ if (buf < e)
+ srvc_id->is_primary = atoi(buf);
+ }
+}
+
+/* Converts array of uint8_t to string representation */
+static char *array2str(const uint8_t *v, int size, char *buf, int out_size)
+{
+ int limit = size;
+ int i;
+
+ if (out_size > 0) {
+ *buf = '\0';
+ if (size >= 2 * out_size)
+ limit = (out_size - 2) / 2;
+
+ for (i = 0; i < limit; ++i)
+ sprintf(buf + 2 * i, "%02x", v[i]);
+
+ /* output buffer not enough to hold whole field fill with ...*/
+ if (limit < size)
+ sprintf(buf + 2 * i, "...");
+ }
+
+ return buf;
+}
+
+/* Converts btgatt_notify_params_t to string */
+static char *btgatt_notify_params_t2str(const btgatt_notify_params_t *data,
+ char *buf)
+{
+ char addr[MAX_ADDR_STR_LEN];
+ char srvc_id[MAX_SRVC_ID_STR_LEN];
+ char char_id[MAX_CHAR_ID_STR_LEN];
+ char value[MAX_HEX_VAL_STR_LEN];
+
+ sprintf(buf, "{bda=%s, srvc_id=%s, char_id=%s, val=%s, is_notify=%u}",
+ bt_bdaddr_t2str(&data->bda, addr),
+ btgatt_srvc_id_t2str(&data->srvc_id, srvc_id),
+ btgatt_gatt_id_t2str(&data->char_id, char_id),
+ array2str(data->value, data->len, value, sizeof(value)),
+ data->is_notify);
+ return buf;
+}
+
+static char *btgatt_unformatted_value_t2str(const btgatt_unformatted_value_t *v,
+ char *buf, int size)
+{
+ return array2str(v->value, v->len, buf, size);
+}
+
+static char *btgatt_read_params_t2str(const btgatt_read_params_t *data,
+ char *buf)
+{
+ char srvc_id[MAX_SRVC_ID_STR_LEN];
+ char char_id[MAX_CHAR_ID_STR_LEN];
+ char descr_id[MAX_UUID_STR_LEN];
+ char value[MAX_HEX_VAL_STR_LEN];
+
+ sprintf(buf, "{srvc_id=%s, char_id=%s, descr_id=%s, val=%s value_type=%d, status=%d}",
+ btgatt_srvc_id_t2str(&data->srvc_id, srvc_id),
+ btgatt_gatt_id_t2str(&data->char_id, char_id),
+ btgatt_descr_id_t2str(&data->descr_id, descr_id),
+ btgatt_unformatted_value_t2str(&data->value, value, 100),
+ data->value_type, data->status);
+ return buf;
+}
+
+/* BT-GATT Client callbacks. */
+
+/* Cache client_if and conn_id for tab completion */
+static char client_if_str[20];
+static char conn_id_str[20];
+/* Cache address for tab completion */
+static char last_addr[MAX_ADDR_STR_LEN];
+
+/* Callback invoked in response to register_client */
+static void gattc_register_client_cb(int status, int client_if,
+ bt_uuid_t *app_uuid)
+{
+ char buf[MAX_UUID_STR_LEN];
+
+ snprintf(client_if_str, sizeof(client_if_str), "%d", client_if);
+
+ haltest_info("%s: status=%d client_if=%d app_uuid=%s\n", __func__,
+ status, client_if,
+ gatt_uuid_t2str(app_uuid, buf));
+}
+
+/* Callback for scan results */
+static void gattc_scan_result_cb(bt_bdaddr_t *bda, int rssi, uint8_t *adv_data)
+{
+ char buf[MAX_ADDR_STR_LEN];
+
+ haltest_info("%s: bda=%s rssi=%d adv_data=%p\n", __func__,
+ bt_bdaddr_t2str(bda, buf), rssi, adv_data);
+}
+
+/* GATT open callback invoked in response to open */
+static void gattc_connect_cb(int conn_id, int status, int client_if,
+ bt_bdaddr_t *bda)
+{
+ haltest_info("%s: conn_id=%d status=%d, client_if=%d bda=%s\n",
+ __func__, conn_id, status, client_if,
+ bt_bdaddr_t2str(bda, last_addr));
+}
+
+/* Callback invoked in response to close */
+static void gattc_disconnect_cb(int conn_id, int status, int client_if,
+ bt_bdaddr_t *bda)
+{
+ char buf[MAX_ADDR_STR_LEN];
+
+ haltest_info("%s: conn_id=%d status=%d, client_if=%d bda=%s\n",
+ __func__, conn_id, status, client_if,
+ bt_bdaddr_t2str(bda, buf));
+}
+
+/*
+ * Invoked in response to search_service when the GATT service search
+ * has been completed.
+ */
+static void gattc_search_complete_cb(int conn_id, int status)
+{
+ haltest_info("%s: conn_id=%d status=%d\n", __func__, conn_id, status);
+}
+
+/* Reports GATT services on a remote device */
+static void gattc_search_result_cb(int conn_id, btgatt_srvc_id_t *srvc_id)
+{
+ char srvc_id_buf[MAX_SRVC_ID_STR_LEN];
+
+ haltest_info("%s: conn_id=%d srvc_id=%s\n", __func__, conn_id,
+ btgatt_srvc_id_t2str(srvc_id, srvc_id_buf));
+}
+
+/* GATT characteristic enumeration result callback */
+static void gattc_get_characteristic_cb(int conn_id, int status,
+ btgatt_srvc_id_t *srvc_id,
+ btgatt_gatt_id_t *char_id,
+ int char_prop)
+{
+ char srvc_id_buf[MAX_SRVC_ID_STR_LEN];
+ char char_id_buf[MAX_CHAR_ID_STR_LEN];
+
+ haltest_info("%s: conn_id=%d status=%d srvc_id=%s char_id=%s, char_prop=0x%x\n",
+ __func__, conn_id, status,
+ btgatt_srvc_id_t2str(srvc_id, srvc_id_buf),
+ btgatt_gatt_id_t2str(char_id, char_id_buf), char_prop);
+
+ /* enumerate next characteristic */
+ if (status == 0)
+ EXEC(if_gatt->client->get_characteristic, conn_id, srvc_id,
+ char_id);
+}
+
+/* GATT descriptor enumeration result callback */
+static void gattc_get_descriptor_cb(int conn_id, int status,
+ btgatt_srvc_id_t *srvc_id, btgatt_gatt_id_t *char_id,
+ btgatt_descr_id_t *descr_id)
+{
+ char buf[MAX_UUID_STR_LEN];
+ char srvc_id_buf[MAX_SRVC_ID_STR_LEN];
+ char char_id_buf[MAX_CHAR_ID_STR_LEN];
+
+ haltest_info("%s: conn_id=%d status=%d srvc_id=%s char_id=%s, descr_id=%s\n",
+ __func__, conn_id, status,
+ btgatt_srvc_id_t2str(srvc_id, srvc_id_buf),
+ btgatt_gatt_id_t2str(char_id, char_id_buf),
+ btgatt_descr_id_t2str(descr_id, buf));
+
+ if (status == 0)
+ EXEC(if_gatt->client->get_descriptor, conn_id, srvc_id, char_id,
+ descr_id);
+}
+
+/* GATT included service enumeration result callback */
+static void gattc_get_included_service_cb(int conn_id, int status,
+ btgatt_srvc_id_t *srvc_id,
+ btgatt_srvc_id_t *incl_srvc_id)
+{
+ char srvc_id_buf[MAX_SRVC_ID_STR_LEN];
+ char incl_srvc_id_buf[MAX_SRVC_ID_STR_LEN];
+
+ haltest_info("%s: conn_id=%d status=%d srvc_id=%s incl_srvc_id=%s)\n",
+ __func__, conn_id, status,
+ btgatt_srvc_id_t2str(srvc_id, srvc_id_buf),
+ btgatt_srvc_id_t2str(incl_srvc_id, incl_srvc_id_buf));
+
+ if (status == 0)
+ EXEC(if_gatt->client->get_included_service, conn_id, srvc_id,
+ incl_srvc_id);
+}
+
+/* Callback invoked in response to [de]register_for_notification */
+static void gattc_register_for_notification_cb(int conn_id, int registered,
+ int status,
+ btgatt_srvc_id_t *srvc_id,
+ btgatt_gatt_id_t *char_id)
+{
+ char srvc_id_buf[MAX_SRVC_ID_STR_LEN];
+ char char_id_buf[MAX_CHAR_ID_STR_LEN];
+
+ haltest_info("%s: conn_id=%d registered=%d status=%d srvc_id=%s char_id=%s\n",
+ __func__, conn_id, registered, status,
+ btgatt_srvc_id_t2str(srvc_id, srvc_id_buf),
+ btgatt_gatt_id_t2str(char_id, char_id_buf));
+}
+
+/*
+ * Remote device notification callback, invoked when a remote device sends
+ * a notification or indication that a client has registered for.
+ */
+static void gattc_notify_cb(int conn_id, btgatt_notify_params_t *p_data)
+{
+ char buf[MAX_NOTIFY_PARAMS_STR_LEN];
+
+ haltest_info("%s: conn_id=%d data=%s\n", __func__, conn_id,
+ btgatt_notify_params_t2str(p_data, buf));
+}
+
+/* Reports result of a GATT read operation */
+static void gattc_read_characteristic_cb(int conn_id, int status,
+ btgatt_read_params_t *p_data)
+{
+ char buf[MAX_READ_PARAMS_STR_LEN];
+
+ haltest_info("%s: conn_id=%d status=%d data=%s\n", __func__, conn_id,
+ status, btgatt_read_params_t2str(p_data, buf));
+}
+
+/* GATT write characteristic operation callback */
+static void gattc_write_characteristic_cb(int conn_id, int status,
+ btgatt_write_params_t *p_data)
+{
+ haltest_info("%s: conn_id=%d status=%d\n", __func__, conn_id, status);
+}
+
+/* GATT execute prepared write callback */
+static void gattc_execute_write_cb(int conn_id, int status)
+{
+ haltest_info("%s: conn_id=%d status=%d\n", __func__, conn_id, status);
+}
+
+/* Callback invoked in response to read_descriptor */
+static void gattc_read_descriptor_cb(int conn_id, int status,
+ btgatt_read_params_t *p_data)
+{
+ char buf[MAX_READ_PARAMS_STR_LEN];
+
+ haltest_info("%s: conn_id=%d status=%d data=%s\n", __func__, conn_id,
+ status, btgatt_read_params_t2str(p_data, buf));
+}
+
+/* Callback invoked in response to write_descriptor */
+static void gattc_write_descriptor_cb(int conn_id, int status,
+ btgatt_write_params_t *p_data)
+{
+ haltest_info("%s: conn_id=%d status=%d\n", __func__, conn_id, status);
+}
+
+/* Callback triggered in response to read_remote_rssi */
+static void gattc_read_remote_rssi_cb(int client_if, bt_bdaddr_t *bda, int rssi,
+ int status)
+{
+ char buf[MAX_ADDR_STR_LEN];
+
+ haltest_info("%s: client_if=%d bda=%s rssi=%d satus=%d\n", __func__,
+ client_if, bt_bdaddr_t2str(bda, buf), rssi, status);
+}
+
+/* Callback invoked in response to listen */
+static void gattc_listen_cb(int status, int client_if)
+{
+ haltest_info("%s: client_if=%d status=%d\n", __func__, client_if,
+ status);
+}
+
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+/* Callback invoked when the MTU for a given connection changes */
+static void gattc_configure_mtu_cb(int conn_id, int status, int mtu)
+{
+ haltest_info("%s: conn_id=%d, status=%d, mtu=%d\n", __func__, conn_id,
+ status, mtu);
+}
+
+/* Callback invoked when a scan filter configuration command has completed */
+static void gattc_scan_filter_cfg_cb(int action, int client_if, int status,
+ int filt_type, int avbl_space)
+{
+ haltest_info("%s: action=%d, client_if=%d, status=%d, filt_type=%d"
+ ", avbl_space=%d", __func__, action, client_if, status,
+ filt_type, avbl_space);
+}
+
+/* Callback invoked when scan param has been added, cleared, or deleted */
+static void gattc_scan_filter_param_cb(int action, int client_if, int status,
+ int avbl_space)
+{
+ haltest_info("%s: action=%d, client_if=%d, status=%d, avbl_space=%d",
+ __func__, action, client_if, status, avbl_space);
+}
+
+/* Callback invoked when a scan filter configuration command has completed */
+static void gattc_scan_filter_status_cb(int enable, int client_if, int status)
+{
+ haltest_info("%s: enable=%d, client_if=%d, status=%d", __func__,
+ enable, client_if, status);
+}
+
+/* Callback invoked when multi-adv enable operation has completed */
+static void gattc_multi_adv_enable_cb(int client_if, int status)
+{
+ haltest_info("%s: client_if=%d, status=%d", __func__, client_if,
+ status);
+}
+
+/* Callback invoked when multi-adv param update operation has completed */
+static void gattc_multi_adv_update_cb(int client_if, int status)
+{
+ haltest_info("%s: client_if=%d, status=%d", __func__, client_if,
+ status);
+}
+
+/* Callback invoked when multi-adv instance data set operation has completed */
+static void gattc_multi_adv_data_cb(int client_if, int status)
+{
+ haltest_info("%s: client_if=%d, status=%d", __func__, client_if,
+ status);
+}
+
+/* Callback invoked when multi-adv disable operation has completed */
+static void gattc_multi_adv_disable_cb(int client_if, int status)
+{
+ haltest_info("%s: client_if=%d, status=%d", __func__, client_if,
+ status);
+}
+
+/*
+ * Callback notifying an application that a remote device connection is
+ * currently congested and cannot receive any more data. An application should
+ * avoid sending more data until a further callback is received indicating the
+ * congestion status has been cleared.
+ */
+static void gattc_congestion_cb(int conn_id, bool congested)
+{
+ haltest_info("%s: conn_id=%d, congested=%d", __func__, conn_id,
+ congested);
+}
+
+/* Callback invoked when batchscan storage config operation has completed */
+static void gattc_batchscan_cfg_storage_cb(int client_if, int status)
+{
+ haltest_info("%s: client_if=%d, status=%d", __func__, client_if,
+ status);
+}
+
+/* Callback invoked when batchscan enable / disable operation has completed */
+static void gattc_batchscan_enable_disable_cb(int action, int client_if,
+ int status)
+{
+ haltest_info("%s: action=%d, client_if=%d, status=%d", __func__, action,
+ client_if, status);
+}
+
+/* Callback invoked when batchscan reports are obtained */
+static void gattc_batchscan_reports_cb(int client_if, int status,
+ int report_format, int num_records,
+ int data_len, uint8_t* rep_data)
+{
+ /* BTGATT_MAX_ATTR_LEN = 600 */
+ char valbuf[600];
+
+ haltest_info("%s: client_if=%d, status=%d, report_format=%d"
+ ", num_records=%d, data_len=%d, rep_data=%s", __func__,
+ client_if, status, report_format, num_records, data_len,
+ array2str(rep_data, data_len, valbuf, sizeof(valbuf)));
+}
+
+/* Callback invoked when batchscan storage threshold limit is crossed */
+static void gattc_batchscan_threshold_cb(int client_if)
+{
+ haltest_info("%s: client_if=%d", __func__, client_if);
+}
+
+/* Track ADV VSE callback invoked when tracked device is found or lost */
+static void gattc_track_adv_event_cb(int client_if, int filt_index,
+ int addr_type, bt_bdaddr_t* bda,
+ int adv_state)
+{
+ char buf[MAX_ADDR_STR_LEN];
+
+ haltest_info("%s, client_if=%d, filt_index=%d, addr_type=%d, bda=%s"
+ ", adv_state=%d", __func__, client_if, filt_index,
+ addr_type, bt_bdaddr_t2str(bda, buf), adv_state);
+}
+#endif
+
+static const btgatt_client_callbacks_t btgatt_client_callbacks = {
+ .register_client_cb = gattc_register_client_cb,
+ .scan_result_cb = gattc_scan_result_cb,
+ .open_cb = gattc_connect_cb,
+ .close_cb = gattc_disconnect_cb,
+ .search_complete_cb = gattc_search_complete_cb,
+ .search_result_cb = gattc_search_result_cb,
+ .get_characteristic_cb = gattc_get_characteristic_cb,
+ .get_descriptor_cb = gattc_get_descriptor_cb,
+ .get_included_service_cb = gattc_get_included_service_cb,
+ .register_for_notification_cb = gattc_register_for_notification_cb,
+ .notify_cb = gattc_notify_cb,
+ .read_characteristic_cb = gattc_read_characteristic_cb,
+ .write_characteristic_cb = gattc_write_characteristic_cb,
+ .read_descriptor_cb = gattc_read_descriptor_cb,
+ .write_descriptor_cb = gattc_write_descriptor_cb,
+ .execute_write_cb = gattc_execute_write_cb,
+ .read_remote_rssi_cb = gattc_read_remote_rssi_cb,
+ .listen_cb = gattc_listen_cb,
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+ .configure_mtu_cb = gattc_configure_mtu_cb,
+ .scan_filter_cfg_cb = gattc_scan_filter_cfg_cb,
+ .scan_filter_param_cb = gattc_scan_filter_param_cb,
+ .scan_filter_status_cb = gattc_scan_filter_status_cb,
+ .multi_adv_enable_cb = gattc_multi_adv_enable_cb,
+ .multi_adv_update_cb = gattc_multi_adv_update_cb,
+ .multi_adv_data_cb = gattc_multi_adv_data_cb,
+ .multi_adv_disable_cb = gattc_multi_adv_disable_cb,
+ .congestion_cb = gattc_congestion_cb,
+ .batchscan_cfg_storage_cb = gattc_batchscan_cfg_storage_cb,
+ .batchscan_enb_disable_cb = gattc_batchscan_enable_disable_cb,
+ .batchscan_reports_cb = gattc_batchscan_reports_cb,
+ .batchscan_threshold_cb = gattc_batchscan_threshold_cb,
+ .track_adv_event_cb = gattc_track_adv_event_cb,
+#endif
+};
+
+/* BT-GATT Server callbacks */
+
+/* Cache server_if and conn_id for tab completion */
+static char server_if_str[20];
+
+/* Callback invoked in response to register_server */
+static void gatts_register_server_cb(int status, int server_if,
+ bt_uuid_t *app_uuid)
+{
+ char buf[MAX_UUID_STR_LEN];
+
+ haltest_info("%s: status=%d server_if=%d app_uuid=%s\n", __func__,
+ status, server_if, gatt_uuid_t2str(app_uuid, buf));
+}
+
+/*
+ * Callback indicating that a remote device has connected
+ * or been disconnected
+ */
+static void gatts_connection_cb(int conn_id, int server_if, int connected,
+ bt_bdaddr_t *bda)
+{
+ haltest_info("%s: conn_id=%d server_if=%d connected=%d bda=%s\n",
+ __func__, conn_id, server_if, connected,
+ bt_bdaddr_t2str(bda, last_addr));
+ snprintf(conn_id_str, sizeof(conn_id_str), "%d", conn_id);
+}
+
+/* Callback invoked in response to create_service */
+static void gatts_service_added_cb(int status, int server_if,
+ btgatt_srvc_id_t *srvc_id, int srvc_handle)
+{
+ char buf[MAX_SRVC_ID_STR_LEN];
+
+ snprintf(server_if_str, sizeof(server_if_str), "%d", server_if);
+
+ haltest_info("%s: status=%d server_if=%d srvc_id=%s handle=0x%x\n",
+ __func__, status, server_if,
+ btgatt_srvc_id_t2str(srvc_id, buf), srvc_handle);
+}
+
+/* Callback indicating that an included service has been added to a service */
+static void gatts_included_service_added_cb(int status, int server_if,
+ int srvc_handle,
+ int incl_srvc_handle)
+{
+ haltest_info("%s: status=%d server_if=%d srvc_handle=0x%x inc_srvc_handle=0x%x\n",
+ __func__, status, server_if,
+ srvc_handle, incl_srvc_handle);
+}
+
+/* Callback invoked when a characteristic has been added to a service */
+static void gatts_characteristic_added_cb(int status, int server_if,
+ bt_uuid_t *uuid,
+ int srvc_handle,
+ int char_handle)
+{
+ char buf[MAX_SRVC_ID_STR_LEN];
+
+ haltest_info("%s: status=%d server_if=%d uuid=%s srvc_handle=0x%x char_handle=0x%x\n",
+ __func__, status, server_if, gatt_uuid_t2str(uuid, buf),
+ srvc_handle, char_handle);
+}
+
+/* Callback invoked when a descriptor has been added to a characteristic */
+static void gatts_descriptor_added_cb(int status, int server_if,
+ bt_uuid_t *uuid, int srvc_handle,
+ int descr_handle)
+{
+ char buf[MAX_SRVC_ID_STR_LEN];
+
+ haltest_info("%s: status=%d server_if=%d uuid=%s srvc_handle=0x%x descr_handle=0x%x\n",
+ __func__, status, server_if, gatt_uuid_t2str(uuid, buf),
+ srvc_handle, descr_handle);
+}
+
+/* Callback invoked in response to start_service */
+static void gatts_service_started_cb(int status, int server_if, int srvc_handle)
+{
+ haltest_info("%s: status=%d server_if=%d srvc_handle=0x%x\n",
+ __func__, status, server_if, srvc_handle);
+}
+
+/* Callback invoked in response to stop_service */
+static void gatts_service_stopped_cb(int status, int server_if, int srvc_handle)
+{
+ haltest_info("%s: status=%d server_if=%d srvc_handle=0x%x\n",
+ __func__, status, server_if, srvc_handle);
+}
+
+/* Callback triggered when a service has been deleted */
+static void gatts_service_deleted_cb(int status, int server_if, int srvc_handle)
+{
+ haltest_info("%s: status=%d server_if=%d srvc_handle=0x%x\n",
+ __func__, status, server_if, srvc_handle);
+}
+
+/*
+ * Callback invoked when a remote device has requested to read a characteristic
+ * or descriptor. The application must respond by calling send_response
+ */
+static void gatts_request_read_cb(int conn_id, int trans_id, bt_bdaddr_t *bda,
+ int attr_handle, int offset,
+ bool is_long)
+{
+ char buf[MAX_ADDR_STR_LEN];
+
+ haltest_info("%s: conn_id=%d trans_id=%d bda=%s attr_handle=0x%x offset=%d is_long=%d\n",
+ __func__, conn_id, trans_id, bt_bdaddr_t2str(bda, buf),
+ attr_handle, offset, is_long);
+}
+
+/*
+ * Callback invoked when a remote device has requested to write to a
+ * characteristic or descriptor.
+ */
+static void gatts_request_write_cb(int conn_id, int trans_id, bt_bdaddr_t *bda,
+ int attr_handle, int offset, int length,
+ bool need_rsp, bool is_prep,
+ uint8_t *value)
+{
+ char buf[MAX_ADDR_STR_LEN];
+ char valbuf[100];
+
+ haltest_info("%s: conn_id=%d trans_id=%d bda=%s attr_handle=0x%x offset=%d length=%d need_rsp=%d is_prep=%d value=%s\n",
+ __func__, conn_id, trans_id, bt_bdaddr_t2str(bda, buf),
+ attr_handle, offset, length, need_rsp, is_prep,
+ array2str(value, length, valbuf, sizeof(valbuf)));
+}
+
+/* Callback invoked when a previously prepared write is to be executed */
+static void gatts_request_exec_write_cb(int conn_id, int trans_id,
+ bt_bdaddr_t *bda, int exec_write)
+{
+ char buf[MAX_ADDR_STR_LEN];
+
+ haltest_info("%s: conn_id=%d trans_id=%d bda=%s exec_write=%d\n",
+ __func__, conn_id, trans_id, bt_bdaddr_t2str(bda, buf),
+ exec_write);
+}
+
+/*
+ * Callback triggered in response to send_response if the remote device
+ * sends a confirmation.
+ */
+static void gatts_response_confirmation_cb(int status, int handle)
+{
+ haltest_info("%s: status=%d handle=0x%x\n", __func__, status, handle);
+}
+
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+/**
+ * Callback confirming that a notification or indication has been sent
+ * to a remote device.
+ */
+static void gatts_indication_sent_cb(int conn_id, int status)
+{
+ haltest_info("%s: status=%d conn_id=%d", __func__, status, conn_id);
+}
+
+/**
+ * Callback notifying an application that a remote device connection is
+ * currently congested and cannot receive any more data. An application
+ * should avoid sending more data until a further callback is received
+ * indicating the congestion status has been cleared.
+ */
+static void gatts_congestion_cb(int conn_id, bool congested)
+{
+ haltest_info("%s: conn_id=%d congested=%d", __func__, conn_id,
+ congested);
+}
+#endif
+
+static const btgatt_server_callbacks_t btgatt_server_callbacks = {
+ .register_server_cb = gatts_register_server_cb,
+ .connection_cb = gatts_connection_cb,
+ .service_added_cb = gatts_service_added_cb,
+ .included_service_added_cb = gatts_included_service_added_cb,
+ .characteristic_added_cb = gatts_characteristic_added_cb,
+ .descriptor_added_cb = gatts_descriptor_added_cb,
+ .service_started_cb = gatts_service_started_cb,
+ .service_stopped_cb = gatts_service_stopped_cb,
+ .service_deleted_cb = gatts_service_deleted_cb,
+ .request_read_cb = gatts_request_read_cb,
+ .request_write_cb = gatts_request_write_cb,
+ .request_exec_write_cb = gatts_request_exec_write_cb,
+ .response_confirmation_cb = gatts_response_confirmation_cb,
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+ .indication_sent_cb = gatts_indication_sent_cb,
+ .congestion_cb = gatts_congestion_cb,
+#endif
+};
+
+static const btgatt_callbacks_t gatt_cbacks = {
+ .size = sizeof(gatt_cbacks),
+ .client = &btgatt_client_callbacks,
+ .server = &btgatt_server_callbacks
+};
+
+/*
+ * convert hex string to uint8_t array
+ */
+static int fill_buffer(const char *str, uint8_t *out, int out_size)
+{
+ int str_len;
+ int i, j;
+ char c;
+ uint8_t b;
+
+ str_len = strlen(str);
+
+ /* Hex string must be byte format */
+ if (str_len % 2)
+ return -1;
+
+ for (i = 0, j = 0; i < out_size && j < str_len; i++, j++) {
+ c = str[j];
+
+ if (c >= 'a' && c <= 'f')
+ c += 'A' - 'a';
+
+ if (c >= '0' && c <= '9')
+ b = c - '0';
+ else if (c >= 'A' && c <= 'F')
+ b = 10 + c - 'A';
+ else
+ return 0;
+
+ j++;
+
+ c = str[j];
+
+ if (c >= 'a' && c <= 'f')
+ c += 'A' - 'a';
+
+ if (c >= '0' && c <= '9')
+ b = b * 16 + c - '0';
+ else if (c >= 'A' && c <= 'F')
+ b = b * 16 + 10 + c - 'A';
+ else
+ return 0;
+
+ out[i] = b;
+ }
+
+ return i;
+}
+
+/* gatt client methods */
+
+/* init */
+
+static void init_p(int argc, const char **argv)
+{
+ RETURN_IF_NULL(if_gatt);
+
+ EXEC(if_gatt->init, &gatt_cbacks);
+}
+
+/* cleanup */
+
+static void cleanup_p(int argc, const char **argv)
+{
+ RETURN_IF_NULL(if_gatt);
+
+ EXECV(if_gatt->cleanup);
+
+ if_gatt = NULL;
+}
+
+static struct method methods[] = {
+ STD_METHOD(init),
+ STD_METHOD(cleanup),
+ END_METHOD
+};
+
+const struct interface gatt_if = {
+ .name = "gatt",
+ .methods = methods
+};
+
+/* register_client */
+
+static void register_client_p(int argc, const char **argv)
+{
+ bt_uuid_t uuid;
+
+ RETURN_IF_NULL(if_gatt);
+
+ /* uuid */
+ if (argc <= 2)
+ gatt_str2bt_uuid_t("babe4bed", -1, &uuid);
+ else
+ gatt_str2bt_uuid_t(argv[2], -1, &uuid);
+
+ EXEC(if_gatt->client->register_client, &uuid);
+}
+
+/* unregister_client */
+
+static void unregister_client_c(int argc, const char **argv,
+ enum_func *enum_func, void **user)
+{
+ if (argc == 3) {
+ *user = client_if_str;
+ *enum_func = enum_one_string;
+ }
+}
+
+static void unregister_client_p(int argc, const char **argv)
+{
+ int client_if;
+
+ RETURN_IF_NULL(if_gatt);
+ VERIFY_CLIENT_IF(2, client_if);
+
+ EXEC(if_gatt->client->unregister_client, client_if);
+}
+
+/* scan */
+
+/* Same completion as unregister for now, start stop is not auto completed */
+#define scan_c unregister_client_c
+
+static void scan_p(int argc, const char **argv)
+{
+#if ANDROID_VERSION < PLATFORM_VER(5, 0, 0)
+ int client_if;
+#endif
+ int start = 1;
+
+ RETURN_IF_NULL(if_gatt);
+
+#if ANDROID_VERSION < PLATFORM_VER(5, 0, 0)
+ VERIFY_CLIENT_IF(2, client_if);
+
+ /* start */
+ if (argc >= 4)
+ start = strtol(argv[3], NULL, 0);
+
+ EXEC(if_gatt->client->scan, client_if, start);
+#else
+ /* start */
+ if (argc >= 3)
+ start = strtol(argv[2], NULL, 0);
+
+ EXEC(if_gatt->client->scan, start);
+#endif
+}
+
+/* connect */
+
+static void connect_c(int argc, const char **argv, enum_func *enum_func,
+ void **user)
+{
+ if (argc == 3) {
+ *user = client_if_str;
+ *enum_func = enum_one_string;
+ } else if (argc == 4) {
+ *user = NULL;
+ *enum_func = enum_devices;
+ }
+}
+
+static void connect_p(int argc, const char **argv)
+{
+ int client_if;
+ bt_bdaddr_t bd_addr;
+ int is_direct = 1;
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+ int transport = 1;
+#endif
+
+ RETURN_IF_NULL(if_gatt);
+ VERIFY_CLIENT_IF(2, client_if);
+ VERIFY_ADDR_ARG(3, &bd_addr);
+
+ /* is_direct */
+ if (argc > 4)
+ is_direct = strtol(argv[4], NULL, 0);
+
+#if ANDROID_VERSION < PLATFORM_VER(5, 0, 0)
+ EXEC(if_gatt->client->connect, client_if, &bd_addr, is_direct);
+#else
+ /* transport */
+ if (argc > 5)
+ transport = strtol(argv[5], NULL, 0);
+
+ EXEC(if_gatt->client->connect, client_if, &bd_addr, is_direct,
+ transport);
+#endif
+}
+
+/* disconnect */
+
+static void disconnect_c(int argc, const char **argv, enum_func *enum_func,
+ void **user)
+{
+ if (argc == 3) {
+ *user = client_if_str;
+ *enum_func = enum_one_string;
+ } else if (argc == 4) {
+ *user = last_addr;
+ *enum_func = enum_one_string;
+ } else if (argc == 5) {
+ *user = conn_id_str;
+ *enum_func = enum_one_string;
+ }
+}
+
+static void disconnect_p(int argc, const char **argv)
+{
+ int client_if;
+ bt_bdaddr_t bd_addr;
+ int conn_id;
+
+ RETURN_IF_NULL(if_gatt);
+ VERIFY_CLIENT_IF(2, client_if);
+ VERIFY_ADDR_ARG(3, &bd_addr);
+ VERIFY_CONN_ID(4, conn_id);
+
+ EXEC(if_gatt->client->disconnect, client_if, &bd_addr, conn_id);
+}
+
+/* listen */
+
+/* Same completion as unregister for now, start stop is not auto completed */
+#define listen_c unregister_client_c
+
+static void listen_p(int argc, const char **argv)
+{
+ int client_if;
+ int start = 1;
+
+ RETURN_IF_NULL(if_gatt);
+
+ VERIFY_CLIENT_IF(2, client_if);
+
+ /* start */
+ if (argc >= 4)
+ start = strtol(argv[3], NULL, 0);
+
+ EXEC(if_gatt->client->listen, client_if, start);
+}
+
+/* refresh */
+
+static void refresh_c(int argc, const char **argv, enum_func *enum_func,
+ void **user)
+{
+ if (argc == 3) {
+ *user = client_if_str;
+ *enum_func = enum_one_string;
+ } else if (argc == 4) {
+ *enum_func = enum_devices;
+ }
+}
+
+static void refresh_p(int argc, const char **argv)
+{
+ int client_if;
+ bt_bdaddr_t bd_addr;
+
+ RETURN_IF_NULL(if_gatt);
+ VERIFY_CLIENT_IF(2, client_if);
+ VERIFY_ADDR_ARG(3, &bd_addr);
+
+ EXEC(if_gatt->client->refresh, client_if, &bd_addr);
+}
+
+/* search_service */
+
+static void search_service_c(int argc, const char **argv, enum_func *enum_func,
+ void **user)
+{
+ if (argc == 3) {
+ *user = conn_id_str;
+ *enum_func = enum_one_string;
+ }
+}
+
+static void search_service_p(int argc, const char **argv)
+{
+ int conn_id;
+
+ RETURN_IF_NULL(if_gatt);
+
+ VERIFY_CONN_ID(2, conn_id);
+
+ /* uuid */
+ if (argc <= 3) {
+ EXEC(if_gatt->client->search_service, conn_id, NULL);
+
+ } else {
+ bt_uuid_t filter_uuid;
+
+ gatt_str2bt_uuid_t(argv[3], -1, &filter_uuid);
+ EXEC(if_gatt->client->search_service, conn_id, &filter_uuid);
+ }
+}
+
+/* get_included_service */
+
+static void get_included_service_c(int argc, const char **argv,
+ enum_func *enum_func, void **user)
+{
+ if (argc == 3) {
+ *user = conn_id_str;
+ *enum_func = enum_one_string;
+ }
+}
+
+static void get_included_service_p(int argc, const char **argv)
+{
+ int conn_id;
+ btgatt_srvc_id_t srvc_id;
+
+ RETURN_IF_NULL(if_gatt);
+ VERIFY_CONN_ID(2, conn_id);
+ VERIFY_SRVC_ID(3, &srvc_id);
+
+ EXEC(if_gatt->client->get_included_service, conn_id, &srvc_id, NULL);
+}
+
+/* get_characteristic */
+
+/* Same completion as get_included_service_c */
+#define get_characteristic_c get_included_service_c
+
+static void get_characteristic_p(int argc, const char **argv)
+{
+ int conn_id;
+ btgatt_srvc_id_t srvc_id;
+
+ RETURN_IF_NULL(if_gatt);
+ VERIFY_CONN_ID(2, conn_id);
+ VERIFY_SRVC_ID(3, &srvc_id);
+
+ EXEC(if_gatt->client->get_characteristic, conn_id, &srvc_id, NULL);
+}
+
+/* get_descriptor */
+
+/* Same completion as get_included_service_c */
+#define get_descriptor_c get_included_service_c
+
+static void get_descriptor_p(int argc, const char **argv)
+{
+ int conn_id;
+ btgatt_srvc_id_t srvc_id;
+ btgatt_gatt_id_t char_id;
+
+ RETURN_IF_NULL(if_gatt);
+ VERIFY_CONN_ID(2, conn_id);
+ VERIFY_SRVC_ID(3, &srvc_id);
+ VERIFY_CHAR_ID(4, &char_id);
+
+ EXEC(if_gatt->client->get_descriptor, conn_id, &srvc_id, &char_id,
+ NULL);
+}
+
+/* read_characteristic */
+
+/* Same completion as get_included_service_c */
+#define read_characteristic_c get_included_service_c
+
+static void read_characteristic_p(int argc, const char **argv)
+{
+ int conn_id;
+ btgatt_srvc_id_t srvc_id;
+ btgatt_gatt_id_t char_id;
+ int auth_req = 0;
+
+ RETURN_IF_NULL(if_gatt);
+ VERIFY_CONN_ID(2, conn_id);
+ VERIFY_SRVC_ID(3, &srvc_id);
+ VERIFY_CHAR_ID(4, &char_id);
+
+ /* auth_req */
+ if (argc > 5)
+ auth_req = strtol(argv[5], NULL, 0);
+
+ EXEC(if_gatt->client->read_characteristic, conn_id, &srvc_id, &char_id,
+ auth_req);
+}
+
+/* write_characteristic */
+
+static void write_characteristic_c(int argc, const char **argv,
+ enum_func *enum_func, void **user)
+{
+ /*
+ * This should be from tGATT_WRITE_TYPE but it's burried
+ * inside bluedroid guts
+ */
+ static const char *wrtypes[] = { "1", "2", "3", NULL };
+
+ if (argc == 3) {
+ *user = conn_id_str;
+ *enum_func = enum_one_string;
+ } else if (argc == 6) {
+ *user = wrtypes;
+ *enum_func = enum_strings;
+ }
+}
+
+static void write_characteristic_p(int argc, const char **argv)
+{
+ int conn_id;
+ btgatt_srvc_id_t srvc_id;
+ btgatt_gatt_id_t char_id;
+ int write_type;
+ int len;
+ int auth_req = 0;
+ uint8_t value[100];
+
+ RETURN_IF_NULL(if_gatt);
+ VERIFY_CONN_ID(2, conn_id);
+ VERIFY_SRVC_ID(3, &srvc_id);
+ VERIFY_CHAR_ID(4, &char_id);
+
+ /* write type */
+ if (argc <= 5) {
+ haltest_error("No write type specified\n");
+ return;
+ }
+ write_type = strtol(argv[5], NULL, 0);
+
+ GET_VERIFY_HEX_STRING(6, value, len);
+
+ /* auth_req */
+ if (argc > 7)
+ auth_req = strtol(argv[7], NULL, 0);
+
+ EXEC(if_gatt->client->write_characteristic, conn_id, &srvc_id, &char_id,
+ write_type, len, auth_req, (char *) value);
+}
+
+/* read_descriptor */
+
+/* Same completion as get_included_service_c */
+#define read_descriptor_c get_included_service_c
+
+static void read_descriptor_p(int argc, const char **argv)
+{
+ int conn_id;
+ btgatt_srvc_id_t srvc_id;
+ btgatt_gatt_id_t char_id;
+ btgatt_descr_id_t descr_id;
+ int auth_req = 0;
+
+ RETURN_IF_NULL(if_gatt);
+ VERIFY_CONN_ID(2, conn_id);
+ VERIFY_SRVC_ID(3, &srvc_id);
+ VERIFY_CHAR_ID(4, &char_id);
+ VERIFY_DESCR_ID(5, &descr_id);
+
+ /* auth_req */
+ if (argc > 6)
+ auth_req = strtol(argv[6], NULL, 0);
+
+ EXEC(if_gatt->client->read_descriptor, conn_id, &srvc_id, &char_id,
+ &descr_id, auth_req);
+}
+
+/* write_descriptor */
+
+static void write_descriptor_c(int argc, const char **argv,
+ enum_func *enum_func, void **user)
+{
+ /*
+ * This should be from tGATT_WRITE_TYPE but it's burried
+ * inside bluedroid guts
+ */
+ static const char *wrtypes[] = { "1", "2", "3", NULL };
+
+ if (argc == 3) {
+ *user = conn_id_str;
+ *enum_func = enum_one_string;
+ } else if (argc == 7) {
+ *user = wrtypes;
+ *enum_func = enum_strings;
+ }
+}
+
+static void write_descriptor_p(int argc, const char **argv)
+{
+ int conn_id;
+ btgatt_srvc_id_t srvc_id;
+ btgatt_gatt_id_t char_id;
+ btgatt_descr_id_t descr_id;
+ int write_type;
+ int len;
+ int auth_req = 0;
+ uint8_t value[200] = {0};
+
+ RETURN_IF_NULL(if_gatt);
+ VERIFY_CONN_ID(2, conn_id);
+ VERIFY_SRVC_ID(3, &srvc_id);
+ VERIFY_CHAR_ID(4, &char_id);
+ VERIFY_DESCR_ID(5, &descr_id);
+
+ /* write type */
+ if (argc <= 6) {
+ haltest_error("No write type specified\n");
+ return;
+ }
+ write_type = strtol(argv[6], NULL, 0);
+
+ /* value */
+ if (argc <= 7) {
+ haltest_error("No value specified\n");
+ return;
+ }
+
+ /* len in chars */
+ if (strncmp(argv[7], "0X", 2) && strncmp(argv[7], "0x", 2)) {
+ haltest_error("Value must be hex string");
+ return;
+ }
+
+ len = fill_buffer(argv[7] + 2, value, sizeof(value));
+
+ /* auth_req */
+ if (argc > 8)
+ auth_req = strtol(argv[8], NULL, 0);
+
+ EXEC(if_gatt->client->write_descriptor, conn_id, &srvc_id, &char_id,
+ &descr_id, write_type, len, auth_req, (char *) value);
+}
+
+/* execute_write */
+
+/* Same completion as search_service */
+#define execute_write_c search_service_c
+
+static void execute_write_p(int argc, const char **argv)
+{
+ int conn_id;
+ int execute;
+
+ RETURN_IF_NULL(if_gatt);
+ VERIFY_CONN_ID(2, conn_id);
+
+ /* execute */
+ if (argc <= 3) {
+ haltest_error("No execute specified\n");
+ return;
+ }
+ execute = strtol(argv[3], NULL, 0);
+
+ EXEC(if_gatt->client->execute_write, conn_id, execute);
+}
+
+/* register_for_notification */
+
+static void register_for_notification_c(int argc, const char **argv,
+ enum_func *enum_func, void **user)
+{
+ if (argc == 3) {
+ *user = client_if_str;
+ *enum_func = enum_one_string;
+ } else if (argc == 4) {
+ *user = last_addr;
+ *enum_func = enum_one_string;
+ }
+}
+
+static void register_for_notification_p(int argc, const char **argv)
+{
+ int client_if;
+ bt_bdaddr_t bd_addr;
+ btgatt_srvc_id_t srvc_id;
+ btgatt_gatt_id_t char_id;
+
+ RETURN_IF_NULL(if_gatt);
+ VERIFY_CLIENT_IF(2, client_if);
+ VERIFY_ADDR_ARG(3, &bd_addr);
+ VERIFY_SRVC_ID(4, &srvc_id);
+ VERIFY_CHAR_ID(5, &char_id);
+
+ EXEC(if_gatt->client->register_for_notification, client_if, &bd_addr,
+ &srvc_id, &char_id);
+}
+
+/* deregister_for_notification */
+
+/* Same completion as search_service */
+#define deregister_for_notification_c register_for_notification_c
+
+static void deregister_for_notification_p(int argc, const char **argv)
+{
+ int client_if;
+ bt_bdaddr_t bd_addr;
+ btgatt_srvc_id_t srvc_id;
+ btgatt_gatt_id_t char_id;
+
+ RETURN_IF_NULL(if_gatt);
+ VERIFY_CLIENT_IF(2, client_if);
+ VERIFY_ADDR_ARG(3, &bd_addr);
+ VERIFY_SRVC_ID(4, &srvc_id);
+ VERIFY_CHAR_ID(5, &char_id);
+
+ EXEC(if_gatt->client->deregister_for_notification, client_if, &bd_addr,
+ &srvc_id, &char_id);
+}
+
+/* read_remote_rssi */
+
+static void read_remote_rssi_c(int argc, const char **argv,
+ enum_func *enum_func, void **user)
+{
+ if (argc == 3) {
+ *user = client_if_str;
+ *enum_func = enum_one_string;
+ } else if (argc == 4) {
+ *enum_func = enum_devices;
+ }
+}
+
+static void read_remote_rssi_p(int argc, const char **argv)
+{
+ int client_if;
+ bt_bdaddr_t bd_addr;
+
+ RETURN_IF_NULL(if_gatt);
+ VERIFY_CLIENT_IF(2, client_if);
+ VERIFY_ADDR_ARG(3, &bd_addr);
+
+ EXEC(if_gatt->client->read_remote_rssi, client_if, &bd_addr);
+}
+
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+/* scan filter parameter setup */
+static void scan_filter_param_setup_c(int argc, const char **argv,
+ enum_func *enum_func, void **user)
+{
+ if (argc == 2) {
+ *user = client_if_str;
+ *enum_func = enum_one_string;
+ }
+}
+
+static void scan_filter_param_setup_p(int argc, const char **argv)
+{
+ int client_if;
+ int action;
+ int filt_index;
+ int feat_seln;
+ int list_logic_type, filt_logic_type;
+ int rssi_high_thres, rssi_low_thres;
+ int dely_mode;
+ int found_timeout, lost_timeout, found_timeout_cnt;
+
+ RETURN_IF_NULL(if_gatt);
+ VERIFY_CLIENT_IF(2, client_if);
+ VERIFY_ACTION(3, action);
+ VERIFY_FILT_INDEX(4, filt_index);
+ VERIFY_FEAT_SELN(5, feat_seln);
+ VERIFY_LIST_LOGIC_TYPE(6, list_logic_type);
+ VERIFY_FILT_LOGIC_TYPE(7, filt_logic_type);
+ VERIFY_RSSI_HI_THR(8, rssi_high_thres);
+ VERIFY_RSSI_LOW_THR(9, rssi_low_thres);
+ VERIFY_DELY_MODE(10, dely_mode);
+ VERIFY_FND_TIME(11, found_timeout);
+ VERIFY_LOST_TIME(12, lost_timeout);
+ VERIFY_FND_TIME_CNT(13, found_timeout_cnt);
+
+ EXEC(if_gatt->client->scan_filter_param_setup, client_if, action,
+ filt_index, feat_seln, list_logic_type, filt_logic_type,
+ rssi_high_thres, rssi_low_thres, dely_mode, found_timeout,
+ lost_timeout, found_timeout_cnt);
+}
+
+/* scan filter add remove */
+
+static void scan_filter_add_remove_c(int argc, const char **argv,
+ enum_func *enum_func, void **user)
+{
+ if (argc == 2) {
+ *user = client_if_str;
+ *enum_func = enum_one_string;
+ } else if (argc == 10) {
+ *user = last_addr;
+ *enum_func = enum_one_string;
+ }
+}
+
+static void scan_filter_add_remove_p(int argc, const char **argv)
+{
+ int client_if;
+ int action;
+ int filt_type;
+ int filt_index;
+ int company_id;
+ int company_id_mask;
+ bt_uuid_t p_uuid;
+ bt_uuid_t p_uuid_mask;
+ bt_bdaddr_t bd_addr;
+ char addr_type;
+ int data_len;
+ uint8_t p_data[100];
+ int mask_len;
+ uint8_t p_mask[100];
+
+ RETURN_IF_NULL(if_gatt);
+ VERIFY_CLIENT_IF(2, client_if);
+ VERIFY_ACTION(3, action);
+ VERIFY_FILT_TYPE(4, filt_type);
+ VERIFY_FILT_INDEX(5, filt_index);
+ VERIFY_COMP_ID(6, company_id);
+ VERIFY_COMP_ID_MASK(7, company_id_mask);
+
+ if (argc <= 9) {
+ haltest_error("No p_uuid, p_uuid_mask specified\n");
+ return;
+ }
+ gatt_str2bt_uuid_t(argv[8], -1, &p_uuid);
+ gatt_str2bt_uuid_t(argv[9], -1, &p_uuid_mask);
+
+ VERIFY_UUID(8, &p_uuid);
+ VERIFY_UUID(9, &p_uuid_mask);
+ VERIFY_ADDR_ARG(10, &bd_addr);
+ VERIFY_ADDR_TYPE(11, addr_type);
+ GET_VERIFY_HEX_STRING(12, p_data, data_len);
+ GET_VERIFY_HEX_STRING(13, p_mask, mask_len);
+
+ EXEC(if_gatt->client->scan_filter_add_remove, client_if, action,
+ filt_type, filt_index, company_id, company_id_mask,
+ &p_uuid, &p_uuid_mask, &bd_addr, addr_type, data_len,
+ (char *) p_data, mask_len, (char *) p_mask);
+}
+
+/* scan filter clean */
+static void scan_filter_clear_c(int argc, const char **argv,
+ enum_func *enum_func, void **user)
+{
+ if (argc == 2) {
+ *user = client_if_str;
+ *enum_func = enum_one_string;
+ }
+}
+
+static void scan_filter_clear_p(int argc, const char **argv)
+{
+ int client_if;
+ int filt_index;
+
+ RETURN_IF_NULL(if_gatt);
+ VERIFY_CLIENT_IF(2, client_if);
+ VERIFY_FILT_INDEX(3, filt_index);
+
+ EXEC(if_gatt->client->scan_filter_clear, client_if, filt_index);
+}
+
+/* scan filter enable */
+static void scan_filter_enable_c(int argc, const char **argv,
+ enum_func *enum_func, void **user)
+{
+ if (argc == 2) {
+ *user = client_if_str;
+ *enum_func = enum_one_string;
+ }
+}
+
+static void scan_filter_enable_p(int argc, const char **argv)
+{
+ int client_if;
+ int enable = 0;
+
+ RETURN_IF_NULL(if_gatt);
+ VERIFY_CLIENT_IF(2, client_if);
+
+ /* enable */
+ if (argc >= 4)
+ enable = strtol(argv[3], NULL, 0);
+
+ EXEC(if_gatt->client->scan_filter_clear, client_if, enable);
+}
+
+/* set advertising data */
+static void set_adv_data_c(int argc, const char **argv,
+ enum_func *enum_func, void **user)
+{
+ if (argc == 2) {
+ *user = client_if_str;
+ *enum_func = enum_one_string;
+ }
+}
+
+static void set_adv_data_p(int argc, const char **argv)
+{
+ int client_if;
+ bool set_scan_rsp;
+ bool include_name, include_txpower;
+ int min_interval, max_interval;
+ int appearance;
+ uint16_t manufacturer_len;
+ uint8_t manufacturer_data[100];
+ uint16_t service_data_len;
+ uint8_t service_data[100];
+ uint16_t service_uuid_len;
+ uint8_t service_uuid[100];
+
+ RETURN_IF_NULL(if_gatt);
+ VERIFY_CLIENT_IF(2, client_if);
+
+ /* set scan response */
+ if (argc >= 4)
+ set_scan_rsp = strtol(argv[3], NULL, 0);
+ /* include name */
+ if (argc >= 5)
+ include_name = strtol(argv[4], NULL, 0);
+ /* include txpower */
+ if (argc >= 6)
+ include_txpower = strtol(argv[5], NULL, 0);
+
+ VERIFY_MIN_INTERVAL(6, min_interval);
+ VERIFY_MAX_INTERVAL(7, max_interval);
+ VERIFY_APPEARANCE(8, appearance);
+ GET_VERIFY_HEX_STRING(9, manufacturer_data, manufacturer_len);
+ GET_VERIFY_HEX_STRING(10, service_data, service_data_len);
+ GET_VERIFY_HEX_STRING(11, service_uuid, service_uuid_len);
+
+ EXEC(if_gatt->client->set_adv_data, client_if, set_scan_rsp,
+ include_name, include_txpower, min_interval, max_interval,
+ appearance, manufacturer_len, (char *) manufacturer_data,
+ service_data_len, (char *) service_data, service_uuid_len,
+ (char *) service_uuid);
+}
+
+/* configure mtu */
+static void configure_mtu_c(int argc, const char **argv,
+ enum_func *enum_func, void **user)
+{
+ if (argc == 2) {
+ *user = conn_id_str;
+ *enum_func = enum_one_string;
+ }
+}
+
+static void configure_mtu_p(int argc, const char **argv)
+{
+ int conn_id;
+ int mtu;
+
+ RETURN_IF_NULL(if_gatt);
+ VERIFY_CONN_ID(2, conn_id);
+ VERIFY_MTU(3, mtu);
+
+ EXEC(if_gatt->client->configure_mtu, conn_id, mtu);
+}
+
+/* con parameter update */
+static void conn_parameter_update_c(int argc, const char **argv,
+ enum_func *enum_func, void **user)
+{
+ if (argc == 2) {
+ *user = last_addr;
+ *enum_func = enum_one_string;
+ }
+}
+
+static void conn_parameter_update_p(int argc, const char **argv)
+{
+ bt_bdaddr_t bd_addr;
+ int min_interval, max_interval;
+ int latency;
+ int timeout;
+
+ RETURN_IF_NULL(if_gatt);
+ VERIFY_ADDR_ARG(2, &bd_addr);
+ VERIFY_MIN_INTERVAL(3, min_interval);
+ VERIFY_MAX_INTERVAL(4, max_interval);
+ VERIFY_LATENCY(5, latency);
+ VERIFY_TIMEOUT(6, timeout);
+
+ EXEC(if_gatt->client->conn_parameter_update, &bd_addr, min_interval,
+ max_interval, latency, timeout);
+}
+
+/* set scan parameters */
+static void set_scan_parameters_p(int argc, const char **argv)
+{
+ int scan_interval;
+ int scan_window;
+
+ RETURN_IF_NULL(if_gatt);
+ VERIFY_SCAN_INTERVAL(2, scan_interval);
+ VERIFY_SCAN_WINDOW(3, scan_window);
+
+ EXEC(if_gatt->client->set_scan_parameters, scan_interval, scan_window);
+}
+
+/* enable multi advertising */
+static void multi_adv_enable_c(int argc, const char **argv,
+ enum_func *enum_func, void **user)
+{
+ if (argc == 2) {
+ *user = client_if_str;
+ *enum_func = enum_one_string;
+ }
+}
+
+static void multi_adv_enable_p(int argc, const char **argv)
+{
+ int client_if;
+ int min_interval, max_interval;
+ int adv_type;
+ int chnl_map;
+ int tx_power;
+ int timeout_s;
+
+ RETURN_IF_NULL(if_gatt);
+ VERIFY_CLIENT_IF(2, client_if);
+ VERIFY_MIN_INTERVAL(3, min_interval);
+ VERIFY_MAX_INTERVAL(4, max_interval);
+ VERIFY_ADV_TYPE(5, adv_type);
+ VERIFY_CHNL_MAP(6, chnl_map);
+ VERIFY_TX_POWER(7, tx_power);
+ VERIFY_TIMEOUT_S(8, timeout_s);
+
+ EXEC(if_gatt->client->multi_adv_enable, client_if, min_interval,
+ max_interval, adv_type, chnl_map, tx_power, timeout_s);
+}
+
+/* update multi advertiser */
+static void multi_adv_update_c(int argc, const char **argv,
+ enum_func *enum_func, void **user)
+{
+ if (argc == 2) {
+ *user = client_if_str;
+ *enum_func = enum_one_string;
+ }
+}
+
+static void multi_adv_update_p(int argc, const char **argv)
+{
+ int client_if;
+ int min_interval, max_interval;
+ int adv_type;
+ int chnl_map;
+ int tx_power;
+ int timeout_s;
+
+ RETURN_IF_NULL(if_gatt);
+ VERIFY_CLIENT_IF(2, client_if);
+ VERIFY_MIN_INTERVAL(3, min_interval);
+ VERIFY_MAX_INTERVAL(4, max_interval);
+ VERIFY_ADV_TYPE(5, adv_type);
+ VERIFY_CHNL_MAP(6, chnl_map);
+ VERIFY_TX_POWER(7, tx_power);
+ VERIFY_TIMEOUT_S(8, timeout_s);
+
+ EXEC(if_gatt->client->multi_adv_update, client_if, min_interval,
+ max_interval, adv_type, chnl_map, tx_power, timeout_s);
+}
+
+/* set advertising data */
+static void multi_adv_set_inst_data_c(int argc, const char **argv,
+ enum_func *enum_func, void **user)
+{
+ if (argc == 2) {
+ *user = client_if_str;
+ *enum_func = enum_one_string;
+ }
+}
+
+static void multi_adv_set_inst_data_p(int argc, const char **argv)
+{
+ int client_if;
+ bool set_scan_rsp;
+ bool include_name, include_txpower;
+ int appearance;
+ uint16_t manufacturer_len;
+ uint8_t manufacturer_data[100];
+ uint16_t service_data_len;
+ uint8_t service_data[100];
+ uint16_t service_uuid_len;
+ uint8_t service_uuid[100];
+
+ RETURN_IF_NULL(if_gatt);
+ VERIFY_CLIENT_IF(2, client_if);
+
+ /* set scan response */
+ if (argc >= 4)
+ set_scan_rsp = strtol(argv[3], NULL, 0);
+ /* include name */
+ if (argc >= 5)
+ include_name = strtol(argv[4], NULL, 0);
+ /* include txpower */
+ if (argc >= 6)
+ include_txpower = strtol(argv[5], NULL, 0);
+
+ VERIFY_APPEARANCE(6, appearance);
+ GET_VERIFY_HEX_STRING(7, manufacturer_data, manufacturer_len);
+ GET_VERIFY_HEX_STRING(8, service_data, service_data_len);
+ GET_VERIFY_HEX_STRING(9, service_uuid, service_uuid_len);
+
+ EXEC(if_gatt->client->multi_adv_set_inst_data, client_if, set_scan_rsp,
+ include_name, include_txpower, appearance, manufacturer_len,
+ (char *) manufacturer_data, service_data_len,
+ (char *) service_data, service_uuid_len, (char *) service_uuid);
+}
+
+/* multi advertising disable */
+static void multi_adv_disable_c(int argc, const char **argv,
+ enum_func *enum_func, void **user)
+{
+ if (argc == 2) {
+ *user = client_if_str;
+ *enum_func = enum_one_string;
+ }
+}
+
+static void multi_adv_disable_p(int argc, const char **argv)
+{
+ int client_if;
+
+ RETURN_IF_NULL(if_gatt);
+ VERIFY_CLIENT_IF(2, client_if);
+
+ EXEC(if_gatt->client->multi_adv_disable, client_if);
+}
+
+/* batch scanconfigure storage */
+static void batchscan_cfg_storage_c(int argc, const char **argv,
+ enum_func *enum_func, void **user)
+{
+ if (argc == 2) {
+ *user = client_if_str;
+ *enum_func = enum_one_string;
+ }
+}
+
+static void batchscan_cfg_storage_p(int argc, const char **argv)
+{
+ int client_if;
+ int batch_scan_full_max;
+ int batch_scan_trunc_max;
+ int batch_scan_notify_threshold;
+
+ RETURN_IF_NULL(if_gatt);
+ VERIFY_CLIENT_IF(2, client_if);
+ VERIFY_BATCH_SCAN_FULL_MAX(3, batch_scan_full_max);
+ VERIFY_BATCH_SCAN_TRUNC_MAX(4, batch_scan_trunc_max);
+ VERIFY_BATCH_SCAN_NOTIFY_THR(5, batch_scan_notify_threshold);
+
+ EXEC(if_gatt->client->batchscan_cfg_storage, client_if,
+ batch_scan_full_max, batch_scan_trunc_max,
+ batch_scan_notify_threshold);
+}
+
+/* enable batch scan */
+static void batchscan_enb_batch_scan_c(int argc, const char **argv,
+ enum_func *enum_func, void **user)
+{
+ if (argc == 2) {
+ *user = client_if_str;
+ *enum_func = enum_one_string;
+ }
+}
+
+static void batchscan_enb_batch_scan_p(int argc, const char **argv)
+{
+ int client_if;
+ int scan_mode, scan_interval, scan_window;
+ int addr_type;
+ int discard_rule;
+
+ RETURN_IF_NULL(if_gatt);
+ VERIFY_CLIENT_IF(2, client_if);
+ VERIFY_SCAN_MODE(3, scan_mode);
+ VERIFY_SCAN_INTERVAL(4, scan_interval);
+ VERIFY_SCAN_WINDOW(5, scan_window);
+ VERIFY_ADDR_TYPE(6, addr_type);
+ VERIFY_DISCARD_RULE(7, discard_rule);
+
+ EXEC(if_gatt->client->batchscan_enb_batch_scan, client_if, scan_mode,
+ scan_interval, scan_window, addr_type, discard_rule);
+}
+
+/* batchscan disable */
+static void batchscan_dis_batch_scan_c(int argc, const char **argv,
+ enum_func *enum_func, void **user)
+{
+ if (argc == 2) {
+ *user = client_if_str;
+ *enum_func = enum_one_string;
+ }
+}
+
+static void batchscan_dis_batch_scan_p(int argc, const char **argv)
+{
+ int client_if;
+
+ RETURN_IF_NULL(if_gatt);
+ VERIFY_CLIENT_IF(2, client_if);
+
+ EXEC(if_gatt->client->batchscan_dis_batch_scan, client_if);
+}
+
+/* batchscan read reports */
+static void batchscan_read_reports_c(int argc, const char **argv,
+ enum_func *enum_func, void **user)
+{
+ if (argc == 2) {
+ *user = client_if_str;
+ *enum_func = enum_one_string;
+ }
+}
+
+static void batchscan_read_reports_p(int argc, const char **argv)
+{
+ int client_if;
+ int scan_mode;
+
+ RETURN_IF_NULL(if_gatt);
+ VERIFY_CLIENT_IF(2, client_if);
+ VERIFY_SCAN_MODE(3, scan_mode);
+
+ EXEC(if_gatt->client->batchscan_read_reports, client_if, scan_mode);
+}
+#endif
+
+/* get_device_type */
+
+static void get_device_type_c(int argc, const char **argv, enum_func *enum_func,
+ void **user)
+{
+ if (argc == 3)
+ *enum_func = enum_devices;
+}
+
+static void get_device_type_p(int argc, const char **argv)
+{
+ bt_bdaddr_t bd_addr;
+ int dev_type;
+
+ RETURN_IF_NULL(if_gatt);
+ VERIFY_ADDR_ARG(2, &bd_addr);
+
+ dev_type = if_gatt->client->get_device_type(&bd_addr);
+ haltest_info("%s: %d\n", "get_device_type", dev_type);
+}
+
+/* test_command */
+
+static void test_command_c(int argc, const char **argv, enum_func *enum_func,
+ void **user)
+{
+ if (argc == 4)
+ *enum_func = enum_devices;
+}
+
+static void test_command_p(int argc, const char **argv)
+{
+ int command;
+ int i;
+ bt_bdaddr_t bd_addr;
+ bt_uuid_t uuid;
+ btgatt_test_params_t params = {
+ .bda1 = &bd_addr,
+ .uuid1 = &uuid
+ };
+ uint16_t *u = ¶ms.u1;
+
+ RETURN_IF_NULL(if_gatt);
+
+ /* command */
+ if (argc <= 2) {
+ haltest_error("No command specified\n");
+ return;
+ }
+ command = strtol(argv[2], NULL, 0);
+
+ VERIFY_ADDR_ARG(3, &bd_addr);
+ VERIFY_UUID(4, &uuid);
+
+ for (i = 5; i < argc; i++)
+ VERIFY_TEST_ARG(i, *u++);
+
+ EXEC(if_gatt->client->test_command, command, ¶ms);
+}
+
+static struct method client_methods[] = {
+ STD_METHODH(register_client, "[<uuid>]"),
+ STD_METHODCH(unregister_client, "<client_if>"),
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+ STD_METHODCH(scan, "[1|0]"),
+ STD_METHODCH(connect, "<client_if> <addr> [<is_direct>] [<transport]"),
+ STD_METHODCH(scan_filter_param_setup, "<client_if> <action>"
+ " <filt_index> <feat_seln> <list_logic_type>"
+ " <filt_logic_type> <rssi_high_thres> <rssi_low_thres>"
+ " <dely_mode> <found_timeout> <lost_timeout>"
+ " <found_timeout_cnt>"),
+ STD_METHODCH(scan_filter_add_remove, "<client_if> <action> <filt_type>"
+ " <filt_index> <company_id> <company_id_mask>"
+ " [<p_uuid>] <p_uuid_mask> <addr> <addr_type>"
+ " [<p_data>] [<p_mask>]"),
+ STD_METHODCH(scan_filter_clear, "<client_if> <filt_index>"),
+ STD_METHODCH(scan_filter_enable, "<client_if> [<enable>]"),
+ STD_METHODCH(set_adv_data, "<client_if> [<set_scan_rsp>] <include_name>"
+ " [<include_txpower>] <min_interval> <max_interval>"
+ " <appearance> [<manufacturer_data>] [<service_data>]"
+ " [<service_uuid>]"),
+ STD_METHODCH(configure_mtu, "<conn_id> <mtu>"),
+ STD_METHODCH(conn_parameter_update, "<bd_addr> <min_interval>"
+ " <max_interval> <latency> <timeout>"),
+ STD_METHODH(set_scan_parameters, "<scan_inverval> <scan_window>"),
+ STD_METHODCH(multi_adv_enable, "<client_if> <min_interval>"
+ " <max_interval> <adv_type> <chnl_map> <tx_power>"
+ " <timeout_s>"),
+ STD_METHODCH(multi_adv_update, "<client_if> <min_interval>"
+ " <max_interval> <adv_type> <chnl_map> <tx_power>"
+ " <timeout_s>"),
+ STD_METHODCH(multi_adv_set_inst_data, "<client_if> [<set_scan_rsp>]"
+ " <include_name> [<include_txpower>] <appearance>"
+ " [<manufacturer_data>] [<service_data>]"
+ " [<service_uuid>]"),
+ STD_METHODCH(multi_adv_disable, "<client_if>"),
+ STD_METHODCH(batchscan_cfg_storage, "<client_if> <batch_scan_full_max>"
+ " <batch_scan_trunc_max>"
+ " <batch_scan_notify_threshold>"),
+ STD_METHODCH(batchscan_enb_batch_scan, "<client_if> <scan_mode>"
+ " <scan_interval> <scan_window> <addr_type>"
+ " <discard_rule>"),
+ STD_METHODCH(batchscan_dis_batch_scan, "<client_if>"),
+ STD_METHODCH(batchscan_read_reports, "<client_if> <scan_mode>"),
+#else
+ STD_METHODCH(scan, "<client_if> [1|0]"),
+ STD_METHODCH(connect, "<client_if> <addr> [<is_direct>]"),
+#endif
+ STD_METHODCH(disconnect, "<client_if> <addr> <conn_id>"),
+ STD_METHODCH(refresh, "<client_if> <addr>"),
+ STD_METHODCH(search_service, "<conn_id> [<uuid>]"),
+ STD_METHODCH(get_included_service, "<conn_id> <srvc_id>"),
+ STD_METHODCH(get_characteristic, "<conn_id> <srvc_id>"),
+ STD_METHODCH(get_descriptor, "<conn_id> <srvc_id> <char_id>"),
+ STD_METHODCH(read_characteristic,
+ "<conn_id> <srvc_id> <char_id> [<auth_req>]"),
+ STD_METHODCH(write_characteristic,
+ "<conn_id> <srvc_id> <char_id> <write_type> <hex_value> [<auth_req>]"),
+ STD_METHODCH(read_descriptor,
+ "<conn_id> <srvc_id> <char_id> <descr_id> [<auth_req>]"),
+ STD_METHODCH(write_descriptor,
+ "<conn_id> <srvc_id> <char_id> <descr_id> <write_type> <hex_value> [<auth_req>]"),
+ STD_METHODCH(execute_write, "<conn_id> <execute>"),
+ STD_METHODCH(register_for_notification,
+ "<client_if> <addr> <srvc_id> <char_id>"),
+ STD_METHODCH(deregister_for_notification,
+ "<client_if> <addr> <srvc_id> <char_id>"),
+ STD_METHODCH(read_remote_rssi, "<client_if> <addr>"),
+ STD_METHODCH(get_device_type, "<addr>"),
+ STD_METHODCH(test_command,
+ "<cmd> <addr> <uuid> [u1] [u2] [u3] [u4] [u5]"),
+ STD_METHODCH(listen, "<client_if> [1|0]"),
+ END_METHOD
+};
+
+const struct interface gatt_client_if = {
+ .name = "gattc",
+ .methods = client_methods
+};
+
+/* gatt server methods */
+
+/* register_server */
+
+static void gatts_register_server_p(int argc, const char *argv[])
+{
+ bt_uuid_t uuid;
+
+ RETURN_IF_NULL(if_gatt);
+
+ /* uuid */
+ if (argc <= 2)
+ gatt_str2bt_uuid_t("bed4babe", -1, &uuid);
+ else
+ gatt_str2bt_uuid_t(argv[2], -1, &uuid);
+
+ EXEC(if_gatt->server->register_server, &uuid);
+}
+
+/* unregister_server */
+
+static void gatts_unregister_server_c(int argc, const char **argv,
+ enum_func *enum_func, void **user)
+{
+ if (argc == 3) {
+ *user = server_if_str;
+ *enum_func = enum_one_string;
+ }
+}
+
+static void gatts_unregister_server_p(int argc, const char *argv[])
+{
+ int server_if;
+
+ RETURN_IF_NULL(if_gatt);
+ VERIFY_SERVER_IF(2, server_if);
+
+ EXEC(if_gatt->server->unregister_server, server_if);
+}
+
+/* connect */
+
+static void gatts_connect_c(int argc, const char **argv, enum_func *enum_func,
+ void **user)
+{
+ if (argc == 3) {
+ *user = server_if_str;
+ *enum_func = enum_one_string;
+ } else if (argc == 4) {
+ *user = NULL;
+ *enum_func = enum_devices;
+ }
+}
+
+static void gatts_connect_p(int argc, const char *argv[])
+{
+ int server_if;
+ bt_bdaddr_t bd_addr;
+ int is_direct = 1;
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+ int transport = 1;
+#endif
+
+ RETURN_IF_NULL(if_gatt);
+ VERIFY_SERVER_IF(2, server_if);
+ VERIFY_ADDR_ARG(3, &bd_addr);
+
+ /* is_direct */
+ if (argc > 4)
+ is_direct = strtol(argv[4], NULL, 0);
+
+#if ANDROID_VERSION < PLATFORM_VER(5, 0, 0)
+ EXEC(if_gatt->server->connect, server_if, &bd_addr, is_direct);
+#else
+ /* transport */
+ if (argc > 5)
+ transport = strtol(argv[5], NULL, 0);
+
+ EXEC(if_gatt->server->connect, server_if, &bd_addr, is_direct,
+ transport);
+#endif
+}
+
+/* disconnect */
+
+static void gatts_disconnect_c(int argc, const char **argv,
+ enum_func *enum_func, void **user)
+{
+ if (argc == 3) {
+ *user = server_if_str;
+ *enum_func = enum_one_string;
+ } else if (argc == 4) {
+ *user = last_addr;
+ *enum_func = enum_one_string;
+ } else if (argc == 5) {
+ *user = conn_id_str;
+ *enum_func = enum_one_string;
+ }
+}
+
+static void gatts_disconnect_p(int argc, const char *argv[])
+{
+ int server_if;
+ bt_bdaddr_t bd_addr;
+ int conn_id;
+
+ RETURN_IF_NULL(if_gatt);
+ VERIFY_SERVER_IF(2, server_if);
+ VERIFY_ADDR_ARG(3, &bd_addr);
+ VERIFY_CONN_ID(4, conn_id);
+
+ EXEC(if_gatt->server->disconnect, server_if, &bd_addr, conn_id);
+}
+
+/* add_service */
+
+/* Same completion as gatts_unregister_server_c */
+#define gatts_add_service_c gatts_unregister_server_c
+
+static void gatts_add_service_p(int argc, const char *argv[])
+{
+ int server_if;
+ btgatt_srvc_id_t srvc_id;
+ int num_handles;
+
+ RETURN_IF_NULL(if_gatt);
+ VERIFY_SERVER_IF(2, server_if);
+ VERIFY_SRVC_ID(3, &srvc_id);
+
+ /* num handles */
+ if (argc <= 4) {
+ haltest_error("No num_handles specified\n");
+ return;
+ }
+ num_handles = strtol(argv[4], NULL, 0);
+
+ EXEC(if_gatt->server->add_service, server_if, &srvc_id, num_handles);
+}
+
+/* add_included_service */
+
+/* Same completion as gatts_unregister_server_c */
+#define gatts_add_included_service_c gatts_unregister_server_c
+
+static void gatts_add_included_service_p(int argc, const char *argv[])
+{
+ int server_if;
+ int service_handle;
+ int included_handle;
+
+ RETURN_IF_NULL(if_gatt);
+ VERIFY_SERVER_IF(2, server_if);
+ VERIFY_SERVICE_HANDLE(3, service_handle);
+ VERIFY_HANDLE(4, included_handle);
+
+ EXEC(if_gatt->server->add_included_service, server_if, service_handle,
+ included_handle);
+}
+
+/* add_characteristic */
+
+/* Same completion as gatts_unregister_server_c */
+#define gatts_add_characteristic_c gatts_unregister_server_c
+
+static void gatts_add_characteristic_p(int argc, const char *argv[])
+{
+ int server_if;
+ int service_handle;
+ int properties;
+ int permissions;
+ bt_uuid_t uuid;
+
+ RETURN_IF_NULL(if_gatt);
+ VERIFY_SERVER_IF(2, server_if);
+ VERIFY_SERVICE_HANDLE(3, service_handle);
+ VERIFY_UUID(4, &uuid);
+
+ /* properties */
+ if (argc <= 5) {
+ haltest_error("No properties specified\n");
+ return;
+ }
+ properties = strtol(argv[5], NULL, 0);
+
+ /* permissions */
+ if (argc <= 6) {
+ haltest_error("No permissions specified\n");
+ return;
+ }
+ permissions = strtol(argv[6], NULL, 0);
+
+ EXEC(if_gatt->server->add_characteristic, server_if, service_handle,
+ &uuid, properties, permissions);
+}
+
+/* add_descriptor */
+
+/* Same completion as gatts_unregister_server_c */
+#define gatts_add_descriptor_c gatts_unregister_server_c
+
+static void gatts_add_descriptor_p(int argc, const char *argv[])
+{
+ int server_if;
+ int service_handle;
+ int permissions;
+ bt_uuid_t uuid;
+
+ RETURN_IF_NULL(if_gatt);
+ VERIFY_SERVER_IF(2, server_if);
+ VERIFY_SERVICE_HANDLE(3, service_handle);
+ VERIFY_UUID(4, &uuid);
+
+ /* permissions */
+ if (argc <= 5) {
+ haltest_error("No permissions specified\n");
+ return;
+ }
+ permissions = strtol(argv[5], NULL, 0);
+
+ EXEC(if_gatt->server->add_descriptor, server_if, service_handle, &uuid,
+ permissions);
+}
+
+/* start_service */
+
+/* Same completion as gatts_unregister_server_c */
+#define gatts_start_service_c gatts_unregister_server_c
+
+static void gatts_start_service_p(int argc, const char *argv[])
+{
+ int server_if;
+ int service_handle;
+ int transport;
+
+ RETURN_IF_NULL(if_gatt);
+ VERIFY_SERVER_IF(2, server_if);
+ VERIFY_SERVICE_HANDLE(3, service_handle);
+
+ /* transport */
+ if (argc <= 4) {
+ haltest_error("No transport specified\n");
+ return;
+ }
+ transport = strtol(argv[4], NULL, 0);
+
+ EXEC(if_gatt->server->start_service, server_if, service_handle,
+ transport);
+}
+
+/* stop_service */
+
+/* Same completion as gatts_unregister_server_c */
+#define gatts_stop_service_c gatts_unregister_server_c
+
+static void gatts_stop_service_p(int argc, const char *argv[])
+{
+ int server_if;
+ int service_handle;
+
+ RETURN_IF_NULL(if_gatt);
+ VERIFY_SERVER_IF(2, server_if);
+ VERIFY_SERVICE_HANDLE(3, service_handle);
+
+ EXEC(if_gatt->server->stop_service, server_if, service_handle);
+}
+
+/* delete_service */
+
+/* Same completion as gatts_unregister_server_c */
+#define gatts_delete_service_c gatts_unregister_server_c
+
+static void gatts_delete_service_p(int argc, const char *argv[])
+{
+ int server_if;
+ int service_handle;
+
+ RETURN_IF_NULL(if_gatt);
+ VERIFY_SERVER_IF(2, server_if);
+ VERIFY_SERVICE_HANDLE(3, service_handle);
+
+ EXEC(if_gatt->server->delete_service, server_if, service_handle);
+}
+
+/* send_indication */
+
+static void gatts_send_indication_p(int argc, const char *argv[])
+{
+ int server_if;
+ int attr_handle;
+ int conn_id;
+ int confirm;
+ char data[200];
+ int len = 0;
+
+ RETURN_IF_NULL(if_gatt);
+ VERIFY_SERVER_IF(2, server_if);
+ VERIFY_HANDLE(3, attr_handle);
+ VERIFY_CONN_ID(4, conn_id);
+
+ /* confirm */
+ if (argc <= 5) {
+ haltest_error("No transport specified\n");
+ return;
+ }
+ confirm = strtol(argv[5], NULL, 0);
+
+ GET_VERIFY_HEX_STRING(6, data, len);
+
+ EXEC(if_gatt->server->send_indication, server_if, attr_handle, conn_id,
+ len, confirm, data);
+}
+
+/* send_response */
+
+static void gatts_send_response_p(int argc, const char *argv[])
+{
+ int conn_id;
+ int trans_id;
+ int status;
+ btgatt_response_t data;
+
+ memset(&data, 0, sizeof(data));
+
+ RETURN_IF_NULL(if_gatt);
+
+ VERIFY_CONN_ID(2, conn_id);
+ VERIFY_TRANS_ID(3, trans_id);
+ VERIFY_STATUS(4, status);
+ VERIFY_HANDLE(5, data.attr_value.handle);
+ VERIFY_OFFSET(6, data.attr_value.offset);
+
+ data.attr_value.auth_req = 0;
+ data.attr_value.len = 0;
+
+ GET_VERIFY_HEX_STRING(7, data.attr_value.value, data.attr_value.len);
+
+ haltest_info("conn_id %d, trans_id %d, status %d", conn_id, trans_id,
+ status);
+
+ EXEC(if_gatt->server->send_response, conn_id, trans_id, status, &data);
+}
+
+#define GATTS_METHODH(n, h) METHOD(#n, gatts_##n##_p, NULL, h)
+#define GATTS_METHODCH(n, h) METHOD(#n, gatts_##n##_p, gatts_##n##_c, h)
+
+static struct method server_methods[] = {
+ GATTS_METHODH(register_server, "[<uuid>]"),
+ GATTS_METHODCH(unregister_server, "<server_if>"),
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+ GATTS_METHODCH(connect, "<server_if> <addr> [<is_direct>] [<transport>]"),
+#else
+ GATTS_METHODCH(connect, "<server_if> <addr> [<is_direct>]"),
+#endif
+ GATTS_METHODCH(disconnect, "<server_if> <addr> <conn_id>"),
+ GATTS_METHODCH(add_service, "<server_if> <srvc_id> <num_handles>"),
+ GATTS_METHODCH(add_included_service,
+ "<server_if> <service_handle> <included_handle>"),
+ GATTS_METHODCH(add_characteristic,
+ "<server_if> <service_handle> <uuid> <properites> <permissions>"),
+ GATTS_METHODCH(add_descriptor,
+ "<server_if> <service_handle> <uuid> <permissions>"),
+ GATTS_METHODCH(start_service,
+ "<server_if> <service_handle> <transport>"),
+ GATTS_METHODCH(stop_service, "<server_if> <service_handle>"),
+ GATTS_METHODCH(delete_service, "<server_if> <service_handle>"),
+ GATTS_METHODH(send_indication,
+ "<server_if> <attr_handle> <conn_id> <confirm> [<data>]"),
+ GATTS_METHODH(send_response,
+ "<conn_id> <trans_id> <status> <handle> <offset> [<data>]"),
+ END_METHOD
+};
+
+const struct interface gatt_server_if = {
+ .name = "gatts",
+ .methods = server_methods
+};
diff --git a/repo/android/client/if-hf-client.c b/repo/android/client/if-hf-client.c
new file mode 100644
index 0000000..3f6f7c2
--- /dev/null
+++ b/repo/android/client/if-hf-client.c
@@ -0,0 +1,668 @@
+/*
+ * Copyright (C) 2014 Intel Corporation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include "if-main.h"
+#include "../hal-utils.h"
+
+const bthf_client_interface_t *if_hf_client = NULL;
+
+static char last_addr[MAX_ADDR_STR_LEN];
+
+SINTMAP(bthf_client_connection_state_t, -1, "(unknown)")
+ DELEMENT(BTHF_CLIENT_CONNECTION_STATE_DISCONNECTED),
+ DELEMENT(BTHF_CLIENT_CONNECTION_STATE_CONNECTING),
+ DELEMENT(BTHF_CLIENT_CONNECTION_STATE_CONNECTED),
+ DELEMENT(BTHF_CLIENT_CONNECTION_STATE_SLC_CONNECTED),
+ DELEMENT(BTHF_CLIENT_CONNECTION_STATE_DISCONNECTING),
+ENDMAP
+
+SINTMAP(bthf_client_audio_state_t, -1, "(unknown)")
+ DELEMENT(BTHF_CLIENT_AUDIO_STATE_DISCONNECTED),
+ DELEMENT(BTHF_CLIENT_AUDIO_STATE_CONNECTING),
+ DELEMENT(BTHF_CLIENT_AUDIO_STATE_CONNECTED),
+ DELEMENT(BTHF_CLIENT_AUDIO_STATE_CONNECTED_MSBC),
+ENDMAP
+
+SINTMAP(bthf_client_vr_state_t, -1, "(unknown)")
+ DELEMENT(BTHF_CLIENT_VR_STATE_STOPPED),
+ DELEMENT(BTHF_CLIENT_VR_STATE_STARTED),
+ENDMAP
+
+SINTMAP(bthf_client_network_state_t, -1, "(unknown)")
+ DELEMENT(BTHF_CLIENT_NETWORK_STATE_NOT_AVAILABLE),
+ DELEMENT(BTHF_CLIENT_NETWORK_STATE_AVAILABLE),
+ENDMAP
+
+SINTMAP(bthf_client_service_type_t, -1, "(unknown)")
+ DELEMENT(BTHF_CLIENT_SERVICE_TYPE_HOME),
+ DELEMENT(BTHF_CLIENT_SERVICE_TYPE_ROAMING),
+ENDMAP
+
+SINTMAP(bthf_client_call_t, -1, "(unknown)")
+ DELEMENT(BTHF_CLIENT_CALL_NO_CALLS_IN_PROGRESS),
+ DELEMENT(BTHF_CLIENT_CALL_CALLS_IN_PROGRESS),
+ENDMAP
+
+SINTMAP(bthf_client_callsetup_t, -1, "(unknown)")
+ DELEMENT(BTHF_CLIENT_CALLSETUP_NONE),
+ DELEMENT(BTHF_CLIENT_CALLSETUP_INCOMING),
+ DELEMENT(BTHF_CLIENT_CALLSETUP_OUTGOING),
+ DELEMENT(BTHF_CLIENT_CALLSETUP_ALERTING),
+ENDMAP
+
+SINTMAP(bthf_client_callheld_t, -1, "(unknown)")
+ DELEMENT(BTHF_CLIENT_CALLHELD_NONE),
+ DELEMENT(BTHF_CLIENT_CALLHELD_HOLD_AND_ACTIVE),
+ DELEMENT(BTHF_CLIENT_CALLHELD_HOLD),
+ENDMAP
+
+SINTMAP(bthf_client_resp_and_hold_t, -1, "(unknown)")
+ DELEMENT(BTHF_CLIENT_RESP_AND_HOLD_HELD),
+ DELEMENT(BTRH_CLIENT_RESP_AND_HOLD_ACCEPT),
+ DELEMENT(BTRH_CLIENT_RESP_AND_HOLD_REJECT),
+ENDMAP
+
+SINTMAP(bthf_client_call_direction_t, -1, "(unknown)")
+ DELEMENT(BTHF_CLIENT_CALL_DIRECTION_OUTGOING),
+ DELEMENT(BTHF_CLIENT_CALL_DIRECTION_INCOMING),
+ENDMAP
+
+SINTMAP(bthf_client_call_state_t, -1, "(unknown)")
+ DELEMENT(BTHF_CLIENT_CALL_STATE_ACTIVE),
+ DELEMENT(BTHF_CLIENT_CALL_STATE_HELD),
+ DELEMENT(BTHF_CLIENT_CALL_STATE_DIALING),
+ DELEMENT(BTHF_CLIENT_CALL_STATE_ALERTING),
+ DELEMENT(BTHF_CLIENT_CALL_STATE_INCOMING),
+ DELEMENT(BTHF_CLIENT_CALL_STATE_WAITING),
+ DELEMENT(BTHF_CLIENT_CALL_STATE_HELD_BY_RESP_HOLD),
+ENDMAP
+
+SINTMAP(bthf_client_call_mpty_type_t, -1, "(unknown)")
+ DELEMENT(BTHF_CLIENT_CALL_MPTY_TYPE_SINGLE),
+ DELEMENT(BTHF_CLIENT_CALL_MPTY_TYPE_MULTI),
+ENDMAP
+
+SINTMAP(bthf_client_volume_type_t, -1, "(unknown)")
+ DELEMENT(BTHF_CLIENT_VOLUME_TYPE_SPK),
+ DELEMENT(BTHF_CLIENT_VOLUME_TYPE_MIC),
+ENDMAP
+
+SINTMAP(bthf_client_cmd_complete_t, -1, "(unknown)")
+ DELEMENT(BTHF_CLIENT_CMD_COMPLETE_OK),
+ DELEMENT(BTHF_CLIENT_CMD_COMPLETE_ERROR),
+ DELEMENT(BTHF_CLIENT_CMD_COMPLETE_ERROR_NO_CARRIER),
+ DELEMENT(BTHF_CLIENT_CMD_COMPLETE_ERROR_BUSY),
+ DELEMENT(BTHF_CLIENT_CMD_COMPLETE_ERROR_NO_ANSWER),
+ DELEMENT(BTHF_CLIENT_CMD_COMPLETE_ERROR_DELAYED),
+ DELEMENT(BTHF_CLIENT_CMD_COMPLETE_ERROR_BLACKLISTED),
+ DELEMENT(BTHF_CLIENT_CMD_COMPLETE_ERROR_CME),
+ENDMAP
+
+SINTMAP(bthf_client_subscriber_service_type_t, -1, "(unknown)")
+ DELEMENT(BTHF_CLIENT_SERVICE_UNKNOWN),
+ DELEMENT(BTHF_CLIENT_SERVICE_VOICE),
+ DELEMENT(BTHF_CLIENT_SERVICE_FAX),
+ENDMAP
+
+SINTMAP(bthf_client_in_band_ring_state_t, -1, "(unknown)")
+ DELEMENT(BTHF_CLIENT_IN_BAND_RINGTONE_NOT_PROVIDED),
+ DELEMENT(BTHF_CLIENT_IN_BAND_RINGTONE_PROVIDED),
+ENDMAP
+
+SINTMAP(bthf_client_call_action_t, -1, "(unknown)")
+ DELEMENT(BTHF_CLIENT_CALL_ACTION_CHLD_0),
+ DELEMENT(BTHF_CLIENT_CALL_ACTION_CHLD_1),
+ DELEMENT(BTHF_CLIENT_CALL_ACTION_CHLD_2),
+ DELEMENT(BTHF_CLIENT_CALL_ACTION_CHLD_3),
+ DELEMENT(BTHF_CLIENT_CALL_ACTION_CHLD_4),
+ DELEMENT(BTHF_CLIENT_CALL_ACTION_CHLD_1x),
+ DELEMENT(BTHF_CLIENT_CALL_ACTION_CHLD_2x),
+ DELEMENT(BTHF_CLIENT_CALL_ACTION_ATA),
+ DELEMENT(BTHF_CLIENT_CALL_ACTION_CHUP),
+ DELEMENT(BTHF_CLIENT_CALL_ACTION_BTRH_0),
+ DELEMENT(BTHF_CLIENT_CALL_ACTION_BTRH_1),
+ DELEMENT(BTHF_CLIENT_CALL_ACTION_BTRH_2),
+ENDMAP
+
+/* Callbacks */
+
+static char features_str[512];
+
+static const char *pear_features_t2str(int feat)
+{
+ memset(features_str, 0, sizeof(features_str));
+
+ sprintf(features_str, "BTHF_CLIENT_PEER_FEAT_3WAY: %s,\n"
+ "BTHF_CLIENT_PEER_FEAT_ECNR: %s,\n"
+ "BTHF_CLIENT_PEER_FEAT_VREC: %s,\n"
+ "BTHF_CLIENT_PEER_FEAT_INBAND: %s,\n"
+ "BTHF_CLIENT_PEER_FEAT_VTAG: %s,\n"
+ "BTHF_CLIENT_PEER_FEAT_REJECT: %s,\n"
+ "BTHF_CLIENT_PEER_FEAT_ECS: %s,\n"
+ "BTHF_CLIENT_PEER_FEAT_ECC: %s,\n"
+ "BTHF_CLIENT_PEER_FEAT_EXTERR: %s,\n"
+ "BTHF_CLIENT_PEER_FEAT_CODEC: %s,\n",
+ feat & BTHF_CLIENT_PEER_FEAT_3WAY ? "True" : "False",
+ feat & BTHF_CLIENT_PEER_FEAT_ECNR ? "True" : "False",
+ feat & BTHF_CLIENT_PEER_FEAT_VREC ? "True" : "False",
+ feat & BTHF_CLIENT_PEER_FEAT_INBAND ? "True" : "False",
+ feat & BTHF_CLIENT_PEER_FEAT_VTAG ? "True" : "False",
+ feat & BTHF_CLIENT_PEER_FEAT_REJECT ? "True" : "False",
+ feat & BTHF_CLIENT_PEER_FEAT_ECS ? "True" : "False",
+ feat & BTHF_CLIENT_PEER_FEAT_ECC ? "True" : "False",
+ feat & BTHF_CLIENT_PEER_FEAT_EXTERR ? "True" : "False",
+ feat & BTHF_CLIENT_PEER_FEAT_CODEC ? "True" : "False");
+
+ return features_str;
+}
+
+static const char *chld_features_t2str(int feat)
+{
+ memset(features_str, 0, sizeof(features_str));
+
+ sprintf(features_str,
+ "BTHF_CLIENT_CHLD_FEAT_REL: %s,\n"
+ "BTHF_CLIENT_CHLD_FEAT_REL_ACC: %s,\n"
+ "BTHF_CLIENT_CHLD_FEAT_REL_X: %s,\n"
+ "BTHF_CLIENT_CHLD_FEAT_HOLD_ACC: %s,\n"
+ "BTHF_CLIENT_CHLD_FEAT_PRIV_X: %s,\n"
+ "BTHF_CLIENT_CHLD_FEAT_MERGE: %s,\n"
+ "BTHF_CLIENT_CHLD_FEAT_MERGE_DETACH: %s,\n",
+ feat & BTHF_CLIENT_CHLD_FEAT_REL ? "True" : "False",
+ feat & BTHF_CLIENT_CHLD_FEAT_REL_ACC ? "True" : "False",
+ feat & BTHF_CLIENT_CHLD_FEAT_REL_X ? "True" : "False",
+ feat & BTHF_CLIENT_CHLD_FEAT_HOLD_ACC ? "True" : "False",
+ feat & BTHF_CLIENT_CHLD_FEAT_PRIV_X ? "True" : "False",
+ feat & BTHF_CLIENT_CHLD_FEAT_MERGE ? "True" : "False",
+ feat & BTHF_CLIENT_CHLD_FEAT_MERGE_DETACH ? "True" : "False");
+
+ return features_str;
+}
+
+/* Callback for connection state change. */
+static void hf_client_connection_state_callback(
+ bthf_client_connection_state_t state,
+ unsigned int peer_feat,
+ unsigned int chld_feat,
+ bt_bdaddr_t *bd_addr)
+{
+ haltest_info("%s: state=%s bd_addr=%s\n", __func__,
+ bthf_client_connection_state_t2str(state),
+ bt_bdaddr_t2str(bd_addr, last_addr));
+
+ if (state != BTHF_CLIENT_CONNECTION_STATE_CONNECTED)
+ return;
+
+ haltest_info("\tpeer_features%s\n", pear_features_t2str(peer_feat));
+ haltest_info("\tchld_feat=%s\n", chld_features_t2str(chld_feat));
+}
+
+/* Callback for audio connection state change. */
+static void hf_client_audio_state_callback(bthf_client_audio_state_t state,
+ bt_bdaddr_t *bd_addr)
+{
+ haltest_info("%s: state=%s bd_addr=%s\n", __func__,
+ bthf_client_audio_state_t2str(state),
+ bt_bdaddr_t2str(bd_addr, last_addr));
+}
+
+/* Callback for VR connection state change. */
+static void hf_client_vr_cmd_callback(bthf_client_vr_state_t state)
+{
+ haltest_info("%s: vr_state=%s\n", __func__,
+ bthf_client_vr_state_t2str(state));
+}
+
+/* Callback for network state change */
+static void hf_client_network_state_callback(bthf_client_network_state_t state)
+{
+ haltest_info("%s: network_state=%s\n", __func__,
+ bthf_client_network_state_t2str(state));
+}
+
+/* Callback for network roaming status change */
+static void hf_client_network_roaming_callback(bthf_client_service_type_t type)
+{
+ haltest_info("%s: service_type=%s\n", __func__,
+ bthf_client_service_type_t2str(type));
+}
+
+/* Callback for signal strength indication */
+static void hf_client_network_signal_callback(int signal_strength)
+{
+ haltest_info("%s: signal strength=%d\n", __func__, signal_strength);
+}
+
+/* Callback for battery level indication */
+static void hf_client_battery_level_callback(int battery_level)
+{
+ haltest_info("%s: battery_lvl=%d\n", __func__, battery_level);
+}
+
+/* Callback for current operator name */
+static void hf_client_current_operator_callback(const char *name)
+{
+ haltest_info("%s: operator_name=%s\n", __func__, name);
+}
+
+/* Callback for call indicator */
+static void hf_client_call_callback(bthf_client_call_t call)
+{
+ haltest_info("%s: call_state=%s\n", __func__,
+ bthf_client_call_t2str(call));
+}
+
+/* Callback for callsetup indicator */
+static void hf_client_callsetup_callback(bthf_client_callsetup_t callsetup)
+{
+ haltest_info("%s: callsetup=%s\n", __func__,
+ bthf_client_callsetup_t2str(callsetup));
+}
+
+/* Callback for callheld indicator */
+static void hf_client_callheld_callback(bthf_client_callheld_t callheld)
+{
+ haltest_info("%s: callheld=%s\n", __func__,
+ bthf_client_callheld_t2str(callheld));
+}
+
+/* Callback for response and hold */
+static void hf_client_resp_and_hold_callback(
+ bthf_client_resp_and_hold_t resp_and_hold)
+{
+ haltest_info("%s: resp_and_hold=%s\n", __func__,
+ bthf_client_resp_and_hold_t2str(resp_and_hold));
+}
+
+/* Callback for Calling Line Identification notification */
+static void hf_client_clip_callback(const char *number)
+{
+ haltest_info("%s: number=%s\n", __func__, number);
+}
+
+/* Callback for Call Waiting notification */
+static void hf_client_call_waiting_callback(const char *number)
+{
+ haltest_info("%s: number=%s\n", __func__, number);
+}
+
+/* Callback for listing current calls. Can be called multiple time. */
+static void hf_client_current_calls_callback(int index,
+ bthf_client_call_direction_t dir,
+ bthf_client_call_state_t state,
+ bthf_client_call_mpty_type_t mpty,
+ const char *number)
+{
+ haltest_info("%s: index=%d, direction=%s, state=%s, m_party=%s\n",
+ __func__, index,
+ bthf_client_call_direction_t2str(dir),
+ bthf_client_call_state_t2str(state),
+ bthf_client_call_mpty_type_t2str(mpty));
+
+ if (number)
+ haltest_info("%s: number=%s\n", __func__, number);
+}
+
+/* Callback for audio volume change */
+static void hf_client_volume_change_callback(bthf_client_volume_type_t type,
+ int volume)
+{
+ haltest_info("%s: vol_type=%s, value=%d\n", __func__,
+ bthf_client_volume_type_t2str(type), volume);
+}
+
+/* Callback for command complete event */
+static void hf_client_cmd_complete_callback(bthf_client_cmd_complete_t type,
+ int cme)
+{
+ haltest_info("%s: type=%s, cme=%d\n", __func__,
+ bthf_client_cmd_complete_t2str(type), cme);
+}
+
+/* Callback for subscriber information */
+static void hf_client_subscriber_info_callback(const char *name,
+ bthf_client_subscriber_service_type_t type)
+{
+ haltest_info("%s: name=%s, type=%s\n", __func__, name,
+ bthf_client_subscriber_service_type_t2str(type));
+}
+
+/* Callback for in-band ring tone settings */
+static void hf_client_in_band_ring_tone_callback(
+ bthf_client_in_band_ring_state_t state)
+{
+ haltest_info("%s: state=%s\n", __func__,
+ bthf_client_in_band_ring_state_t2str(state));
+}
+
+/* Callback for requested number from AG */
+static void hf_client_last_voice_tag_number_callback(const char *number)
+{
+ haltest_info("%s: number=%s\n", __func__, number);
+}
+
+/* Callback for sending ring indication to app */
+static void hf_client_ring_indication_callback(void)
+{
+ haltest_info("%s\n", __func__);
+}
+
+static bthf_client_callbacks_t hf_client_cbacks = {
+ .size = sizeof(hf_client_cbacks),
+ .connection_state_cb = hf_client_connection_state_callback,
+ .audio_state_cb = hf_client_audio_state_callback,
+ .vr_cmd_cb = hf_client_vr_cmd_callback,
+ .network_state_cb = hf_client_network_state_callback,
+ .network_roaming_cb = hf_client_network_roaming_callback,
+ .network_signal_cb = hf_client_network_signal_callback,
+ .battery_level_cb = hf_client_battery_level_callback,
+ .current_operator_cb = hf_client_current_operator_callback,
+ .call_cb = hf_client_call_callback,
+ .callsetup_cb = hf_client_callsetup_callback,
+ .callheld_cb = hf_client_callheld_callback,
+ .resp_and_hold_cb = hf_client_resp_and_hold_callback,
+ .clip_cb = hf_client_clip_callback,
+ .call_waiting_cb = hf_client_call_waiting_callback,
+ .current_calls_cb = hf_client_current_calls_callback,
+ .volume_change_cb = hf_client_volume_change_callback,
+ .cmd_complete_cb = hf_client_cmd_complete_callback,
+ .subscriber_info_cb = hf_client_subscriber_info_callback,
+ .in_band_ring_tone_cb = hf_client_in_band_ring_tone_callback,
+ .last_voice_tag_number_callback =
+ hf_client_last_voice_tag_number_callback,
+ .ring_indication_cb = hf_client_ring_indication_callback,
+};
+
+/* init */
+static void init_p(int argc, const char **argv)
+{
+ RETURN_IF_NULL(if_hf_client);
+
+ EXEC(if_hf_client->init, &hf_client_cbacks);
+}
+
+static void connect_c(int argc, const char **argv, enum_func *enum_func,
+ void **user)
+{
+ if (argc == 3) {
+ *user = NULL;
+ *enum_func = enum_devices;
+ }
+}
+
+/* connect to audio gateway */
+static void connect_p(int argc, const char **argv)
+{
+ bt_bdaddr_t addr;
+
+ RETURN_IF_NULL(if_hf_client);
+ VERIFY_ADDR_ARG(2, &addr);
+
+ EXEC(if_hf_client->connect, &addr);
+}
+
+/*
+ * This completion function will be used for several methods
+ * returning recently connected address
+ */
+static void connected_addr_c(int argc, const char **argv, enum_func *enum_func,
+ void **user)
+{
+ if (argc == 3) {
+ *user = last_addr;
+ *enum_func = enum_one_string;
+ }
+}
+
+/* Map completion to connected_addr_c */
+#define disconnect_c connected_addr_c
+
+/* disconnect from audio gateway */
+static void disconnect_p(int argc, const char **argv)
+{
+ bt_bdaddr_t addr;
+
+ RETURN_IF_NULL(if_hf_client);
+ VERIFY_ADDR_ARG(2, &addr);
+
+ EXEC(if_hf_client->disconnect, &addr);
+}
+
+static void connect_audio_c(int argc, const char **argv, enum_func *enum_func,
+ void **user)
+{
+ if (argc == 3) {
+ *user = NULL;
+ *enum_func = enum_devices;
+ }
+}
+
+/* create an audio connection */
+static void connect_audio_p(int argc, const char **argv)
+{
+ bt_bdaddr_t addr;
+
+ RETURN_IF_NULL(if_hf_client);
+ VERIFY_ADDR_ARG(2, &addr);
+
+ EXEC(if_hf_client->connect_audio, &addr);
+}
+
+/* Map completion to connected_addr_c */
+#define disconnect_audio_c connected_addr_c
+
+/* close the audio connection */
+static void disconnect_audio_p(int argc, const char **argv)
+{
+ bt_bdaddr_t addr;
+
+ RETURN_IF_NULL(if_hf_client);
+ VERIFY_ADDR_ARG(2, &addr);
+
+ EXEC(if_hf_client->disconnect_audio, &addr);
+}
+
+/* start voice recognition */
+static void start_voice_recognition_p(int argc, const char **argv)
+{
+ RETURN_IF_NULL(if_hf_client);
+
+ EXEC(if_hf_client->start_voice_recognition);
+}
+
+/* stop voice recognition */
+static void stop_voice_recognition_p(int argc, const char **argv)
+{
+ RETURN_IF_NULL(if_hf_client);
+
+ EXEC(if_hf_client->stop_voice_recognition);
+}
+
+static void volume_control_c(int argc, const char **argv, enum_func *enum_func,
+ void **user)
+{
+ if (argc == 3) {
+ *user = TYPE_ENUM(bthf_client_volume_type_t);
+ *enum_func = enum_defines;
+ }
+}
+
+/* volume control */
+static void volume_control_p(int argc, const char **argv)
+{
+ bthf_client_volume_type_t type;
+ int volume;
+
+ RETURN_IF_NULL(if_hf_client);
+
+ /* volume type */
+ if (argc <= 2) {
+ haltest_error("No volume type specified\n");
+ return;
+ }
+ type = str2bthf_client_volume_type_t(argv[2]);
+
+ /* volume */
+ if (argc <= 3) {
+ haltest_error("No volume specified\n");
+ return;
+ }
+ volume = atoi(argv[3]);
+
+ EXEC(if_hf_client->volume_control, type, volume);
+}
+
+/* place a call with number a number */
+static void dial_p(int argc, const char **argv)
+{
+ RETURN_IF_NULL(if_hf_client);
+
+ /* number string */
+ if (argc <= 2) {
+ haltest_info("Number not specified. Redial\n");
+ EXEC(if_hf_client->dial, NULL);
+ return;
+ }
+
+ EXEC(if_hf_client->dial, argv[2]);
+}
+
+/* place a call with number specified by location (speed dial) */
+static void dial_memory_p(int argc, const char **argv)
+{
+ RETURN_IF_NULL(if_hf_client);
+
+ /* memory index */
+ if (argc <= 2) {
+ haltest_error("No memory index specified\n");
+ return;
+ }
+
+ EXEC(if_hf_client->dial_memory, atoi(argv[2]));
+}
+
+static void handle_call_action_c(int argc, const char **argv,
+ enum_func *enum_func, void **user)
+{
+ if (argc == 3) {
+ *user = TYPE_ENUM(bthf_client_call_action_t);
+ *enum_func = enum_defines;
+ }
+}
+
+/* perform specified call related action */
+static void handle_call_action_p(int argc, const char **argv)
+{
+ bthf_client_call_action_t action;
+ int index = 0;
+
+ RETURN_IF_NULL(if_hf_client);
+
+ /* action */
+ if (argc <= 2) {
+ haltest_error("No action specified\n");
+ return;
+ }
+ action = str2bthf_client_call_action_t(argv[2]);
+
+ /* call index */
+ if (action == BTHF_CLIENT_CALL_ACTION_CHLD_1x ||
+ action == BTHF_CLIENT_CALL_ACTION_CHLD_2x) {
+ if (argc <= 3) {
+ haltest_error("No call index specified\n");
+ return;
+ }
+ index = atoi(argv[3]);
+ }
+
+ EXEC(if_hf_client->handle_call_action, action, index);
+}
+
+/* query list of current calls */
+static void query_current_calls_p(int argc, const char **argv)
+{
+ RETURN_IF_NULL(if_hf_client);
+
+ EXEC(if_hf_client->query_current_calls);
+}
+
+/* query name of current selected operator */
+static void query_current_operator_name_p(int argc, const char **argv)
+{
+ RETURN_IF_NULL(if_hf_client);
+
+ EXEC(if_hf_client->query_current_operator_name);
+}
+
+/* Retrieve subscriber information */
+static void retrieve_subscriber_info_p(int argc, const char **argv)
+{
+ RETURN_IF_NULL(if_hf_client);
+
+ EXEC(if_hf_client->retrieve_subscriber_info);
+}
+
+/* Send DTMF code*/
+static void send_dtmf_p(int argc, const char **argv)
+{
+ RETURN_IF_NULL(if_hf_client);
+
+ EXEC(if_hf_client->send_dtmf, *argv[2]);
+}
+
+/* Request a phone number from AG corresponding to last voice tag recorded */
+static void request_last_voice_tag_number_p(int argc, const char **argv)
+{
+ RETURN_IF_NULL(if_hf_client);
+
+ EXEC(if_hf_client->request_last_voice_tag_number);
+}
+
+/* Closes the interface. */
+static void cleanup_p(int argc, const char **argv)
+{
+ RETURN_IF_NULL(if_hf_client);
+
+ EXECV(if_hf_client->cleanup);
+ if_hf_client = NULL;
+}
+
+static struct method methods[] = {
+ STD_METHOD(init),
+ STD_METHODCH(connect, "<addr>"),
+ STD_METHODCH(disconnect, "<addr>"),
+ STD_METHODCH(connect_audio, "<addr>"),
+ STD_METHODCH(disconnect_audio, "<addr>"),
+ STD_METHOD(start_voice_recognition),
+ STD_METHOD(stop_voice_recognition),
+ STD_METHODCH(volume_control, "<volume_type> <value>"),
+ STD_METHODH(dial, "<destination_number>"),
+ STD_METHODH(dial_memory, "<memory_location>"),
+ STD_METHODCH(handle_call_action, "<call_action> <call_index>"),
+ STD_METHOD(query_current_calls),
+ STD_METHOD(query_current_operator_name),
+ STD_METHOD(retrieve_subscriber_info),
+ STD_METHODH(send_dtmf, "<code>"),
+ STD_METHOD(request_last_voice_tag_number),
+ STD_METHOD(cleanup),
+ END_METHOD
+};
+
+const struct interface hf_client_if = {
+ .name = "handsfree_client",
+ .methods = methods
+};
diff --git a/repo/android/client/if-hf.c b/repo/android/client/if-hf.c
new file mode 100644
index 0000000..6b798f8
--- /dev/null
+++ b/repo/android/client/if-hf.c
@@ -0,0 +1,1062 @@
+/*
+ * Copyright (C) 2013 Intel Corporation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include "if-main.h"
+#include "../hal-utils.h"
+
+const bthf_interface_t *if_hf = NULL;
+
+SINTMAP(bthf_at_response_t, -1, "(unknown)")
+ DELEMENT(BTHF_AT_RESPONSE_ERROR),
+ DELEMENT(BTHF_AT_RESPONSE_OK),
+ENDMAP
+
+SINTMAP(bthf_connection_state_t, -1, "(unknown)")
+ DELEMENT(BTHF_CONNECTION_STATE_DISCONNECTED),
+ DELEMENT(BTHF_CONNECTION_STATE_CONNECTING),
+ DELEMENT(BTHF_CONNECTION_STATE_CONNECTED),
+ DELEMENT(BTHF_CONNECTION_STATE_SLC_CONNECTED),
+ DELEMENT(BTHF_CONNECTION_STATE_DISCONNECTING),
+ENDMAP
+
+SINTMAP(bthf_audio_state_t, -1, "(unknown)")
+ DELEMENT(BTHF_AUDIO_STATE_DISCONNECTED),
+ DELEMENT(BTHF_AUDIO_STATE_CONNECTING),
+ DELEMENT(BTHF_AUDIO_STATE_CONNECTED),
+ DELEMENT(BTHF_AUDIO_STATE_DISCONNECTING),
+ENDMAP
+
+SINTMAP(bthf_vr_state_t, -1, "(unknown)")
+ DELEMENT(BTHF_VR_STATE_STOPPED),
+ DELEMENT(BTHF_VR_STATE_STARTED),
+ENDMAP
+
+SINTMAP(bthf_volume_type_t, -1, "(unknown)")
+ DELEMENT(BTHF_VOLUME_TYPE_SPK),
+ DELEMENT(BTHF_VOLUME_TYPE_MIC),
+ENDMAP
+
+SINTMAP(bthf_nrec_t, -1, "(unknown)")
+ DELEMENT(BTHF_NREC_STOP),
+ DELEMENT(BTHF_NREC_START),
+ENDMAP
+
+SINTMAP(bthf_chld_type_t, -1, "(unknown)")
+ DELEMENT(BTHF_CHLD_TYPE_RELEASEHELD),
+ DELEMENT(BTHF_CHLD_TYPE_RELEASEACTIVE_ACCEPTHELD),
+ DELEMENT(BTHF_CHLD_TYPE_HOLDACTIVE_ACCEPTHELD),
+ DELEMENT(BTHF_CHLD_TYPE_ADDHELDTOCONF),
+ENDMAP
+
+/* Network Status */
+SINTMAP(bthf_network_state_t, -1, "(unknown)")
+ DELEMENT(BTHF_NETWORK_STATE_NOT_AVAILABLE),
+ DELEMENT(BTHF_NETWORK_STATE_AVAILABLE),
+ENDMAP
+
+/* Service type */
+SINTMAP(bthf_service_type_t, -1, "(unknown)")
+ DELEMENT(BTHF_SERVICE_TYPE_HOME),
+ DELEMENT(BTHF_SERVICE_TYPE_ROAMING),
+ENDMAP
+
+SINTMAP(bthf_call_state_t, -1, "(unknown)")
+ DELEMENT(BTHF_CALL_STATE_ACTIVE),
+ DELEMENT(BTHF_CALL_STATE_HELD),
+ DELEMENT(BTHF_CALL_STATE_DIALING),
+ DELEMENT(BTHF_CALL_STATE_ALERTING),
+ DELEMENT(BTHF_CALL_STATE_INCOMING),
+ DELEMENT(BTHF_CALL_STATE_WAITING),
+ DELEMENT(BTHF_CALL_STATE_IDLE),
+ENDMAP
+
+SINTMAP(bthf_call_direction_t, -1, "(unknown)")
+ DELEMENT(BTHF_CALL_DIRECTION_OUTGOING),
+ DELEMENT(BTHF_CALL_DIRECTION_INCOMING),
+ENDMAP
+
+SINTMAP(bthf_call_mode_t, -1, "(unknown)")
+ DELEMENT(BTHF_CALL_TYPE_VOICE),
+ DELEMENT(BTHF_CALL_TYPE_DATA),
+ DELEMENT(BTHF_CALL_TYPE_FAX),
+ENDMAP
+
+SINTMAP(bthf_call_mpty_type_t, -1, "(unknown)")
+ DELEMENT(BTHF_CALL_MPTY_TYPE_SINGLE),
+ DELEMENT(BTHF_CALL_MPTY_TYPE_MULTI),
+ENDMAP
+
+SINTMAP(bthf_call_addrtype_t, -1, "(unknown)")
+ DELEMENT(BTHF_CALL_ADDRTYPE_UNKNOWN),
+ DELEMENT(BTHF_CALL_ADDRTYPE_INTERNATIONAL),
+ENDMAP
+
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+SINTMAP(bthf_wbs_config_t, -1, "(unknown)")
+ DELEMENT(BTHF_WBS_NONE),
+ DELEMENT(BTHF_WBS_NO),
+ DELEMENT(BTHF_WBS_YES),
+ENDMAP
+#endif
+
+/* Callbacks */
+
+static char last_addr[MAX_ADDR_STR_LEN];
+
+/*
+ * Callback for connection state change.
+ * state will have one of the values from BtHfConnectionState
+ */
+static void connection_state_cb(bthf_connection_state_t state,
+ bt_bdaddr_t *bd_addr)
+{
+ haltest_info("%s: state=%s bd_addr=%s\n", __func__,
+ bthf_connection_state_t2str(state),
+ bt_bdaddr_t2str(bd_addr, last_addr));
+}
+
+/*
+ * Callback for audio connection state change.
+ * state will have one of the values from BtHfAudioState
+ */
+static void audio_state_cb(bthf_audio_state_t state, bt_bdaddr_t *bd_addr)
+{
+ haltest_info("%s: state=%s bd_addr=%s\n", __func__,
+ bthf_audio_state_t2str(state),
+ bt_bdaddr_t2str(bd_addr, last_addr));
+}
+
+/*
+ * Callback for VR connection state change.
+ * state will have one of the values from BtHfVRState
+ */
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+static void vr_cmd_cb(bthf_vr_state_t state, bt_bdaddr_t *bd_addr)
+{
+ haltest_info("%s: state=%s bd_addr=%s\n", __func__,
+ bthf_vr_state_t2str(state),
+ bt_bdaddr_t2str(bd_addr, last_addr));
+}
+#else
+static void vr_cmd_cb(bthf_vr_state_t state)
+{
+ haltest_info("%s: state=%s\n", __func__, bthf_vr_state_t2str(state));
+}
+#endif
+
+/* Callback for answer incoming call (ATA) */
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+static void answer_call_cmd_cb(bt_bdaddr_t *bd_addr)
+{
+ haltest_info("%s: bd_addr=%s\n", __func__,
+ bt_bdaddr_t2str(bd_addr, last_addr));
+}
+#else
+static void answer_call_cmd_cb(void)
+{
+ haltest_info("%s\n", __func__);
+}
+#endif
+
+/* Callback for disconnect call (AT+CHUP) */
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+static void hangup_call_cmd_cb(bt_bdaddr_t *bd_addr)
+{
+ haltest_info("%s: bd_addr=%s\n", __func__,
+ bt_bdaddr_t2str(bd_addr, last_addr));
+}
+#else
+static void hangup_call_cmd_cb(void)
+{
+ haltest_info("%s\n", __func__);
+}
+#endif
+
+/*
+ * Callback for disconnect call (AT+CHUP)
+ * type will denote Speaker/Mic gain (BtHfVolumeControl).
+ */
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+static void volume_cmd_cb(bthf_volume_type_t type, int volume,
+ bt_bdaddr_t *bd_addr)
+{
+ haltest_info("%s: type=%s volume=%d bd_addr=%s\n", __func__,
+ bthf_volume_type_t2str(type), volume,
+ bt_bdaddr_t2str(bd_addr, last_addr));
+}
+#else
+static void volume_cmd_cb(bthf_volume_type_t type, int volume)
+{
+ haltest_info("%s: type=%s volume=%d\n", __func__,
+ bthf_volume_type_t2str(type), volume);
+}
+#endif
+
+/*
+ * Callback for dialing an outgoing call
+ * If number is NULL, redial
+ */
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+static void dial_call_cmd_cb(char *number, bt_bdaddr_t *bd_addr)
+{
+ haltest_info("%s: number=%s bd_addr=%s\n", __func__, number,
+ bt_bdaddr_t2str(bd_addr, last_addr));
+}
+#else
+static void dial_call_cmd_cb(char *number)
+{
+ haltest_info("%s: number=%s\n", __func__, number);
+}
+#endif
+
+/*
+ * Callback for sending DTMF tones
+ * tone contains the dtmf character to be sent
+ */
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+static void dtmf_cmd_cb(char tone, bt_bdaddr_t *bd_addr)
+{
+ haltest_info("%s: tone=%d bd_addr=%s\n", __func__, tone,
+ bt_bdaddr_t2str(bd_addr, last_addr));
+}
+#else
+static void dtmf_cmd_cb(char tone)
+{
+ haltest_info("%s: tone=%d\n", __func__, tone);
+}
+#endif
+
+/*
+ * Callback for enabling/disabling noise reduction/echo cancellation
+ * value will be 1 to enable, 0 to disable
+ */
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+static void nrec_cmd_cb(bthf_nrec_t nrec, bt_bdaddr_t *bd_addr)
+{
+ haltest_info("%s: nrec=%s bd_addr=%s\n", __func__,
+ bthf_nrec_t2str(nrec),
+ bt_bdaddr_t2str(bd_addr, last_addr));
+}
+#else
+static void nrec_cmd_cb(bthf_nrec_t nrec)
+{
+ haltest_info("%s: nrec=%s\n", __func__, bthf_nrec_t2str(nrec));
+}
+#endif
+
+/*
+ * Callback for call hold handling (AT+CHLD)
+ * value will contain the call hold command (0, 1, 2, 3)
+ */
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+static void chld_cmd_cb(bthf_chld_type_t chld, bt_bdaddr_t *bd_addr)
+{
+ haltest_info("%s: chld=%s bd_addr=%s\n", __func__,
+ bthf_chld_type_t2str(chld),
+ bt_bdaddr_t2str(bd_addr, last_addr));
+}
+#else
+static void chld_cmd_cb(bthf_chld_type_t chld)
+{
+ haltest_info("%s: chld=%s\n", __func__, bthf_chld_type_t2str(chld));
+}
+#endif
+
+/* Callback for CNUM (subscriber number) */
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+static void cnum_cmd_cb(bt_bdaddr_t *bd_addr)
+{
+ haltest_info("%s: bd_addr=%s\n", __func__,
+ bt_bdaddr_t2str(bd_addr, last_addr));
+}
+#else
+static void cnum_cmd_cb(void)
+{
+ haltest_info("%s\n", __func__);
+}
+#endif
+
+/* Callback for indicators (CIND) */
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+static void cind_cmd_cb(bt_bdaddr_t *bd_addr)
+{
+ haltest_info("%s: bd_addr=%s\n", __func__,
+ bt_bdaddr_t2str(bd_addr, last_addr));
+}
+#else
+static void cind_cmd_cb(void)
+{
+ haltest_info("%s\n", __func__);
+}
+#endif
+
+/* Callback for operator selection (COPS) */
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+static void cops_cmd_cb(bt_bdaddr_t *bd_addr)
+{
+ haltest_info("%s: bd_addr=%s\n", __func__,
+ bt_bdaddr_t2str(bd_addr, last_addr));
+}
+#else
+static void cops_cmd_cb(void)
+{
+ haltest_info("%s\n", __func__);
+}
+#endif
+
+/* Callback for call list (AT+CLCC) */
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+static void clcc_cmd_cb(bt_bdaddr_t *bd_addr)
+{
+ haltest_info("%s: bd_addr=%s\n", __func__,
+ bt_bdaddr_t2str(bd_addr, last_addr));
+}
+#else
+static void clcc_cmd_cb(void)
+{
+ haltest_info("%s\n", __func__);
+}
+#endif
+
+/*
+ * Callback for unknown AT command recd from HF
+ * at_string will contain the unparsed AT string
+ */
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+static void unknown_at_cmd_cb(char *at_string, bt_bdaddr_t *bd_addr)
+{
+ haltest_info("%s: at_string=%s bd_addr=%s\n", __func__, at_string,
+ bt_bdaddr_t2str(bd_addr, last_addr));
+}
+#else
+static void unknown_at_cmd_cb(char *at_string)
+{
+ haltest_info("%s: at_string=%s\n", __func__, at_string);
+}
+#endif
+
+/* Callback for keypressed (HSP) event. */
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+static void key_pressed_cmd_cb(bt_bdaddr_t *bd_addr)
+{
+ haltest_info("%s: bd_addr=%s\n", __func__,
+ bt_bdaddr_t2str(bd_addr, last_addr));
+}
+#else
+static void key_pressed_cmd_cb(void)
+{
+ haltest_info("%s\n", __func__);
+}
+#endif
+
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+static void wbs_cb(bthf_wbs_config_t wbs, bt_bdaddr_t *bd_addr)
+{
+ haltest_info("%s: bd_addr=%s\n", __func__,
+ bt_bdaddr_t2str(bd_addr, last_addr));
+}
+#endif
+
+static bthf_callbacks_t hf_cbacks = {
+ .size = sizeof(hf_cbacks),
+ .connection_state_cb = connection_state_cb,
+ .audio_state_cb = audio_state_cb,
+ .vr_cmd_cb = vr_cmd_cb,
+ .answer_call_cmd_cb = answer_call_cmd_cb,
+ .hangup_call_cmd_cb = hangup_call_cmd_cb,
+ .volume_cmd_cb = volume_cmd_cb,
+ .dial_call_cmd_cb = dial_call_cmd_cb,
+ .dtmf_cmd_cb = dtmf_cmd_cb,
+ .nrec_cmd_cb = nrec_cmd_cb,
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+ .wbs_cb = wbs_cb,
+#endif
+ .chld_cmd_cb = chld_cmd_cb,
+ .cnum_cmd_cb = cnum_cmd_cb,
+ .cind_cmd_cb = cind_cmd_cb,
+ .cops_cmd_cb = cops_cmd_cb,
+ .clcc_cmd_cb = clcc_cmd_cb,
+ .unknown_at_cmd_cb = unknown_at_cmd_cb,
+ .key_pressed_cmd_cb = key_pressed_cmd_cb,
+};
+
+/* init */
+
+static void init_p(int argc, const char **argv)
+{
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+ int max_hf_clients;
+#endif
+
+ RETURN_IF_NULL(if_hf);
+
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+ if (argc <= 2)
+ max_hf_clients = 1;
+ else
+ max_hf_clients = atoi(argv[2]);
+
+ EXEC(if_hf->init, &hf_cbacks, max_hf_clients);
+#else
+ EXEC(if_hf->init, &hf_cbacks);
+#endif
+}
+
+/* connect */
+
+static void connect_c(int argc, const char **argv, enum_func *enum_func,
+ void **user)
+{
+ if (argc == 3) {
+ *user = NULL;
+ *enum_func = enum_devices;
+ }
+}
+
+static void connect_p(int argc, const char **argv)
+{
+ bt_bdaddr_t addr;
+
+ RETURN_IF_NULL(if_hf);
+ VERIFY_ADDR_ARG(2, &addr);
+
+ EXEC(if_hf->connect, &addr);
+}
+
+/* disconnect */
+
+/*
+ * This completion function will be used for several methods
+ * returning recently connected address
+ */
+static void connected_addr_c(int argc, const char **argv, enum_func *enum_func,
+ void **user)
+{
+ if (argc == 3) {
+ *user = last_addr;
+ *enum_func = enum_one_string;
+ }
+}
+
+/* Map completion to connected_addr_c */
+#define disconnect_c connected_addr_c
+
+static void disconnect_p(int argc, const char **argv)
+{
+ bt_bdaddr_t addr;
+
+ RETURN_IF_NULL(if_hf);
+ VERIFY_ADDR_ARG(2, &addr);
+
+ EXEC(if_hf->disconnect, &addr);
+}
+
+/* create an audio connection */
+
+/* Map completion to connected_addr_c */
+#define connect_audio_c connected_addr_c
+
+static void connect_audio_p(int argc, const char **argv)
+{
+ bt_bdaddr_t addr;
+
+ RETURN_IF_NULL(if_hf);
+ VERIFY_ADDR_ARG(2, &addr);
+
+ EXEC(if_hf->connect_audio, &addr);
+}
+
+/* close the audio connection */
+
+/* Map completion to connected_addr_c */
+#define disconnect_audio_c connected_addr_c
+
+static void disconnect_audio_p(int argc, const char **argv)
+{
+ bt_bdaddr_t addr;
+
+ RETURN_IF_NULL(if_hf);
+ VERIFY_ADDR_ARG(2, &addr);
+
+ EXEC(if_hf->disconnect_audio, &addr);
+}
+
+/* start voice recognition */
+
+static void start_voice_recognition_p(int argc, const char **argv)
+{
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+ bt_bdaddr_t addr;
+#endif
+
+ RETURN_IF_NULL(if_hf);
+
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+ VERIFY_ADDR_ARG(2, &addr);
+
+ EXEC(if_hf->start_voice_recognition, &addr);
+#else
+ EXEC(if_hf->start_voice_recognition);
+#endif
+}
+
+/* stop voice recognition */
+
+static void stop_voice_recognition_p(int argc, const char **argv)
+{
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+ bt_bdaddr_t addr;
+#endif
+
+ RETURN_IF_NULL(if_hf);
+
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+ VERIFY_ADDR_ARG(2, &addr);
+
+ EXEC(if_hf->stop_voice_recognition, &addr);
+#else
+ EXEC(if_hf->stop_voice_recognition);
+#endif
+}
+
+/* volume control */
+
+static void volume_control_c(int argc, const char **argv, enum_func *enum_func,
+ void **user)
+{
+ if (argc == 3) {
+ *user = TYPE_ENUM(bthf_volume_type_t);
+ *enum_func = enum_defines;
+ }
+}
+
+static void volume_control_p(int argc, const char **argv)
+{
+ bthf_volume_type_t type;
+ int volume;
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+ bt_bdaddr_t addr;
+#endif
+
+ RETURN_IF_NULL(if_hf);
+
+ /* volume type */
+ if (argc <= 2) {
+ haltest_error("No volume type specified\n");
+ return;
+ }
+ type = str2bthf_volume_type_t(argv[2]);
+
+ /* volume */
+ if (argc <= 3) {
+ haltest_error("No volume specified\n");
+ return;
+ }
+ volume = atoi(argv[3]);
+
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+ VERIFY_ADDR_ARG(4, &addr);
+
+ EXEC(if_hf->volume_control, type, volume, &addr);
+#else
+ EXEC(if_hf->volume_control, type, volume);
+#endif
+}
+
+/* Combined device status change notification */
+
+static void device_status_notification_c(int argc, const char **argv,
+ enum_func *enum_func,
+ void **user)
+{
+ if (argc == 3) {
+ *user = TYPE_ENUM(bthf_network_state_t);
+ *enum_func = enum_defines;
+ } else if (argc == 4) {
+ *user = TYPE_ENUM(bthf_service_type_t);
+ *enum_func = enum_defines;
+ }
+}
+
+static void device_status_notification_p(int argc, const char **argv)
+{
+ bthf_network_state_t ntk_state;
+ bthf_service_type_t svc_type;
+ int signal;
+ int batt_chg;
+
+ RETURN_IF_NULL(if_hf);
+
+ /* network state */
+ if (argc <= 2) {
+ haltest_error("No network state specified\n");
+ return;
+ }
+ ntk_state = str2bthf_network_state_t(argv[2]);
+
+ /* service type */
+ if (argc <= 3) {
+ haltest_error("No service type specified\n");
+ return;
+ }
+ svc_type = str2bthf_service_type_t(argv[3]);
+
+ /* signal */
+ if (argc <= 4) {
+ haltest_error("No signal specified\n");
+ return;
+ }
+ signal = atoi(argv[4]);
+
+ /* batt_chg */
+ if (argc <= 5) {
+ haltest_error("No batt_chg specified\n");
+ return;
+ }
+ batt_chg = atoi(argv[5]);
+
+ EXEC(if_hf->device_status_notification, ntk_state, svc_type, signal,
+ batt_chg);
+}
+
+/* Response for COPS command */
+
+static void cops_response_p(int argc, const char **argv)
+{
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+ bt_bdaddr_t addr;
+#endif
+
+ RETURN_IF_NULL(if_hf);
+
+ /* response */
+ if (argc <= 2) {
+ haltest_error("No cops specified\n");
+ return;
+ }
+
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+ VERIFY_ADDR_ARG(3, &addr);
+
+ EXEC(if_hf->cops_response, argv[2], &addr);
+#else
+ EXEC(if_hf->cops_response, argv[2]);
+#endif
+}
+
+/* Response for CIND command */
+
+static void cind_response_c(int argc, const char **argv, enum_func *enum_func,
+ void **user)
+{
+ if (argc == 6) {
+ *user = TYPE_ENUM(bthf_call_state_t);
+ *enum_func = enum_defines;
+ }
+}
+
+static void cind_response_p(int argc, const char **argv)
+{
+ int svc;
+ int num_active;
+ int num_held;
+ bthf_call_state_t call_setup_state;
+ int signal;
+ int roam;
+ int batt_chg;
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+ bt_bdaddr_t addr;
+#endif
+
+ RETURN_IF_NULL(if_hf);
+
+ /* svc */
+ if (argc <= 2) {
+ haltest_error("No service specified\n");
+ return;
+ }
+ svc = atoi(argv[2]);
+
+ /* num active */
+ if (argc <= 3) {
+ haltest_error("No num active specified\n");
+ return;
+ }
+ num_active = atoi(argv[3]);
+
+ /* num held */
+ if (argc <= 4) {
+ haltest_error("No num held specified\n");
+ return;
+ }
+ num_held = atoi(argv[4]);
+
+ /* call setup state */
+ if (argc <= 5) {
+ haltest_error("No call setup state specified\n");
+ return;
+ }
+ call_setup_state = str2bthf_call_state_t(argv[5]);
+
+ /* signal */
+ if (argc <= 6) {
+ haltest_error("No signal specified\n");
+ return;
+ }
+ signal = atoi(argv[6]);
+
+ /* roam */
+ if (argc <= 7) {
+ haltest_error("No roam specified\n");
+ return;
+ }
+ roam = atoi(argv[7]);
+
+ /* batt_chg */
+ if (argc <= 8) {
+ haltest_error("No batt_chg specified\n");
+ return;
+ }
+ batt_chg = atoi(argv[8]);
+
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+ VERIFY_ADDR_ARG(9, &addr);
+
+ EXEC(if_hf->cind_response, svc, num_active, num_held, call_setup_state,
+ signal, roam, batt_chg, &addr);
+#else
+ EXEC(if_hf->cind_response, svc, num_active, num_held, call_setup_state,
+ signal, roam, batt_chg);
+#endif
+}
+
+/* Pre-formatted AT response, typically in response to unknown AT cmd */
+
+static void formatted_at_response_p(int argc, const char **argv)
+{
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+ bt_bdaddr_t addr;
+#endif
+
+ RETURN_IF_NULL(if_hf);
+
+ /* response */
+ if (argc <= 2) {
+ haltest_error("No response specified\n");
+ return;
+ }
+
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+ VERIFY_ADDR_ARG(3, &addr);
+
+ EXEC(if_hf->formatted_at_response, argv[2], &addr);
+#else
+ EXEC(if_hf->formatted_at_response, argv[2]);
+#endif
+}
+
+/* at_response */
+
+static void at_response_c(int argc, const char **argv, enum_func *enum_func,
+ void **user)
+{
+ if (argc == 3) {
+ *user = TYPE_ENUM(bthf_at_response_t);
+ *enum_func = enum_defines;
+ }
+}
+
+static void at_response_p(int argc, const char **argv)
+{
+ bthf_at_response_t response_code;
+ int error_code;
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+ bt_bdaddr_t addr;
+#endif
+
+ RETURN_IF_NULL(if_hf);
+
+ /* response type */
+ if (argc <= 2) {
+ haltest_error("No response specified\n");
+ return;
+ }
+ response_code = str2bthf_at_response_t(argv[2]);
+
+ /* error code */
+ if (argc <= 3)
+ error_code = 0;
+ else
+ error_code = atoi(argv[3]);
+
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+ VERIFY_ADDR_ARG(4, &addr);
+
+ EXEC(if_hf->at_response, response_code, error_code, &addr);
+#else
+ EXEC(if_hf->at_response, response_code, error_code);
+#endif
+}
+
+/* response for CLCC command */
+
+static void clcc_response_c(int argc, const char **argv, enum_func *enum_func,
+ void **user)
+{
+ if (argc == 4) {
+ *user = TYPE_ENUM(bthf_call_direction_t);
+ *enum_func = enum_defines;
+ } else if (argc == 5) {
+ *user = TYPE_ENUM(bthf_call_state_t);
+ *enum_func = enum_defines;
+ } else if (argc == 6) {
+ *user = TYPE_ENUM(bthf_call_mode_t);
+ *enum_func = enum_defines;
+ } else if (argc == 7) {
+ *user = TYPE_ENUM(bthf_call_mpty_type_t);
+ *enum_func = enum_defines;
+ } else if (argc == 9) {
+ *user = TYPE_ENUM(bthf_call_addrtype_t);
+ *enum_func = enum_defines;
+ }
+}
+
+static void clcc_response_p(int argc, const char **argv)
+{
+ int index;
+ bthf_call_direction_t dir;
+ bthf_call_state_t state;
+ bthf_call_mode_t mode;
+ bthf_call_mpty_type_t mpty;
+ const char *number;
+ bthf_call_addrtype_t type;
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+ bt_bdaddr_t addr;
+#endif
+
+ RETURN_IF_NULL(if_hf);
+
+ /* index */
+ if (argc <= 2) {
+ haltest_error("No index specified\n");
+ return;
+ }
+ index = atoi(argv[2]);
+
+ /* direction */
+ if (argc <= 3) {
+ haltest_error("No direction specified\n");
+ return;
+ }
+ dir = str2bthf_call_direction_t(argv[3]);
+
+ /* call state */
+ if (argc <= 4) {
+ haltest_error("No call state specified\n");
+ return;
+ }
+ state = str2bthf_call_state_t(argv[4]);
+
+ /* call mode */
+ if (argc <= 5) {
+ haltest_error("No mode specified\n");
+ return;
+ }
+ mode = str2bthf_call_mode_t(argv[5]);
+
+ /* call mpty type */
+ if (argc <= 6) {
+ haltest_error("No mpty type specified\n");
+ return;
+ }
+ mpty = str2bthf_call_mpty_type_t(argv[6]);
+
+ /* number */
+ if (argc <= 7) {
+ haltest_error("No number specified\n");
+ return;
+ }
+ number = argv[7];
+
+ /* call mpty type */
+ if (argc <= 8) {
+ haltest_error("No address type specified\n");
+ return;
+ }
+ type = str2bthf_call_addrtype_t(argv[8]);
+
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+ VERIFY_ADDR_ARG(9, &addr);
+
+ EXEC(if_hf->clcc_response, index, dir, state, mode, mpty, number,
+ type, &addr);
+#else
+ EXEC(if_hf->clcc_response, index, dir, state, mode, mpty, number,
+ type);
+#endif
+}
+
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+static void configure_wbs_c(int argc, const char **argv, enum_func *enum_func,
+ void **user)
+{
+ if (argc == 4) {
+ *user = TYPE_ENUM(bthf_wbs_config_t);
+ *enum_func = enum_defines;
+ }
+}
+
+static void configure_wbs_p(int argc, const char **argv)
+{
+ bthf_wbs_config_t wbs;
+ bt_bdaddr_t addr;
+
+ RETURN_IF_NULL(if_hf);
+
+ if (argc <= 3) {
+ haltest_error("Too few parameters specified\n");
+ return;
+ }
+
+ VERIFY_ADDR_ARG(2, &addr);
+ wbs = str2bthf_wbs_config_t(argv[3]);
+
+ EXEC(if_hf->configure_wbs, &addr, wbs);
+}
+#endif
+
+/* phone state change */
+
+static void phone_state_change_c(int argc, const char **argv,
+ enum_func *enum_func, void **user)
+{
+ if (argc == 5) {
+ *user = TYPE_ENUM(bthf_call_state_t);
+ *enum_func = enum_defines;
+ } else if (argc == 7) {
+ *user = TYPE_ENUM(bthf_call_addrtype_t);
+ *enum_func = enum_defines;
+ }
+}
+
+static void phone_state_change_p(int argc, const char **argv)
+{
+ int num_active;
+ int num_held;
+ bthf_call_state_t call_setup_state;
+ const char *number;
+ bthf_call_addrtype_t type;
+
+ RETURN_IF_NULL(if_hf);
+
+ /* num_active */
+ if (argc <= 2) {
+ haltest_error("No num_active specified\n");
+ return;
+ }
+ num_active = atoi(argv[2]);
+
+ /* num_held */
+ if (argc <= 3) {
+ haltest_error("No num_held specified\n");
+ return;
+ }
+ num_held = atoi(argv[3]);
+
+ /* setup state */
+ if (argc <= 4) {
+ haltest_error("No call setup state specified\n");
+ return;
+ }
+ call_setup_state = str2bthf_call_state_t(argv[4]);
+
+ /* number */
+ if (argc <= 5) {
+ haltest_error("No number specified\n");
+ return;
+ }
+ number = argv[5];
+
+ /* call mpty type */
+ if (argc <= 6) {
+ haltest_error("No address type specified\n");
+ return;
+ }
+ type = str2bthf_call_addrtype_t(argv[6]);
+
+ EXEC(if_hf->phone_state_change, num_active, num_held, call_setup_state,
+ number, type);
+}
+
+/* cleanup */
+
+static void cleanup_p(int argc, const char **argv)
+{
+ RETURN_IF_NULL(if_hf);
+
+ EXECV(if_hf->cleanup);
+ if_hf = NULL;
+}
+
+static struct method methods[] = {
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+ STD_METHODH(init, "[<max_hf_clients>]"),
+ STD_METHODH(start_voice_recognition, "<addr>"),
+ STD_METHODH(stop_voice_recognition, "<addr>"),
+ STD_METHODCH(volume_control, "<vol_type> <volume> <addr>"),
+ STD_METHODH(cops_response, "<cops string> <addr>"),
+ STD_METHODCH(cind_response,
+ "<svc> <num_active> <num_held> <setup_state> <signal> "
+ "<roam> <batt_chg> <addr>"),
+ STD_METHODH(formatted_at_response, "<at_response> <addr>"),
+ STD_METHODCH(at_response, "<response_code> [<error_code> <bdaddr>]"),
+ STD_METHODCH(clcc_response,
+ "<index> <direction> <state> <mode> <mpty> <number> "
+ "<type> <addr>"),
+ STD_METHODCH(configure_wbs, "<addr> <wbs config>"),
+#else
+ STD_METHOD(init),
+ STD_METHOD(start_voice_recognition),
+ STD_METHOD(stop_voice_recognition),
+ STD_METHODCH(volume_control, "<vol_type> <volume>"),
+ STD_METHODH(cops_response, "<cops string>"),
+ STD_METHODCH(cind_response,
+ "<svc> <num_active> <num_held> <setup_state> <signal> "
+ "<roam> <batt_chg>"),
+ STD_METHODH(formatted_at_response, "<at_response>"),
+ STD_METHODCH(at_response, "<response_code> [<error_code>]"),
+ STD_METHODCH(clcc_response,
+ "<index> <direction> <state> <mode> <mpty> <number> "
+ "<type>"),
+#endif
+ STD_METHODCH(connect, "<addr>"),
+ STD_METHODCH(disconnect, "<addr>"),
+ STD_METHODCH(connect_audio, "<addr>"),
+ STD_METHODCH(disconnect_audio, "<addr>"),
+ STD_METHODCH(device_status_notification,
+ "<ntk_state> <svt_type> <signal> <batt_chg>"),
+ STD_METHODCH(phone_state_change,
+ "<num_active> <num_held> <setup_state> <number> "
+ "<type>"),
+ STD_METHOD(cleanup),
+ END_METHOD
+};
+
+const struct interface hf_if = {
+ .name = "handsfree",
+ .methods = methods
+};
diff --git a/repo/android/client/if-hh.c b/repo/android/client/if-hh.c
new file mode 100644
index 0000000..25519e5
--- /dev/null
+++ b/repo/android/client/if-hh.c
@@ -0,0 +1,454 @@
+/*
+ * Copyright (C) 2013 Intel Corporation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include <stdio.h>
+#include <ctype.h>
+#include <string.h>
+
+#include <hardware/bluetooth.h>
+#include <hardware/bt_hh.h>
+
+#include "if-main.h"
+#include "pollhandler.h"
+#include "../hal-utils.h"
+
+const bthh_interface_t *if_hh = NULL;
+
+SINTMAP(bthh_protocol_mode_t, -1, "(unknown)")
+ DELEMENT(BTHH_REPORT_MODE),
+ DELEMENT(BTHH_BOOT_MODE),
+ DELEMENT(BTHH_UNSUPPORTED_MODE),
+ENDMAP
+
+SINTMAP(bthh_report_type_t, -1, "(unknown)")
+ DELEMENT(BTHH_INPUT_REPORT),
+ DELEMENT(BTHH_OUTPUT_REPORT),
+ DELEMENT(BTHH_FEATURE_REPORT),
+ENDMAP
+
+SINTMAP(bthh_connection_state_t, -1, "(unknown)")
+ DELEMENT(BTHH_CONN_STATE_CONNECTED),
+ DELEMENT(BTHH_CONN_STATE_CONNECTING),
+ DELEMENT(BTHH_CONN_STATE_DISCONNECTED),
+ DELEMENT(BTHH_CONN_STATE_DISCONNECTING),
+ DELEMENT(BTHH_CONN_STATE_FAILED_MOUSE_FROM_HOST),
+ DELEMENT(BTHH_CONN_STATE_FAILED_KBD_FROM_HOST),
+ DELEMENT(BTHH_CONN_STATE_FAILED_TOO_MANY_DEVICES),
+ DELEMENT(BTHH_CONN_STATE_FAILED_NO_BTHID_DRIVER),
+ DELEMENT(BTHH_CONN_STATE_FAILED_GENERIC),
+ DELEMENT(BTHH_CONN_STATE_UNKNOWN),
+ENDMAP
+
+SINTMAP(bthh_status_t, -1, "(unknown)")
+ DELEMENT(BTHH_OK),
+ DELEMENT(BTHH_HS_HID_NOT_READY),
+ DELEMENT(BTHH_HS_INVALID_RPT_ID),
+ DELEMENT(BTHH_HS_TRANS_NOT_SPT),
+ DELEMENT(BTHH_HS_INVALID_PARAM),
+ DELEMENT(BTHH_HS_ERROR),
+ DELEMENT(BTHH_ERR),
+ DELEMENT(BTHH_ERR_SDP),
+ DELEMENT(BTHH_ERR_PROTO),
+ DELEMENT(BTHH_ERR_DB_FULL),
+ DELEMENT(BTHH_ERR_TOD_UNSPT),
+ DELEMENT(BTHH_ERR_NO_RES),
+ DELEMENT(BTHH_ERR_AUTH_FAILED),
+ DELEMENT(BTHH_ERR_HDL),
+ENDMAP
+
+static char connected_device_addr[MAX_ADDR_STR_LEN];
+/*
+ * Callback for connection state change.
+ * state will have one of the values from bthh_connection_state_t
+ */
+static void connection_state_cb(bt_bdaddr_t *bd_addr,
+ bthh_connection_state_t state)
+{
+ char addr[MAX_ADDR_STR_LEN];
+
+ haltest_info("%s: bd_addr=%s connection_state=%s\n", __func__,
+ bt_bdaddr_t2str(bd_addr, addr),
+ bthh_connection_state_t2str(state));
+ if (state == BTHH_CONN_STATE_CONNECTED)
+ strcpy(connected_device_addr, addr);
+}
+
+/*
+ * Callback for virtual unplug api.
+ * the status of the virtual unplug
+ */
+static void virtual_unplug_cb(bt_bdaddr_t *bd_addr, bthh_status_t hh_status)
+{
+ char addr[MAX_ADDR_STR_LEN];
+
+ haltest_info("%s: bd_addr=%s hh_status=%s\n", __func__,
+ bt_bdaddr_t2str(bd_addr, addr),
+ bthh_status_t2str(hh_status));
+}
+
+/* Callback for Android 5.0 handshake api. */
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+static void handshake_cb(bt_bdaddr_t *bd_addr, bthh_status_t hh_status)
+{
+ char addr[MAX_ADDR_STR_LEN];
+
+ haltest_info("%s: bd_addr=%s hh_status=%s\n", __func__,
+ bt_bdaddr_t2str(bd_addr, addr),
+ bthh_status_t2str(hh_status));
+}
+#endif
+
+/*
+ * Callback for get hid info
+ * hid_info will contain attr_mask, sub_class, app_id, vendor_id, product_id,
+ * version, ctry_code, len
+ */
+static void hid_info_cb(bt_bdaddr_t *bd_addr, bthh_hid_info_t hid_info)
+{
+ char addr[MAX_ADDR_STR_LEN];
+
+ /* TODO: bluedroid does not seem to ever call this callback */
+ haltest_info("%s: bd_addr=%s\n", __func__,
+ bt_bdaddr_t2str(bd_addr, addr));
+}
+
+/*
+ * Callback for get/set protocol api.
+ * the protocol mode is one of the value from bthh_protocol_mode_t
+ */
+static void protocol_mode_cb(bt_bdaddr_t *bd_addr, bthh_status_t hh_status,
+ bthh_protocol_mode_t mode)
+{
+ char addr[MAX_ADDR_STR_LEN];
+
+ haltest_info("%s: bd_addr=%s hh_status=%s mode=%s\n", __func__,
+ bt_bdaddr_t2str(bd_addr, addr),
+ bthh_status_t2str(hh_status),
+ bthh_protocol_mode_t2str(mode));
+}
+
+/* Callback for get/set_idle_time api. */
+static void idle_time_cb(bt_bdaddr_t *bd_addr, bthh_status_t hh_status,
+ int idle_rate)
+{
+ char addr[MAX_ADDR_STR_LEN];
+
+ haltest_info("%s: bd_addr=%s hh_status=%s idle_rate=%d\n", __func__,
+ bt_bdaddr_t2str(bd_addr, addr),
+ bthh_status_t2str(hh_status), idle_rate);
+}
+
+
+/*
+ * Callback for get report api.
+ * if status is ok rpt_data contains the report data
+ */
+static void get_report_cb(bt_bdaddr_t *bd_addr, bthh_status_t hh_status,
+ uint8_t *rpt_data, int rpt_size)
+{
+ char addr[MAX_ADDR_STR_LEN];
+
+ /* TODO: print actual report */
+ haltest_info("%s: bd_addr=%s hh_status=%s rpt_size=%d\n", __func__,
+ bt_bdaddr_t2str(bd_addr, addr),
+ bthh_status_t2str(hh_status), rpt_size);
+}
+
+static bthh_callbacks_t bthh_callbacks = {
+ .size = sizeof(bthh_callbacks),
+ .connection_state_cb = connection_state_cb,
+ .hid_info_cb = hid_info_cb,
+ .protocol_mode_cb = protocol_mode_cb,
+ .idle_time_cb = idle_time_cb,
+ .get_report_cb = get_report_cb,
+ .virtual_unplug_cb = virtual_unplug_cb,
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+ .handshake_cb = handshake_cb
+#endif
+};
+
+/* init */
+
+static void init_p(int argc, const char **argv)
+{
+ RETURN_IF_NULL(if_hh);
+
+ EXEC(if_hh->init, &bthh_callbacks);
+}
+
+/* connect */
+
+static void connect_c(int argc, const char **argv, enum_func *enum_func,
+ void **user)
+{
+ if (argc == 3) {
+ *user = (void *) connected_device_addr;
+ *enum_func = enum_one_string;
+ }
+}
+
+static void connect_p(int argc, const char **argv)
+{
+ bt_bdaddr_t addr;
+
+ RETURN_IF_NULL(if_hh);
+ VERIFY_ADDR_ARG(2, &addr);
+
+ EXEC(if_hh->connect, &addr);
+}
+
+/* disconnect */
+
+/* Same completion as connect_c */
+#define disconnect_c connect_c
+
+static void disconnect_p(int argc, const char **argv)
+{
+ bt_bdaddr_t addr;
+
+ RETURN_IF_NULL(if_hh);
+ VERIFY_ADDR_ARG(2, &addr);
+
+ EXEC(if_hh->disconnect, &addr);
+}
+
+/* virtual_unplug */
+
+/* Same completion as connect_c */
+#define virtual_unplug_c connect_c
+
+static void virtual_unplug_p(int argc, const char **argv)
+{
+ bt_bdaddr_t addr;
+
+ RETURN_IF_NULL(if_hh);
+ VERIFY_ADDR_ARG(2, &addr);
+
+ EXEC(if_hh->virtual_unplug, &addr);
+}
+
+/* set_info */
+
+/* Same completion as connect_c */
+#define set_info_c connect_c
+
+static void set_info_p(int argc, const char **argv)
+{
+ bt_bdaddr_t addr;
+ bthh_hid_info_t hid_info;
+
+ RETURN_IF_NULL(if_hh);
+ VERIFY_ADDR_ARG(2, &addr);
+
+ memset(&hid_info, 0, sizeof(hid_info));
+
+ /*
+ * This command is intentionally not supported. See comment from
+ * bt_hid_info() in android/hidhost.c
+ */
+ EXEC(if_hh->set_info, &addr, hid_info);
+}
+
+/* get_protocol */
+
+static void get_protocol_c(int argc, const char **argv, enum_func *enum_func,
+ void **user)
+{
+ if (argc == 3) {
+ *user = connected_device_addr;
+ *enum_func = enum_one_string;
+ } else if (argc == 4) {
+ *user = TYPE_ENUM(bthh_protocol_mode_t);
+ *enum_func = enum_defines;
+ }
+}
+
+static void get_protocol_p(int argc, const char **argv)
+{
+ bt_bdaddr_t addr;
+ bthh_protocol_mode_t protocolMode;
+
+ RETURN_IF_NULL(if_hh);
+ VERIFY_ADDR_ARG(2, &addr);
+
+ if (argc < 4) {
+ haltest_error("No protocol mode specified\n");
+ return;
+ }
+ protocolMode = str2bthh_protocol_mode_t(argv[3]);
+
+ EXEC(if_hh->get_protocol, &addr, protocolMode);
+}
+
+/* set_protocol */
+
+/* Same completion as get_protocol_c */
+#define set_protocol_c get_protocol_c
+
+static void set_protocol_p(int argc, const char **argv)
+{
+ bt_bdaddr_t addr;
+ bthh_protocol_mode_t protocolMode;
+
+ RETURN_IF_NULL(if_hh);
+ VERIFY_ADDR_ARG(2, &addr);
+
+ if (argc < 4) {
+ haltest_error("No protocol mode specified\n");
+ return;
+ }
+ protocolMode = str2bthh_protocol_mode_t(argv[3]);
+
+ EXEC(if_hh->set_protocol, &addr, protocolMode);
+}
+
+/* get_report */
+
+static void get_report_c(int argc, const char **argv, enum_func *enum_func,
+ void **user)
+{
+ if (argc == 3) {
+ *user = connected_device_addr;
+ *enum_func = enum_one_string;
+ } else if (argc == 4) {
+ *user = TYPE_ENUM(bthh_report_type_t);
+ *enum_func = enum_defines;
+ }
+}
+
+static void get_report_p(int argc, const char **argv)
+{
+ bt_bdaddr_t addr;
+ bthh_report_type_t reportType;
+ uint8_t reportId;
+ int bufferSize;
+
+ RETURN_IF_NULL(if_hh);
+ VERIFY_ADDR_ARG(2, &addr);
+
+ if (argc < 4) {
+ haltest_error("No report type specified\n");
+ return;
+ }
+ reportType = str2bthh_report_type_t(argv[3]);
+
+ if (argc < 5) {
+ haltest_error("No reportId specified\n");
+ return;
+ }
+ reportId = (uint8_t) atoi(argv[4]);
+
+ if (argc < 6) {
+ haltest_error("No bufferSize specified\n");
+ return;
+ }
+ bufferSize = atoi(argv[5]);
+
+ EXEC(if_hh->get_report, &addr, reportType, reportId, bufferSize);
+}
+
+/* set_report */
+
+static void set_report_c(int argc, const char **argv, enum_func *enum_func,
+ void **user)
+{
+ if (argc == 3) {
+ *user = connected_device_addr;
+ *enum_func = enum_one_string;
+ } else if (argc == 4) {
+ *user = TYPE_ENUM(bthh_report_type_t);
+ *enum_func = enum_defines;
+ }
+}
+
+static void set_report_p(int argc, const char **argv)
+{
+ bt_bdaddr_t addr;
+ bthh_report_type_t reportType;
+
+ RETURN_IF_NULL(if_hh);
+ VERIFY_ADDR_ARG(2, &addr);
+
+ if (argc <= 3) {
+ haltest_error("No report type specified\n");
+ return;
+ }
+ reportType = str2bthh_report_type_t(argv[3]);
+
+ if (argc <= 4) {
+ haltest_error("No report specified\n");
+ return;
+ }
+
+ EXEC(if_hh->set_report, &addr, reportType, (char *) argv[4]);
+}
+
+/* send_data */
+
+static void send_data_c(int argc, const char **argv, enum_func *enum_func,
+ void **user)
+{
+ if (argc == 3) {
+ *user = connected_device_addr;
+ *enum_func = enum_one_string;
+ }
+}
+
+static void send_data_p(int argc, const char **argv)
+{
+ bt_bdaddr_t addr;
+
+ RETURN_IF_NULL(if_hh);
+ VERIFY_ADDR_ARG(2, &addr);
+
+ if (argc <= 3) {
+ haltest_error("No data to send specified\n");
+ return;
+ }
+
+ EXEC(if_hh->send_data, &addr, (char *) argv[3]);
+}
+
+/* cleanup */
+
+static void cleanup_p(int argc, const char **argv)
+{
+ RETURN_IF_NULL(if_hh);
+
+ EXECV(if_hh->cleanup);
+}
+
+/* Methods available in bthh_interface_t */
+static struct method methods[] = {
+ STD_METHOD(init),
+ STD_METHODCH(connect, "<addr>"),
+ STD_METHODCH(disconnect, "<addr>"),
+ STD_METHODCH(virtual_unplug, "<addr>"),
+ STD_METHODCH(set_info, "<addr>"),
+ STD_METHODCH(get_protocol, "<addr> <mode>"),
+ STD_METHODCH(set_protocol, "<addr> <mode>"),
+ STD_METHODCH(get_report, "<addr> <type> <report_id> <size>"),
+ STD_METHODCH(set_report, "<addr> <type> <hex_encoded_report>"),
+ STD_METHODCH(send_data, "<addr> <hex_encoded_data>"),
+ STD_METHOD(cleanup),
+ END_METHOD
+};
+
+const struct interface hh_if = {
+ .name = "hidhost",
+ .methods = methods
+};
diff --git a/repo/android/client/if-hl.c b/repo/android/client/if-hl.c
new file mode 100644
index 0000000..bd05671
--- /dev/null
+++ b/repo/android/client/if-hl.c
@@ -0,0 +1,377 @@
+/*
+ * Copyright (C) 2014 Intel Corporation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include <stdio.h>
+#include <ctype.h>
+#include <unistd.h>
+#include <string.h>
+
+#include <hardware/bluetooth.h>
+#include <hardware/bt_hl.h>
+
+#include "if-main.h"
+#include "pollhandler.h"
+#include "../hal-utils.h"
+
+SINTMAP(bthl_mdep_role_t, -1, "(unknown)")
+ DELEMENT(BTHL_MDEP_ROLE_SOURCE),
+ DELEMENT(BTHL_MDEP_ROLE_SINK),
+ENDMAP
+
+SINTMAP(bthl_channel_type_t, -1, "(unknown)")
+ DELEMENT(BTHL_CHANNEL_TYPE_RELIABLE),
+ DELEMENT(BTHL_CHANNEL_TYPE_STREAMING),
+ DELEMENT(BTHL_CHANNEL_TYPE_ANY),
+ENDMAP
+
+SINTMAP(bthl_app_reg_state_t, -1, "(unknown)")
+ DELEMENT(BTHL_APP_REG_STATE_REG_SUCCESS),
+ DELEMENT(BTHL_APP_REG_STATE_REG_FAILED),
+ DELEMENT(BTHL_APP_REG_STATE_DEREG_SUCCESS),
+ DELEMENT(BTHL_APP_REG_STATE_DEREG_FAILED),
+ENDMAP
+
+SINTMAP(bthl_channel_state_t, -1, "(unknown)")
+ DELEMENT(BTHL_CONN_STATE_CONNECTING),
+ DELEMENT(BTHL_CONN_STATE_CONNECTED),
+ DELEMENT(BTHL_CONN_STATE_DISCONNECTING),
+ DELEMENT(BTHL_CONN_STATE_DISCONNECTED),
+ DELEMENT(BTHL_CONN_STATE_DESTROYED),
+ENDMAP
+
+#define APP_ID_SIZE 20
+#define MDEP_CFG_SIZE 10
+#define CHANNEL_ID_SIZE 50
+
+struct channel_info {
+ int fd;
+};
+
+struct mdep_cfg {
+ uint8_t role;
+ struct channel_info channel[CHANNEL_ID_SIZE];
+};
+
+struct {
+ struct mdep_cfg mdep[MDEP_CFG_SIZE];
+} app[APP_ID_SIZE];
+
+const bthl_interface_t *if_hl = NULL;
+
+static void app_reg_state_cb(int app_id, bthl_app_reg_state_t state)
+{
+ haltest_info("%s: app_id=%d app_reg_state=%s\n", __func__,
+ app_id, bthl_app_reg_state_t2str(state));
+}
+
+static void channel_state_cb(int app_id, bt_bdaddr_t *bd_addr,
+ int index, int channel_id,
+ bthl_channel_state_t state, int fd)
+{
+ char addr[MAX_ADDR_STR_LEN];
+
+ haltest_info("%s: app_id=%d bd_addr=%s mdep_cfg_index=%d\n"
+ "channel_id=%d channel_state=%s fd=%d\n", __func__,
+ app_id, bt_bdaddr_t2str(bd_addr, addr), index,
+ channel_id, bthl_channel_state_t2str(state), fd);
+
+ if (app_id >= APP_ID_SIZE || index >= MDEP_CFG_SIZE
+ || channel_id >= CHANNEL_ID_SIZE) {
+ haltest_error("exceeds maximum limit");
+ return;
+ }
+
+ if (state == BTHL_CONN_STATE_CONNECTED) {
+ app[app_id].mdep[index].channel[channel_id].fd = fd;
+
+ /*
+ * PTS expects dummy data on fd when it
+ * connects in source role.
+ */
+ if (app[app_id].mdep[index].role == BTHL_MDEP_ROLE_SOURCE)
+ if (write(fd, "0", sizeof("0")) < 0)
+ haltest_error("writing data on fd failed\n");
+
+ return;
+ }
+
+ if (state == BTHL_CONN_STATE_DISCONNECTED ||
+ state == BTHL_CONN_STATE_DESTROYED) {
+ if (app[app_id].mdep[index].channel[channel_id].fd >= 0) {
+ close(app[app_id].mdep[index].channel[channel_id].fd);
+ app[app_id].mdep[index].channel[channel_id].fd = -1;
+ }
+ }
+}
+
+static bthl_callbacks_t hl_cbacks = {
+ .size = sizeof(hl_cbacks),
+ .app_reg_state_cb = app_reg_state_cb,
+ .channel_state_cb = channel_state_cb,
+};
+
+/* init */
+
+static void init_p(int argc, const char **argv)
+{
+ int i, j, k;
+
+ for (i = 0; i < APP_ID_SIZE; i++) {
+ for (j = 0; j < MDEP_CFG_SIZE; j++) {
+ app[i].mdep[j].role = 0;
+ for (k = 0; k < CHANNEL_ID_SIZE; k++)
+ app[i].mdep[j].channel[k].fd = -1;
+ }
+ }
+
+
+ RETURN_IF_NULL(if_hl);
+
+ EXEC(if_hl->init, &hl_cbacks);
+}
+
+/* register_application */
+
+static void register_application_p(int argc, const char **argv)
+{
+ bthl_reg_param_t reg;
+ uint16_t mdep_argc_init, mdep_argc_off;
+ int app_id = -1;
+ int i;
+
+ RETURN_IF_NULL(if_hl);
+
+ if (argc <= 2) {
+ haltest_error("No app name is specified\n");
+ return;
+ }
+
+ if (argc <= 3) {
+ haltest_error("No provider is specified\n");
+ return;
+ }
+
+ if (argc <= 4) {
+ haltest_error("No service name is specified\n");
+ return;
+ }
+
+ if (argc <= 5) {
+ haltest_error("No service description is specified\n");
+ return;
+ }
+
+ if (argc <= 6) {
+ haltest_error("No num of mdeps is specified\n");
+ return;
+ }
+
+ memset(®, 0, sizeof(reg));
+
+ if (argc != ((atoi(argv[6]) * 4) + 7)) {
+ haltest_error("mdep cfg argumetns are not proper\n");
+ return;
+ }
+
+ reg.application_name = argv[2];
+
+ if (strcmp("-", argv[3]))
+ reg.provider_name = argv[3];
+
+ if (strcmp("-", argv[4]))
+ reg.srv_name = argv[4];
+
+ if (strcmp("-", argv[5]))
+ reg.srv_desp = argv[5];
+
+ reg.number_of_mdeps = atoi(argv[6]);
+
+ reg.mdep_cfg = malloc(reg.number_of_mdeps * sizeof(bthl_mdep_cfg_t));
+ if (!reg.mdep_cfg) {
+ haltest_error("malloc failed\n");
+ return;
+ }
+ mdep_argc_init = 7;
+
+ for (i = 0; i < reg.number_of_mdeps; i++) {
+ mdep_argc_off = mdep_argc_init + (4 * i);
+ reg.mdep_cfg[i].mdep_role =
+ str2bthl_mdep_role_t(argv[mdep_argc_off]);
+ reg.mdep_cfg[i].data_type = atoi(argv[mdep_argc_off + 1]);
+ reg.mdep_cfg[i].channel_type =
+ str2bthl_channel_type_t(argv[mdep_argc_off + 2]);
+
+ if (!strcmp("-", argv[mdep_argc_off + 3])) {
+ reg.mdep_cfg[i].mdep_description = NULL;
+ continue;
+ }
+
+ reg.mdep_cfg[i].mdep_description = argv[mdep_argc_off + 3];
+ }
+
+ EXEC(if_hl->register_application, ®, &app_id);
+
+ for (i = 0; i < reg.number_of_mdeps; i++)
+ app[app_id].mdep[i].role = reg.mdep_cfg[i].mdep_role;
+
+ free(reg.mdep_cfg);
+}
+
+/* unregister_application */
+
+static void unregister_application_p(int argc, const char **argv)
+{
+ uint32_t app_id;
+
+ RETURN_IF_NULL(if_hl);
+
+ if (argc <= 2) {
+ haltest_error("No app id is specified");
+ return;
+ }
+
+ app_id = (uint32_t) atoi(argv[2]);
+
+ EXEC(if_hl->unregister_application, app_id);
+}
+
+/* connect_channel */
+
+static void connect_channel_p(int argc, const char **argv)
+{
+ uint32_t app_id, mdep_cfg_index;
+ int channel_id = -1;
+ bt_bdaddr_t bd_addr;
+
+ RETURN_IF_NULL(if_hl);
+
+ if (argc <= 2) {
+ haltest_error("No app id is specified");
+ return;
+ }
+
+ VERIFY_ADDR_ARG(3, &bd_addr);
+
+ if (argc <= 4) {
+ haltest_error("No mdep cfg index is specified");
+ return;
+ }
+
+ app_id = (uint32_t) atoi(argv[2]);
+ mdep_cfg_index = (uint32_t) atoi(argv[4]);
+
+ EXEC(if_hl->connect_channel, app_id, &bd_addr, mdep_cfg_index,
+ &channel_id);
+}
+
+/* destroy_channel */
+
+static void destroy_channel_p(int argc, const char **argv)
+{
+ uint32_t channel_id;
+
+ RETURN_IF_NULL(if_hl);
+
+ if (argc <= 2) {
+ haltest_error("No channel id is specified");
+ return;
+ }
+
+ channel_id = (uint32_t) atoi(argv[2]);
+
+ EXEC(if_hl->destroy_channel, channel_id);
+}
+
+/* close_channel */
+
+static void close_channel_p(int argc, const char **argv)
+{
+ uint32_t app_id;
+ uint8_t index;
+ int channel_id;
+
+ RETURN_IF_NULL(if_hl);
+
+ if (argc <= 2) {
+ haltest_error("No app id is specified");
+ return;
+ }
+
+ if (argc <= 3) {
+ haltest_error("No mdep_cfg_index is specified");
+ return;
+ }
+
+ if (argc <= 4) {
+ haltest_error("No channel_id is specified");
+ return;
+ }
+
+ app_id = (uint32_t) atoi(argv[2]);
+ if (app_id >= APP_ID_SIZE) {
+ haltest_error("Wrong app_id specified: %u\n", app_id);
+ return;
+ }
+
+ index = (uint8_t) atoi(argv[3]);
+ if (index >= MDEP_CFG_SIZE) {
+ haltest_error("Wrong mdep cfg index: %u\n", index);
+ return;
+ }
+
+ channel_id = atoi(argv[4]);
+ if (channel_id >= CHANNEL_ID_SIZE) {
+ haltest_error("Wrong channel id: %u\n", channel_id);
+ return;
+ }
+
+ if (app[app_id].mdep[index].channel[channel_id].fd >= 0) {
+ shutdown(app[app_id].mdep[index].channel[channel_id].fd,
+ SHUT_RDWR);
+ app[app_id].mdep[index].channel[channel_id].fd = -1;
+ }
+}
+
+/* cleanup */
+
+static void cleanup_p(int argc, const char **argv)
+{
+ RETURN_IF_NULL(if_hl);
+
+ EXECV(if_hl->cleanup);
+ if_hl = NULL;
+}
+
+static struct method methods[] = {
+ STD_METHOD(init),
+ STD_METHODH(register_application,
+ "<app_name> <provider_name> <srv_name> <srv_descr>\n"
+ "<num_of_mdeps>\n"
+ "[[<mdep_role>] [<data_type>] [<channel_type>] [<mdep_descr>]]"
+ "..."),
+ STD_METHODH(unregister_application, "<app_id>"),
+ STD_METHODH(connect_channel, "<app_id> <bd_addr> <mdep_cfg_index>"),
+ STD_METHODH(destroy_channel, "<channel_id>"),
+ STD_METHODH(close_channel, "<app_id> <mdep_cfg_index> <channel_id>"),
+ STD_METHOD(cleanup),
+ END_METHOD
+};
+
+const struct interface hl_if = {
+ .name = "hl",
+ .methods = methods
+};
diff --git a/repo/android/client/if-main.h b/repo/android/client/if-main.h
new file mode 100644
index 0000000..d239bb2
--- /dev/null
+++ b/repo/android/client/if-main.h
@@ -0,0 +1,198 @@
+/*
+ * Copyright (C) 2013 Intel Corporation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <signal.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/un.h>
+#include <poll.h>
+
+#include <hardware/audio.h>
+#include <hardware/bluetooth.h>
+#include <hardware/bt_av.h>
+#include <hardware/bt_hh.h>
+#include <hardware/bt_pan.h>
+#include <hardware/bt_sock.h>
+#include <hardware/bt_hf.h>
+#include <hardware/bt_hl.h>
+
+#include "hal.h"
+
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+#include <hardware/bt_hf_client.h>
+#include <hardware/bt_mce.h>
+#endif
+
+#include <hardware/bt_rc.h>
+#include <hardware/bt_gatt.h>
+#include <hardware/bt_gatt_types.h>
+#include <hardware/bt_gatt_client.h>
+#include <hardware/bt_gatt_server.h>
+
+extern audio_hw_device_t *if_audio;
+
+/* Interfaces from hal that can be populated during application lifetime */
+extern const bt_interface_t *if_bluetooth;
+extern const btav_interface_t *if_av;
+extern const btrc_interface_t *if_rc;
+extern const bthf_interface_t *if_hf;
+extern const bthh_interface_t *if_hh;
+extern const btpan_interface_t *if_pan;
+extern const bthl_interface_t *if_hl;
+extern const btsock_interface_t *if_sock;
+extern const btgatt_interface_t *if_gatt;
+extern const btgatt_server_interface_t *if_gatt_server;
+extern const btgatt_client_interface_t *if_gatt_client;
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+extern const btrc_ctrl_interface_t *if_rc_ctrl;
+extern const bthf_client_interface_t *if_hf_client;
+extern const btmce_interface_t *if_mce;
+extern const btav_interface_t *if_av_sink;
+#endif
+
+/*
+ * Structure defines top level interfaces that can be used in test tool
+ * this will contain values as: bluetooth, av, gatt, socket, pan...
+ */
+struct interface {
+ const char *name; /* interface name */
+ struct method *methods; /* methods available for this interface */
+};
+
+extern const struct interface audio_if;
+extern const struct interface sco_if;
+extern const struct interface bluetooth_if;
+extern const struct interface av_if;
+extern const struct interface rc_if;
+extern const struct interface gatt_if;
+extern const struct interface gatt_client_if;
+extern const struct interface gatt_server_if;
+extern const struct interface pan_if;
+extern const struct interface sock_if;
+extern const struct interface hf_if;
+extern const struct interface hh_if;
+extern const struct interface hl_if;
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+extern const struct interface ctrl_rc_if;
+extern const struct interface hf_client_if;
+extern const struct interface mce_if;
+extern const struct interface av_sink_if;
+#endif
+
+/* Interfaces that will show up in tool (first part of command line) */
+extern const struct interface *interfaces[];
+
+#define METHOD(name, func, comp, help) {name, func, comp, help}
+#define STD_METHOD(m) {#m, m##_p, NULL, NULL}
+#define STD_METHODC(m) {#m, m##_p, m##_c, NULL}
+#define STD_METHODH(m, h) {#m, m##_p, NULL, h}
+#define STD_METHODCH(m, h) {#m, m##_p, m##_c, h}
+#define END_METHOD {"", NULL, NULL, NULL}
+
+/*
+ * Function to parse argument for function, argv[0] and argv[1] are already
+ * parsed before this function is called and contain interface and method name
+ * up to argc - 1 arguments are finished and should be used to decide which
+ * function enumeration function to return
+ */
+typedef void (*parse_and_call)(int argc, const char **argv);
+
+/*
+ * This is prototype of function that will return string for given number.
+ * Purpose is to enumerate string for auto completion.
+ * Function of this type will always be called in loop.
+ * First time function is called i = 0, then if function returns non-NULL
+ * it will be called again till for some value of i it will return NULL
+ */
+typedef const char *(*enum_func)(void *user, int i);
+
+/*
+ * This is prototype of function that when given argc, argv will
+ * fill enum_func with pointer to function that will enumerate
+ * parameters for argc argument, user will be passed to enum_func.
+ */
+typedef void (*tab_complete)(int argc, const char **argv, enum_func *enum_func,
+ void **user);
+
+/*
+ * For each method there is name and two functions to parse command line
+ * and call proper hal function on.
+ */
+struct method {
+ const char *name;
+ parse_and_call func;
+ tab_complete complete;
+ const char *help;
+};
+
+int haltest_error(const char *format, ...)
+ __attribute__((format(printf, 1, 2)));
+int haltest_info(const char *format, ...)__attribute__((format(printf, 1, 2)));
+int haltest_warn(const char *format, ...)__attribute__((format(printf, 1, 2)));
+
+/* Enumerator for discovered devices, to be used as tab completion enum_func */
+const char *enum_devices(void *v, int i);
+const char *interface_name(void *v, int i);
+const char *command_name(void *v, int i);
+void add_remote_device(const bt_bdaddr_t *addr);
+bool close_hw_bt_dev(void);
+
+const struct interface *get_interface(const char *name);
+struct method *get_method(struct method *methods, const char *name);
+struct method *get_command(const char *name);
+const struct method *get_interface_method(const char *iname,
+ const char *mname);
+
+#define NELEM(x) ((int) (sizeof(x) / sizeof((x)[0])))
+
+/* Helper macro for executing function on interface and printing BT_STATUS */
+#define EXEC(f, ...) \
+ { \
+ if (f) { \
+ int err = f(__VA_ARGS__); \
+ haltest_info("%s: %s\n", #f, bt_status_t2str(err)); \
+ } else { \
+ haltest_info("%s is NULL\n", #f); \
+ } \
+ }
+
+/* Helper macro for executing void function on interface */
+#define EXECV(f, ...) \
+ { \
+ (void) f(__VA_ARGS__); \
+ haltest_info("%s: void\n", #f); \
+ }
+
+#define RETURN_IF_NULL(x) \
+ do { if (!x) { haltest_error("%s is NULL\n", #x); return; } } while (0)
+
+#define VERIFY_ADDR_ARG(n, adr) \
+ do { \
+ if (n < argc) {\
+ str2bt_bdaddr_t(argv[n], adr); \
+ } else { \
+ haltest_error("No address specified\n");\
+ return;\
+ } \
+ } while (0)
diff --git a/repo/android/client/if-mce.c b/repo/android/client/if-mce.c
new file mode 100644
index 0000000..5f101e0
--- /dev/null
+++ b/repo/android/client/if-mce.c
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2014 Intel Corporation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include "if-main.h"
+#include "../hal-utils.h"
+
+const btmce_interface_t *if_mce = NULL;
+
+/*
+ * Callback for get_remote_mas_instances
+ */
+static void btmce_remote_mas_instances_cb(bt_status_t status,
+ bt_bdaddr_t *bd_addr,
+ int num_instances,
+ btmce_mas_instance_t *instances)
+{
+ int i;
+
+ haltest_info("%s: status=%s bd_addr=%s num_instance=%d\n", __func__,
+ bt_status_t2str(status), bdaddr2str(bd_addr),
+ num_instances);
+
+ for (i = 0; i < num_instances; i++)
+ haltest_info("id=%d scn=%d msg_types=%d name=%s\n",
+ instances[i].id, instances[i].scn,
+ instances[i].msg_types, instances[i].p_name);
+}
+
+static btmce_callbacks_t mce_cbacks = {
+ .size = sizeof(mce_cbacks),
+ .remote_mas_instances_cb = btmce_remote_mas_instances_cb,
+};
+
+/* init */
+
+static void init_p(int argc, const char **argv)
+{
+ RETURN_IF_NULL(if_mce);
+
+ EXEC(if_mce->init, &mce_cbacks);
+}
+
+static void get_remote_mas_instances_c(int argc, const char **argv,
+ enum_func *enum_func, void **user)
+{
+ if (argc == 3) {
+ *user = NULL;
+ *enum_func = enum_devices;
+ }
+}
+
+/* search for MAS instances on remote device */
+
+static void get_remote_mas_instances_p(int argc, const char **argv)
+{
+ bt_bdaddr_t addr;
+
+ RETURN_IF_NULL(if_mce);
+ VERIFY_ADDR_ARG(2, &addr);
+
+ EXEC(if_mce->get_remote_mas_instances, &addr);
+}
+
+static struct method methods[] = {
+ STD_METHOD(init),
+ STD_METHODCH(get_remote_mas_instances, "<addr>"),
+ END_METHOD
+};
+
+const struct interface mce_if = {
+ .name = "mce",
+ .methods = methods
+};
diff --git a/repo/android/client/if-pan.c b/repo/android/client/if-pan.c
new file mode 100644
index 0000000..b0c4b84
--- /dev/null
+++ b/repo/android/client/if-pan.c
@@ -0,0 +1,213 @@
+/*
+ * Copyright (C) 2013 Intel Corporation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include "if-main.h"
+#include "../hal-utils.h"
+
+const btpan_interface_t *if_pan = NULL;
+
+typedef int btpan_role_t;
+
+SINTMAP(btpan_role_t, -1, "(unknown)")
+ DELEMENT(BTPAN_ROLE_NONE),
+ DELEMENT(BTPAN_ROLE_PANNAP),
+ DELEMENT(BTPAN_ROLE_PANU),
+ENDMAP
+
+SINTMAP(btpan_connection_state_t, -1, "(unknown)")
+ DELEMENT(BTPAN_STATE_CONNECTED),
+ DELEMENT(BTPAN_STATE_CONNECTING),
+ DELEMENT(BTPAN_STATE_DISCONNECTED),
+ DELEMENT(BTPAN_STATE_DISCONNECTING),
+ENDMAP
+
+SINTMAP(btpan_control_state_t, -1, "(unknown)")
+ DELEMENT(BTPAN_STATE_ENABLED),
+ DELEMENT(BTPAN_STATE_DISABLED),
+ENDMAP
+
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+static void control_state_cb(btpan_control_state_t state, int local_role,
+ bt_status_t error, const char *ifname)
+#else
+static void control_state_cb(btpan_control_state_t state, bt_status_t error,
+ int local_role, const char *ifname)
+#endif
+{
+ haltest_info("%s: state=%s error=%s local_role=%s ifname=%s\n",
+ __func__, btpan_control_state_t2str(state),
+ bt_status_t2str(error), btpan_role_t2str(local_role),
+ ifname);
+}
+
+static char last_used_addr[MAX_ADDR_STR_LEN];
+
+static void connection_state_cb(btpan_connection_state_t state,
+ bt_status_t error, const bt_bdaddr_t *bd_addr,
+ int local_role, int remote_role)
+{
+ haltest_info("%s: state=%s error=%s bd_addr=%s local_role=%s remote_role=%s\n",
+ __func__, btpan_connection_state_t2str(state),
+ bt_status_t2str(error),
+ bt_bdaddr_t2str(bd_addr, last_used_addr),
+ btpan_role_t2str(local_role),
+ btpan_role_t2str(remote_role));
+}
+
+static btpan_callbacks_t pan_cbacks = {
+ .size = sizeof(pan_cbacks),
+ .control_state_cb = control_state_cb,
+ .connection_state_cb = connection_state_cb
+};
+
+static void init_p(int argc, const char **argv)
+{
+ RETURN_IF_NULL(if_pan);
+
+ EXEC(if_pan->init, &pan_cbacks);
+}
+
+/* enable */
+
+static void enable_c(int argc, const char **argv, enum_func *enum_func,
+ void **user)
+{
+ if (argc == 3) {
+ *user = TYPE_ENUM(btpan_role_t);
+ *enum_func = enum_defines;
+ }
+}
+
+static void enable_p(int argc, const char **argv)
+{
+ int local_role;
+
+ RETURN_IF_NULL(if_pan);
+
+ /* local role */
+ if (argc < 3) {
+ haltest_error("No local mode specified\n");
+ return;
+ }
+ local_role = str2btpan_role_t(argv[2]);
+ if (local_role == -1)
+ local_role = atoi(argv[2]);
+
+ EXEC(if_pan->enable, local_role);
+}
+
+/* get_local_role */
+
+static void get_local_role_p(int argc, const char **argv)
+{
+ int local_role;
+
+ RETURN_IF_NULL(if_pan);
+
+ local_role = if_pan->get_local_role();
+ haltest_info("local_role: %s\n", btpan_role_t2str(local_role));
+}
+
+/* connect */
+
+static void connect_c(int argc, const char **argv, enum_func *enum_func,
+ void **user)
+{
+ if (argc == 3) {
+ *user = NULL;
+ *enum_func = enum_devices;
+ } else if (argc == 4 || argc == 5) {
+ *user = TYPE_ENUM(btpan_role_t);
+ *enum_func = enum_defines;
+ }
+}
+
+static void connect_p(int argc, const char **argv)
+{
+ bt_bdaddr_t addr;
+ int local_role;
+ int remote_role;
+
+ RETURN_IF_NULL(if_pan);
+ VERIFY_ADDR_ARG(2, &addr);
+
+ /* local role */
+ if (argc < 4) {
+ haltest_error("No local mode specified\n");
+ return;
+ }
+ local_role = str2btpan_role_t(argv[3]);
+ if (local_role == -1)
+ local_role = atoi(argv[3]);
+
+ /* remote role */
+ if (argc < 5) {
+ haltest_error("No remote mode specified\n");
+ return;
+ }
+ remote_role = str2btpan_role_t(argv[4]);
+ if (remote_role == -1)
+ remote_role = atoi(argv[4]);
+
+ EXEC(if_pan->connect, &addr, local_role, remote_role);
+}
+
+/* disconnect */
+
+static void disconnect_c(int argc, const char **argv, enum_func *enum_func,
+ void **user)
+{
+ if (argc == 3) {
+ *user = last_used_addr;
+ *enum_func = enum_one_string;
+ }
+}
+
+static void disconnect_p(int argc, const char **argv)
+{
+ bt_bdaddr_t addr;
+
+ RETURN_IF_NULL(if_pan);
+ VERIFY_ADDR_ARG(2, &addr);
+
+ EXEC(if_pan->disconnect, &addr);
+}
+
+/* cleanup */
+
+static void cleanup_p(int argc, const char **argv)
+{
+ RETURN_IF_NULL(if_pan);
+
+ EXECV(if_pan->cleanup);
+ if_pan = NULL;
+}
+
+static struct method methods[] = {
+ STD_METHOD(init),
+ STD_METHODCH(connect, "<addr> <local_role> <remote_role>"),
+ STD_METHODCH(enable, "<local_role>"),
+ STD_METHOD(get_local_role),
+ STD_METHODCH(disconnect, "<addr>"),
+ STD_METHOD(cleanup),
+ END_METHOD
+};
+
+const struct interface pan_if = {
+ .name = "pan",
+ .methods = methods
+};
diff --git a/repo/android/client/if-rc-ctrl.c b/repo/android/client/if-rc-ctrl.c
new file mode 100644
index 0000000..3980764
--- /dev/null
+++ b/repo/android/client/if-rc-ctrl.c
@@ -0,0 +1,114 @@
+/*
+ * Copyright (C) 2014 Intel Corporation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include <stdio.h>
+#include <ctype.h>
+#include <string.h>
+
+#include <hardware/bluetooth.h>
+#include <hardware/bt_rc.h>
+
+#include "if-main.h"
+#include "pollhandler.h"
+#include "../hal-utils.h"
+
+const btrc_ctrl_interface_t *if_rc_ctrl = NULL;
+static char last_addr[MAX_ADDR_STR_LEN];
+
+static void passthrough_rsp_cb(int id, int key_state)
+{
+ haltest_info("%s: id=%d key_state=%d\n", __func__, id, key_state);
+}
+
+static void connection_state_cb(bool state, bt_bdaddr_t *bd_addr)
+{
+ haltest_info("%s: state=%s bd_addr=%s\n", __func__,
+ state ? "true" : "false",
+ bt_bdaddr_t2str(bd_addr, last_addr));
+}
+
+static btrc_ctrl_callbacks_t rc_ctrl_cbacks = {
+ .size = sizeof(rc_ctrl_cbacks),
+ .passthrough_rsp_cb = passthrough_rsp_cb,
+ .connection_state_cb = connection_state_cb,
+};
+
+/* init */
+
+static void init_p(int argc, const char **argv)
+{
+ RETURN_IF_NULL(if_rc_ctrl);
+
+ EXEC(if_rc_ctrl->init, &rc_ctrl_cbacks);
+}
+
+/* cleanup */
+
+static void cleanup_p(int argc, const char **argv)
+{
+ RETURN_IF_NULL(if_rc_ctrl);
+
+ EXECV(if_rc_ctrl->cleanup);
+ if_rc_ctrl = NULL;
+}
+
+/* send_pass_through_cmd */
+static void send_pass_through_cmd_c(int argc, const char **argv,
+ enum_func *enum_func, void **user)
+{
+ if (argc == 3) {
+ *user = NULL;
+ *enum_func = enum_devices;
+ }
+}
+
+static void send_pass_through_cmd_p(int argc, const char **argv)
+{
+ bt_bdaddr_t addr;
+ uint8_t key_code, key_state;
+
+ RETURN_IF_NULL(if_rc);
+ VERIFY_ADDR_ARG(2, &addr);
+
+ if (argc < 4) {
+ haltest_error("No key code specified\n");
+ return;
+ }
+
+ key_code = (uint8_t) atoi(argv[3]);
+
+ if (argc < 5) {
+ haltest_error("No key state specified\n");
+ return;
+ }
+
+ key_state = (uint8_t) atoi(argv[4]);
+
+ EXEC(if_rc_ctrl->send_pass_through_cmd, &addr, key_code, key_state);
+}
+
+static struct method methods[] = {
+ STD_METHOD(init),
+ STD_METHODCH(send_pass_through_cmd, "<bd_addr> <key_code> <key_state>"),
+ STD_METHOD(cleanup),
+ END_METHOD
+};
+
+const struct interface ctrl_rc_if = {
+ .name = "rc-ctrl",
+ .methods = methods
+};
diff --git a/repo/android/client/if-rc.c b/repo/android/client/if-rc.c
new file mode 100644
index 0000000..ed65600
--- /dev/null
+++ b/repo/android/client/if-rc.c
@@ -0,0 +1,400 @@
+/*
+ * Copyright (C) 2014 Intel Corporation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include <stdio.h>
+#include <ctype.h>
+#include <string.h>
+
+#include <hardware/bluetooth.h>
+#include <hardware/bt_hh.h>
+
+#include "if-main.h"
+#include "pollhandler.h"
+#include "../hal-utils.h"
+
+const btrc_interface_t *if_rc = NULL;
+
+SINTMAP(btrc_play_status_t, -1, "(unknown)")
+ DELEMENT(BTRC_PLAYSTATE_STOPPED),
+ DELEMENT(BTRC_PLAYSTATE_PLAYING),
+ DELEMENT(BTRC_PLAYSTATE_PAUSED),
+ DELEMENT(BTRC_PLAYSTATE_FWD_SEEK),
+ DELEMENT(BTRC_PLAYSTATE_REV_SEEK),
+ DELEMENT(BTRC_PLAYSTATE_ERROR),
+ENDMAP
+
+SINTMAP(btrc_media_attr_t, -1, "(unknown)")
+ DELEMENT(BTRC_MEDIA_ATTR_TITLE),
+ DELEMENT(BTRC_MEDIA_ATTR_ARTIST),
+ DELEMENT(BTRC_MEDIA_ATTR_ALBUM),
+ DELEMENT(BTRC_MEDIA_ATTR_TRACK_NUM),
+ DELEMENT(BTRC_MEDIA_ATTR_NUM_TRACKS),
+ DELEMENT(BTRC_MEDIA_ATTR_GENRE),
+ DELEMENT(BTRC_MEDIA_ATTR_PLAYING_TIME),
+ENDMAP
+
+SINTMAP(btrc_status_t, -1, "(unknown)")
+ DELEMENT(BTRC_STS_BAD_CMD),
+ DELEMENT(BTRC_STS_BAD_PARAM),
+ DELEMENT(BTRC_STS_NOT_FOUND),
+ DELEMENT(BTRC_STS_INTERNAL_ERR),
+ DELEMENT(BTRC_STS_NO_ERROR),
+ENDMAP
+
+SINTMAP(btrc_event_id_t, -1, "(unknown)")
+ DELEMENT(BTRC_EVT_PLAY_STATUS_CHANGED),
+ DELEMENT(BTRC_EVT_TRACK_CHANGE),
+ DELEMENT(BTRC_EVT_TRACK_REACHED_END),
+ DELEMENT(BTRC_EVT_TRACK_REACHED_START),
+ DELEMENT(BTRC_EVT_PLAY_POS_CHANGED),
+ DELEMENT(BTRC_EVT_APP_SETTINGS_CHANGED),
+ENDMAP
+
+SINTMAP(btrc_notification_type_t, -1, "(unknown)")
+ DELEMENT(BTRC_NOTIFICATION_TYPE_INTERIM),
+ DELEMENT(BTRC_NOTIFICATION_TYPE_CHANGED),
+ENDMAP
+
+static char last_addr[MAX_ADDR_STR_LEN];
+
+static void remote_features_cb(bt_bdaddr_t *bd_addr,
+ btrc_remote_features_t features)
+{
+ haltest_info("%s: remote_bd_addr=%s features=%u\n", __func__,
+ bt_bdaddr_t2str(bd_addr, last_addr), features);
+}
+
+static void get_play_status_cb(void)
+{
+ haltest_info("%s\n", __func__);
+}
+
+static void list_player_app_attr_cb(void)
+{
+ haltest_info("%s\n", __func__);
+}
+
+static void list_player_app_values_cb(btrc_player_attr_t attr_id)
+{
+ haltest_info("%s, attr_id=%d\n", __func__, attr_id);
+}
+
+static void get_player_app_value_cb(uint8_t num_attr,
+ btrc_player_attr_t *p_attrs)
+{
+ int i;
+
+ haltest_info("%s, num_attr=%d\n", __func__, num_attr);
+
+ for (i = 0; i < num_attr; i++)
+ haltest_info("attribute=%u\n", p_attrs[i]);
+}
+
+static void get_player_app_attrs_text_cb(uint8_t num_attr,
+ btrc_player_attr_t *p_attrs)
+{
+ int i;
+
+ haltest_info("%s, num_attr=%d\n", __func__, num_attr);
+
+ for (i = 0; i < num_attr; i++)
+ haltest_info("attribute=%u\n", p_attrs[i]);
+
+}
+
+static void get_player_app_values_text_cb(uint8_t attr_id, uint8_t num_val,
+ uint8_t *p_vals)
+{
+ haltest_info("%s, attr_id=%d num_val=%d values=%p\n", __func__,
+ attr_id, num_val, p_vals);
+}
+
+static void set_player_app_value_cb(btrc_player_settings_t *p_vals)
+{
+ int i;
+
+ haltest_info("%s, num_attr=%u\n", __func__, p_vals->num_attr);
+
+ for (i = 0; i < p_vals->num_attr; i++)
+ haltest_info("attr id=%u, values=%u\n", p_vals->attr_ids[i],
+ p_vals->attr_values[i]);
+}
+
+static void get_element_attr_cb(uint8_t num_attr, btrc_media_attr_t *attrs)
+{
+ uint8_t i;
+
+ haltest_info("%s, num_of_attributes=%d\n", __func__, num_attr);
+
+ for (i = 0; i < num_attr; i++)
+ haltest_info("attr id=%s\n", btrc_media_attr_t2str(attrs[i]));
+}
+
+static void register_notification_cb(btrc_event_id_t event_id, uint32_t param)
+{
+ haltest_info("%s, event=%u param=%u\n", __func__, event_id, param);
+}
+
+static void volume_change_cb(uint8_t volume, uint8_t ctype)
+{
+ haltest_info("%s, volume=%d ctype=%d\n", __func__, volume, ctype);
+}
+
+static void passthrough_cmd_cb(int id, int key_state)
+{
+ haltest_info("%s, id=%d key_state=%d\n", __func__, id, key_state);
+}
+
+static btrc_callbacks_t rc_cbacks = {
+ .size = sizeof(rc_cbacks),
+ .remote_features_cb = remote_features_cb,
+ .get_play_status_cb = get_play_status_cb,
+ .list_player_app_attr_cb = list_player_app_attr_cb,
+ .list_player_app_values_cb = list_player_app_values_cb,
+ .get_player_app_value_cb = get_player_app_value_cb,
+ .get_player_app_attrs_text_cb = get_player_app_attrs_text_cb,
+ .get_player_app_values_text_cb = get_player_app_values_text_cb,
+ .set_player_app_value_cb = set_player_app_value_cb,
+ .get_element_attr_cb = get_element_attr_cb,
+ .register_notification_cb = register_notification_cb,
+ .volume_change_cb = volume_change_cb,
+ .passthrough_cmd_cb = passthrough_cmd_cb,
+};
+
+/* init */
+
+static void init_p(int argc, const char **argv)
+{
+ RETURN_IF_NULL(if_rc);
+
+ EXEC(if_rc->init, &rc_cbacks);
+}
+
+/* get_play_status_rsp */
+
+static void get_play_status_rsp_c(int argc, const char **argv,
+ enum_func *enum_func, void **user)
+{
+ if (argc == 3) {
+ *user = TYPE_ENUM(btrc_play_status_t);
+ *enum_func = enum_defines;
+ }
+}
+
+static void get_play_status_rsp_p(int argc, const char **argv)
+{
+ btrc_play_status_t play_status;
+ uint32_t song_len, song_pos;
+
+ RETURN_IF_NULL(if_rc);
+
+ if (argc <= 2) {
+ haltest_error("No play status specified");
+ return;
+ }
+
+ if (argc <= 3) {
+ haltest_error("No song length specified");
+ return;
+ }
+
+ if (argc <= 4) {
+ haltest_error("No song position specified");
+ return;
+ }
+
+ play_status = str2btrc_play_status_t(argv[2]);
+ song_len = (uint32_t) atoi(argv[3]);
+ song_pos = (uint32_t) atoi(argv[4]);
+
+ EXEC(if_rc->get_play_status_rsp, play_status, song_len, song_pos);
+}
+
+/* get_element_attr_rsp */
+
+static void get_element_attr_rsp_c(int argc, const char **argv,
+ enum_func *enum_func, void **user)
+{
+ if (argc == 4) {
+ *user = TYPE_ENUM(btrc_media_attr_t);
+ *enum_func = enum_defines;
+ }
+}
+
+static void get_element_attr_rsp_p(int argc, const char **argv)
+{
+ uint8_t num_attr;
+ btrc_element_attr_val_t attrs;
+
+ RETURN_IF_NULL(if_rc);
+
+ if (argc <= 2) {
+ haltest_error("No number of attributes specified");
+ return;
+ }
+
+ if (argc <= 4) {
+ haltest_error("No attr id and value specified");
+ return;
+ }
+
+ num_attr = (uint8_t) atoi(argv[2]);
+ attrs.attr_id = str2btrc_media_attr_t(argv[3]);
+ strcpy((char *)attrs.text, argv[4]);
+
+ EXEC(if_rc->get_element_attr_rsp, num_attr, &attrs);
+}
+
+/* set_volume */
+
+static void set_volume_c(int argc, const char **argv,
+ enum_func *enum_func, void **user)
+{
+}
+
+static void set_volume_p(int argc, const char **argv)
+{
+ uint8_t volume;
+
+ RETURN_IF_NULL(if_rc);
+
+ if (argc <= 2) {
+ haltest_error("No volume specified");
+ return;
+ }
+
+ volume = (uint8_t) atoi(argv[2]);
+
+ EXEC(if_rc->set_volume, volume);
+}
+
+/* set_player_app_value_rsp */
+
+static void set_player_app_value_rsp_c(int argc, const char **argv,
+ enum_func *enum_func, void **user)
+{
+ if (argc == 3) {
+ *user = TYPE_ENUM(btrc_status_t);
+ *enum_func = enum_defines;
+ }
+}
+
+static void set_player_app_value_rsp_p(int argc, const char **argv)
+{
+ btrc_status_t rsp_status;
+
+ RETURN_IF_NULL(if_rc);
+
+ if (argc <= 2) {
+ haltest_error("No response status specified");
+ return;
+ }
+
+ rsp_status = str2btrc_status_t(argv[2]);
+
+ EXEC(if_rc->set_player_app_value_rsp, rsp_status);
+}
+
+/* register_notification_rsp */
+
+static void register_notification_rsp_c(int argc, const char **argv,
+ enum_func *enum_func, void **user)
+{
+ if (argc == 3) {
+ *user = TYPE_ENUM(btrc_event_id_t);
+ *enum_func = enum_defines;
+ }
+
+ if (argc == 4) {
+ *user = TYPE_ENUM(btrc_notification_type_t);
+ *enum_func = enum_defines;
+ }
+}
+
+static void register_notification_rsp_p(int argc, const char **argv)
+{
+ btrc_event_id_t event_id;
+ btrc_notification_type_t type;
+ btrc_register_notification_t reg;
+ uint32_t song_pos;
+ uint64_t track;
+
+ RETURN_IF_NULL(if_rc);
+
+ memset(®, 0, sizeof(reg));
+ event_id = str2btrc_event_id_t(argv[2]);
+ type = str2btrc_notification_type_t(argv[3]);
+
+ switch (event_id) {
+ case BTRC_EVT_PLAY_STATUS_CHANGED:
+ reg.play_status = str2btrc_play_status_t(argv[4]);
+ break;
+
+ case BTRC_EVT_TRACK_CHANGE:
+ track = strtoull(argv[5], NULL, 10);
+ memcpy(reg.track, &track, sizeof(btrc_uid_t));
+ break;
+
+ case BTRC_EVT_TRACK_REACHED_END:
+ case BTRC_EVT_TRACK_REACHED_START:
+ break;
+
+ case BTRC_EVT_PLAY_POS_CHANGED:
+ song_pos = strtoul(argv[4], NULL, 10);
+ memcpy(®.song_pos, &song_pos, sizeof(uint32_t));
+ break;
+
+ case BTRC_EVT_APP_SETTINGS_CHANGED:
+ haltest_error("not supported");
+ return;
+ }
+
+ EXEC(if_rc->register_notification_rsp, event_id, type, ®);
+}
+
+/* cleanup */
+
+static void cleanup_p(int argc, const char **argv)
+{
+ RETURN_IF_NULL(if_rc);
+
+ EXECV(if_rc->cleanup);
+ if_rc = NULL;
+}
+
+static struct method methods[] = {
+ STD_METHOD(init),
+ STD_METHODCH(get_play_status_rsp,
+ "<play_status> <song_len> <song_pos>"),
+ STD_METHODCH(get_element_attr_rsp, "<num_attr> <attrs_id> <value>"),
+ STD_METHODCH(set_player_app_value_rsp, "<rsp_status>"),
+ STD_METHODCH(set_volume, "<volume>"),
+ STD_METHODCH(register_notification_rsp,
+ "<event_id> <type> <respective_data...>\n"
+ "BTRC_EVT_PLAY_STATUS_CHANGED <type> <play_status>\n"
+ "BTRC_EVT_TRACK_CHANGE <type> <track>\n"
+ "BTRC_EVT_TRACK_REACHED_END <type>\n"
+ "BTRC_EVT_TRACK_REACHED_START <type>\n"
+ "BTRC_EVT_PLAY_POS_CHANGED <type> <song_pos>\n"),
+ STD_METHOD(cleanup),
+ END_METHOD
+};
+
+const struct interface rc_if = {
+ .name = "rc",
+ .methods = methods
+};
diff --git a/repo/android/client/if-sco.c b/repo/android/client/if-sco.c
new file mode 100644
index 0000000..5a68ed5
--- /dev/null
+++ b/repo/android/client/if-sco.c
@@ -0,0 +1,815 @@
+/*
+ * Copyright (C) 2014 Intel Corporation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include <pthread.h>
+#include <unistd.h>
+#include <math.h>
+
+#include "if-main.h"
+#include "../hal-utils.h"
+
+audio_hw_device_t *if_audio_sco = NULL;
+static struct audio_stream_out *stream_out = NULL;
+static struct audio_stream_in *stream_in = NULL;
+
+static size_t buffer_size = 0;
+static size_t buffer_size_in = 0;
+static pthread_t play_thread = 0;
+static pthread_mutex_t outstream_mutex = PTHREAD_MUTEX_INITIALIZER;
+static pthread_mutex_t state_mutex = PTHREAD_MUTEX_INITIALIZER;
+
+enum state {
+ STATE_STOPPED,
+ STATE_STOPPING,
+ STATE_PLAYING,
+ STATE_SUSPENDED,
+ STATE_MAX
+};
+
+SINTMAP(audio_channel_mask_t, -1, "(AUDIO_CHANNEL_INVALID)")
+ DELEMENT(AUDIO_CHANNEL_OUT_FRONT_LEFT),
+ DELEMENT(AUDIO_CHANNEL_OUT_FRONT_RIGHT),
+ DELEMENT(AUDIO_CHANNEL_OUT_FRONT_CENTER),
+ DELEMENT(AUDIO_CHANNEL_OUT_LOW_FREQUENCY),
+ DELEMENT(AUDIO_CHANNEL_OUT_BACK_LEFT),
+ DELEMENT(AUDIO_CHANNEL_OUT_BACK_RIGHT),
+ DELEMENT(AUDIO_CHANNEL_OUT_FRONT_LEFT_OF_CENTER),
+ DELEMENT(AUDIO_CHANNEL_OUT_FRONT_RIGHT_OF_CENTER),
+ DELEMENT(AUDIO_CHANNEL_OUT_BACK_CENTER),
+ DELEMENT(AUDIO_CHANNEL_OUT_SIDE_LEFT),
+ DELEMENT(AUDIO_CHANNEL_OUT_SIDE_RIGHT),
+ DELEMENT(AUDIO_CHANNEL_OUT_TOP_CENTER),
+ DELEMENT(AUDIO_CHANNEL_OUT_TOP_FRONT_LEFT),
+ DELEMENT(AUDIO_CHANNEL_OUT_TOP_FRONT_CENTER),
+ DELEMENT(AUDIO_CHANNEL_OUT_TOP_FRONT_RIGHT),
+ DELEMENT(AUDIO_CHANNEL_OUT_TOP_BACK_LEFT),
+ DELEMENT(AUDIO_CHANNEL_OUT_TOP_BACK_CENTER),
+ DELEMENT(AUDIO_CHANNEL_OUT_TOP_BACK_RIGHT),
+ DELEMENT(AUDIO_CHANNEL_OUT_MONO),
+ DELEMENT(AUDIO_CHANNEL_OUT_STEREO),
+ DELEMENT(AUDIO_CHANNEL_OUT_QUAD),
+#if ANDROID_VERSION < PLATFORM_VER(5, 0, 0)
+ DELEMENT(AUDIO_CHANNEL_OUT_SURROUND),
+#else
+ DELEMENT(AUDIO_CHANNEL_OUT_QUAD_BACK),
+ DELEMENT(AUDIO_CHANNEL_OUT_QUAD_SIDE),
+ DELEMENT(AUDIO_CHANNEL_OUT_5POINT1_BACK),
+ DELEMENT(AUDIO_CHANNEL_OUT_5POINT1_SIDE),
+#endif
+ DELEMENT(AUDIO_CHANNEL_OUT_5POINT1),
+ DELEMENT(AUDIO_CHANNEL_OUT_7POINT1),
+ DELEMENT(AUDIO_CHANNEL_OUT_ALL),
+ DELEMENT(AUDIO_CHANNEL_OUT_FRONT_LEFT),
+ DELEMENT(AUDIO_CHANNEL_OUT_FRONT_LEFT),
+ DELEMENT(AUDIO_CHANNEL_OUT_FRONT_LEFT),
+ DELEMENT(AUDIO_CHANNEL_OUT_FRONT_LEFT),
+ DELEMENT(AUDIO_CHANNEL_OUT_FRONT_LEFT),
+ENDMAP
+
+SINTMAP(audio_format_t, -1, "(AUDIO_FORMAT_INVALID)")
+ DELEMENT(AUDIO_FORMAT_DEFAULT),
+ DELEMENT(AUDIO_FORMAT_PCM),
+ DELEMENT(AUDIO_FORMAT_MP3),
+ DELEMENT(AUDIO_FORMAT_AMR_NB),
+ DELEMENT(AUDIO_FORMAT_AMR_WB),
+ DELEMENT(AUDIO_FORMAT_AAC),
+ DELEMENT(AUDIO_FORMAT_HE_AAC_V1),
+ DELEMENT(AUDIO_FORMAT_HE_AAC_V2),
+ DELEMENT(AUDIO_FORMAT_VORBIS),
+ DELEMENT(AUDIO_FORMAT_MAIN_MASK),
+ DELEMENT(AUDIO_FORMAT_SUB_MASK),
+ DELEMENT(AUDIO_FORMAT_PCM_16_BIT),
+ DELEMENT(AUDIO_FORMAT_PCM_8_BIT),
+ DELEMENT(AUDIO_FORMAT_PCM_32_BIT),
+ DELEMENT(AUDIO_FORMAT_PCM_8_24_BIT),
+ENDMAP
+
+static int current_state = STATE_STOPPED;
+
+#define SAMPLERATE 44100
+static short sample[SAMPLERATE];
+static uint16_t sample_pos;
+
+static void init_p(int argc, const char **argv)
+{
+ int err;
+ const hw_module_t *module;
+ audio_hw_device_t *device;
+
+ err = hw_get_module_by_class(AUDIO_HARDWARE_MODULE_ID, "sco", &module);
+ if (err) {
+ haltest_error("hw_get_module_by_class returned %d\n", err);
+ return;
+ }
+
+ err = audio_hw_device_open(module, &device);
+ if (err) {
+ haltest_error("audio_hw_device_open returned %d\n", err);
+ return;
+ }
+
+ if_audio_sco = device;
+}
+
+static int feed_from_file(short *buffer, void *data)
+{
+ FILE *in = data;
+ return fread(buffer, buffer_size, 1, in);
+}
+
+static int feed_from_generator(short *buffer, void *data)
+{
+ size_t i = 0;
+ float volume = 0.5;
+ float *freq = data;
+ float f = 1;
+
+ if (freq)
+ f = *freq;
+
+ /* buffer_size is in bytes but we are using buffer of shorts (2 bytes)*/
+ for (i = 0; i < buffer_size / sizeof(*buffer) - 1;) {
+ if (sample_pos >= SAMPLERATE)
+ sample_pos = sample_pos % SAMPLERATE;
+
+ /* Use the same sample for both channels */
+ buffer[i++] = sample[sample_pos] * volume;
+ buffer[i++] = sample[sample_pos] * volume;
+
+ sample_pos += f;
+ }
+
+ return buffer_size;
+}
+
+static int feed_from_in(short *buffer, void *data)
+{
+ return stream_in->read(stream_in, buffer, buffer_size_in);
+}
+
+static void prepare_sample(void)
+{
+ int x;
+ double s;
+
+ haltest_info("Preparing audio sample...\n");
+
+ for (x = 0; x < SAMPLERATE; x++) {
+ /* prepare sinusoidal 1Hz sample */
+ s = (2.0 * 3.14159) * ((double)x / SAMPLERATE);
+ s = sin(s);
+
+ /* remap <-1, 1> to signed 16bit PCM range */
+ sample[x] = s * 32767;
+ }
+
+ sample_pos = 0;
+}
+
+static void mono_to_stereo_pcm16(const int16_t *in, int16_t *out, size_t samples)
+{
+ size_t i;
+
+ for (i = 0; i < samples; i++) {
+ out[2 * i] = in[i];
+ out[2 * i + 1] = in[i];
+ }
+}
+
+static void *playback_thread(void *data)
+{
+ int (*filbuff_cb) (short*, void*);
+ short buffer[buffer_size / sizeof(short)];
+ short buffer_in[buffer_size_in / sizeof(short)];
+ size_t len = 0;
+ ssize_t w_len = 0;
+ FILE *in = data;
+ void *cb_data = NULL;
+ float freq = 440.0;
+
+ /* Use file or fall back to generator */
+ if (in) {
+ if (data == stream_in)
+ filbuff_cb = feed_from_in;
+ else {
+ filbuff_cb = feed_from_file;
+ cb_data = in;
+ }
+ } else {
+ prepare_sample();
+ filbuff_cb = feed_from_generator;
+ cb_data = &freq;
+ }
+
+ pthread_mutex_lock(&state_mutex);
+ current_state = STATE_PLAYING;
+ pthread_mutex_unlock(&state_mutex);
+
+ do {
+ pthread_mutex_lock(&state_mutex);
+
+ if (current_state == STATE_STOPPING) {
+ haltest_info("Detected stopping\n");
+ pthread_mutex_unlock(&state_mutex);
+ break;
+ } else if (current_state == STATE_SUSPENDED) {
+ pthread_mutex_unlock(&state_mutex);
+ usleep(500);
+ continue;
+ }
+
+ pthread_mutex_unlock(&state_mutex);
+
+ if (data && data == stream_in) {
+ int chan_in = popcount(stream_in->common.get_channels(&stream_in->common));
+ int chan_out = popcount(stream_out->common.get_channels(&stream_out->common));
+
+ len = filbuff_cb(buffer_in, cb_data);
+
+ if (chan_in == 1 && chan_out == 2) {
+ mono_to_stereo_pcm16(buffer_in,
+ buffer,
+ buffer_size_in / 2);
+ }
+ } else
+ len = filbuff_cb(buffer, cb_data);
+
+ pthread_mutex_lock(&outstream_mutex);
+ if (!stream_out) {
+ pthread_mutex_unlock(&outstream_mutex);
+ break;
+ }
+
+ w_len = stream_out->write(stream_out, buffer, buffer_size);
+ pthread_mutex_unlock(&outstream_mutex);
+ } while (len && w_len > 0);
+
+ if (in && data != stream_in)
+ fclose(in);
+
+ pthread_mutex_lock(&state_mutex);
+ current_state = STATE_STOPPED;
+ pthread_mutex_unlock(&state_mutex);
+
+ haltest_info("Done playing.\n");
+
+ return NULL;
+}
+
+static void write_stereo_pcm16(const short *input, size_t len, FILE *out)
+{
+ short sample[2];
+ size_t i;
+
+ for (i = 0; i < len / 2; i++) {
+ sample[0] = input[i];
+ sample[1] = input[i];
+
+ fwrite(sample, sizeof(sample), 1, out);
+ }
+}
+
+static void *read_thread(void *data)
+{
+ int (*filbuff_cb) (short*, void*) = feed_from_in;
+ short buffer[buffer_size_in / sizeof(short)];
+ ssize_t len = 0;
+ void *cb_data = NULL;
+ FILE *out = data;
+
+ pthread_mutex_lock(&state_mutex);
+ current_state = STATE_PLAYING;
+ pthread_mutex_unlock(&state_mutex);
+
+ do {
+ pthread_mutex_lock(&state_mutex);
+
+ if (current_state == STATE_STOPPING) {
+ haltest_info("Detected stopping\n");
+ pthread_mutex_unlock(&state_mutex);
+ break;
+ } else if (current_state == STATE_SUSPENDED) {
+ pthread_mutex_unlock(&state_mutex);
+ usleep(500);
+ continue;
+ }
+
+ pthread_mutex_unlock(&state_mutex);
+
+ len = filbuff_cb(buffer, cb_data);
+ if (len < 0) {
+ haltest_error("Error receiving SCO data");
+ break;
+ }
+
+ haltest_info("Read %zd bytes\n", len);
+
+ if (out) {
+ write_stereo_pcm16(buffer, len, out);
+ haltest_info("Written %zd bytes\n", len * 2);
+ }
+ } while (len);
+
+ if (out)
+ fclose(out);
+
+ pthread_mutex_lock(&state_mutex);
+ current_state = STATE_STOPPED;
+ pthread_mutex_unlock(&state_mutex);
+
+ haltest_info("Done reading.\n");
+
+ return NULL;
+}
+
+static void play_p(int argc, const char **argv)
+{
+ const char *fname = NULL;
+ FILE *in = NULL;
+
+ RETURN_IF_NULL(if_audio_sco);
+ RETURN_IF_NULL(stream_out);
+
+ if (argc < 3) {
+ haltest_error("Invalid audio file path.\n");
+ haltest_info("Using sound generator.\n");
+ } else {
+ fname = argv[2];
+ in = fopen(fname, "r");
+
+ if (in == NULL) {
+ haltest_error("Cannot open file: %s\n", fname);
+ return;
+ }
+ haltest_info("Playing file: %s\n", fname);
+ }
+
+ if (buffer_size == 0) {
+ haltest_error("Invalid buffer size. Was stream_out opened?\n");
+ goto fail;
+ }
+
+ pthread_mutex_lock(&state_mutex);
+ if (current_state != STATE_STOPPED) {
+ haltest_error("Already playing or stream suspended!\n");
+ pthread_mutex_unlock(&state_mutex);
+ goto fail;
+ }
+ pthread_mutex_unlock(&state_mutex);
+
+ if (pthread_create(&play_thread, NULL, playback_thread, in) != 0) {
+ haltest_error("Cannot create playback thread!\n");
+ goto fail;
+ }
+
+ return;
+fail:
+ if (in)
+ fclose(in);
+}
+
+static void loop_p(int argc, const char **argv)
+{
+ int chan_out, chan_in;
+
+ RETURN_IF_NULL(if_audio_sco);
+ RETURN_IF_NULL(stream_out);
+ RETURN_IF_NULL(stream_in);
+
+ chan_out = popcount(stream_out->common.get_channels(&stream_out->common));
+ chan_in = popcount(stream_in->common.get_channels(&stream_in->common));
+
+ if (!buffer_size || !buffer_size_in) {
+ haltest_error("Invalid buffer sizes. Streams opened\n");
+ return;
+ }
+
+ if (buffer_size / chan_out != buffer_size_in / chan_in) {
+ haltest_error("read/write buffers differ, not supported\n");
+ return;
+ }
+
+ pthread_mutex_lock(&state_mutex);
+ if (current_state != STATE_STOPPED) {
+ haltest_error("Already playing or stream suspended!\n");
+ pthread_mutex_unlock(&state_mutex);
+ return;
+ }
+ pthread_mutex_unlock(&state_mutex);
+
+ if (pthread_create(&play_thread, NULL, playback_thread,
+ stream_in) != 0)
+ haltest_error("Cannot create playback thread!\n");
+}
+
+static void read_p(int argc, const char **argv)
+{
+ const char *fname = NULL;
+ FILE *out = NULL;
+
+ RETURN_IF_NULL(if_audio_sco);
+ RETURN_IF_NULL(stream_in);
+
+ pthread_mutex_lock(&state_mutex);
+ if (current_state != STATE_STOPPED) {
+ haltest_error("Already playing or stream suspended!\n");
+ pthread_mutex_unlock(&state_mutex);
+ return;
+ }
+ pthread_mutex_unlock(&state_mutex);
+
+ if (argc < 3) {
+ haltest_error("Invalid audio file path.\n");
+ haltest_info("Using read and through away\n");
+ } else {
+ fname = argv[2];
+ out = fopen(fname, "w");
+ if (!out) {
+ haltest_error("Cannot open file: %s\n", fname);
+ return;
+ }
+
+ haltest_info("Reading to file: %s\n", fname);
+ }
+
+ if (!buffer_size_in) {
+ haltest_error("Invalid buffer size.\n");
+ goto failed;
+ }
+
+ if (pthread_create(&play_thread, NULL, read_thread, out) != 0) {
+ haltest_error("Cannot create playback thread!\n");
+ goto failed;
+ }
+
+ return;
+failed:
+ if (out)
+ fclose(out);
+}
+
+static void stop_p(int argc, const char **argv)
+{
+ RETURN_IF_NULL(if_audio_sco);
+ RETURN_IF_NULL(play_thread);
+
+ pthread_mutex_lock(&state_mutex);
+ if (current_state == STATE_STOPPED || current_state == STATE_STOPPING) {
+ pthread_mutex_unlock(&state_mutex);
+ return;
+ }
+
+ if (stream_out) {
+ pthread_mutex_lock(&outstream_mutex);
+ stream_out->common.standby(&stream_out->common);
+ pthread_mutex_unlock(&outstream_mutex);
+ }
+
+ current_state = STATE_STOPPING;
+ pthread_mutex_unlock(&state_mutex);
+
+ pthread_join(play_thread, NULL);
+ play_thread = 0;
+
+ haltest_info("Ended %s\n", __func__);
+}
+
+static void open_output_stream_p(int argc, const char **argv)
+{
+ struct audio_config *config;
+ int err;
+
+ RETURN_IF_NULL(if_audio_sco);
+
+ pthread_mutex_lock(&state_mutex);
+ if (current_state == STATE_PLAYING) {
+ haltest_error("Already playing!\n");
+ pthread_mutex_unlock(&state_mutex);
+ return;
+ }
+ pthread_mutex_unlock(&state_mutex);
+
+ if (argc < 3) {
+ haltest_info("No sampling rate specified. Use default conf\n");
+ config = NULL;
+ } else {
+ config = calloc(1, sizeof(struct audio_config));
+ if (!config)
+ return;
+
+ config->sample_rate = atoi(argv[2]);
+ config->channel_mask = AUDIO_CHANNEL_OUT_STEREO;
+ config->format = AUDIO_FORMAT_PCM_16_BIT;
+ }
+
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+ err = if_audio_sco->open_output_stream(if_audio_sco,
+ 0,
+ AUDIO_DEVICE_OUT_ALL_SCO,
+ AUDIO_OUTPUT_FLAG_NONE,
+ config,
+ &stream_out, NULL);
+#else
+ err = if_audio_sco->open_output_stream(if_audio_sco,
+ 0,
+ AUDIO_DEVICE_OUT_ALL_SCO,
+ AUDIO_OUTPUT_FLAG_NONE,
+ config,
+ &stream_out);
+#endif
+ if (err < 0) {
+ haltest_error("open output stream returned %d\n", err);
+ goto failed;
+ }
+
+ buffer_size = stream_out->common.get_buffer_size(&stream_out->common);
+ if (buffer_size == 0)
+ haltest_error("Invalid buffer size received!\n");
+ else
+ haltest_info("Using buffer size: %zu\n", buffer_size);
+failed:
+ if (config)
+ free(config);
+}
+
+static void close_output_stream_p(int argc, const char **argv)
+{
+ RETURN_IF_NULL(if_audio_sco);
+ RETURN_IF_NULL(stream_out);
+
+ if (play_thread)
+ stop_p(argc, argv);
+
+ if_audio_sco->close_output_stream(if_audio_sco, stream_out);
+
+ stream_out = NULL;
+ buffer_size = 0;
+}
+
+static void open_input_stream_p(int argc, const char **argv)
+{
+ struct audio_config *config;
+ int err;
+
+ RETURN_IF_NULL(if_audio_sco);
+
+ pthread_mutex_lock(&state_mutex);
+ if (current_state == STATE_PLAYING) {
+ haltest_error("Already playing!\n");
+ pthread_mutex_unlock(&state_mutex);
+ return;
+ }
+ pthread_mutex_unlock(&state_mutex);
+
+ if (argc < 3) {
+ haltest_info("No sampling rate specified. Use default conf\n");
+ config = NULL;
+ } else {
+ config = calloc(1, sizeof(struct audio_config));
+ if (!config)
+ return;
+
+ config->sample_rate = atoi(argv[2]);
+ config->channel_mask = AUDIO_CHANNEL_OUT_MONO;
+ config->format = AUDIO_FORMAT_PCM_16_BIT;
+ }
+
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+ err = if_audio_sco->open_input_stream(if_audio_sco,
+ 0,
+ AUDIO_DEVICE_IN_BLUETOOTH_SCO_HEADSET,
+ config,
+ &stream_in, 0, NULL, 0);
+#else
+ err = if_audio_sco->open_input_stream(if_audio_sco,
+ 0,
+ AUDIO_DEVICE_IN_BLUETOOTH_SCO_HEADSET,
+ config,
+ &stream_in);
+#endif
+ if (err < 0) {
+ haltest_error("open output stream returned %d\n", err);
+ goto failed;
+ }
+
+ buffer_size_in = stream_in->common.get_buffer_size(&stream_in->common);
+ if (buffer_size_in == 0)
+ haltest_error("Invalid buffer size received!\n");
+ else
+ haltest_info("Using buffer size: %zu\n", buffer_size_in);
+failed:
+ if (config)
+ free(config);
+}
+
+static void close_input_stream_p(int argc, const char **argv)
+{
+ RETURN_IF_NULL(if_audio_sco);
+ RETURN_IF_NULL(stream_in);
+
+ if (play_thread)
+ stop_p(argc, argv);
+
+ if_audio_sco->close_input_stream(if_audio_sco, stream_in);
+
+ stream_in = NULL;
+ buffer_size_in = 0;
+}
+
+static void cleanup_p(int argc, const char **argv)
+{
+ int err;
+
+ RETURN_IF_NULL(if_audio_sco);
+
+ pthread_mutex_lock(&state_mutex);
+ if (current_state != STATE_STOPPED) {
+ pthread_mutex_unlock(&state_mutex);
+ close_output_stream_p(0, NULL);
+ } else {
+ pthread_mutex_unlock(&state_mutex);
+ }
+
+ err = audio_hw_device_close(if_audio_sco);
+ if (err < 0) {
+ haltest_error("audio_hw_device_close returned %d\n", err);
+ return;
+ }
+
+ if_audio_sco = NULL;
+}
+
+static void suspend_p(int argc, const char **argv)
+{
+ RETURN_IF_NULL(if_audio_sco);
+ RETURN_IF_NULL(stream_out);
+
+ pthread_mutex_lock(&state_mutex);
+ if (current_state != STATE_PLAYING) {
+ pthread_mutex_unlock(&state_mutex);
+ return;
+ }
+ current_state = STATE_SUSPENDED;
+ pthread_mutex_unlock(&state_mutex);
+
+ pthread_mutex_lock(&outstream_mutex);
+ stream_out->common.standby(&stream_out->common);
+ pthread_mutex_unlock(&outstream_mutex);
+}
+
+static void resume_p(int argc, const char **argv)
+{
+ RETURN_IF_NULL(if_audio_sco);
+ RETURN_IF_NULL(stream_out);
+
+ pthread_mutex_lock(&state_mutex);
+ if (current_state == STATE_SUSPENDED)
+ current_state = STATE_PLAYING;
+ pthread_mutex_unlock(&state_mutex);
+}
+
+static void get_latency_p(int argc, const char **argv)
+{
+ RETURN_IF_NULL(if_audio_sco);
+ RETURN_IF_NULL(stream_out);
+
+ haltest_info("Output audio stream latency: %d\n",
+ stream_out->get_latency(stream_out));
+}
+
+static void get_buffer_size_p(int argc, const char **argv)
+{
+ RETURN_IF_NULL(if_audio_sco);
+ RETURN_IF_NULL(stream_out);
+
+ haltest_info("Current output buffer size: %zu\n",
+ stream_out->common.get_buffer_size(&stream_out->common));
+}
+
+static void get_channels_p(int argc, const char **argv)
+{
+ audio_channel_mask_t channels;
+
+ RETURN_IF_NULL(if_audio_sco);
+ RETURN_IF_NULL(stream_out);
+
+ channels = stream_out->common.get_channels(&stream_out->common);
+
+ haltest_info("Channels: %s\n", audio_channel_mask_t2str(channels));
+}
+
+static void get_format_p(int argc, const char **argv)
+{
+ audio_format_t format;
+
+ RETURN_IF_NULL(if_audio_sco);
+ RETURN_IF_NULL(stream_out);
+
+ format = stream_out->common.get_format(&stream_out->common);
+
+ haltest_info("Format: %s\n", audio_format_t2str(format));
+}
+
+static void get_sample_rate_p(int argc, const char **argv)
+{
+ RETURN_IF_NULL(if_audio_sco);
+ RETURN_IF_NULL(stream_out);
+
+ haltest_info("Current sample rate: %d\n",
+ stream_out->common.get_sample_rate(&stream_out->common));
+}
+
+static void get_parameters_p(int argc, const char **argv)
+{
+ const char *keystr;
+
+ RETURN_IF_NULL(if_audio_sco);
+ RETURN_IF_NULL(stream_out);
+
+ if (argc < 3) {
+ haltest_info("No keys given.\n");
+ keystr = "";
+ } else {
+ keystr = argv[2];
+ }
+
+ haltest_info("Current parameters: %s\n",
+ stream_out->common.get_parameters(&stream_out->common,
+ keystr));
+}
+
+static void set_parameters_p(int argc, const char **argv)
+{
+ RETURN_IF_NULL(if_audio_sco);
+ RETURN_IF_NULL(stream_out);
+
+ if (argc < 3) {
+ haltest_error("No key=value; pairs given.\n");
+ return;
+ }
+
+ stream_out->common.set_parameters(&stream_out->common, argv[2]);
+}
+
+static void set_sample_rate_p(int argc, const char **argv)
+{
+ RETURN_IF_NULL(if_audio_sco);
+ RETURN_IF_NULL(stream_out);
+
+ if (argc < 3)
+ return;
+
+ stream_out->common.set_sample_rate(&stream_out->common, atoi(argv[2]));
+}
+
+static void init_check_p(int argc, const char **argv)
+{
+ RETURN_IF_NULL(if_audio_sco);
+
+ haltest_info("Init check result: %d\n",
+ if_audio_sco->init_check(if_audio_sco));
+}
+
+static struct method methods[] = {
+ STD_METHOD(init),
+ STD_METHOD(cleanup),
+ STD_METHODH(open_output_stream, "sample_rate"),
+ STD_METHOD(close_output_stream),
+ STD_METHODH(open_input_stream, "sampling rate"),
+ STD_METHOD(close_input_stream),
+ STD_METHODH(play, "<path to pcm file>"),
+ STD_METHOD(read),
+ STD_METHOD(loop),
+ STD_METHOD(stop),
+ STD_METHOD(suspend),
+ STD_METHOD(resume),
+ STD_METHOD(get_latency),
+ STD_METHOD(get_buffer_size),
+ STD_METHOD(get_channels),
+ STD_METHOD(get_format),
+ STD_METHOD(get_sample_rate),
+ STD_METHODH(get_parameters, "<closing>"),
+ STD_METHODH(set_parameters, "<closing=value>"),
+ STD_METHODH(set_sample_rate, "<sample rate>"),
+ STD_METHOD(init_check),
+ END_METHOD
+};
+
+const struct interface sco_if = {
+ .name = "sco",
+ .methods = methods
+};
diff --git a/repo/android/client/if-sock.c b/repo/android/client/if-sock.c
new file mode 100644
index 0000000..ee2c1e8
--- /dev/null
+++ b/repo/android/client/if-sock.c
@@ -0,0 +1,350 @@
+/*
+ * Copyright (C) 2013 Intel Corporation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include <stdio.h>
+#include <ctype.h>
+#include <unistd.h>
+#include <string.h>
+
+#include "if-main.h"
+#include "pollhandler.h"
+#include "../hal-utils.h"
+
+const btsock_interface_t *if_sock = NULL;
+
+SINTMAP(btsock_type_t, -1, "(unknown)")
+ DELEMENT(BTSOCK_RFCOMM),
+ DELEMENT(BTSOCK_SCO),
+ DELEMENT(BTSOCK_L2CAP),
+ENDMAP
+
+#define MAX_LISTEN_FD 15
+static int listen_fd[MAX_LISTEN_FD];
+static int listen_fd_count;
+
+static const char * const uuids[] = {
+ "00001101", "00001105", "0000112f", NULL
+};
+
+/*
+ * This function reads data from file descriptor and
+ * prints it to the user
+ */
+static void receive_from_client(struct pollfd *pollfd)
+{
+ char buf[16];
+ /*
+ * Buffer for lines:
+ * 41 42 43 20 20 00 31 32 00 07 04 00 00 00 00 00 ABC .12.....
+ */
+ char outbuf[sizeof(buf) * 4 + 2];
+ int i;
+ int ret;
+
+ if (pollfd->revents & POLLHUP) {
+ haltest_error("Disconnected fd=%d\n", pollfd->fd);
+ poll_unregister_fd(pollfd->fd, receive_from_client);
+ } else if (pollfd->revents & POLLIN) {
+ haltest_info("receiving from client fd=%d\n", pollfd->fd);
+
+ do {
+ memset(outbuf, ' ', sizeof(outbuf));
+ outbuf[sizeof(outbuf) - 1] = 0;
+ ret = recv(pollfd->fd, buf, sizeof(buf), MSG_DONTWAIT);
+
+ for (i = 0; i < ret; ++i)
+ sprintf(outbuf + i * 3, "%02X ",
+ (unsigned) buf[i]);
+ outbuf[i * 3] = ' ';
+ for (i = 0; i < ret; ++i)
+ sprintf(outbuf + 48 + i, "%c",
+ (isprint(buf[i]) ? buf[i] : '.'));
+ if (ret > 0)
+ haltest_info("%s\n", outbuf);
+ } while (ret > 0);
+ } else {
+ /* For now disconnect on all other events */
+ haltest_error("Poll event %x\n", pollfd->revents);
+ poll_unregister_fd(pollfd->fd, receive_from_client);
+ }
+}
+
+/*
+ * This function read from fd socket information about
+ * connected socket
+ */
+static void receive_sock_connect_signal(struct pollfd *pollfd)
+{
+ sock_connect_signal_t cs;
+ char addr_str[MAX_ADDR_STR_LEN];
+
+ if (pollfd->revents & POLLIN) {
+ int ret;
+
+ poll_unregister_fd(pollfd->fd, receive_sock_connect_signal);
+ ret = read(pollfd->fd, &cs, sizeof(cs));
+ if (ret != sizeof(cs)) {
+ haltest_info("Read on connect return %d\n", ret);
+ return;
+ }
+
+ haltest_info("Connection to %s channel %d status=%d\n",
+ bt_bdaddr_t2str(&cs.bd_addr, addr_str),
+ cs.channel, cs.status);
+
+ if (cs.status == 0)
+ poll_register_fd(pollfd->fd, POLLIN,
+ receive_from_client);
+ }
+
+ if (pollfd->revents & POLLHUP) {
+ haltest_error("Disconnected fd=%d revents=0x%X\n", pollfd->fd,
+ pollfd->revents);
+ poll_unregister_fd(pollfd->fd, receive_sock_connect_signal);
+ }
+}
+
+/*
+ * This function read from fd socket information about
+ * incoming connection and starts monitoring new connection
+ * on file descriptor read from fd.
+ */
+static void read_accepted(int fd)
+{
+ int ret;
+ struct msghdr msg;
+ struct iovec iv;
+ char cmsgbuf[CMSG_SPACE(1)];
+ struct cmsghdr *cmsgptr;
+ sock_connect_signal_t cs;
+ int accepted_fd = -1;
+ char addr_str[MAX_ADDR_STR_LEN];
+
+ memset(&msg, 0, sizeof(msg));
+ memset(&iv, 0, sizeof(iv));
+ memset(cmsgbuf, 0, sizeof(cmsgbuf));
+
+ iv.iov_base = &cs;
+ iv.iov_len = sizeof(cs);
+
+ msg.msg_iov = &iv;
+ msg.msg_iovlen = 1;
+ msg.msg_control = cmsgbuf;
+ msg.msg_controllen = sizeof(cmsgbuf);
+
+ do {
+ ret = recvmsg(fd, &msg, MSG_NOSIGNAL);
+ } while (ret < 0 && errno == EINTR);
+
+ if (ret < 16 ||
+ (msg.msg_flags & (MSG_CTRUNC | MSG_OOB | MSG_ERRQUEUE)) != 0)
+ haltest_error("Failed to accept connection\n");
+
+ for (cmsgptr = CMSG_FIRSTHDR(&msg);
+ cmsgptr != NULL; cmsgptr = CMSG_NXTHDR(&msg, cmsgptr)) {
+ int count;
+
+ if (cmsgptr->cmsg_level != SOL_SOCKET ||
+ cmsgptr->cmsg_type != SCM_RIGHTS)
+ continue;
+
+ memcpy(&accepted_fd, CMSG_DATA(cmsgptr), sizeof(accepted_fd));
+ count = ((cmsgptr->cmsg_len - CMSG_LEN(0)) / sizeof(int));
+
+ if (count != 1)
+ haltest_error("Failed to accept descriptors count=%d\n",
+ count);
+
+ break;
+ }
+
+ haltest_info("Incoming connection from %s channel %d status=%d fd=%d\n",
+ bt_bdaddr_t2str(&cs.bd_addr, addr_str),
+ cs.channel, cs.status, accepted_fd);
+ poll_register_fd(accepted_fd, POLLIN, receive_from_client);
+}
+
+/* handles incoming connections on socket */
+static void client_connected(struct pollfd *pollfd)
+{
+ haltest_info("client connected %x\n", pollfd->revents);
+
+ if (pollfd->revents & POLLHUP)
+ poll_unregister_fd(pollfd->fd, client_connected);
+ else if (pollfd->revents & POLLIN)
+ read_accepted(pollfd->fd);
+}
+
+/* listen */
+
+static void listen_c(int argc, const char **argv, enum_func *enum_func,
+ void **user)
+{
+ if (argc == 3) {
+ *user = TYPE_ENUM(btsock_type_t);
+ *enum_func = enum_defines;
+ } else if (argc == 5) {
+ *user = (void *) uuids;
+ *enum_func = enum_strings;
+ }
+}
+
+static void listen_p(int argc, const char **argv)
+{
+ btsock_type_t type;
+ const char *service_name;
+ bt_uuid_t service_uuid;
+ int channel;
+ int sock_fd = -1;
+ int flags;
+
+ RETURN_IF_NULL(if_sock);
+
+ /* Socket type */
+ if (argc < 3) {
+ haltest_error("No socket type specified\n");
+ return;
+ }
+ type = str2btsock_type_t(argv[2]);
+ if ((int) type == -1)
+ type = atoi(argv[2]);
+
+ /* service name */
+ if (argc < 4) {
+ haltest_error("No service name specified\n");
+ return;
+ }
+ service_name = argv[3];
+
+ /* uuid */
+ if (argc < 5) {
+ haltest_error("No uuid specified\n");
+ return;
+ }
+ str2bt_uuid_t(argv[4], &service_uuid);
+
+ /* channel */
+ channel = argc > 5 ? atoi(argv[5]) : 0;
+
+ /* flags */
+ flags = argc > 6 ? atoi(argv[6]) : 0;
+
+ if (listen_fd_count >= MAX_LISTEN_FD) {
+ haltest_error("Max (%d) listening sockets exceeded\n",
+ listen_fd_count);
+ return;
+ }
+ EXEC(if_sock->listen, type, service_name,
+ &service_uuid.uu[0], channel, &sock_fd, flags);
+ if (sock_fd > 0) {
+ int channel = 0;
+ int ret = read(sock_fd, &channel, 4);
+ if (ret != 4)
+ haltest_info("Read channel failed\n");
+ haltest_info("Channel returned from first read %d\n", channel);
+ listen_fd[listen_fd_count++] = sock_fd;
+ poll_register_fd(sock_fd, POLLIN, client_connected);
+ }
+}
+
+/* connect */
+
+static void connect_c(int argc, const char **argv, enum_func *enum_func,
+ void **user)
+{
+ if (argc == 3) {
+ *enum_func = enum_devices;
+ } else if (argc == 4) {
+ *user = TYPE_ENUM(btsock_type_t);
+ *enum_func = enum_defines;
+ } else if (argc == 5) {
+ *user = (void *) uuids;
+ *enum_func = enum_strings;
+ }
+}
+
+static void connect_p(int argc, const char **argv)
+{
+ bt_bdaddr_t addr;
+ btsock_type_t type;
+ bt_uuid_t uuid;
+ int channel;
+ int sock_fd = -1;
+ int flags;
+
+ /* Address */
+ if (argc <= 2) {
+ haltest_error("No address specified\n");
+ return;
+ }
+ str2bt_bdaddr_t(argv[2], &addr);
+
+ /* Socket type */
+ if (argc <= 3) {
+ haltest_error("No socket type specified\n");
+ return;
+ }
+ type = str2btsock_type_t(argv[3]);
+ if ((int) type == -1)
+ type = atoi(argv[3]);
+
+ /* uuid */
+ if (argc <= 4) {
+ haltest_error("No uuid specified\n");
+ return;
+ }
+ str2bt_uuid_t(argv[4], &uuid);
+
+ /* channel */
+ if (argc <= 5) {
+ haltest_error("No channel specified\n");
+ return;
+ }
+ channel = atoi(argv[5]);
+
+ /* flags */
+ flags = argc <= 6 ? 0 : atoi(argv[6]);
+
+ RETURN_IF_NULL(if_sock);
+
+ EXEC(if_sock->connect, &addr, type, &uuid.uu[0], channel, &sock_fd,
+ flags);
+ if (sock_fd > 0) {
+ int channel = 0;
+ int ret = read(sock_fd, &channel, 4);
+
+ if (ret != 4)
+ haltest_info("Read channel failed\n");
+ haltest_info("Channel returned from first read %d\n", channel);
+ listen_fd[listen_fd_count++] = sock_fd;
+ poll_register_fd(sock_fd, POLLIN, receive_sock_connect_signal);
+ }
+}
+
+/* Methods available in btsock_interface_t */
+static struct method methods[] = {
+ STD_METHODCH(listen,
+ "<sock_type> <srvc_name> <uuid> [<channel>] [<flags>]"),
+ STD_METHODCH(connect,
+ "<addr> <sock_type> <uuid> <channel> [<flags>]"),
+ END_METHOD
+};
+
+const struct interface sock_if = {
+ .name = "socket",
+ .methods = methods
+};
diff --git a/repo/android/client/pollhandler.c b/repo/android/client/pollhandler.c
new file mode 100644
index 0000000..6160921
--- /dev/null
+++ b/repo/android/client/pollhandler.c
@@ -0,0 +1,117 @@
+/*
+ * Copyright (C) 2013 Intel Corporation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include <stdio.h>
+#include <errno.h>
+#include <poll.h>
+
+#include "pollhandler.h"
+
+/*
+ * Code that allows to poll multiply file descriptors for events
+ * File descriptors can be added and removed at runtime
+ *
+ * Call poll_register_fd function first to add file descriptors to monitor
+ * Then call poll_dispatch_loop that will poll all registered file descriptors
+ * as long as they are not unregistered.
+ *
+ * When event happen on given fd appropriate user supplied handler is called
+ */
+
+/* Maximum number of files to monitor */
+#define MAX_OPEN_FD 10
+
+/* Storage for pollfd structures for monitored file descriptors */
+static struct pollfd fds[MAX_OPEN_FD];
+static poll_handler fds_handler[MAX_OPEN_FD];
+/* Number of registered file descriptors */
+static int fds_count = 0;
+
+/*
+ * Function polls file descriptor in loop and calls appropriate handler
+ * on event. Function returns when there is no more file descriptor to
+ * monitor
+ */
+void poll_dispatch_loop(void)
+{
+ while (fds_count > 0) {
+ int i;
+ int cur_fds_count = fds_count;
+ int ready = poll(fds, fds_count, 1000);
+
+ for (i = 0; i < fds_count && ready > 0; ++i) {
+ if (fds[i].revents == 0)
+ continue;
+
+ fds_handler[i](fds + i);
+ ready--;
+ /*
+ * If handler was remove from table
+ * just skip the rest and poll again
+ * This is due to reordering of tables in
+ * register/unregister functions
+ */
+ if (cur_fds_count != fds_count)
+ break;
+ }
+ }
+}
+
+/*
+ * Registers file descriptor to be monitored for events (see man poll(2))
+ * for events.
+ *
+ * return non negative value on success
+ * -EMFILE when there are to much descriptors
+ */
+int poll_register_fd(int fd, short events, poll_handler ph)
+{
+ if (fds_count >= MAX_OPEN_FD)
+ return -EMFILE;
+
+ fds_handler[fds_count] = ph;
+ fds[fds_count].fd = fd;
+ fds[fds_count].events = events;
+ fds_count++;
+
+ return fds_count;
+}
+
+/*
+ * Unregisters file descriptor
+ * Both fd and ph must match previously registered data
+ *
+ * return 0 if unregister succeeded
+ * -EBADF if arguments do not match any register handler
+ */
+int poll_unregister_fd(int fd, poll_handler ph)
+{
+ int i;
+
+ for (i = 0; i < fds_count; ++i) {
+ if (fds_handler[i] == ph && fds[i].fd == fd) {
+ fds_count--;
+ if (i < fds_count) {
+ fds[i].fd = fds[fds_count].fd;
+ fds[i].events = fds[fds_count].events;
+ fds_handler[i] = fds_handler[fds_count];
+ }
+ return 0;
+ }
+ }
+ return -EBADF;
+}
diff --git a/repo/android/client/pollhandler.h b/repo/android/client/pollhandler.h
new file mode 100644
index 0000000..e2f22df
--- /dev/null
+++ b/repo/android/client/pollhandler.h
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2013 Intel Corporation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include <poll.h>
+
+/* Function to be called when there are event for some descriptor */
+typedef void (*poll_handler)(struct pollfd *pollfd);
+
+int poll_register_fd(int fd, short events, poll_handler ph);
+int poll_unregister_fd(int fd, poll_handler ph);
+
+void poll_dispatch_loop(void);
diff --git a/repo/android/client/tabcompletion.c b/repo/android/client/tabcompletion.c
new file mode 100644
index 0000000..bcca5fa
--- /dev/null
+++ b/repo/android/client/tabcompletion.c
@@ -0,0 +1,374 @@
+/*
+ * Copyright (C) 2013 Intel Corporation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include <stdio.h>
+#include <ctype.h>
+#include <string.h>
+#include "if-main.h"
+#include "terminal.h"
+
+/* how many times tab was hit */
+static int tab_hit_count;
+
+typedef struct split_arg {
+ struct split_arg *next; /* next argument in buffer */
+ const char *origin; /* pointer to original argument */
+ char ntcopy[1]; /* null terminated copy of argument */
+} split_arg_t;
+
+/* function returns method of given name or NULL if not found */
+const struct method *get_interface_method(const char *iname,
+ const char *mname)
+{
+ const struct interface *iface = get_interface(iname);
+
+ if (iface == NULL)
+ return NULL;
+
+ return get_method(iface->methods, mname);
+}
+
+/* prints matching elements */
+static void print_matches(enum_func f, void *user, const char *prefix, int len)
+{
+ int i;
+ const char *enum_name;
+
+ putchar('\n');
+ for (i = 0; NULL != (enum_name = f(user, i)); ++i) {
+ if (strncmp(enum_name, prefix, len) == 0)
+ printf("%s\t", enum_name);
+ }
+ putchar('\n');
+ terminal_draw_command_line();
+}
+
+/*
+ * This function splits command line into linked list of arguments.
+ * line_buffer - pointer to input command line
+ * size - size of command line to parse
+ * buf - output buffer to keep split arguments list
+ * buf_size_in_bytes - size of buf
+ */
+static int split_command(const char *line_buffer, int size, split_arg_t *buf,
+ int buf_size_in_bytes)
+{
+ split_arg_t *prev = NULL;
+ split_arg_t *arg = buf;
+ int argc = 0;
+ const char *p = line_buffer;
+ const char *e = p + (size > 0 ? size : (int) strlen(p));
+ int len;
+
+ do {
+ while (p < e && isspace(*p))
+ p++;
+ arg->origin = p;
+ arg->next = NULL;
+ while (p < e && !isspace(*p))
+ p++;
+ len = p - arg->origin;
+ if (&arg->ntcopy[0] + len + 1 >
+ (const char *) buf + buf_size_in_bytes)
+ break;
+ strncpy(arg->ntcopy, arg->origin, len);
+ arg->ntcopy[len] = 0;
+ if (prev != NULL)
+ prev->next = arg;
+ prev = arg;
+ arg += (2 * sizeof(*arg) + len) / sizeof(*arg);
+ argc++;
+ } while (p < e);
+
+ return argc;
+}
+
+/* Function to enumerate method names */
+static const char *methods_name(void *v, int i)
+{
+ const struct interface *iface = v;
+
+ return iface->methods[i].name[0] ? iface->methods[i].name : NULL;
+}
+
+struct command_completion_args;
+typedef void (*short_help)(struct command_completion_args *args);
+
+struct command_completion_args {
+ const split_arg_t *arg; /* list of arguments */
+ const char *typed; /* last typed element */
+ enum_func func; /* enumerating function */
+ void *user; /* argument to enumerating function */
+ short_help help; /* help function */
+ const char *user_help; /* additional data (used by short_help) */
+};
+
+/* complete command line */
+static void tab_completion(struct command_completion_args *args)
+{
+ const char *name = args->typed;
+ const int len = strlen(name);
+ int i;
+ int j;
+ char prefix[128] = {0};
+ int prefix_len = 0;
+ int count = 0;
+ const char *enum_name;
+
+ for (i = 0; NULL != (enum_name = args->func(args->user, i)); ++i) {
+ /* prefix does not match */
+ if (strncmp(enum_name, name, len) != 0)
+ continue;
+
+ /* prefix matches first time */
+ if (count++ == 0) {
+ strcpy(prefix, enum_name);
+ prefix_len = strlen(prefix);
+ continue;
+ }
+
+ /* Prefix matches next time reduce prefix to common part */
+ for (j = 0; prefix[j] != 0
+ && prefix[j] == enum_name[j];)
+ ++j;
+ prefix_len = j;
+ prefix[j] = 0;
+ }
+
+ if (count == 0) {
+ /* no matches */
+ if (args->help != NULL)
+ args->help(args);
+ tab_hit_count = 0;
+ return;
+ }
+
+ /* len == prefix_len => nothing new was added */
+ if (len == prefix_len) {
+ if (count != 1) {
+ if (tab_hit_count == 1) {
+ putchar('\a');
+ } else if (tab_hit_count == 2 ||
+ args->help == NULL) {
+ print_matches(args->func,
+ args->user, name, len);
+ } else {
+ args->help(args);
+ tab_hit_count = 1;
+ }
+ } else if (count == 1) {
+ /* nothing to add, exact match add space */
+ terminal_insert_into_command_line(" ");
+ }
+ } else {
+ /* new chars can be added from some interface name(s) */
+ if (count == 1) {
+ /* exact match, add space */
+ prefix[prefix_len++] = ' ';
+ prefix[prefix_len] = '\0';
+ }
+
+ terminal_insert_into_command_line(prefix + len);
+ tab_hit_count = 0;
+ }
+}
+
+/* interface completion */
+static void command_completion(split_arg_t *arg)
+{
+ struct command_completion_args args = {
+ .arg = arg,
+ .typed = arg->ntcopy,
+ .func = command_name
+ };
+
+ tab_completion(&args);
+}
+
+/* method completion */
+static void method_completion(const struct interface *iface, split_arg_t *arg)
+{
+ struct command_completion_args args = {
+ .arg = arg,
+ .typed = arg->next->ntcopy,
+ .func = methods_name,
+ .user = (void *) iface
+ };
+
+ if (iface == NULL)
+ return;
+
+ tab_completion(&args);
+}
+
+static const char *bold = "\x1b[1m";
+static const char *normal = "\x1b[0m";
+
+static bool find_nth_argument(const char *str, int n, const char **s,
+ const char **e)
+{
+ const char *p = str;
+ int argc = 0;
+ *e = NULL;
+
+ while (p != NULL && *p != 0) {
+
+ while (isspace(*p))
+ ++p;
+
+ if (n == argc)
+ *s = p;
+
+ if (*p == '[') {
+ p = strchr(p, ']');
+ if (p != NULL)
+ *e = ++p;
+ } else if (*p == '<') {
+ p = strchr(p, '>');
+ if (p != NULL)
+ *e = ++p;
+ } else {
+ *e = strchr(p, ' ');
+ if (*e == NULL)
+ *e = p + strlen(p);
+ p = *e;
+ }
+
+ if (n == argc)
+ break;
+
+ argc++;
+ *e = NULL;
+ }
+ return *e != NULL;
+}
+
+/* prints short help on method for interface */
+static void method_help(struct command_completion_args *args)
+{
+ int argc;
+ const split_arg_t *arg = args->arg;
+ const char *sb = NULL;
+ const char *eb = NULL;
+ const char *arg1 = "";
+ int arg1_size = 0; /* size of method field (for methods > 0) */
+
+ if (args->user_help == NULL)
+ return;
+
+ for (argc = 0; arg != NULL; argc++)
+ arg = arg->next;
+
+ /* Check if this is method from interface */
+ if (get_command(args->arg->ntcopy) == NULL) {
+ /* if so help is missing interface and method name */
+ arg1 = args->arg->next->ntcopy;
+ arg1_size = strlen(arg1) + 1;
+ }
+
+ find_nth_argument(args->user_help, argc - (arg1_size ? 3 : 2),
+ &sb, &eb);
+
+ if (eb != NULL)
+ haltest_info("%s %-*s%.*s%s%.*s%s%s\n", args->arg->ntcopy,
+ arg1_size, arg1, (int) (sb - args->user_help),
+ args->user_help, bold, (int) (eb - sb),
+ sb, normal, eb);
+ else
+ haltest_info("%s %-*s%s\n", args->arg->ntcopy,
+ arg1_size, arg1, args->user_help);
+}
+
+/* So we have empty enumeration */
+static const char *return_null(void *user, int i)
+{
+ return NULL;
+}
+
+/*
+ * parameter completion function
+ * argc - number of elements in arg list
+ * arg - list of arguments
+ * method - method to get completion from (can be NULL)
+ */
+static void param_completion(int argc, const split_arg_t *arg,
+ const struct method *method, int hlpix)
+{
+ int i;
+ const char *argv[argc];
+ const split_arg_t *tmp = arg;
+ struct command_completion_args args = {
+ .arg = arg,
+ .func = return_null
+ };
+
+ /* prepare standard argv from arg */
+ for (i = 0; i < argc; ++i) {
+ argv[i] = tmp->ntcopy;
+ tmp = tmp->next;
+ }
+
+ if (method != NULL && method->complete != NULL) {
+ /* ask method for completion function */
+ method->complete(argc, argv, &args.func, &args.user);
+ }
+
+ /* If method provided enumeration function call try to complete */
+ if (args.func != NULL) {
+ args.typed = argv[argc - 1];
+ args.help = method_help;
+ args.user_help = method ? method->help : NULL;
+
+ tab_completion(&args);
+ }
+}
+
+/*
+ * This method gets called when user tapped tab key.
+ * line - points to command line
+ * len - size of line that should be used for completions. This should be
+ * cursor position during tab hit.
+ */
+void process_tab(const char *line, int len)
+{
+ int argc;
+ static split_arg_t buf[(LINE_BUF_MAX * 2) / sizeof(split_arg_t)];
+ const struct method *method;
+
+ argc = split_command(line, len, buf, sizeof(buf));
+ tab_hit_count++;
+
+ if (argc == 0)
+ return;
+
+ if (argc == 1) {
+ command_completion(buf);
+ return;
+ }
+
+ method = get_command(buf[0].ntcopy);
+ if (method != NULL) {
+ param_completion(argc, buf, method, 1);
+ } else if (argc == 2) {
+ method_completion(get_interface(buf[0].ntcopy), buf);
+ } else {
+ /* Find method for <interface, name> pair */
+ method = get_interface_method(buf[0].ntcopy,
+ buf[0].next->ntcopy);
+ param_completion(argc, buf, method, 2);
+ }
+}
diff --git a/repo/android/client/terminal.c b/repo/android/client/terminal.c
new file mode 100644
index 0000000..f7b56de
--- /dev/null
+++ b/repo/android/client/terminal.c
@@ -0,0 +1,824 @@
+/*
+ * Copyright (C) 2013 Intel Corporation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+#include <stdbool.h>
+#include <termios.h>
+#include <stdlib.h>
+
+#include "terminal.h"
+#include "history.h"
+
+/*
+ * Character sequences recognized by code in this file
+ * Leading ESC 0x1B is not included
+ */
+#define SEQ_INSERT "[2~"
+#define SEQ_DELETE "[3~"
+#define SEQ_HOME "OH"
+#define SEQ_END "OF"
+#define SEQ_PGUP "[5~"
+#define SEQ_PGDOWN "[6~"
+#define SEQ_LEFT "[D"
+#define SEQ_RIGHT "[C"
+#define SEQ_UP "[A"
+#define SEQ_DOWN "[B"
+#define SEQ_STAB "[Z"
+#define SEQ_M_n "n"
+#define SEQ_M_p "p"
+#define SEQ_CLEFT "[1;5D"
+#define SEQ_CRIGHT "[1;5C"
+#define SEQ_CUP "[1;5A"
+#define SEQ_CDOWN "[1;5B"
+#define SEQ_SLEFT "[1;2D"
+#define SEQ_SRIGHT "[1;2C"
+#define SEQ_SUP "[1;2A"
+#define SEQ_SDOWN "[1;2B"
+#define SEQ_MLEFT "[1;3D"
+#define SEQ_MRIGHT "[1;3C"
+#define SEQ_MUP "[1;3A"
+#define SEQ_MDOWN "[1;3B"
+
+#define KEY_SEQUENCE(k) { KEY_##k, SEQ_##k }
+struct ansii_sequence {
+ int code;
+ const char *sequence;
+};
+
+/* Table connects single int key codes with character sequences */
+static const struct ansii_sequence ansii_sequnces[] = {
+ KEY_SEQUENCE(INSERT),
+ KEY_SEQUENCE(DELETE),
+ KEY_SEQUENCE(HOME),
+ KEY_SEQUENCE(END),
+ KEY_SEQUENCE(PGUP),
+ KEY_SEQUENCE(PGDOWN),
+ KEY_SEQUENCE(LEFT),
+ KEY_SEQUENCE(RIGHT),
+ KEY_SEQUENCE(UP),
+ KEY_SEQUENCE(DOWN),
+ KEY_SEQUENCE(CLEFT),
+ KEY_SEQUENCE(CRIGHT),
+ KEY_SEQUENCE(CUP),
+ KEY_SEQUENCE(CDOWN),
+ KEY_SEQUENCE(SLEFT),
+ KEY_SEQUENCE(SRIGHT),
+ KEY_SEQUENCE(SUP),
+ KEY_SEQUENCE(SDOWN),
+ KEY_SEQUENCE(MLEFT),
+ KEY_SEQUENCE(MRIGHT),
+ KEY_SEQUENCE(MUP),
+ KEY_SEQUENCE(MDOWN),
+ KEY_SEQUENCE(STAB),
+ KEY_SEQUENCE(M_p),
+ KEY_SEQUENCE(M_n),
+ { 0, NULL }
+};
+
+#define KEY_SEQUNCE_NOT_FINISHED -1
+#define KEY_C_C 3
+#define KEY_C_D 4
+#define KEY_C_L 12
+
+#define isseqence(c) ((c) == 0x1B)
+
+/*
+ * Number of characters that consist of ANSI sequence
+ * Should not be less then longest string in ansi_sequences
+ */
+#define MAX_ASCII_SEQUENCE 10
+
+static char current_sequence[MAX_ASCII_SEQUENCE];
+static int current_sequence_len = -1;
+
+/* single line typed by user goes here */
+static char line_buf[LINE_BUF_MAX];
+/* index of cursor in input line */
+static int line_buf_ix = 0;
+/* current length of input line */
+static int line_len = 0;
+
+/* line index used for fetching lines from history */
+static int line_index = 0;
+
+static char prompt_buf[10] = "> ";
+static const char *const noprompt = "";
+static const char *current_prompt = prompt_buf;
+static const char *prompt = prompt_buf;
+/*
+ * Moves cursor to right or left
+ *
+ * n - positive - moves cursor right
+ * n - negative - moves cursor left
+ */
+static void terminal_move_cursor(int n)
+{
+ if (n < 0) {
+ for (; n < 0; n++)
+ putchar('\b');
+ } else if (n > 0) {
+ printf("%*s", n, line_buf + line_buf_ix);
+ }
+}
+
+/* Draw command line */
+void terminal_draw_command_line(void)
+{
+ /*
+ * this needs to be checked here since line_buf is not cleared
+ * before parsing event though line_len and line_buf_ix are
+ */
+ if (line_len > 0)
+ printf("%s%s", prompt, line_buf);
+ else
+ printf("%s", prompt);
+
+ /* move cursor to it's place */
+ terminal_move_cursor(line_buf_ix - line_len);
+}
+
+/* inserts string into command line at cursor position */
+void terminal_insert_into_command_line(const char *p)
+{
+ int len = strlen(p);
+
+ if (line_len == line_buf_ix) {
+ strcat(line_buf, p);
+ printf("%s", p);
+ line_len = line_len + len;
+ line_buf_ix = line_len;
+ } else {
+ memmove(line_buf + line_buf_ix + len,
+ line_buf + line_buf_ix, line_len - line_buf_ix + 1);
+ memmove(line_buf + line_buf_ix, p, len);
+ printf("%s", line_buf + line_buf_ix);
+ line_buf_ix += len;
+ line_len += len;
+ terminal_move_cursor(line_buf_ix - line_len);
+ }
+}
+
+/* Prints string and redraws command line */
+int terminal_print(const char *format, ...)
+{
+ va_list args;
+ int ret;
+
+ va_start(args, format);
+
+ ret = terminal_vprint(format, args);
+
+ va_end(args);
+ return ret;
+}
+
+/* Prints string and redraws command line */
+int terminal_vprint(const char *format, va_list args)
+{
+ int ret;
+
+ printf("\r%*s\r", (int) line_len + 1, " ");
+
+ ret = vprintf(format, args);
+
+ terminal_draw_command_line();
+
+ fflush(stdout);
+
+ return ret;
+}
+
+/*
+ * Call this when text in line_buf was changed
+ * and line needs to be redrawn
+ */
+static void terminal_line_replaced(void)
+{
+ int len = strlen(line_buf);
+
+ /* line is shorter that previous */
+ if (len < line_len) {
+ /* if new line is shorter move cursor to end of new end */
+ while (line_buf_ix > len) {
+ putchar('\b');
+ line_buf_ix--;
+ }
+
+ /* If cursor was not at the end, move it to the end */
+ if (line_buf_ix < line_len)
+ printf("%.*s", line_len - line_buf_ix,
+ line_buf + line_buf_ix);
+ /* over write end of previous line */
+ while (line_len >= len++)
+ putchar(' ');
+ }
+
+ /* draw new line */
+ printf("\r%s%s", prompt, line_buf);
+ /* set up indexes to new line */
+ line_len = strlen(line_buf);
+ line_buf_ix = line_len;
+ fflush(stdout);
+}
+
+static void terminal_clear_line(void)
+{
+ line_buf[0] = '\0';
+ terminal_line_replaced();
+}
+
+static void terminal_clear_screen(void)
+{
+ line_buf[0] = '\0';
+ line_buf_ix = 0;
+ line_len = 0;
+
+ printf("\x1b[2J\x1b[1;1H%s", prompt);
+}
+
+static void terminal_delete_char(void)
+{
+ /* delete character under cursor if not at the very end */
+ if (line_buf_ix >= line_len)
+ return;
+ /*
+ * Prepare buffer with one character missing
+ * trailing 0 is moved
+ */
+ line_len--;
+ memmove(line_buf + line_buf_ix, line_buf + line_buf_ix + 1,
+ line_len - line_buf_ix + 1);
+ /* print rest of line from current cursor position */
+ printf("%s \b", line_buf + line_buf_ix);
+ /* move back cursor */
+ terminal_move_cursor(line_buf_ix - line_len);
+}
+
+/*
+ * Function tries to replace current line with specified line in history
+ * new_line_index - new line to show, -1 to show oldest
+ */
+static void terminal_get_line_from_history(int new_line_index)
+{
+ new_line_index = history_get_line(new_line_index,
+ line_buf, LINE_BUF_MAX);
+
+ if (new_line_index >= 0) {
+ terminal_line_replaced();
+ line_index = new_line_index;
+ }
+}
+
+/*
+ * Function searches history back or forward for command line that starts
+ * with characters up to cursor position
+ *
+ * back - true - searches backward
+ * back - false - searches forward (more recent commands)
+ */
+static void terminal_match_hitory(bool back)
+{
+ char buf[line_buf_ix + 1];
+ int line;
+ int matching_line = -1;
+ int dir = back ? 1 : -1;
+
+ line = line_index + dir;
+ while (matching_line == -1 && line >= 0) {
+ int new_line_index;
+
+ new_line_index = history_get_line(line, buf, line_buf_ix + 1);
+ if (new_line_index < 0)
+ break;
+
+ if (0 == strncmp(line_buf, buf, line_buf_ix))
+ matching_line = line;
+ line += dir;
+ }
+
+ if (matching_line >= 0) {
+ int pos = line_buf_ix;
+ terminal_get_line_from_history(matching_line);
+ /* move back to cursor position to original place */
+ line_buf_ix = pos;
+ terminal_move_cursor(pos - line_len);
+ }
+}
+
+/*
+ * Converts terminal character sequences to single value representing
+ * keyboard keys
+ */
+static int terminal_convert_sequence(int c)
+{
+ int i;
+
+ /* Not in sequence yet? */
+ if (current_sequence_len == -1) {
+ /* Is ansi sequence detected by 0x1B ? */
+ if (isseqence(c)) {
+ current_sequence_len++;
+ return KEY_SEQUNCE_NOT_FINISHED;
+ }
+
+ return c;
+ }
+
+ /* Inside sequence */
+ current_sequence[current_sequence_len++] = c;
+ current_sequence[current_sequence_len] = '\0';
+ for (i = 0; ansii_sequnces[i].code; ++i) {
+ /* Matches so far? */
+ if (0 != strncmp(current_sequence, ansii_sequnces[i].sequence,
+ current_sequence_len))
+ continue;
+
+ /* Matches as a whole? */
+ if (ansii_sequnces[i].sequence[current_sequence_len] == 0) {
+ current_sequence_len = -1;
+ return ansii_sequnces[i].code;
+ }
+
+ /* partial match (not whole sequence yet) */
+ return KEY_SEQUNCE_NOT_FINISHED;
+ }
+
+ terminal_print("ansi char 0x%X %c\n", c);
+ /*
+ * Sequence does not match
+ * mark that no in sequence any more, return char
+ */
+ current_sequence_len = -1;
+ return c;
+}
+
+typedef void (*terminal_action)(int c, line_callback process_line);
+
+#define TERMINAL_ACTION(n) \
+ static void n(int c, void (*process_line)(char *line))
+
+TERMINAL_ACTION(terminal_action_null)
+{
+}
+
+/* Mapping between keys and function */
+typedef struct {
+ int key;
+ terminal_action func;
+} KeyAction;
+
+int action_keys[] = {
+ KEY_SEQUNCE_NOT_FINISHED,
+ KEY_LEFT,
+ KEY_RIGHT,
+ KEY_HOME,
+ KEY_END,
+ KEY_DELETE,
+ KEY_CLEFT,
+ KEY_CRIGHT,
+ KEY_SUP,
+ KEY_SDOWN,
+ KEY_UP,
+ KEY_DOWN,
+ KEY_BACKSPACE,
+ KEY_INSERT,
+ KEY_PGUP,
+ KEY_PGDOWN,
+ KEY_CUP,
+ KEY_CDOWN,
+ KEY_SLEFT,
+ KEY_SRIGHT,
+ KEY_MLEFT,
+ KEY_MRIGHT,
+ KEY_MUP,
+ KEY_MDOWN,
+ KEY_STAB,
+ KEY_M_n,
+ KEY_M_p,
+ KEY_C_C,
+ KEY_C_D,
+ KEY_C_L,
+ '\t',
+ '\r',
+ '\n',
+};
+
+#define NELEM(x) ((int) (sizeof(x) / sizeof((x)[0])))
+
+/*
+ * current_actions holds all recognizable kes and actions for them
+ * additional element (index 0) is used for default action
+ */
+static KeyAction current_actions[NELEM(action_keys) + 1];
+
+/* KeyAction comparator by key, for qsort and bsearch */
+static int KeyActionKeyCompare(const void *a, const void *b)
+{
+ return ((const KeyAction *) a)->key - ((const KeyAction *) b)->key;
+}
+
+/* Find action by key, NULL if no action for this key */
+static KeyAction *terminal_get_action(int key)
+{
+ KeyAction a = { .key = key };
+
+ return bsearch(&a, current_actions + 1, NELEM(action_keys), sizeof(a),
+ KeyActionKeyCompare);
+}
+
+/* Sets new set of actions to use */
+static void terminal_set_actions(const KeyAction *actions)
+{
+ int i;
+
+ /* Make map with empty function for every key */
+ for (i = 0; i < NELEM(action_keys); ++i) {
+ /*
+ * + 1 due to 0 index reserved for default action that is
+ * called for non mapped key
+ */
+ current_actions[i + 1].key = action_keys[i];
+ current_actions[i + 1].func = terminal_action_null;
+ }
+
+ /* Sort action from 1 (index 0 - default action) */
+ qsort(current_actions + 1, NELEM(action_keys), sizeof(KeyAction),
+ KeyActionKeyCompare);
+ /* Set default action (first in array) */
+ current_actions[0] = *actions++;
+
+ /* Copy rest of actions into their places */
+ for (; actions->key; ++actions) {
+ KeyAction *place = terminal_get_action(actions->key);
+
+ if (place)
+ place->func = actions->func;
+ }
+}
+
+TERMINAL_ACTION(terminal_action_left)
+{
+ /* if not at the beginning move to previous character */
+ if (line_buf_ix <= 0)
+ return;
+ line_buf_ix--;
+ terminal_move_cursor(-1);
+}
+
+TERMINAL_ACTION(terminal_action_right)
+{
+ /*
+ * If not at the end, just print current character
+ * and modify position
+ */
+ if (line_buf_ix < line_len)
+ putchar(line_buf[line_buf_ix++]);
+}
+
+TERMINAL_ACTION(terminal_action_home)
+{
+ /* move to beginning of line and update position */
+ printf("\r%s", prompt);
+ line_buf_ix = 0;
+}
+
+TERMINAL_ACTION(terminal_action_end)
+{
+ /* if not at the end of line */
+ if (line_buf_ix < line_len) {
+ /* print everything from cursor */
+ printf("%s", line_buf + line_buf_ix);
+ /* just modify current position */
+ line_buf_ix = line_len;
+ }
+}
+
+TERMINAL_ACTION(terminal_action_del)
+{
+ terminal_delete_char();
+}
+
+TERMINAL_ACTION(terminal_action_word_left)
+{
+ int old_pos;
+ /*
+ * Move by word left
+ *
+ * Are we at the beginning of line?
+ */
+ if (line_buf_ix <= 0)
+ return;
+
+ old_pos = line_buf_ix;
+ line_buf_ix--;
+ /* skip spaces left */
+ while (line_buf_ix && isspace(line_buf[line_buf_ix]))
+ line_buf_ix--;
+
+ /* skip all non spaces to the left */
+ while (line_buf_ix > 0 &&
+ !isspace(line_buf[line_buf_ix - 1]))
+ line_buf_ix--;
+
+ /* move cursor to new position */
+ terminal_move_cursor(line_buf_ix - old_pos);
+}
+
+TERMINAL_ACTION(terminal_action_word_right)
+{
+ int old_pos;
+ /*
+ * Move by word right
+ *
+ * are we at the end of line?
+ */
+ if (line_buf_ix >= line_len)
+ return;
+
+ old_pos = line_buf_ix;
+ /* skip all spaces */
+ while (line_buf_ix < line_len && isspace(line_buf[line_buf_ix]))
+ line_buf_ix++;
+
+ /* skip all non spaces */
+ while (line_buf_ix < line_len && !isspace(line_buf[line_buf_ix]))
+ line_buf_ix++;
+ /*
+ * Move cursor to right by printing text
+ * between old cursor and new
+ */
+ if (line_buf_ix > old_pos)
+ printf("%.*s", (int) (line_buf_ix - old_pos),
+ line_buf + old_pos);
+}
+
+TERMINAL_ACTION(terminal_action_history_begin)
+{
+ terminal_get_line_from_history(-1);
+}
+
+TERMINAL_ACTION(terminal_action_history_end)
+{
+ if (line_index > 0)
+ terminal_get_line_from_history(0);
+}
+
+TERMINAL_ACTION(terminal_action_history_up)
+{
+ terminal_get_line_from_history(line_index + 1);
+}
+
+TERMINAL_ACTION(terminal_action_history_down)
+{
+ if (line_index > 0)
+ terminal_get_line_from_history(line_index - 1);
+}
+
+TERMINAL_ACTION(terminal_action_tab)
+{
+ /* tab processing */
+ process_tab(line_buf, line_buf_ix);
+}
+
+
+TERMINAL_ACTION(terminal_action_backspace)
+{
+ if (line_buf_ix <= 0)
+ return;
+
+ if (line_buf_ix == line_len) {
+ printf("\b \b");
+ line_len = --line_buf_ix;
+ line_buf[line_len] = 0;
+ } else {
+ putchar('\b');
+ line_buf_ix--;
+ line_len--;
+ memmove(line_buf + line_buf_ix,
+ line_buf + line_buf_ix + 1,
+ line_len - line_buf_ix + 1);
+ printf("%s \b", line_buf + line_buf_ix);
+ terminal_move_cursor(line_buf_ix - line_len);
+ }
+}
+
+TERMINAL_ACTION(terminal_action_find_history_forward)
+{
+ /* Search history forward */
+ terminal_match_hitory(false);
+}
+
+TERMINAL_ACTION(terminal_action_find_history_backward)
+{
+ /* Search history forward */
+ terminal_match_hitory(true);
+}
+
+TERMINAL_ACTION(terminal_action_ctrl_c)
+{
+ terminal_clear_line();
+}
+
+TERMINAL_ACTION(terminal_action_ctrl_d)
+{
+ if (line_len > 0) {
+ terminal_delete_char();
+ } else {
+ puts("");
+ exit(0);
+ }
+}
+
+TERMINAL_ACTION(terminal_action_clear_screen)
+{
+ terminal_clear_screen();
+}
+
+TERMINAL_ACTION(terminal_action_enter)
+{
+ /*
+ * On new line add line to history
+ * forget history position
+ */
+ history_add_line(line_buf);
+ line_len = 0;
+ line_buf_ix = 0;
+ line_index = -1;
+ /* print new line */
+ putchar(c);
+ prompt = noprompt;
+ process_line(line_buf);
+ /* clear current line */
+ line_buf[0] = '\0';
+ prompt = current_prompt;
+ printf("%s", prompt);
+}
+
+TERMINAL_ACTION(terminal_action_default)
+{
+ char str[2] = { c, 0 };
+
+ if (!isprint(c))
+ /*
+ * TODO: remove this print once all meaningful sequences
+ * are identified
+ */
+ printf("char-0x%02x\n", c);
+ else if (line_buf_ix < LINE_BUF_MAX - 1)
+ terminal_insert_into_command_line(str);
+}
+
+/* Callback to call when user hit enter during prompt for */
+static line_callback prompt_callback;
+
+static KeyAction normal_actions[] = {
+ { 0, terminal_action_default },
+ { KEY_LEFT, terminal_action_left },
+ { KEY_RIGHT, terminal_action_right },
+ { KEY_HOME, terminal_action_home },
+ { KEY_END, terminal_action_end },
+ { KEY_DELETE, terminal_action_del },
+ { KEY_CLEFT, terminal_action_word_left },
+ { KEY_CRIGHT, terminal_action_word_right },
+ { KEY_SUP, terminal_action_history_begin },
+ { KEY_SDOWN, terminal_action_history_end },
+ { KEY_UP, terminal_action_history_up },
+ { KEY_DOWN, terminal_action_history_down },
+ { '\t', terminal_action_tab },
+ { KEY_BACKSPACE, terminal_action_backspace },
+ { KEY_M_n, terminal_action_find_history_forward },
+ { KEY_M_p, terminal_action_find_history_backward },
+ { KEY_C_C, terminal_action_ctrl_c },
+ { KEY_C_D, terminal_action_ctrl_d },
+ { KEY_C_L, terminal_action_clear_screen },
+ { '\r', terminal_action_enter },
+ { '\n', terminal_action_enter },
+ { 0, NULL },
+};
+
+TERMINAL_ACTION(terminal_action_answer)
+{
+ putchar(c);
+
+ terminal_set_actions(normal_actions);
+ /* Restore default prompt */
+ current_prompt = prompt_buf;
+
+ /* No prompt for prints */
+ prompt = noprompt;
+ line_buf_ix = 0;
+ line_len = 0;
+ /* Call user function with what was typed */
+ prompt_callback(line_buf);
+
+ line_buf[0] = 0;
+ /* promot_callback could change current_prompt */
+ prompt = current_prompt;
+
+ printf("%s", prompt);
+}
+
+TERMINAL_ACTION(terminal_action_prompt_ctrl_c)
+{
+ printf("^C\n");
+ line_buf_ix = 0;
+ line_len = 0;
+ line_buf[0] = 0;
+
+ current_prompt = prompt_buf;
+ prompt = current_prompt;
+ terminal_set_actions(normal_actions);
+
+ printf("%s", prompt);
+}
+
+static KeyAction prompt_actions[] = {
+ { 0, terminal_action_default },
+ { KEY_LEFT, terminal_action_left },
+ { KEY_RIGHT, terminal_action_right },
+ { KEY_HOME, terminal_action_home },
+ { KEY_END, terminal_action_end },
+ { KEY_DELETE, terminal_action_del },
+ { KEY_CLEFT, terminal_action_word_left },
+ { KEY_CRIGHT, terminal_action_word_right },
+ { KEY_BACKSPACE, terminal_action_backspace },
+ { KEY_C_C, terminal_action_prompt_ctrl_c },
+ { KEY_C_D, terminal_action_ctrl_d },
+ { '\r', terminal_action_answer },
+ { '\n', terminal_action_answer },
+ { 0, NULL },
+};
+
+void terminal_process_char(int c, line_callback process_line)
+{
+ KeyAction *a;
+
+ c = terminal_convert_sequence(c);
+
+ /* Get action for this key */
+ a = terminal_get_action(c);
+
+ /* No action found, get default one */
+ if (a == NULL)
+ a = ¤t_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/repo/android/client/terminal.h b/repo/android/client/terminal.h
new file mode 100644
index 0000000..0e63936
--- /dev/null
+++ b/repo/android/client/terminal.h
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2013 Intel Corporation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include <stdarg.h>
+
+/* size of supported line */
+#define LINE_BUF_MAX 1024
+
+enum key_codes {
+ KEY_BACKSPACE = 0x7F,
+ KEY_INSERT = 1000, /* arbitrary value */
+ KEY_DELETE,
+ KEY_HOME,
+ KEY_END,
+ KEY_PGUP,
+ KEY_PGDOWN,
+ KEY_LEFT,
+ KEY_RIGHT,
+ KEY_UP,
+ KEY_DOWN,
+ KEY_CLEFT,
+ KEY_CRIGHT,
+ KEY_CUP,
+ KEY_CDOWN,
+ KEY_SLEFT,
+ KEY_SRIGHT,
+ KEY_SUP,
+ KEY_SDOWN,
+ KEY_MLEFT,
+ KEY_MRIGHT,
+ KEY_MUP,
+ KEY_MDOWN,
+ KEY_STAB,
+ KEY_M_p,
+ KEY_M_n
+};
+
+typedef void (*line_callback)(char *);
+
+void terminal_setup(void);
+int terminal_print(const char *format, ...);
+int terminal_vprint(const char *format, va_list args);
+void terminal_process_char(int c, line_callback process_line);
+void terminal_insert_into_command_line(const char *p);
+void terminal_draw_command_line(void);
+void terminal_prompt_for(const char *s, line_callback process_line);
+
+void process_tab(const char *line, int len);
diff --git a/repo/android/compat/readline/history.h b/repo/android/compat/readline/history.h
new file mode 100644
index 0000000..decc2f4
--- /dev/null
+++ b/repo/android/compat/readline/history.h
@@ -0,0 +1,31 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 1987-2011 Free Software Foundation, Inc.
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#ifndef _HISTORY_H_
+#define _HISTORY_H_
+
+static inline void add_history(const char *c)
+{
+}
+
+#endif
diff --git a/repo/android/compat/readline/readline.h b/repo/android/compat/readline/readline.h
new file mode 100644
index 0000000..aaf6f31
--- /dev/null
+++ b/repo/android/compat/readline/readline.h
@@ -0,0 +1,110 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 1987-2011 Free Software Foundation, Inc.
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#ifndef _READLINE_H_
+#define _READLINE_H_
+
+typedef void (*rl_vcpfunc_t)(char *c);
+typedef void (*rl_compdisp_func_t)(char **c, int i, int j);
+typedef char *(*rl_compentry_func_t)(const char *c, int i);
+typedef char **(*rl_completion_func_t)(const char *c, int i, int j);
+
+#define RL_STATE_DONE 0x1000000
+#define RL_ISSTATE(x) (rl_readline_state & (x))
+
+static int rl_end;
+static int rl_point;
+static int rl_readline_state;
+static int rl_erase_empty_line;
+static int rl_attempted_completion_over;
+static char *rl_prompt;
+static char *rl_line_buffer;
+static rl_compdisp_func_t rl_completion_display_matches_hook;
+static rl_completion_func_t rl_attempted_completion_function;
+
+static inline void rl_callback_handler_install(const char *c, rl_vcpfunc_t f)
+{
+ printf("readline not available\n");
+ exit(1);
+}
+
+static inline int rl_set_prompt(const char *c)
+{
+ return -1;
+}
+
+static inline void rl_replace_line(const char *c, int i)
+{
+}
+
+static inline void rl_redisplay(void)
+{
+}
+
+static inline char **rl_completion_matches(const char *c, rl_compentry_func_t f)
+{
+ return NULL;
+}
+
+static inline int rl_insert_text(const char *c)
+{
+ return -1;
+}
+
+static inline int rl_crlf(void)
+{
+ return -1;
+}
+
+static inline void rl_callback_read_char(void)
+{
+}
+
+static inline int rl_message(const char *c, ...)
+{
+ return -1;
+}
+
+static inline void rl_callback_handler_remove(void)
+{
+}
+
+static inline char *rl_copy_text(int i, int j)
+{
+ return NULL;
+}
+
+static inline void rl_save_prompt(void)
+{
+}
+
+static inline void rl_restore_prompt(void)
+{
+}
+
+static inline int rl_forced_update_display(void)
+{
+ return -1;
+}
+
+#endif
diff --git a/repo/android/compat/wordexp.h b/repo/android/compat/wordexp.h
new file mode 100644
index 0000000..ff1f21c
--- /dev/null
+++ b/repo/android/compat/wordexp.h
@@ -0,0 +1,44 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 1991-2013 Free Software Foundation, Inc.
+ *
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#ifndef _WORDEXP_H_
+#define _WORDEXP_H_
+
+#define WRDE_NOCMD 0
+
+typedef struct {
+ size_t we_wordc;
+ char **we_wordv;
+ size_t we_offs;
+} wordexp_t;
+
+static inline int wordexp(const char *c, wordexp_t *w, int _i)
+{
+ return -1;
+}
+
+static inline void wordfree(wordexp_t *__wordexp)
+{
+}
+
+#endif
diff --git a/repo/android/cts.txt b/repo/android/cts.txt
new file mode 100644
index 0000000..f679263
--- /dev/null
+++ b/repo/android/cts.txt
@@ -0,0 +1,58 @@
+Android Compatibility Test Suite results
+
+Tested: 27-Nov-2014
+Android version: 5.0
+Kernel Version: 3.18
+CTS version: 5.0 R1 (android-5.0.0_r7)
+
+Note:
+CTS reliable write GATT tests require using CTS from master branch
+or https://android-review.googlesource.com/99354 applied.
+
+(*) Tests are disabled due to CTS quality issues. Link for reference:
+https://android.googlesource.com/platform/cts/+/0a62e4a0a9910101ccf2ccc43f6%5E!/
+
+-------------------------------------------------------------------------------
+android.bluetooth.cts.BasicAdapterTest (automated tests)
+Test Name Result Notes
+-------------------------------------------------------------------------------
+testAndroidTestCaseSetupProperly PASS
+test_checkBluetoothAddress PASS
+test_enableDisable PASS
+test_getAddress PASS
+test_getBondedDevices PASS
+test_getDefaultAdapter PASS
+test_getName PASS
+test_getRemoteDevice PASS
+test_listenUsingRfcommWithServiceRecord PASS
+-------------------------------------------------------------------------------
+
+
+-------------------------------------------------------------------------------
+com.android.cts.verifier (manual tests)
+Test Name Result Notes
+-------------------------------------------------------------------------------
+Toggle Bluetooth PASS
+BLE Client Test:
+ connect N/A (*)
+ discover service N/A (*)
+ read/write characteristic N/A (*)
+ reliable write N/A (*)
+ notify characteristic N/A (*)
+ read/write descriptor N/A (*)
+ read RSSI N/A (*)
+ disconnect N/A (*)
+BLE Server Test:
+ add service N/A (*)
+ connection N/A (*)
+ read characteristic request N/A (*)
+ write characteristic request N/A (*)
+ read descriptor request N/A (*)
+ write descriptor request N/A (*)
+ reliable write N/A (*)
+ disconnection N/A (*)
+Insecure Client PASS
+Insecure Server PASS
+Secure Client PASS
+Secure Server PASS
+-------------------------------------------------------------------------------
diff --git a/repo/android/cutils/properties.h b/repo/android/cutils/properties.h
new file mode 100644
index 0000000..0163eb5
--- /dev/null
+++ b/repo/android/cutils/properties.h
@@ -0,0 +1,95 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2013 Intel Corporation. All rights reserved.
+ *
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+
+#define PROPERTY_VALUE_MAX 32
+#define PROPERTY_KEY_MAX 32
+
+#define BLUETOOTH_MODE_PROPERTY_NAME "persist.sys.bluetooth.mode"
+#define BLUETOOTH_MODE_PROPERTY_HANDSFREE "persist.sys.bluetooth.handsfree"
+
+static inline int property_get(const char *key, char *value,
+ const char *default_value)
+{
+ const char *prop = NULL;
+
+ if (!strcmp(key, BLUETOOTH_MODE_PROPERTY_NAME))
+ prop = getenv("BLUETOOTH_MODE");
+
+ if (!strcmp(key, BLUETOOTH_MODE_PROPERTY_HANDSFREE))
+ prop = getenv("BLUETOOTH_HANDSFREE_MODE");
+
+ if (!prop)
+ prop = default_value;
+
+ if (prop) {
+ strncpy(value, prop, PROPERTY_VALUE_MAX);
+
+ value[PROPERTY_VALUE_MAX - 1] = '\0';
+
+ return strlen(value);
+ }
+
+ return 0;
+}
+
+/* property_set: returns 0 on success, < 0 on failure
+*/
+static inline int property_set(const char *key, const char *value)
+{
+ static const char SYSTEM_SOCKET_PATH[] = "\0android_system";
+
+ struct sockaddr_un addr;
+ char msg[256];
+ int fd, len;
+
+ fd = socket(PF_LOCAL, SOCK_DGRAM, 0);
+ if (fd < 0)
+ return -1;
+
+ memset(&addr, 0, sizeof(addr));
+ addr.sun_family = AF_UNIX;
+ memcpy(addr.sun_path, SYSTEM_SOCKET_PATH, sizeof(SYSTEM_SOCKET_PATH));
+
+ if (connect(fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+ close(fd);
+ return 0;
+ }
+
+ len = snprintf(msg, sizeof(msg), "%s=%s", key, value);
+
+ if (send(fd, msg, len + 1, 0) < 0) {
+ close(fd);
+ return -1;
+ }
+
+ close(fd);
+
+ return 0;
+}
diff --git a/repo/android/gatt.c b/repo/android/gatt.c
new file mode 100644
index 0000000..28635ed
--- /dev/null
+++ b/repo/android/gatt.c
@@ -0,0 +1,7193 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2014 Intel Corporation. All rights reserved.
+ *
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdbool.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <glib.h>
+#include <errno.h>
+#include <sys/socket.h>
+
+#include "ipc.h"
+#include "ipc-common.h"
+#include "lib/sdp.h"
+#include "lib/sdp_lib.h"
+#include "lib/uuid.h"
+#include "bluetooth.h"
+#include "gatt.h"
+#include "src/log.h"
+#include "hal-msg.h"
+#include "utils.h"
+#include "src/shared/util.h"
+#include "src/shared/queue.h"
+#include "src/shared/att.h"
+#include "src/shared/gatt-db.h"
+#include "attrib/gattrib.h"
+#include "attrib/att.h"
+#include "attrib/gatt.h"
+#include "btio/btio.h"
+
+/* set according to Android bt_gatt_client.h */
+#define GATT_MAX_ATTR_LEN 600
+
+#define GATT_SUCCESS 0x00000000
+#define GATT_FAILURE 0x00000101
+
+#define BASE_UUID16_OFFSET 12
+
+#define GATT_PERM_READ 0x00000001
+#define GATT_PERM_READ_ENCRYPTED 0x00000002
+#define GATT_PERM_READ_MITM 0x00000004
+#define GATT_PERM_READ_AUTHORIZATION 0x00000008
+#define GATT_PERM_WRITE 0x00000100
+#define GATT_PERM_WRITE_ENCRYPTED 0x00000200
+#define GATT_PERM_WRITE_MITM 0x00000400
+#define GATT_PERM_WRITE_AUTHORIZATION 0x00000800
+#define GATT_PERM_WRITE_SIGNED 0x00010000
+#define GATT_PERM_WRITE_SIGNED_MITM 0x00020000
+#define GATT_PERM_NONE 0x10000000
+
+#define GATT_PAIR_CONN_TIMEOUT 30
+#define GATT_CONN_TIMEOUT 2
+
+static const uint8_t BLUETOOTH_UUID[] = {
+ 0xfb, 0x34, 0x9b, 0x5f, 0x80, 0x00, 0x00, 0x80,
+ 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+};
+
+typedef enum {
+ DEVICE_DISCONNECTED = 0,
+ DEVICE_CONNECT_INIT, /* connection procedure initiated */
+ DEVICE_CONNECT_READY, /* dev found during LE scan */
+ DEVICE_CONNECTED, /* connection has been established */
+} gatt_device_state_t;
+
+static const char *device_state_str[] = {
+ "DISCONNECTED",
+ "CONNECT INIT",
+ "CONNECT READY",
+ "CONNECTED",
+};
+
+struct pending_trans_data {
+ unsigned int id;
+ uint8_t opcode;
+ struct gatt_db_attribute *attrib;
+ unsigned int serial_id;
+};
+
+struct gatt_app {
+ int32_t id;
+ uint8_t uuid[16];
+
+ gatt_type_t type;
+
+ /* Valid for client applications */
+ struct queue *notifications;
+
+ gatt_conn_cb_t func;
+};
+
+struct element_id {
+ bt_uuid_t uuid;
+ uint8_t instance;
+};
+
+struct descriptor {
+ struct element_id id;
+ uint16_t handle;
+};
+
+struct characteristic {
+ struct element_id id;
+ struct gatt_char ch;
+ uint16_t end_handle;
+
+ struct queue *descriptors;
+};
+
+struct service {
+ struct element_id id;
+ struct gatt_primary prim;
+ struct gatt_included incl;
+
+ bool primary;
+
+ struct queue *chars;
+ struct queue *included; /* Valid only for primary services */
+ bool incl_search_done;
+};
+
+struct notification_data {
+ struct hal_gatt_srvc_id service;
+ struct hal_gatt_gatt_id ch;
+ struct app_connection *conn;
+ guint notif_id;
+ guint ind_id;
+ int ref;
+};
+
+struct gatt_device {
+ bdaddr_t bdaddr;
+
+ gatt_device_state_t state;
+
+ GAttrib *attrib;
+ GIOChannel *att_io;
+ struct queue *services;
+ bool partial_srvc_search;
+
+ guint watch_id;
+ guint server_id;
+ guint ind_id;
+
+ int ref;
+
+ struct queue *autoconnect_apps;
+
+ struct queue *pending_requests;
+};
+
+struct app_connection {
+ struct gatt_device *device;
+ struct gatt_app *app;
+ struct queue *transactions;
+ int32_t id;
+
+ guint timeout_id;
+
+ bool wait_execute_write;
+};
+
+struct service_sdp {
+ int32_t service_handle;
+ uint32_t sdp_handle;
+};
+
+static struct ipc *hal_ipc = NULL;
+static bdaddr_t adapter_addr;
+static bool scanning = false;
+static unsigned int advertising_cnt = 0;
+
+static struct queue *gatt_apps = NULL;
+static struct queue *gatt_devices = NULL;
+static struct queue *app_connections = NULL;
+
+static struct queue *services_sdp = NULL;
+
+static struct queue *listen_apps = NULL;
+static struct gatt_db *gatt_db = NULL;
+
+static struct gatt_db_attribute *service_changed_attrib = NULL;
+
+static GIOChannel *le_io = NULL;
+static GIOChannel *bredr_io = NULL;
+
+static uint32_t gatt_sdp_handle = 0;
+static uint32_t gap_sdp_handle = 0;
+static uint32_t dis_sdp_handle = 0;
+
+static struct bt_crypto *crypto = NULL;
+
+static int test_client_if = 0;
+static const uint8_t TEST_UUID[] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04
+};
+
+static bool is_bluetooth_uuid(const uint8_t *uuid)
+{
+ int i;
+
+ for (i = 0; i < 16; i++) {
+ /* ignore minimal uuid (16) value */
+ if (i == 12 || i == 13)
+ continue;
+
+ if (uuid[i] != BLUETOOTH_UUID[i])
+ return false;
+ }
+
+ return true;
+}
+
+static void android2uuid(const uint8_t *uuid, bt_uuid_t *dst)
+{
+ if (is_bluetooth_uuid(uuid)) {
+ /* copy 16 bit uuid value from full android 128bit uuid */
+ dst->type = BT_UUID16;
+ dst->value.u16 = (uuid[13] << 8) + uuid[12];
+ } else {
+ int i;
+
+ dst->type = BT_UUID128;
+ for (i = 0; i < 16; i++)
+ dst->value.u128.data[i] = uuid[15 - i];
+ }
+}
+
+static void uuid2android(const bt_uuid_t *src, uint8_t *uuid)
+{
+ bt_uuid_t uu128;
+ uint8_t i;
+
+ if (src->type != BT_UUID128) {
+ bt_uuid_to_uuid128(src, &uu128);
+ src = &uu128;
+ }
+
+ for (i = 0; i < 16; i++)
+ uuid[15 - i] = src->value.u128.data[i];
+}
+
+static void hal_srvc_id_to_element_id(const struct hal_gatt_srvc_id *from,
+ struct element_id *to)
+{
+ to->instance = from->inst_id;
+ android2uuid(from->uuid, &to->uuid);
+}
+
+static void element_id_to_hal_srvc_id(const struct element_id *from,
+ uint8_t primary,
+ struct hal_gatt_srvc_id *to)
+{
+ to->is_primary = primary;
+ to->inst_id = from->instance;
+ uuid2android(&from->uuid, to->uuid);
+}
+
+static void hal_gatt_id_to_element_id(const struct hal_gatt_gatt_id *from,
+ struct element_id *to)
+{
+ to->instance = from->inst_id;
+ android2uuid(from->uuid, &to->uuid);
+}
+
+static void element_id_to_hal_gatt_id(const struct element_id *from,
+ struct hal_gatt_gatt_id *to)
+{
+ to->inst_id = from->instance;
+ uuid2android(&from->uuid, to->uuid);
+}
+
+static void destroy_characteristic(void *data)
+{
+ struct characteristic *chars = data;
+
+ if (!chars)
+ return;
+
+ queue_destroy(chars->descriptors, free);
+ free(chars);
+}
+
+static void destroy_service(void *data)
+{
+ struct service *srvc = data;
+
+ if (!srvc)
+ return;
+
+ queue_destroy(srvc->chars, destroy_characteristic);
+
+ /*
+ * Included services we keep on two queues.
+ * 1. On the same queue with primary services.
+ * 2. On the queue inside primary service.
+ * So we need to free service memory only once but we need to destroy
+ * two queues
+ */
+ queue_destroy(srvc->included, NULL);
+
+ free(srvc);
+}
+
+static bool match_app_by_uuid(const void *data, const void *user_data)
+{
+ const uint8_t *exp_uuid = user_data;
+ const struct gatt_app *client = data;
+
+ return !memcmp(exp_uuid, client->uuid, sizeof(client->uuid));
+}
+
+static bool match_app_by_id(const void *data, const void *user_data)
+{
+ int32_t exp_id = PTR_TO_INT(user_data);
+ const struct gatt_app *client = data;
+
+ return client->id == exp_id;
+}
+
+static struct gatt_app *find_app_by_id(int32_t id)
+{
+ return queue_find(gatt_apps, match_app_by_id, INT_TO_PTR(id));
+}
+
+static bool match_device_by_bdaddr(const void *data, const void *user_data)
+{
+ const struct gatt_device *dev = data;
+ const bdaddr_t *addr = user_data;
+
+ return !bacmp(&dev->bdaddr, addr);
+}
+
+static bool match_device_by_state(const void *data, const void *user_data)
+{
+ const struct gatt_device *dev = data;
+
+ if (dev->state != PTR_TO_UINT(user_data))
+ return false;
+
+ return true;
+}
+
+static bool match_pending_device(const void *data, const void *user_data)
+{
+ const struct gatt_device *dev = data;
+
+ if ((dev->state == DEVICE_CONNECT_INIT) ||
+ (dev->state == DEVICE_CONNECT_READY))
+ return true;
+
+ return false;
+}
+
+static bool match_connection_by_id(const void *data, const void *user_data)
+{
+ const struct app_connection *conn = data;
+ const int32_t id = PTR_TO_INT(user_data);
+
+ return conn->id == id;
+}
+
+static bool match_connection_by_device_and_app(const void *data,
+ const void *user_data)
+{
+ const struct app_connection *conn = data;
+ const struct app_connection *match = user_data;
+
+ return conn->device == match->device && conn->app == match->app;
+}
+
+static struct app_connection *find_connection_by_id(int32_t conn_id)
+{
+ struct app_connection *conn;
+
+ conn = queue_find(app_connections, match_connection_by_id,
+ INT_TO_PTR(conn_id));
+ if (conn && conn->device->state == DEVICE_CONNECTED)
+ return conn;
+
+ return NULL;
+}
+
+static bool match_connection_by_device(const void *data, const void *user_data)
+{
+ const struct app_connection *conn = data;
+ const struct gatt_device *dev = user_data;
+
+ return conn->device == dev;
+}
+
+static bool match_connection_by_app(const void *data, const void *user_data)
+{
+ const struct app_connection *conn = data;
+ const struct gatt_app *app = user_data;
+
+ return conn->app == app;
+}
+
+static struct gatt_device *find_device_by_addr(const bdaddr_t *addr)
+{
+ return queue_find(gatt_devices, match_device_by_bdaddr, addr);
+}
+
+static struct gatt_device *find_pending_device(void)
+{
+ return queue_find(gatt_devices, match_pending_device, NULL);
+}
+
+static struct gatt_device *find_device_by_state(uint32_t state)
+{
+ return queue_find(gatt_devices, match_device_by_state,
+ UINT_TO_PTR(state));
+}
+
+static bool match_srvc_by_element_id(const void *data, const void *user_data)
+{
+ const struct element_id *exp_id = user_data;
+ const struct service *service = data;
+
+ if (service->id.instance == exp_id->instance)
+ return !bt_uuid_cmp(&service->id.uuid, &exp_id->uuid);
+
+ return false;
+}
+
+static bool match_srvc_by_higher_inst_id(const void *data,
+ const void *user_data)
+{
+ const struct service *s = data;
+ uint8_t inst_id = PTR_TO_INT(user_data);
+
+ /* For now we match inst_id as it is unique */
+ return inst_id < s->id.instance;
+}
+
+static bool match_srvc_by_bt_uuid(const void *data, const void *user_data)
+{
+ const bt_uuid_t *exp_uuid = user_data;
+ const struct service *service = data;
+
+ return !bt_uuid_cmp(exp_uuid, &service->id.uuid);
+}
+
+static bool match_srvc_by_range(const void *data, const void *user_data)
+{
+ const struct service *srvc = data;
+ const struct att_range *range = user_data;
+
+ return !memcmp(&srvc->prim.range, range, sizeof(srvc->prim.range));
+}
+
+static bool match_char_by_higher_inst_id(const void *data,
+ const void *user_data)
+{
+ const struct characteristic *ch = data;
+ uint8_t inst_id = PTR_TO_INT(user_data);
+
+ /* For now we match inst_id as it is unique, we'll match uuids later */
+ return inst_id < ch->id.instance;
+}
+
+static bool match_descr_by_element_id(const void *data, const void *user_data)
+{
+ const struct element_id *exp_id = user_data;
+ const struct descriptor *descr = data;
+
+ if (exp_id->instance == descr->id.instance)
+ return !bt_uuid_cmp(&descr->id.uuid, &exp_id->uuid);
+
+ return false;
+}
+
+static bool match_descr_by_higher_inst_id(const void *data,
+ const void *user_data)
+{
+ const struct descriptor *descr = data;
+ uint8_t instance = PTR_TO_INT(user_data);
+
+ /* For now we match instance as it is unique */
+ return instance < descr->id.instance;
+}
+
+static bool match_notification(const void *a, const void *b)
+{
+ const struct notification_data *a1 = a;
+ const struct notification_data *b1 = b;
+
+ if (a1->conn != b1->conn)
+ return false;
+
+ if (memcmp(&a1->ch, &b1->ch, sizeof(a1->ch)))
+ return false;
+
+ if (memcmp(&a1->service, &b1->service, sizeof(a1->service)))
+ return false;
+
+ return true;
+}
+
+static bool match_char_by_element_id(const void *data, const void *user_data)
+{
+ const struct element_id *exp_id = user_data;
+ const struct characteristic *chars = data;
+
+ if (exp_id->instance == chars->id.instance)
+ return !bt_uuid_cmp(&chars->id.uuid, &exp_id->uuid);
+
+ return false;
+}
+
+static void destroy_notification(void *data)
+{
+ struct notification_data *notification = data;
+ struct gatt_app *app;
+
+ if (!notification)
+ return;
+
+ if (--notification->ref)
+ return;
+
+ app = notification->conn->app;
+ queue_remove_if(app->notifications, match_notification, notification);
+ free(notification);
+}
+
+static void unregister_notification(void *data)
+{
+ struct notification_data *notification = data;
+ struct gatt_device *dev = notification->conn->device;
+
+ /*
+ * No device means it was already disconnected and client cleanup was
+ * triggered afterwards, but once client unregisters, device stays if
+ * used by others. Then just unregister single handle.
+ */
+ if (!queue_find(gatt_devices, NULL, dev))
+ return;
+
+ if (notification->notif_id && dev)
+ g_attrib_unregister(dev->attrib, notification->notif_id);
+
+ if (notification->ind_id && dev)
+ g_attrib_unregister(dev->attrib, notification->ind_id);
+}
+
+static void device_set_state(struct gatt_device *dev, uint32_t state)
+{
+ char bda[18];
+
+ if (dev->state == state)
+ return;
+
+ ba2str(&dev->bdaddr, bda);
+ DBG("gatt: Device %s state changed %s -> %s", bda,
+ device_state_str[dev->state], device_state_str[state]);
+
+ dev->state = state;
+}
+
+static bool auto_connect_le(struct gatt_device *dev)
+{
+ /* For LE devices use auto connect feature if possible */
+ if (bt_kernel_conn_control()) {
+ if (!bt_auto_connect_add(bt_get_id_addr(&dev->bdaddr, NULL)))
+ return false;
+ } else {
+ /* Trigger discovery if not already started */
+ if (!scanning && !bt_le_discovery_start()) {
+ error("gatt: Could not start scan");
+ return false;
+ }
+ }
+
+ device_set_state(dev, DEVICE_CONNECT_INIT);
+ return true;
+}
+
+static void connection_cleanup(struct gatt_device *device)
+{
+ if (device->watch_id) {
+ g_source_remove(device->watch_id);
+ device->watch_id = 0;
+ }
+
+ if (device->att_io) {
+ g_io_channel_shutdown(device->att_io, FALSE, NULL);
+ g_io_channel_unref(device->att_io);
+ device->att_io = NULL;
+ }
+
+ if (device->attrib) {
+ GAttrib *attrib = device->attrib;
+
+ if (device->server_id > 0)
+ g_attrib_unregister(device->attrib, device->server_id);
+
+ if (device->ind_id > 0)
+ g_attrib_unregister(device->attrib, device->ind_id);
+
+ device->attrib = NULL;
+ g_attrib_cancel_all(attrib);
+ g_attrib_unref(attrib);
+ }
+
+ /*
+ * If device was in connection_pending or connectable state we
+ * search device list if we should stop the scan.
+ */
+ if (!scanning && (device->state == DEVICE_CONNECT_INIT ||
+ device->state == DEVICE_CONNECT_READY)) {
+ if (!find_pending_device())
+ bt_le_discovery_stop(NULL);
+ }
+
+ /* If device is not bonded service cache should be refreshed */
+ if (!bt_device_is_bonded(&device->bdaddr))
+ queue_remove_all(device->services, NULL, NULL, destroy_service);
+
+ device_set_state(device, DEVICE_DISCONNECTED);
+
+ if (!queue_isempty(device->autoconnect_apps))
+ auto_connect_le(device);
+ else
+ bt_auto_connect_remove(&device->bdaddr);
+}
+
+static void destroy_gatt_app(void *data)
+{
+ struct gatt_app *app = data;
+
+ if (!app)
+ return;
+
+ /*
+ * First we want to get all notifications and unregister them.
+ * We don't pass unregister_notification to queue_destroy,
+ * because destroy notification performs operations on queue
+ * too. So remove all elements and then destroy queue.
+ */
+
+ if (app->type == GATT_CLIENT)
+ while (queue_peek_head(app->notifications)) {
+ struct notification_data *notification;
+
+ notification = queue_pop_head(app->notifications);
+ unregister_notification(notification);
+ }
+
+ queue_destroy(app->notifications, free);
+
+ free(app);
+}
+
+struct pending_request {
+ struct gatt_db_attribute *attrib;
+ int length;
+ uint8_t *value;
+ uint16_t offset;
+
+ uint8_t *filter_value;
+ uint16_t filter_vlen;
+
+ bool completed;
+ uint8_t error;
+};
+
+static void destroy_pending_request(void *data)
+{
+ struct pending_request *entry = data;
+
+ if (!entry)
+ return;
+
+ free(entry->value);
+ free(entry->filter_value);
+ free(entry);
+}
+
+static void destroy_device(void *data)
+{
+ struct gatt_device *dev = data;
+
+ if (!dev)
+ return;
+
+ queue_destroy(dev->services, destroy_service);
+ queue_destroy(dev->pending_requests, destroy_pending_request);
+ queue_destroy(dev->autoconnect_apps, NULL);
+
+ bt_auto_connect_remove(&dev->bdaddr);
+
+ free(dev);
+}
+
+static struct gatt_device *device_ref(struct gatt_device *device)
+{
+ if (!device)
+ return NULL;
+
+ device->ref++;
+
+ return device;
+}
+
+static void device_unref(struct gatt_device *device)
+{
+ if (!device)
+ return;
+
+ if (--device->ref)
+ return;
+
+ destroy_device(device);
+}
+
+static struct gatt_device *create_device(const bdaddr_t *addr)
+{
+ struct gatt_device *dev;
+
+ dev = new0(struct gatt_device, 1);
+
+ bacpy(&dev->bdaddr, addr);
+
+ dev->services = queue_new();
+ dev->autoconnect_apps = queue_new();
+ dev->pending_requests = queue_new();
+
+ queue_push_head(gatt_devices, dev);
+
+ return device_ref(dev);
+}
+
+static void send_client_connect_status_notify(struct app_connection *conn,
+ int32_t status)
+{
+ struct hal_ev_gatt_client_connect ev;
+
+ if (conn->app->func) {
+ conn->app->func(&conn->device->bdaddr,
+ status == GATT_SUCCESS ? 0 : -ENOTCONN,
+ conn->device->attrib);
+ return;
+ }
+
+ ev.client_if = conn->app->id;
+ ev.conn_id = conn->id;
+ ev.status = status;
+
+ bdaddr2android(&conn->device->bdaddr, &ev.bda);
+
+ ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT, HAL_EV_GATT_CLIENT_CONNECT,
+ sizeof(ev), &ev);
+}
+
+static void send_server_connection_state_notify(struct app_connection *conn,
+ bool connected)
+{
+ struct hal_ev_gatt_server_connection ev;
+
+ if (conn->app->func) {
+ conn->app->func(&conn->device->bdaddr,
+ connected ? 0 : -ENOTCONN,
+ conn->device->attrib);
+ return;
+ }
+
+ ev.server_if = conn->app->id;
+ ev.conn_id = conn->id;
+ ev.connected = connected;
+
+ bdaddr2android(&conn->device->bdaddr, &ev.bdaddr);
+
+ ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT,
+ HAL_EV_GATT_SERVER_CONNECTION, sizeof(ev), &ev);
+}
+
+static void send_client_disconnect_status_notify(struct app_connection *conn,
+ int32_t status)
+{
+ struct hal_ev_gatt_client_disconnect ev;
+
+ if (conn->app->func) {
+ conn->app->func(&conn->device->bdaddr, -ENOTCONN,
+ conn->device->attrib);
+ return;
+ }
+
+ ev.client_if = conn->app->id;
+ ev.conn_id = conn->id;
+ ev.status = status;
+
+ bdaddr2android(&conn->device->bdaddr, &ev.bda);
+
+ ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT,
+ HAL_EV_GATT_CLIENT_DISCONNECT, sizeof(ev), &ev);
+
+}
+
+static void notify_app_disconnect_status(struct app_connection *conn,
+ int32_t status)
+{
+ if (!conn->app)
+ return;
+
+ if (conn->app->type == GATT_CLIENT)
+ send_client_disconnect_status_notify(conn, status);
+ else
+ send_server_connection_state_notify(conn, !!status);
+}
+
+static void notify_app_connect_status(struct app_connection *conn,
+ int32_t status)
+{
+ if (!conn->app)
+ return;
+
+ if (conn->app->type == GATT_CLIENT)
+ send_client_connect_status_notify(conn, status);
+ else
+ send_server_connection_state_notify(conn, !status);
+}
+
+static void destroy_connection(void *data)
+{
+ struct app_connection *conn = data;
+
+ if (!conn)
+ return;
+
+ if (conn->timeout_id > 0)
+ g_source_remove(conn->timeout_id);
+
+ switch (conn->device->state) {
+ case DEVICE_CONNECTED:
+ notify_app_disconnect_status(conn, GATT_SUCCESS);
+ break;
+ case DEVICE_CONNECT_INIT:
+ case DEVICE_CONNECT_READY:
+ notify_app_connect_status(conn, GATT_FAILURE);
+ break;
+ case DEVICE_DISCONNECTED:
+ break;
+ }
+
+ if (!queue_find(app_connections, match_connection_by_device,
+ conn->device))
+ connection_cleanup(conn->device);
+
+ queue_destroy(conn->transactions, free);
+ device_unref(conn->device);
+ free(conn);
+}
+
+static gboolean disconnected_cb(GIOChannel *io, GIOCondition cond,
+ gpointer user_data)
+{
+ struct gatt_device *dev = user_data;
+ int sock, err = 0;
+ socklen_t len;
+
+ sock = g_io_channel_unix_get_fd(io);
+ len = sizeof(err);
+ if (!getsockopt(sock, SOL_SOCKET, SO_ERROR, &err, &len))
+ DBG("%s (%d)", strerror(err), err);
+
+ queue_remove_all(app_connections, match_connection_by_device, dev,
+ destroy_connection);
+
+ return FALSE;
+}
+
+static bool get_local_mtu(struct gatt_device *dev, uint16_t *mtu)
+{
+ GIOChannel *io;
+ uint16_t imtu, omtu;
+
+ io = g_attrib_get_channel(dev->attrib);
+
+ if (!bt_io_get(io, NULL, BT_IO_OPT_IMTU, &imtu, BT_IO_OPT_OMTU, &omtu,
+ BT_IO_OPT_INVALID)) {
+ error("gatt: Failed to get local MTU");
+ return false;
+ }
+
+ /*
+ * Limit MTU to MIN(IMTU, OMTU). This is to avoid situation where
+ * local OMTU < MIN(remote MTU, IMTU)
+ */
+ if (mtu)
+ *mtu = MIN(imtu, omtu);
+
+ return true;
+}
+
+static void notify_client_mtu_change(struct app_connection *conn, bool success)
+{
+ struct hal_ev_gatt_client_configure_mtu ev;
+ size_t mtu;
+
+ g_attrib_get_buffer(conn->device->attrib, &mtu);
+
+ ev.conn_id = conn->id;
+ ev.status = success ? GATT_SUCCESS : GATT_FAILURE;
+ ev.mtu = mtu;
+
+ ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT,
+ HAL_EV_GATT_CLIENT_CONFIGURE_MTU, sizeof(ev), &ev);
+}
+
+static void notify_server_mtu(struct app_connection *conn)
+{
+ struct hal_ev_gatt_server_mtu_changed ev;
+ size_t mtu;
+
+ g_attrib_get_buffer(conn->device->attrib, &mtu);
+
+ ev.conn_id = conn->id;
+ ev.mtu = mtu;
+
+ ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT,
+ HAL_EV_GATT_SERVER_MTU_CHANGED, sizeof(ev), &ev);
+}
+
+static void notify_mtu_change(void *data, void *user_data)
+{
+ struct gatt_device *device = user_data;
+ struct app_connection *conn = data;
+
+ if (conn->device != device)
+ return;
+
+ if (!conn->app) {
+ error("gatt: can't notify mtu - no app registered for conn");
+ return;
+ }
+
+ switch (conn->app->type) {
+ case GATT_CLIENT:
+ notify_client_mtu_change(conn, true);
+ break;
+ case GATT_SERVER:
+ notify_server_mtu(conn);
+ break;
+ default:
+ break;
+ }
+}
+
+static bool update_mtu(struct gatt_device *device, uint16_t rmtu)
+{
+ uint16_t mtu, lmtu;
+
+ if (!get_local_mtu(device, &lmtu))
+ return false;
+
+ DBG("remote_mtu:%d local_mtu:%d", rmtu, lmtu);
+
+ if (rmtu < ATT_DEFAULT_LE_MTU) {
+ error("gatt: remote MTU invalid (%u bytes)", rmtu);
+ return false;
+ }
+
+ mtu = MIN(lmtu, rmtu);
+
+ if (mtu == ATT_DEFAULT_LE_MTU)
+ return true;
+
+ if (!g_attrib_set_mtu(device->attrib, mtu)) {
+ error("gatt: Failed to set MTU");
+ return false;
+ }
+
+ queue_foreach(app_connections, notify_mtu_change, device);
+
+ return true;
+}
+
+static void att_handler(const uint8_t *ipdu, uint16_t len, gpointer user_data);
+
+static void exchange_mtu_cb(guint8 status, const guint8 *pdu, guint16 plen,
+ gpointer user_data)
+{
+ struct gatt_device *device = user_data;
+ uint16_t rmtu;
+
+ DBG("");
+
+ if (status) {
+ error("gatt: MTU exchange: %s", att_ecode2str(status));
+ goto failed;
+ }
+
+ if (!dec_mtu_resp(pdu, plen, &rmtu)) {
+ error("gatt: MTU exchange: protocol error");
+ goto failed;
+ }
+
+ update_mtu(device, rmtu);
+
+failed:
+ device_unref(device);
+}
+
+static void send_exchange_mtu_request(struct gatt_device *device)
+{
+ uint16_t mtu;
+
+ if (!get_local_mtu(device, &mtu))
+ return;
+
+ DBG("mtu %u", mtu);
+
+ if (!gatt_exchange_mtu(device->attrib, mtu, exchange_mtu_cb,
+ device_ref(device)))
+ device_unref(device);
+}
+
+static void ignore_confirmation_cb(guint8 status, const guint8 *pdu,
+ guint16 len, gpointer user_data)
+{
+ /* Ignored. */
+}
+
+static void notify_att_range_change(struct gatt_device *dev,
+ struct att_range *range)
+{
+ uint16_t handle;
+ uint16_t length = 0;
+ uint16_t ccc;
+ uint8_t *pdu;
+ size_t mtu;
+ GAttribResultFunc confirmation_cb = NULL;
+
+ handle = gatt_db_attribute_get_handle(service_changed_attrib);
+ if (!handle)
+ return;
+
+ ccc = bt_get_gatt_ccc(&dev->bdaddr);
+ if (!ccc)
+ return;
+
+ pdu = g_attrib_get_buffer(dev->attrib, &mtu);
+
+ switch (ccc) {
+ case 0x0001:
+ length = enc_notification(handle, (uint8_t *) range,
+ sizeof(*range), pdu, mtu);
+ break;
+ case 0x0002:
+ length = enc_indication(handle, (uint8_t *) range,
+ sizeof(*range), pdu, mtu);
+ confirmation_cb = ignore_confirmation_cb;
+ break;
+ default:
+ /* 0xfff4 reserved for future use */
+ break;
+ }
+
+ g_attrib_send(dev->attrib, 0, pdu, length, confirmation_cb, NULL, NULL);
+}
+
+static struct app_connection *create_connection(struct gatt_device *device,
+ struct gatt_app *app)
+{
+ struct app_connection *new_conn;
+ static int32_t last_conn_id = 1;
+
+ /* Check if already connected */
+ new_conn = new0(struct app_connection, 1);
+
+ /* Make connection id unique to connection record (app, device) pair */
+ new_conn->app = app;
+ new_conn->id = last_conn_id++;
+ new_conn->transactions = queue_new();
+
+ queue_push_head(app_connections, new_conn);
+
+ new_conn->device = device_ref(device);
+
+ return new_conn;
+}
+
+static struct service *create_service(uint8_t id, bool primary, char *uuid,
+ void *data)
+{
+ struct service *s;
+
+ s = new0(struct service, 1);
+
+ if (bt_string_to_uuid(&s->id.uuid, uuid) < 0) {
+ error("gatt: Cannot convert string to uuid");
+ free(s);
+ return NULL;
+ }
+
+ s->chars = queue_new();
+ s->included = queue_new();
+ s->id.instance = id;
+
+ /* Put primary service to our local list */
+ s->primary = primary;
+ if (s->primary)
+ memcpy(&s->prim, data, sizeof(s->prim));
+ else
+ memcpy(&s->incl, data, sizeof(s->incl));
+
+ return s;
+}
+
+static void send_client_primary_notify(void *data, void *user_data)
+{
+ struct hal_ev_gatt_client_search_result ev;
+ struct service *p = data;
+ int32_t conn_id = PTR_TO_INT(user_data);
+
+ /* In service queue we will have also included services */
+ if (!p->primary)
+ return;
+
+ ev.conn_id = conn_id;
+ element_id_to_hal_srvc_id(&p->id, 1, &ev.srvc_id);
+
+ uuid2android(&p->id.uuid, ev.srvc_id.uuid);
+
+ ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT,
+ HAL_EV_GATT_CLIENT_SEARCH_RESULT, sizeof(ev), &ev);
+}
+
+static void send_client_search_complete_notify(int32_t status, int32_t conn_id)
+{
+ struct hal_ev_gatt_client_search_complete ev;
+
+ ev.status = status;
+ ev.conn_id = conn_id;
+ ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT,
+ HAL_EV_GATT_CLIENT_SEARCH_COMPLETE, sizeof(ev), &ev);
+}
+
+struct discover_srvc_data {
+ bt_uuid_t uuid;
+ struct app_connection *conn;
+};
+
+static void discover_srvc_by_uuid_cb(uint8_t status, GSList *ranges,
+ void *user_data)
+{
+ struct discover_srvc_data *cb_data = user_data;
+ struct gatt_primary prim;
+ struct service *s;
+ int32_t gatt_status;
+ struct gatt_device *dev = cb_data->conn->device;
+ uint8_t instance_id = queue_length(dev->services);
+
+ DBG("Status %d", status);
+
+ if (status) {
+ error("gatt: Discover pri srvc filtered by uuid failed: %s",
+ att_ecode2str(status));
+ gatt_status = GATT_FAILURE;
+ goto reply;
+ }
+
+ if (!ranges) {
+ info("gatt: No primary services searched by uuid found");
+ gatt_status = GATT_SUCCESS;
+ goto reply;
+ }
+
+ bt_uuid_to_string(&cb_data->uuid, prim.uuid, sizeof(prim.uuid));
+
+ for (; ranges; ranges = ranges->next) {
+ memcpy(&prim.range, ranges->data, sizeof(prim.range));
+
+ s = create_service(instance_id++, true, prim.uuid, &prim);
+ if (!s) {
+ gatt_status = GATT_FAILURE;
+ goto reply;
+ }
+
+ queue_push_tail(dev->services, s);
+
+ send_client_primary_notify(s, INT_TO_PTR(cb_data->conn->id));
+
+ DBG("attr handle = 0x%04x, end grp handle = 0x%04x uuid: %s",
+ prim.range.start, prim.range.end, prim.uuid);
+ }
+
+ /* Partial search service scanning was performed */
+ dev->partial_srvc_search = true;
+ gatt_status = GATT_SUCCESS;
+
+reply:
+ send_client_search_complete_notify(gatt_status, cb_data->conn->id);
+ free(cb_data);
+}
+
+static void discover_srvc_all_cb(uint8_t status, GSList *services,
+ void *user_data)
+{
+ struct discover_srvc_data *cb_data = user_data;
+ struct gatt_device *dev = cb_data->conn->device;
+ int32_t gatt_status;
+ GSList *l;
+ /*
+ * There might be multiply services with same uuid. Therefore make sure
+ * each primary service one has unique instance_id
+ */
+ uint8_t instance_id = queue_length(dev->services);
+
+ DBG("Status %d", status);
+
+ if (status) {
+ error("gatt: Discover all primary services failed: %s",
+ att_ecode2str(status));
+ gatt_status = GATT_FAILURE;
+ goto reply;
+ }
+
+ if (!services) {
+ info("gatt: No primary services found");
+ gatt_status = GATT_SUCCESS;
+ goto reply;
+ }
+
+ for (l = services; l; l = l->next) {
+ struct gatt_primary *prim = l->data;
+ struct service *p;
+
+ if (queue_find(dev->services, match_srvc_by_range,
+ &prim->range))
+ continue;
+
+ p = create_service(instance_id++, true, prim->uuid, prim);
+ if (!p)
+ continue;
+
+ queue_push_tail(dev->services, p);
+
+ DBG("attr handle = 0x%04x, end grp handle = 0x%04x uuid: %s",
+ prim->range.start, prim->range.end, prim->uuid);
+ }
+
+ /*
+ * Send all found services notifications - first cache,
+ * then send notifies
+ */
+ queue_foreach(dev->services, send_client_primary_notify,
+ INT_TO_PTR(cb_data->conn->id));
+
+ /* Full search service scanning was performed */
+ dev->partial_srvc_search = false;
+ gatt_status = GATT_SUCCESS;
+
+reply:
+ send_client_search_complete_notify(gatt_status, cb_data->conn->id);
+ free(cb_data);
+}
+
+static gboolean connection_timeout(void *user_data)
+{
+ struct app_connection *conn = user_data;
+
+ conn->timeout_id = 0;
+
+ queue_remove(app_connections, conn);
+ destroy_connection(conn);
+
+ return FALSE;
+}
+
+static void discover_primary_cb(uint8_t status, GSList *services,
+ void *user_data)
+{
+ struct discover_srvc_data *cb_data = user_data;
+ struct app_connection *conn = cb_data->conn;
+ struct gatt_device *dev = conn->device;
+ GSList *l, *uuids = NULL;
+
+ DBG("Status %d", status);
+
+ if (status) {
+ error("gatt: Discover all primary services failed: %s",
+ att_ecode2str(status));
+ free(cb_data);
+
+ return;
+ }
+
+ if (!services) {
+ info("gatt: No primary services found");
+ free(cb_data);
+
+ return;
+ }
+
+ for (l = services; l; l = l->next) {
+ struct gatt_primary *prim = l->data;
+ uint8_t *new_uuid;
+ bt_uuid_t uuid, u128;
+
+ DBG("uuid: %s", prim->uuid);
+
+ if (bt_string_to_uuid(&uuid, prim->uuid) < 0) {
+ error("gatt: Cannot convert string to uuid");
+ continue;
+ }
+
+ bt_uuid_to_uuid128(&uuid, &u128);
+ new_uuid = g_memdup(&u128.value.u128, sizeof(u128.value.u128));
+
+ uuids = g_slist_prepend(uuids, new_uuid);
+ }
+
+ bt_device_set_uuids(&dev->bdaddr, uuids);
+
+ free(cb_data);
+
+ conn->timeout_id = g_timeout_add_seconds(GATT_CONN_TIMEOUT,
+ connection_timeout, conn);
+}
+
+static guint search_dev_for_srvc(struct app_connection *conn, bt_uuid_t *uuid)
+{
+ struct discover_srvc_data *cb_data;
+
+ cb_data = new0(struct discover_srvc_data, 1);
+ cb_data->conn = conn;
+
+ if (uuid) {
+ memcpy(&cb_data->uuid, uuid, sizeof(cb_data->uuid));
+ return gatt_discover_primary(conn->device->attrib, uuid,
+ discover_srvc_by_uuid_cb, cb_data);
+ }
+
+ if (conn->app)
+ return gatt_discover_primary(conn->device->attrib, NULL,
+ discover_srvc_all_cb, cb_data);
+
+ return gatt_discover_primary(conn->device->attrib, NULL,
+ discover_primary_cb, cb_data);
+}
+
+struct connect_data {
+ struct gatt_device *dev;
+ int32_t status;
+};
+
+static void notify_app_connect_status_by_device(void *data, void *user_data)
+{
+ struct app_connection *conn = data;
+ struct connect_data *con_data = user_data;
+
+ if (conn->device == con_data->dev)
+ notify_app_connect_status(conn, con_data->status);
+}
+
+static struct app_connection *find_conn_without_app(struct gatt_device *dev)
+{
+ struct app_connection conn_match;
+
+ conn_match.device = dev;
+ conn_match.app = NULL;
+
+ return queue_find(app_connections, match_connection_by_device_and_app,
+ &conn_match);
+}
+
+static struct app_connection *find_conn(const bdaddr_t *addr, int32_t app_id)
+{
+ struct app_connection conn_match;
+ struct gatt_device *dev;
+ struct gatt_app *app;
+
+ /* Check if app is registered */
+ app = find_app_by_id(app_id);
+ if (!app) {
+ error("gatt: Client id %d not found", app_id);
+ return NULL;
+ }
+
+ /* Check if device is known */
+ dev = find_device_by_addr(addr);
+ if (!dev) {
+ error("gatt: Client id %d not found", app_id);
+ return NULL;
+ }
+
+ conn_match.device = dev;
+ conn_match.app = app;
+
+ return queue_find(app_connections, match_connection_by_device_and_app,
+ &conn_match);
+}
+
+static void create_app_connection(void *data, void *user_data)
+{
+ struct gatt_device *dev = user_data;
+ struct gatt_app *app;
+
+ app = find_app_by_id(PTR_TO_INT(data));
+ if (!app)
+ return;
+
+ DBG("Autoconnect application id=%d", app->id);
+
+ if (!find_conn(&dev->bdaddr, PTR_TO_INT(data)))
+ create_connection(dev, app);
+}
+
+static void ind_handler(const uint8_t *cmd, uint16_t cmd_len,
+ gpointer user_data)
+{
+ struct gatt_device *dev = user_data;
+ uint16_t resp_length = 0;
+ size_t length;
+ uint8_t *opdu = g_attrib_get_buffer(dev->attrib, &length);
+
+ /*
+ * We have to send confirmation here. If some client is
+ * registered for this indication, event will be send in
+ * handle_notification
+ */
+
+ resp_length = enc_confirmation(opdu, length);
+ g_attrib_send(dev->attrib, 0, opdu, resp_length, NULL, NULL, NULL);
+}
+
+static void connect_cb(GIOChannel *io, GError *gerr, gpointer user_data)
+{
+ struct gatt_device *dev = user_data;
+ struct connect_data data;
+ struct att_range range;
+ uint32_t status;
+ GError *err = NULL;
+ GAttrib *attrib;
+ uint16_t mtu, cid;
+
+ if (dev->state != DEVICE_CONNECT_READY) {
+ error("gatt: Device not in a connecting state!?");
+ g_io_channel_shutdown(io, TRUE, NULL);
+ return;
+ }
+
+ if (dev->att_io) {
+ g_io_channel_unref(dev->att_io);
+ dev->att_io = NULL;
+ }
+
+ if (gerr) {
+ error("gatt: connection failed %s", gerr->message);
+ device_set_state(dev, DEVICE_DISCONNECTED);
+ status = GATT_FAILURE;
+ goto reply;
+ }
+
+ if (!bt_io_get(io, &err, BT_IO_OPT_IMTU, &mtu, BT_IO_OPT_CID, &cid,
+ BT_IO_OPT_INVALID)) {
+ error("gatt: Could not get imtu or cid: %s", err->message);
+ device_set_state(dev, DEVICE_DISCONNECTED);
+ status = GATT_FAILURE;
+ g_error_free(err);
+ goto reply;
+ }
+
+ /* on BR/EDR MTU must not be less then minimal allowed MTU */
+ if (cid != ATT_CID && mtu < ATT_DEFAULT_L2CAP_MTU) {
+ error("gatt: MTU too small (%u bytes)", mtu);
+ device_set_state(dev, DEVICE_DISCONNECTED);
+ status = GATT_FAILURE;
+ goto reply;
+ }
+
+ DBG("mtu %u cid %u", mtu, cid);
+
+ /* on LE we always start with default MTU */
+ if (cid == ATT_CID)
+ mtu = ATT_DEFAULT_LE_MTU;
+
+ attrib = g_attrib_new(io, mtu, true);
+ if (!attrib) {
+ error("gatt: unable to create new GAttrib instance");
+ device_set_state(dev, DEVICE_DISCONNECTED);
+ status = GATT_FAILURE;
+ goto reply;
+ }
+
+ dev->attrib = attrib;
+ dev->watch_id = g_io_add_watch(io, G_IO_HUP | G_IO_ERR | G_IO_NVAL,
+ disconnected_cb, dev);
+
+ dev->server_id = g_attrib_register(attrib, GATTRIB_ALL_REQS,
+ GATTRIB_ALL_HANDLES,
+ att_handler, dev, NULL);
+ dev->ind_id = g_attrib_register(attrib, ATT_OP_HANDLE_IND,
+ GATTRIB_ALL_HANDLES,
+ ind_handler, dev, NULL);
+ if ((dev->server_id && dev->ind_id) == 0)
+ error("gatt: Could not attach to server");
+
+ device_set_state(dev, DEVICE_CONNECTED);
+
+ /* Send exchange mtu request as we assume being client and server */
+ /* TODO: Dont exchange mtu if no client apps */
+
+ /* MTU exchange shall not be used on BR/EDR - Vol 3. Part G. 4.3.1 */
+ if (cid == ATT_CID)
+ send_exchange_mtu_request(dev);
+
+ /*
+ * Service Changed Characteristic and CCC Descriptor handles
+ * should not change if there are bonded devices. We have them
+ * constant all the time, thus they should be excluded from
+ * range indicating changes.
+ */
+ range.start = gatt_db_attribute_get_handle(service_changed_attrib) + 2;
+ range.end = 0xffff;
+
+ /*
+ * If there is ccc stored for that device we were acting as server for
+ * it, and as we dont have last connect and last services (de)activation
+ * timestamps we should always assume something has changed.
+ */
+ notify_att_range_change(dev, &range);
+
+ status = GATT_SUCCESS;
+
+reply:
+ /*
+ * Make sure there are app_connections for all apps interested in auto
+ * connect to that device
+ */
+ queue_foreach(dev->autoconnect_apps, create_app_connection, dev);
+
+ if (!queue_find(app_connections, match_connection_by_device, dev)) {
+ struct app_connection *conn;
+
+ if (!dev->attrib)
+ return;
+
+ conn = create_connection(dev, NULL);
+ if (!conn)
+ return;
+
+ if (bt_is_pairing(&dev->bdaddr))
+ /*
+ * If there is bonding ongoing lets wait for paired
+ * callback. Once we get that we can start search
+ * services
+ */
+ conn->timeout_id = g_timeout_add_seconds(
+ GATT_PAIR_CONN_TIMEOUT,
+ connection_timeout, conn);
+ else
+ /*
+ * There is no ongoing bonding, lets search for primary
+ * services
+ */
+ search_dev_for_srvc(conn, NULL);
+ }
+
+ data.dev = dev;
+ data.status = status;
+ queue_foreach(app_connections, notify_app_connect_status_by_device,
+ &data);
+
+ /* For BR/EDR notify about MTU since it is not negotiable*/
+ if (cid != ATT_CID && status == GATT_SUCCESS)
+ queue_foreach(app_connections, notify_mtu_change, dev);
+
+ device_unref(dev);
+
+ /* Check if we should restart scan */
+ if (scanning)
+ bt_le_discovery_start();
+
+ /* FIXME: What to do if discovery won't start here. */
+}
+
+static int connect_le(struct gatt_device *dev)
+{
+ GIOChannel *io;
+ GError *gerr = NULL;
+ char addr[18];
+ const bdaddr_t *bdaddr;
+ uint8_t bdaddr_type;
+
+ ba2str(&dev->bdaddr, addr);
+
+ /* There is one connection attempt going on */
+ if (dev->att_io) {
+ info("gatt: connection to dev %s is ongoing", addr);
+ return -EALREADY;
+ }
+
+ DBG("Connection attempt to: %s", addr);
+
+ bdaddr = bt_get_id_addr(&dev->bdaddr, &bdaddr_type);
+
+ /*
+ * This connection will help us catch any PDUs that comes before
+ * pairing finishes
+ */
+ io = bt_io_connect(connect_cb, device_ref(dev), NULL, &gerr,
+ BT_IO_OPT_SOURCE_BDADDR, &adapter_addr,
+ BT_IO_OPT_SOURCE_TYPE, BDADDR_LE_PUBLIC,
+ BT_IO_OPT_DEST_BDADDR, bdaddr,
+ BT_IO_OPT_DEST_TYPE, bdaddr_type,
+ BT_IO_OPT_CID, ATT_CID,
+ BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_LOW,
+ BT_IO_OPT_INVALID);
+ if (!io) {
+ error("gatt: Failed bt_io_connect(%s): %s", addr,
+ gerr->message);
+ g_error_free(gerr);
+ return -EIO;
+ }
+
+ /* Keep this, so we can cancel the connection */
+ dev->att_io = io;
+
+ device_set_state(dev, DEVICE_CONNECT_READY);
+
+ return 0;
+}
+
+static int connect_next_dev(void)
+{
+ struct gatt_device *dev;
+
+ DBG("");
+
+ dev = find_device_by_state(DEVICE_CONNECT_READY);
+ if (!dev)
+ return -ENODEV;
+
+ return connect_le(dev);
+}
+
+static void bt_le_discovery_stop_cb(void)
+{
+ DBG("");
+
+ /* Check now if there is any device ready to connect */
+ if (connect_next_dev() < 0)
+ bt_le_discovery_start();
+}
+
+static void le_device_found_handler(const bdaddr_t *addr, int rssi,
+ uint16_t eir_len, const void *eir,
+ bool connectable, bool bonded)
+{
+ uint8_t buf[IPC_MTU];
+ struct hal_ev_gatt_client_scan_result *ev = (void *) buf;
+ struct gatt_device *dev;
+ char bda[18];
+
+ if (!scanning)
+ goto done;
+
+ ba2str(addr, bda);
+ DBG("LE Device found: %s, rssi: %d, adv_data: %d", bda, rssi, !!eir);
+
+ bdaddr2android(addr, ev->bda);
+ ev->rssi = rssi;
+ ev->len = eir_len;
+
+ memcpy(ev->adv_data, eir, ev->len);
+
+ ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT,
+ HAL_EV_GATT_CLIENT_SCAN_RESULT,
+ sizeof(*ev) + ev->len, ev);
+
+done:
+ if (!connectable)
+ return;
+
+ /* We use auto connect feature from kernel if possible */
+ if (bt_kernel_conn_control())
+ return;
+
+ dev = find_device_by_addr(addr);
+ if (!dev) {
+ if (!bonded)
+ return;
+
+ dev = create_device(addr);
+ }
+
+ if (dev->state != DEVICE_CONNECT_INIT)
+ return;
+
+ device_set_state(dev, DEVICE_CONNECT_READY);
+
+ /*
+ * We are ok to perform connect now. Stop discovery
+ * and once it is stopped continue with creating ACL
+ */
+ bt_le_discovery_stop(bt_le_discovery_stop_cb);
+}
+
+static struct gatt_app *register_app(const uint8_t *uuid, gatt_type_t type)
+{
+ static int32_t application_id = 1;
+ struct gatt_app *app;
+
+ if (queue_find(gatt_apps, match_app_by_uuid, uuid)) {
+ error("gatt: app uuid is already on list");
+ return NULL;
+ }
+
+ app = new0(struct gatt_app, 1);
+
+ app->type = type;
+
+ if (app->type == GATT_CLIENT)
+ app->notifications = queue_new();
+
+ memcpy(app->uuid, uuid, sizeof(app->uuid));
+
+ app->id = application_id++;
+
+ queue_push_head(gatt_apps, app);
+
+ if (app->type == GATT_SERVER)
+ queue_push_tail(listen_apps, INT_TO_PTR(app->id));
+
+ return app;
+}
+
+static void handle_client_register(const void *buf, uint16_t len)
+{
+ const struct hal_cmd_gatt_client_register *cmd = buf;
+ struct hal_ev_gatt_client_register_client ev;
+ struct gatt_app *app;
+
+ DBG("");
+
+ memset(&ev, 0, sizeof(ev));
+
+ app = register_app(cmd->uuid, GATT_CLIENT);
+
+ if (app) {
+ ev.client_if = app->id;
+ ev.status = GATT_SUCCESS;
+ } else {
+ ev.status = GATT_FAILURE;
+ }
+
+ /* We should send notification with given in cmd UUID */
+ memcpy(ev.app_uuid, cmd->uuid, sizeof(ev.app_uuid));
+
+ ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT,
+ HAL_EV_GATT_CLIENT_REGISTER_CLIENT, sizeof(ev), &ev);
+
+ ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT, HAL_OP_GATT_CLIENT_REGISTER,
+ HAL_STATUS_SUCCESS);
+}
+
+static void handle_client_scan(const void *buf, uint16_t len)
+{
+ const struct hal_cmd_gatt_client_scan *cmd = buf;
+ uint8_t status;
+
+ DBG("new state %d", cmd->start);
+
+ if (cmd->client_if != 0) {
+ void *registered = find_app_by_id(cmd->client_if);
+
+ if (!registered) {
+ error("gatt: Client not registered");
+ status = HAL_STATUS_FAILED;
+ goto reply;
+ }
+ }
+
+ /* Turn off scan */
+ if (!cmd->start) {
+ DBG("Stopping LE SCAN");
+
+ if (scanning) {
+ bt_le_discovery_stop(NULL);
+ scanning = false;
+ }
+
+ status = HAL_STATUS_SUCCESS;
+ goto reply;
+ }
+
+ /* Reply success if we already do scan */
+ if (scanning) {
+ status = HAL_STATUS_SUCCESS;
+ goto reply;
+ }
+
+ /* Turn on scan */
+ if (!bt_le_discovery_start()) {
+ error("gatt: LE scan switch failed");
+ status = HAL_STATUS_FAILED;
+ goto reply;
+ }
+
+ scanning = true;
+ status = HAL_STATUS_SUCCESS;
+
+reply:
+ ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT, HAL_OP_GATT_CLIENT_SCAN,
+ status);
+}
+
+static int connect_bredr(struct gatt_device *dev)
+{
+ BtIOSecLevel sec_level;
+ GIOChannel *io;
+ GError *gerr = NULL;
+ char addr[18];
+
+ ba2str(&dev->bdaddr, addr);
+
+ /* There is one connection attempt going on */
+ if (dev->att_io) {
+ info("gatt: connection to dev %s is ongoing", addr);
+ return -EALREADY;
+ }
+
+ DBG("Connection attempt to: %s", addr);
+
+ sec_level = bt_device_is_bonded(&dev->bdaddr) ? BT_IO_SEC_MEDIUM :
+ BT_IO_SEC_LOW;
+
+ io = bt_io_connect(connect_cb, device_ref(dev), NULL, &gerr,
+ BT_IO_OPT_SOURCE_BDADDR, &adapter_addr,
+ BT_IO_OPT_SOURCE_TYPE, BDADDR_BREDR,
+ BT_IO_OPT_DEST_BDADDR, &dev->bdaddr,
+ BT_IO_OPT_DEST_TYPE, BDADDR_BREDR,
+ BT_IO_OPT_PSM, ATT_PSM,
+ BT_IO_OPT_SEC_LEVEL, sec_level,
+ BT_IO_OPT_INVALID);
+ if (!io) {
+ error("gatt: Failed bt_io_connect(%s): %s", addr,
+ gerr->message);
+ g_error_free(gerr);
+ return -EIO;
+ }
+
+ device_set_state(dev, DEVICE_CONNECT_READY);
+
+ /* Keep this, so we can cancel the connection */
+ dev->att_io = io;
+
+ return 0;
+}
+
+static bool trigger_connection(struct app_connection *conn, bool direct)
+{
+ switch (conn->device->state) {
+ case DEVICE_DISCONNECTED:
+ /*
+ * If device was last seen over BR/EDR connect over it.
+ * Note: Connection state is handled in connect_bredr() func
+ */
+ if (bt_device_last_seen_bearer(&conn->device->bdaddr) ==
+ BDADDR_BREDR)
+ return connect_bredr(conn->device) == 0;
+
+ if (direct)
+ return connect_le(conn->device) == 0;
+
+ bt_gatt_add_autoconnect(conn->app->id, &conn->device->bdaddr);
+ return auto_connect_le(conn->device);
+ case DEVICE_CONNECTED:
+ notify_app_connect_status(conn, GATT_SUCCESS);
+ return true;
+ case DEVICE_CONNECT_READY:
+ case DEVICE_CONNECT_INIT:
+ default:
+ /* In those cases connection is already triggered. */
+ return true;
+ }
+}
+
+static void remove_autoconnect_device(struct gatt_device *dev)
+{
+ bt_auto_connect_remove(&dev->bdaddr);
+
+ if (dev->state == DEVICE_CONNECT_INIT)
+ device_set_state(dev, DEVICE_DISCONNECTED);
+
+ device_unref(dev);
+}
+
+static void clear_autoconnect_devices(void *data, void *user_data)
+{
+ struct gatt_device *dev = data;
+
+ if (queue_remove(dev->autoconnect_apps, user_data))
+ if (queue_isempty(dev->autoconnect_apps))
+ remove_autoconnect_device(dev);
+}
+
+static uint8_t unregister_app(int client_if)
+{
+ struct gatt_app *cl;
+
+ /*
+ * Make sure that there is no devices in auto connect list for this
+ * application
+ */
+ queue_foreach(gatt_devices, clear_autoconnect_devices,
+ INT_TO_PTR(client_if));
+
+ cl = queue_remove_if(gatt_apps, match_app_by_id, INT_TO_PTR(client_if));
+ if (!cl) {
+ error("gatt: client_if=%d not found", client_if);
+
+ return HAL_STATUS_FAILED;
+ }
+
+ /* Destroy app connections with proper notifications for this app. */
+ queue_remove_all(app_connections, match_connection_by_app, cl,
+ destroy_connection);
+ destroy_gatt_app(cl);
+
+ return HAL_STATUS_SUCCESS;
+}
+
+static void send_client_listen_notify(int32_t id, int32_t status)
+{
+ struct hal_ev_gatt_client_listen ev;
+
+ /* Server if because of typo in android headers */
+ ev.server_if = id;
+ ev.status = status;
+
+ ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT, HAL_EV_GATT_CLIENT_LISTEN,
+ sizeof(ev), &ev);
+}
+
+struct listen_data {
+ int32_t client_id;
+ bool start;
+};
+
+static struct listen_data *create_listen_data(int32_t client_id, bool start)
+{
+ struct listen_data *d;
+
+ d = new0(struct listen_data, 1);
+ d->client_id = client_id;
+ d->start = start;
+
+ return d;
+}
+
+static void set_advertising_cb(uint8_t status, void *user_data)
+{
+ struct listen_data *l = user_data;
+
+ send_client_listen_notify(l->client_id, status);
+
+ /* In case of success update advertising state*/
+ if (!status)
+ advertising_cnt = l->start ? 1 : 0;
+
+ /*
+ * Let's remove client from the list in two cases
+ * 1. Start failed
+ * 2. Stop succeed
+ */
+ if ((l->start && status) || (!l->start && !status))
+ queue_remove(listen_apps, INT_TO_PTR(l->client_id));
+
+ free(l);
+}
+
+static void handle_client_unregister(const void *buf, uint16_t len)
+{
+ const struct hal_cmd_gatt_client_unregister *cmd = buf;
+ uint8_t status;
+ void *listening_client;
+ struct listen_data *data;
+
+ DBG("");
+
+ listening_client = queue_find(listen_apps, NULL,
+ INT_TO_PTR(cmd->client_if));
+
+ if (listening_client) {
+ advertising_cnt--;
+ queue_remove(listen_apps, INT_TO_PTR(cmd->client_if));
+ } else {
+ status = unregister_app(cmd->client_if);
+ goto reply;
+ }
+
+ if (!advertising_cnt) {
+ data = create_listen_data(cmd->client_if, false);
+
+ if (!bt_le_set_advertising(data->start, set_advertising_cb,
+ data)) {
+ error("gatt: Could not set advertising");
+ status = HAL_STATUS_FAILED;
+ free(data);
+ goto reply;
+ }
+ }
+
+ status = unregister_app(cmd->client_if);
+
+reply:
+ ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT,
+ HAL_OP_GATT_CLIENT_UNREGISTER, status);
+}
+
+static uint8_t handle_connect(int32_t app_id, const bdaddr_t *addr, bool direct)
+{
+ struct app_connection conn_match;
+ struct app_connection *conn;
+ struct gatt_device *device;
+ struct gatt_app *app;
+
+ DBG("");
+
+ app = find_app_by_id(app_id);
+ if (!app)
+ return HAL_STATUS_FAILED;
+
+ device = find_device_by_addr(addr);
+ if (!device)
+ device = create_device(addr);
+
+ conn_match.device = device;
+ conn_match.app = app;
+
+ conn = queue_find(app_connections, match_connection_by_device_and_app,
+ &conn_match);
+ if (!conn) {
+ conn = create_connection(device, app);
+ if (!conn)
+ return HAL_STATUS_NOMEM;
+ }
+
+ if (!trigger_connection(conn, direct))
+ return HAL_STATUS_FAILED;
+
+ return HAL_STATUS_SUCCESS;
+}
+
+static void handle_client_connect(const void *buf, uint16_t len)
+{
+ const struct hal_cmd_gatt_client_connect *cmd = buf;
+ uint8_t status;
+ bdaddr_t addr;
+
+ DBG("is_direct:%u transport:%u", cmd->is_direct, cmd->transport);
+
+ android2bdaddr(&cmd->bdaddr, &addr);
+
+ /* TODO handle transport flag */
+
+ status = handle_connect(cmd->client_if, &addr, cmd->is_direct);
+
+ ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT, HAL_OP_GATT_CLIENT_CONNECT,
+ status);
+}
+
+static void handle_client_disconnect(const void *buf, uint16_t len)
+{
+ const struct hal_cmd_gatt_client_disconnect *cmd = buf;
+ struct app_connection *conn;
+ uint8_t status;
+
+ DBG("");
+
+ /* TODO: should we care to match also bdaddr when conn_id is unique? */
+ conn = queue_remove_if(app_connections, match_connection_by_id,
+ INT_TO_PTR(cmd->conn_id));
+ destroy_connection(conn);
+
+ status = HAL_STATUS_SUCCESS;
+
+ ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT,
+ HAL_OP_GATT_CLIENT_DISCONNECT, status);
+}
+
+static void handle_client_listen(const void *buf, uint16_t len)
+{
+ const struct hal_cmd_gatt_client_listen *cmd = buf;
+ uint8_t status;
+ struct listen_data *data;
+ bool req_sent = false;
+ void *listening_client;
+
+ DBG("");
+
+ if (!find_app_by_id(cmd->client_if)) {
+ error("gatt: Client not registered");
+ status = HAL_STATUS_FAILED;
+ goto reply;
+ }
+
+ listening_client = queue_find(listen_apps, NULL,
+ INT_TO_PTR(cmd->client_if));
+ /* Start listening */
+ if (cmd->start) {
+ if (listening_client) {
+ status = HAL_STATUS_SUCCESS;
+ goto reply;
+ }
+
+ queue_push_tail(listen_apps, INT_TO_PTR(cmd->client_if));
+
+ /* If listen is already on just return success*/
+ if (advertising_cnt > 0) {
+ advertising_cnt++;
+ status = HAL_STATUS_SUCCESS;
+ goto reply;
+ }
+ } else {
+ /* Stop listening. Check if client was listening */
+ if (!listening_client) {
+ error("gatt: This client %d does not listen",
+ cmd->client_if);
+ status = HAL_STATUS_FAILED;
+ goto reply;
+ }
+
+ /*
+ * In case there is more listening clients don't stop
+ * advertising
+ */
+ if (advertising_cnt > 1) {
+ advertising_cnt--;
+ queue_remove(listen_apps, INT_TO_PTR(cmd->client_if));
+ status = HAL_STATUS_SUCCESS;
+ goto reply;
+ }
+ }
+
+ data = create_listen_data(cmd->client_if, cmd->start);
+
+ if (!bt_le_set_advertising(cmd->start, set_advertising_cb, data)) {
+ error("gatt: Could not set advertising");
+ status = HAL_STATUS_FAILED;
+ free(data);
+ goto reply;
+ }
+
+ /*
+ * Use this flag to keep in mind that we are waiting for callback with
+ * result
+ */
+ req_sent = true;
+
+ status = HAL_STATUS_SUCCESS;
+
+reply:
+ ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT, HAL_OP_GATT_CLIENT_LISTEN,
+ status);
+
+ /* In case of early success or error, just send notification up */
+ if (!req_sent) {
+ int32_t gatt_status = status == HAL_STATUS_SUCCESS ?
+ GATT_SUCCESS : GATT_FAILURE;
+ send_client_listen_notify(cmd->client_if, gatt_status);
+ }
+}
+
+static void handle_client_refresh(const void *buf, uint16_t len)
+{
+ const struct hal_cmd_gatt_client_refresh *cmd = buf;
+ struct gatt_device *dev;
+ uint8_t status;
+ bdaddr_t bda;
+
+ /*
+ * This is Android's framework hidden API call. It seams that no
+ * notification is expected and Bluedroid silently updates device's
+ * cache under the hood. As we use lazy caching ,we can just clear the
+ * cache and we're done.
+ */
+
+ DBG("");
+
+ android2bdaddr(&cmd->bdaddr, &bda);
+ dev = find_device_by_addr(&bda);
+ if (!dev) {
+ status = HAL_STATUS_FAILED;
+ goto done;
+ }
+
+ queue_remove_all(dev->services, NULL, NULL, destroy_service);
+
+ status = HAL_STATUS_SUCCESS;
+
+done:
+ ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT, HAL_OP_GATT_CLIENT_REFRESH,
+ status);
+}
+
+static void handle_client_search_service(const void *buf, uint16_t len)
+{
+ const struct hal_cmd_gatt_client_search_service *cmd = buf;
+ struct app_connection *conn;
+ uint8_t status;
+ struct service *s;
+ bt_uuid_t uuid;
+ guint srvc_search_success;
+
+ DBG("");
+
+ if (len != sizeof(*cmd) + (cmd->filtered ? 16 : 0)) {
+ error("Invalid search service size (%u bytes), terminating",
+ len);
+ raise(SIGTERM);
+ return;
+ }
+
+ conn = find_connection_by_id(cmd->conn_id);
+ if (!conn) {
+ error("gatt: dev with conn_id=%d not found", cmd->conn_id);
+
+ status = HAL_STATUS_FAILED;
+ goto reply;
+ }
+
+ if (conn->device->state != DEVICE_CONNECTED) {
+ char bda[18];
+
+ ba2str(&conn->device->bdaddr, bda);
+ error("gatt: device %s not connected", bda);
+
+ status = HAL_STATUS_FAILED;
+ goto reply;
+ }
+
+ if (cmd->filtered)
+ android2uuid(cmd->filter_uuid, &uuid);
+
+ /* Services not cached yet */
+ if (queue_isempty(conn->device->services)) {
+ if (cmd->filtered)
+ srvc_search_success = search_dev_for_srvc(conn, &uuid);
+ else
+ srvc_search_success = search_dev_for_srvc(conn, NULL);
+
+ if (!srvc_search_success) {
+ status = HAL_STATUS_FAILED;
+ goto reply;
+ }
+
+ status = HAL_STATUS_SUCCESS;
+ goto reply;
+ }
+
+ /* Search in cached services for given service */
+ if (cmd->filtered) {
+ /* Search in cache for service by uuid */
+ s = queue_find(conn->device->services, match_srvc_by_bt_uuid,
+ &uuid);
+
+ if (s) {
+ send_client_primary_notify(s, INT_TO_PTR(conn->id));
+ } else {
+ if (!search_dev_for_srvc(conn, &uuid)) {
+ status = HAL_STATUS_FAILED;
+ goto reply;
+ }
+
+ status = HAL_STATUS_SUCCESS;
+ goto reply;
+ }
+ } else {
+ /* Refresh service cache if only partial search was performed */
+ if (conn->device->partial_srvc_search) {
+ srvc_search_success = search_dev_for_srvc(conn, NULL);
+ if (!srvc_search_success) {
+ status = HAL_STATUS_FAILED;
+ goto reply;
+ }
+ } else
+ queue_foreach(conn->device->services,
+ send_client_primary_notify,
+ INT_TO_PTR(cmd->conn_id));
+ }
+
+ send_client_search_complete_notify(GATT_SUCCESS, conn->id);
+
+ status = HAL_STATUS_SUCCESS;
+
+reply:
+ ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT,
+ HAL_OP_GATT_CLIENT_SEARCH_SERVICE, status);
+}
+
+static void send_client_incl_service_notify(const struct element_id *srvc_id,
+ const struct service *incl,
+ int32_t conn_id)
+{
+ struct hal_ev_gatt_client_get_inc_service ev;
+
+ memset(&ev, 0, sizeof(ev));
+
+ ev.conn_id = conn_id;
+
+ element_id_to_hal_srvc_id(srvc_id, 1, &ev.srvc_id);
+
+ if (incl) {
+ element_id_to_hal_srvc_id(&incl->id, 0, &ev.incl_srvc_id);
+ ev.status = GATT_SUCCESS;
+ } else {
+ ev.status = GATT_FAILURE;
+ }
+
+ ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT ,
+ HAL_EV_GATT_CLIENT_GET_INC_SERVICE,
+ sizeof(ev), &ev);
+}
+
+struct get_included_data {
+ struct service *prim;
+ struct app_connection *conn;
+};
+
+static int get_inst_id_of_prim_services(const struct gatt_device *dev)
+{
+ struct service *s = queue_peek_tail(dev->services);
+
+ if (s)
+ return s->id.instance;
+
+ return -1;
+}
+
+static void get_included_cb(uint8_t status, GSList *included, void *user_data)
+{
+ struct get_included_data *data = user_data;
+ struct app_connection *conn = data->conn;
+ struct service *service = data->prim;
+ struct service *incl = NULL;
+ int instance_id;
+
+ DBG("");
+
+ free(data);
+
+ if (status) {
+ error("gatt: no included services found");
+ goto failed;
+ }
+
+ /* Remember that we already search included services.*/
+ service->incl_search_done = true;
+
+ /*
+ * There might be multiply services with same uuid. Therefore make sure
+ * each service has unique instance id. Let's take the latest instance
+ * id of primary service and start iterate included services from this
+ * point.
+ */
+ instance_id = get_inst_id_of_prim_services(conn->device);
+ if (instance_id < 0)
+ goto failed;
+
+ for (; included; included = included->next) {
+ struct gatt_included *included_service = included->data;
+
+ incl = create_service(++instance_id, false,
+ included_service->uuid,
+ included_service);
+ if (!incl)
+ continue;
+
+ /*
+ * Lets keep included service on two queues.
+ * 1. on services queue together with primary service
+ * 2. on special queue inside primary service
+ */
+ queue_push_tail(service->included, incl);
+ queue_push_tail(conn->device->services, incl);
+ }
+
+ /*
+ * Notify upper layer about first included service.
+ * Android framework will iterate for next one.
+ */
+ incl = queue_peek_head(service->included);
+
+failed:
+ send_client_incl_service_notify(&service->id, incl, conn->id);
+}
+
+static void search_included_services(struct app_connection *conn,
+ struct service *service)
+{
+ struct get_included_data *data;
+ uint16_t start, end;
+
+ data = new0(struct get_included_data, 1);
+ data->prim = service;
+ data->conn = conn;
+
+ if (service->primary) {
+ start = service->prim.range.start;
+ end = service->prim.range.end;
+ } else {
+ start = service->incl.range.start;
+ end = service->incl.range.end;
+ }
+
+ gatt_find_included(conn->device->attrib, start, end, get_included_cb,
+ data);
+}
+
+static bool find_service(int32_t conn_id, struct element_id *service_id,
+ struct app_connection **connection,
+ struct service **service)
+{
+ struct service *srvc;
+ struct app_connection *conn;
+
+ conn = find_connection_by_id(conn_id);
+ if (!conn) {
+ error("gatt: conn_id=%d not found", conn_id);
+ return false;
+ }
+
+ srvc = queue_find(conn->device->services, match_srvc_by_element_id,
+ service_id);
+ if (!srvc) {
+ error("gatt: Service with inst_id: %d not found",
+ service_id->instance);
+ return false;
+ }
+
+ *connection = conn;
+ *service = srvc;
+
+ return true;
+}
+
+static void handle_client_get_included_service(const void *buf, uint16_t len)
+{
+ const struct hal_cmd_gatt_client_get_included_service *cmd = buf;
+ struct app_connection *conn;
+ struct service *prim_service;
+ struct service *incl_service = NULL;
+ struct element_id match_id;
+ struct element_id srvc_id;
+ uint8_t status;
+
+ DBG("");
+
+ hal_srvc_id_to_element_id(&cmd->srvc_id, &srvc_id);
+
+ if (len != sizeof(*cmd) +
+ (cmd->continuation ? sizeof(cmd->incl_srvc_id[0]) : 0)) {
+ error("Invalid get incl services size (%u bytes), terminating",
+ len);
+ raise(SIGTERM);
+ return;
+ }
+
+ hal_srvc_id_to_element_id(&cmd->srvc_id, &match_id);
+ if (!find_service(cmd->conn_id, &match_id, &conn, &prim_service)) {
+ status = HAL_STATUS_FAILED;
+ goto notify;
+ }
+
+ if (!prim_service->incl_search_done) {
+ search_included_services(conn, prim_service);
+ status = HAL_STATUS_SUCCESS;
+ goto reply;
+ }
+
+ /* Try to use cache here */
+ if (!cmd->continuation) {
+ incl_service = queue_peek_head(prim_service->included);
+ } else {
+ uint8_t inst_id = cmd->incl_srvc_id[0].inst_id;
+
+ incl_service = queue_find(prim_service->included,
+ match_srvc_by_higher_inst_id,
+ INT_TO_PTR(inst_id));
+ }
+
+ status = HAL_STATUS_SUCCESS;
+
+notify:
+ /*
+ * In case of error in handling request we need to send event with
+ * service id of cmd and gatt failure status.
+ */
+ send_client_incl_service_notify(&srvc_id, incl_service, cmd->conn_id);
+
+reply:
+ ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT,
+ HAL_OP_GATT_CLIENT_GET_INCLUDED_SERVICE, status);
+}
+
+static void send_client_char_notify(const struct hal_gatt_srvc_id *service,
+ const struct hal_gatt_gatt_id *charac,
+ int32_t char_prop, int32_t conn_id)
+{
+ struct hal_ev_gatt_client_get_characteristic ev;
+
+ ev.conn_id = conn_id;
+
+ if (charac) {
+ memcpy(&ev.char_id, charac, sizeof(struct hal_gatt_gatt_id));
+ ev.char_prop = char_prop;
+ ev.status = GATT_SUCCESS;
+ } else {
+ memset(&ev.char_id, 0, sizeof(struct hal_gatt_gatt_id));
+ ev.char_prop = 0;
+ ev.status = GATT_FAILURE;
+ }
+
+ memcpy(&ev.srvc_id, service, sizeof(struct hal_gatt_srvc_id));
+
+ ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT,
+ HAL_EV_GATT_CLIENT_GET_CHARACTERISTIC,
+ sizeof(ev), &ev);
+}
+
+static void convert_send_client_char_notify(const struct characteristic *ch,
+ int32_t conn_id,
+ const struct service *service)
+{
+ struct hal_gatt_srvc_id srvc;
+ struct hal_gatt_gatt_id charac;
+
+ element_id_to_hal_srvc_id(&service->id, service->primary, &srvc);
+
+ if (ch) {
+ element_id_to_hal_gatt_id(&ch->id, &charac);
+ send_client_char_notify(&srvc, &charac, ch->ch.properties,
+ conn_id);
+ } else {
+ send_client_char_notify(&srvc, NULL, 0, conn_id);
+ }
+}
+
+static void cache_all_srvc_chars(struct service *srvc, GSList *characteristics)
+{
+ uint16_t inst_id = 0;
+ bt_uuid_t uuid;
+
+ for (; characteristics; characteristics = characteristics->next) {
+ struct characteristic *ch;
+
+ ch = new0(struct characteristic, 1);
+ ch->descriptors = queue_new();
+
+ memcpy(&ch->ch, characteristics->data, sizeof(ch->ch));
+
+ bt_string_to_uuid(&uuid, ch->ch.uuid);
+ bt_uuid_to_uuid128(&uuid, &ch->id.uuid);
+
+ /*
+ * For now we increment inst_id and use it as characteristic
+ * handle
+ */
+ ch->id.instance = ++inst_id;
+
+ /* Store end handle to use later for descriptors discovery */
+ if (characteristics->next) {
+ struct gatt_char *next = characteristics->next->data;
+
+ ch->end_handle = next->handle - 1;
+ } else {
+ ch->end_handle = srvc->primary ? srvc->prim.range.end :
+ srvc->incl.range.end;
+ }
+
+ DBG("attr handle = 0x%04x, end handle = 0x%04x uuid: %s",
+ ch->ch.handle, ch->end_handle, ch->ch.uuid);
+
+ queue_push_tail(srvc->chars, ch);
+ }
+}
+
+struct discover_char_data {
+ int32_t conn_id;
+ struct service *service;
+};
+
+static void discover_char_cb(uint8_t status, GSList *characteristics,
+ void *user_data)
+{
+ struct discover_char_data *data = user_data;
+ struct service *srvc = data->service;
+
+ if (status) {
+ error("gatt: Failed to get characteristics: %s",
+ att_ecode2str(status));
+ convert_send_client_char_notify(NULL, data->conn_id, srvc);
+ goto done;
+ }
+
+ if (queue_isempty(srvc->chars))
+ cache_all_srvc_chars(srvc, characteristics);
+
+ convert_send_client_char_notify(queue_peek_head(srvc->chars),
+ data->conn_id, srvc);
+
+done:
+ free(data);
+}
+
+static void handle_client_get_characteristic(const void *buf, uint16_t len)
+{
+ const struct hal_cmd_gatt_client_get_characteristic *cmd = buf;
+ struct characteristic *ch;
+ struct element_id match_id;
+ struct app_connection *conn;
+ struct service *srvc;
+ uint8_t status;
+
+ DBG("");
+
+ if (len != sizeof(*cmd) + (cmd->continuation ? sizeof(cmd->char_id[0]) : 0)) {
+ error("Invalid get characteristic size (%u bytes), terminating",
+ len);
+ raise(SIGTERM);
+ return;
+ }
+
+ hal_srvc_id_to_element_id(&cmd->srvc_id, &match_id);
+ if (!find_service(cmd->conn_id, &match_id, &conn, &srvc)) {
+ status = HAL_STATUS_FAILED;
+ goto done;
+ }
+
+ /* Discover all characteristics for services if not cached yet */
+ if (queue_isempty(srvc->chars)) {
+ struct discover_char_data *cb_data;
+ struct att_range range;
+
+ cb_data = new0(struct discover_char_data, 1);
+ cb_data->service = srvc;
+ cb_data->conn_id = conn->id;
+
+ range = srvc->primary ? srvc->prim.range : srvc->incl.range;
+
+ if (!gatt_discover_char(conn->device->attrib, range.start,
+ range.end, NULL,
+ discover_char_cb, cb_data)) {
+ free(cb_data);
+
+ status = HAL_STATUS_FAILED;
+ goto done;
+ }
+
+ status = HAL_STATUS_SUCCESS;
+ goto done;
+ }
+
+ if (cmd->continuation)
+ ch = queue_find(srvc->chars, match_char_by_higher_inst_id,
+ INT_TO_PTR(cmd->char_id[0].inst_id));
+ else
+ ch = queue_peek_head(srvc->chars);
+
+ convert_send_client_char_notify(ch, conn->id, srvc);
+
+ status = HAL_STATUS_SUCCESS;
+
+done:
+ if (status != HAL_STATUS_SUCCESS)
+ send_client_char_notify(&cmd->srvc_id, NULL, 0, cmd->conn_id);
+
+ ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT,
+ HAL_OP_GATT_CLIENT_GET_CHARACTERISTIC, status);
+}
+
+static void send_client_descr_notify(int32_t status, int32_t conn_id,
+ bool primary,
+ const struct element_id *srvc,
+ const struct element_id *ch,
+ const struct element_id *opt_descr)
+{
+ struct hal_ev_gatt_client_get_descriptor ev;
+
+ memset(&ev, 0, sizeof(ev));
+
+ ev.status = status;
+ ev.conn_id = conn_id;
+
+ element_id_to_hal_srvc_id(srvc, primary, &ev.srvc_id);
+ element_id_to_hal_gatt_id(ch, &ev.char_id);
+
+ if (opt_descr)
+ element_id_to_hal_gatt_id(opt_descr, &ev.descr_id);
+
+ ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT,
+ HAL_EV_GATT_CLIENT_GET_DESCRIPTOR, sizeof(ev), &ev);
+}
+
+struct discover_desc_data {
+ struct app_connection *conn;
+ struct service *srvc;
+ struct characteristic *ch;
+};
+
+static void gatt_discover_desc_cb(guint8 status, GSList *descs,
+ gpointer user_data)
+{
+ struct discover_desc_data *data = user_data;
+ struct app_connection *conn = data->conn;
+ struct service *srvc = data->srvc;
+ struct characteristic *ch = data->ch;
+ struct descriptor *descr;
+ int i = 0;
+
+ if (status != 0) {
+ error("Discover all characteristic descriptors failed [%s]: %s",
+ ch->ch.uuid, att_ecode2str(status));
+ goto reply;
+ }
+
+ for ( ; descs; descs = descs->next) {
+ struct gatt_desc *desc = descs->data;
+ bt_uuid_t uuid;
+
+ descr = new0(struct descriptor, 1);
+
+ bt_string_to_uuid(&uuid, desc->uuid);
+ bt_uuid_to_uuid128(&uuid, &descr->id.uuid);
+
+ descr->id.instance = ++i;
+ descr->handle = desc->handle;
+
+ DBG("attr handle = 0x%04x, uuid: %s", desc->handle, desc->uuid);
+
+ queue_push_tail(ch->descriptors, descr);
+ }
+
+reply:
+ descr = queue_peek_head(ch->descriptors);
+
+ send_client_descr_notify(status ? GATT_FAILURE : GATT_SUCCESS, conn->id,
+ srvc->primary, &srvc->id, &ch->id,
+ descr ? &descr->id : NULL);
+
+ free(data);
+}
+
+static bool build_descr_cache(struct app_connection *conn, struct service *srvc,
+ struct characteristic *ch)
+{
+ struct discover_desc_data *cb_data;
+ uint16_t start, end;
+
+ /* Clip range to given characteristic */
+ start = ch->ch.value_handle + 1;
+ end = ch->end_handle;
+
+ /* If there are no descriptors, notify with fail status. */
+ if (start > end)
+ return false;
+
+ cb_data = new0(struct discover_desc_data, 1);
+ cb_data->conn = conn;
+ cb_data->srvc = srvc;
+ cb_data->ch = ch;
+
+ if (!gatt_discover_desc(conn->device->attrib, start, end, NULL,
+ gatt_discover_desc_cb, cb_data)) {
+ free(cb_data);
+ return false;
+ }
+
+ return true;
+}
+
+static void handle_client_get_descriptor(const void *buf, uint16_t len)
+{
+ const struct hal_cmd_gatt_client_get_descriptor *cmd = buf;
+ struct descriptor *descr = NULL;
+ struct characteristic *ch;
+ struct service *srvc;
+ struct element_id srvc_id;
+ struct element_id char_id;
+ struct app_connection *conn;
+ int32_t conn_id;
+ uint8_t primary;
+ uint8_t status;
+
+ DBG("");
+
+ if (len != sizeof(*cmd) +
+ (cmd->continuation ? sizeof(cmd->descr_id[0]) : 0)) {
+ error("gatt: Invalid get descr command (%u bytes), terminating",
+ len);
+
+ raise(SIGTERM);
+ return;
+ }
+
+ conn_id = cmd->conn_id;
+ primary = cmd->srvc_id.is_primary;
+
+ hal_srvc_id_to_element_id(&cmd->srvc_id, &srvc_id);
+ hal_gatt_id_to_element_id(&cmd->char_id, &char_id);
+
+ if (!find_service(conn_id, &srvc_id, &conn, &srvc)) {
+ error("gatt: Get descr. could not find service");
+
+ status = HAL_STATUS_FAILED;
+ goto failed;
+ }
+
+ ch = queue_find(srvc->chars, match_char_by_element_id, &char_id);
+ if (!ch) {
+ error("gatt: Get descr. could not find characteristic");
+
+ status = HAL_STATUS_FAILED;
+ goto failed;
+ }
+
+ if (queue_isempty(ch->descriptors)) {
+ if (build_descr_cache(conn, srvc, ch)) {
+ ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT,
+ HAL_OP_GATT_CLIENT_GET_DESCRIPTOR,
+ HAL_STATUS_SUCCESS);
+ return;
+ }
+ }
+
+ status = HAL_STATUS_SUCCESS;
+
+ /* Send from cache */
+ if (cmd->continuation)
+ descr = queue_find(ch->descriptors,
+ match_descr_by_higher_inst_id,
+ INT_TO_PTR(cmd->descr_id[0].inst_id));
+ else
+ descr = queue_peek_head(ch->descriptors);
+
+failed:
+ send_client_descr_notify(descr ? GATT_SUCCESS : GATT_FAILURE, conn_id,
+ primary, &srvc_id, &char_id,
+ &descr->id);
+
+ ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT,
+ HAL_OP_GATT_CLIENT_GET_DESCRIPTOR, status);
+}
+
+struct char_op_data {
+ int32_t conn_id;
+ const struct element_id *srvc_id;
+ const struct element_id *char_id;
+ uint8_t primary;
+};
+
+static struct char_op_data *create_char_op_data(int32_t conn_id,
+ const struct element_id *s_id,
+ const struct element_id *ch_id,
+ bool primary)
+{
+ struct char_op_data *d;
+
+ d = new0(struct char_op_data, 1);
+ d->conn_id = conn_id;
+ d->srvc_id = s_id;
+ d->char_id = ch_id;
+ d->primary = primary;
+
+ return d;
+}
+
+static void send_client_read_char_notify(int32_t status, const uint8_t *pdu,
+ uint16_t len, int32_t conn_id,
+ const struct element_id *s_id,
+ const struct element_id *ch_id,
+ uint8_t primary)
+{
+ uint8_t buf[IPC_MTU];
+ struct hal_ev_gatt_client_read_characteristic *ev = (void *) buf;
+ ssize_t vlen;
+
+ memset(buf, 0, sizeof(buf));
+
+ ev->conn_id = conn_id;
+ ev->status = status;
+ ev->data.status = status;
+
+ element_id_to_hal_srvc_id(s_id, primary, &ev->data.srvc_id);
+ element_id_to_hal_gatt_id(ch_id, &ev->data.char_id);
+
+ if (status == 0 && pdu) {
+ vlen = dec_read_resp(pdu, len, ev->data.value, sizeof(buf));
+ if (vlen < 0) {
+ error("gatt: Protocol error");
+ ev->status = GATT_FAILURE;
+ } else {
+ ev->data.len = vlen;
+ }
+ }
+
+ ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT,
+ HAL_EV_GATT_CLIENT_READ_CHARACTERISTIC,
+ sizeof(*ev) + ev->data.len, ev);
+}
+
+static void read_char_cb(guint8 status, const guint8 *pdu, guint16 len,
+ gpointer user_data)
+{
+ struct char_op_data *data = user_data;
+
+ send_client_read_char_notify(status, pdu, len, data->conn_id,
+ data->srvc_id, data->char_id,
+ data->primary);
+
+ free(data);
+}
+
+static int get_cid(struct gatt_device *dev)
+{
+ GIOChannel *io;
+ uint16_t cid;
+
+ io = g_attrib_get_channel(dev->attrib);
+
+ if (!bt_io_get(io, NULL, BT_IO_OPT_CID, &cid, BT_IO_OPT_INVALID)) {
+ error("gatt: Failed to get CID");
+ return -1;
+ }
+
+ return cid;
+}
+
+static int get_sec_level(struct gatt_device *dev)
+{
+ GIOChannel *io;
+ int sec_level;
+
+ io = g_attrib_get_channel(dev->attrib);
+
+ if (!bt_io_get(io, NULL, BT_IO_OPT_SEC_LEVEL, &sec_level,
+ BT_IO_OPT_INVALID)) {
+ error("gatt: Failed to get sec_level");
+ return -1;
+ }
+
+ return sec_level;
+}
+
+static bool set_security(struct gatt_device *device, int req_sec_level)
+{
+ int sec_level;
+ GError *gerr = NULL;
+ GIOChannel *io;
+
+ sec_level = get_sec_level(device);
+ if (sec_level < 0)
+ return false;
+
+ if (req_sec_level <= sec_level)
+ return true;
+
+ io = g_attrib_get_channel(device->attrib);
+ if (!io)
+ return false;
+
+ bt_io_set(io, &gerr, BT_IO_OPT_SEC_LEVEL, req_sec_level,
+ BT_IO_OPT_INVALID);
+ if (gerr) {
+ error("gatt: Failed to set security level: %s", gerr->message);
+ g_error_free(gerr);
+ return false;
+ }
+
+ return true;
+}
+
+bool bt_gatt_set_security(const bdaddr_t *bdaddr, int sec_level)
+{
+ struct gatt_device *device;
+
+ device = find_device_by_addr(bdaddr);
+ if (!device)
+ return false;
+
+ return set_security(device, sec_level);
+}
+
+static bool set_auth_type(struct gatt_device *device, int auth_type)
+{
+ int sec_level;
+
+ switch (auth_type) {
+ case HAL_GATT_AUTHENTICATION_MITM:
+ sec_level = BT_SECURITY_HIGH;
+ break;
+ case HAL_GATT_AUTHENTICATION_NO_MITM:
+ sec_level = BT_SECURITY_MEDIUM;
+ break;
+ case HAL_GATT_AUTHENTICATION_NONE:
+ sec_level = BT_SECURITY_LOW;
+ break;
+ default:
+ error("gatt: Invalid auth_type value: %d", auth_type);
+ return false;
+ }
+
+ return set_security(device, sec_level);
+}
+
+static void handle_client_read_characteristic(const void *buf, uint16_t len)
+{
+ const struct hal_cmd_gatt_client_read_characteristic *cmd = buf;
+ struct char_op_data *cb_data;
+ struct characteristic *ch;
+ struct app_connection *conn;
+ struct service *srvc;
+ struct element_id srvc_id;
+ struct element_id char_id;
+ uint8_t status;
+
+ DBG("");
+
+ /* TODO authorization needs to be handled */
+
+ hal_srvc_id_to_element_id(&cmd->srvc_id, &srvc_id);
+ hal_gatt_id_to_element_id(&cmd->char_id, &char_id);
+
+ if (!find_service(cmd->conn_id, &srvc_id, &conn, &srvc)) {
+ status = HAL_STATUS_FAILED;
+ goto failed;
+ }
+
+ /* search characteristics by element id */
+ ch = queue_find(srvc->chars, match_char_by_element_id, &char_id);
+ if (!ch) {
+ error("gatt: Characteristic with inst_id: %d not found",
+ cmd->char_id.inst_id);
+ status = HAL_STATUS_FAILED;
+ goto failed;
+ }
+
+ cb_data = create_char_op_data(cmd->conn_id, &srvc->id, &ch->id,
+ cmd->srvc_id.is_primary);
+
+ if (!set_auth_type(conn->device, cmd->auth_req)) {
+ error("gatt: Failed to set security %d", cmd->auth_req);
+ status = HAL_STATUS_FAILED;
+ free(cb_data);
+ goto failed;
+ }
+
+ if (!gatt_read_char(conn->device->attrib, ch->ch.value_handle,
+ read_char_cb, cb_data)) {
+ error("gatt: Cannot read characteristic with inst_id: %d",
+ cmd->char_id.inst_id);
+ status = HAL_STATUS_FAILED;
+ free(cb_data);
+ goto failed;
+ }
+
+ status = HAL_STATUS_SUCCESS;
+
+failed:
+ ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT,
+ HAL_OP_GATT_CLIENT_READ_CHARACTERISTIC, status);
+
+ /*
+ * We should send notification with service, characteristic id in case
+ * of errors.
+ */
+ if (status != HAL_STATUS_SUCCESS)
+ send_client_read_char_notify(GATT_FAILURE, NULL, 0,
+ cmd->conn_id, &srvc_id,
+ &char_id,
+ cmd->srvc_id.is_primary);
+}
+
+static void send_client_write_char_notify(int32_t status, int32_t conn_id,
+ const struct element_id *srvc_id,
+ const struct element_id *char_id,
+ uint8_t primary)
+{
+ struct hal_ev_gatt_client_write_characteristic ev;
+
+ memset(&ev, 0, sizeof(ev));
+
+ ev.conn_id = conn_id;
+ ev.status = status;
+ ev.data.status = status;
+
+ element_id_to_hal_srvc_id(srvc_id, primary, &ev.data.srvc_id);
+ element_id_to_hal_gatt_id(char_id, &ev.data.char_id);
+
+ ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT,
+ HAL_EV_GATT_CLIENT_WRITE_CHARACTERISTIC,
+ sizeof(ev), &ev);
+}
+
+static void write_char_cb(guint8 status, const guint8 *pdu, guint16 len,
+ gpointer user_data)
+{
+ struct char_op_data *data = user_data;
+
+ send_client_write_char_notify(status, data->conn_id, data->srvc_id,
+ data->char_id, data->primary);
+
+ free(data);
+}
+
+static guint signed_write_cmd(struct gatt_device *dev, uint16_t handle,
+ const uint8_t *value, uint16_t vlen)
+{
+ uint8_t csrk[16];
+ uint32_t sign_cnt;
+ guint res;
+
+ memset(csrk, 0, 16);
+
+ if (!bt_get_csrk(&dev->bdaddr, true, csrk, &sign_cnt, NULL)) {
+ error("gatt: Could not get csrk key");
+ return 0;
+ }
+
+ res = gatt_signed_write_cmd(dev->attrib, handle, value, vlen, crypto,
+ csrk, sign_cnt, NULL, NULL);
+ if (!res) {
+ error("gatt: Signed write command failed");
+ return 0;
+ }
+
+ bt_update_sign_counter(&dev->bdaddr, true, ++sign_cnt);
+
+ return res;
+}
+
+static void handle_client_write_characteristic(const void *buf, uint16_t len)
+{
+ const struct hal_cmd_gatt_client_write_characteristic *cmd = buf;
+ struct char_op_data *cb_data = NULL;
+ struct characteristic *ch;
+ struct app_connection *conn;
+ struct service *srvc;
+ struct element_id srvc_id;
+ struct element_id char_id;
+ uint8_t status;
+ guint res;
+
+ DBG("");
+
+ if (len != sizeof(*cmd) + cmd->len) {
+ error("Invalid write char size (%u bytes), terminating", len);
+ raise(SIGTERM);
+ return;
+ }
+
+ hal_srvc_id_to_element_id(&cmd->srvc_id, &srvc_id);
+ hal_gatt_id_to_element_id(&cmd->char_id, &char_id);
+
+ if (!find_service(cmd->conn_id, &srvc_id, &conn, &srvc)) {
+ status = HAL_STATUS_FAILED;
+ goto failed;
+ }
+
+ /* search characteristics by instance id */
+ ch = queue_find(srvc->chars, match_char_by_element_id, &char_id);
+ if (!ch) {
+ error("gatt: Characteristic with inst_id: %d not found",
+ cmd->char_id.inst_id);
+ status = HAL_STATUS_FAILED;
+ goto failed;
+ }
+
+ if (cmd->write_type == GATT_WRITE_TYPE_PREPARE ||
+ cmd->write_type == GATT_WRITE_TYPE_DEFAULT) {
+ cb_data = create_char_op_data(cmd->conn_id, &srvc->id, &ch->id,
+ cmd->srvc_id.is_primary);
+ }
+
+ if (!set_auth_type(conn->device, cmd->auth_req)) {
+ error("gatt: Failed to set security %d", cmd->auth_req);
+ status = HAL_STATUS_FAILED;
+ goto failed;
+ }
+
+ switch (cmd->write_type) {
+ case GATT_WRITE_TYPE_NO_RESPONSE:
+ res = gatt_write_cmd(conn->device->attrib, ch->ch.value_handle,
+ cmd->value, cmd->len,
+ NULL, NULL);
+ break;
+ case GATT_WRITE_TYPE_PREPARE:
+ res = gatt_reliable_write_char(conn->device->attrib,
+ ch->ch.value_handle,
+ cmd->value, cmd->len,
+ write_char_cb, cb_data);
+ break;
+ case GATT_WRITE_TYPE_DEFAULT:
+ res = gatt_write_char(conn->device->attrib, ch->ch.value_handle,
+ cmd->value, cmd->len,
+ write_char_cb, cb_data);
+ break;
+ case GATT_WRITE_TYPE_SIGNED:
+ if (get_cid(conn->device) != ATT_CID) {
+ error("gatt: Cannot write signed on BR/EDR bearer");
+ status = HAL_STATUS_FAILED;
+ goto failed;
+ }
+
+ if (get_sec_level(conn->device) > BT_SECURITY_LOW)
+ res = gatt_write_cmd(conn->device->attrib,
+ ch->ch.value_handle, cmd->value,
+ cmd->len, NULL, NULL);
+ else
+ res = signed_write_cmd(conn->device,
+ ch->ch.value_handle, cmd->value,
+ cmd->len);
+ break;
+ default:
+ error("gatt: Write type %d unsupported", cmd->write_type);
+ status = HAL_STATUS_UNSUPPORTED;
+ goto failed;
+ }
+
+ if (!res) {
+ error("gatt: Cannot write char. with inst_id: %d",
+ cmd->char_id.inst_id);
+ status = HAL_STATUS_FAILED;
+ goto failed;
+ }
+
+ status = HAL_STATUS_SUCCESS;
+
+failed:
+ ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT,
+ HAL_OP_GATT_CLIENT_WRITE_CHARACTERISTIC, status);
+
+ /*
+ * We should send notification with service, characteristic id in case
+ * of error and write with no response
+ */
+ if (status != HAL_STATUS_SUCCESS ||
+ cmd->write_type == GATT_WRITE_TYPE_NO_RESPONSE ||
+ cmd->write_type == GATT_WRITE_TYPE_SIGNED) {
+ int32_t gatt_status = (status == HAL_STATUS_SUCCESS) ?
+ GATT_SUCCESS : GATT_FAILURE;
+
+ send_client_write_char_notify(gatt_status, cmd->conn_id,
+ &srvc_id, &char_id,
+ cmd->srvc_id.is_primary);
+ free(cb_data);
+ }
+}
+
+static void send_client_descr_read_notify(int32_t status, const uint8_t *pdu,
+ guint16 len, int32_t conn_id,
+ const struct element_id *srvc,
+ const struct element_id *ch,
+ const struct element_id *descr,
+ uint8_t primary)
+{
+ uint8_t buf[IPC_MTU];
+ struct hal_ev_gatt_client_read_descriptor *ev = (void *) buf;
+
+ memset(buf, 0, sizeof(buf));
+
+ ev->status = status;
+ ev->conn_id = conn_id;
+ ev->data.status = ev->status;
+
+ element_id_to_hal_srvc_id(srvc, primary, &ev->data.srvc_id);
+ element_id_to_hal_gatt_id(ch, &ev->data.char_id);
+ element_id_to_hal_gatt_id(descr, &ev->data.descr_id);
+
+ if (status == 0 && pdu) {
+ ssize_t ret;
+
+ ret = dec_read_resp(pdu, len, ev->data.value,
+ GATT_MAX_ATTR_LEN);
+ if (ret < 0) {
+ error("gatt: Protocol error");
+ ev->status = GATT_FAILURE;
+ } else {
+ ev->data.len = ret;
+ }
+ }
+
+ ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT,
+ HAL_EV_GATT_CLIENT_READ_DESCRIPTOR,
+ sizeof(*ev) + ev->data.len, ev);
+}
+
+struct desc_data {
+ int32_t conn_id;
+ const struct element_id *srvc_id;
+ const struct element_id *char_id;
+ const struct element_id *descr_id;
+ uint8_t primary;
+};
+
+static void read_desc_cb(guint8 status, const guint8 *pdu, guint16 len,
+ gpointer user_data)
+{
+ struct desc_data *cb_data = user_data;
+
+ if (status != 0)
+ error("gatt: Discover all char descriptors failed: %s",
+ att_ecode2str(status));
+
+ send_client_descr_read_notify(status, pdu, len, cb_data->conn_id,
+ cb_data->srvc_id, cb_data->char_id,
+ cb_data->descr_id, cb_data->primary);
+
+ free(cb_data);
+}
+
+static struct desc_data *create_desc_data(int32_t conn_id,
+ const struct element_id *s_id,
+ const struct element_id *ch_id,
+ const struct element_id *d_id,
+ uint8_t primary)
+{
+ struct desc_data *d;
+
+ d = new0(struct desc_data, 1);
+ d->conn_id = conn_id;
+ d->srvc_id = s_id;
+ d->char_id = ch_id;
+ d->descr_id = d_id;
+ d->primary = primary;
+
+ return d;
+}
+
+static void handle_client_read_descriptor(const void *buf, uint16_t len)
+{
+ const struct hal_cmd_gatt_client_read_descriptor *cmd = buf;
+ struct desc_data *cb_data;
+ struct characteristic *ch;
+ struct descriptor *descr;
+ struct service *srvc;
+ struct element_id char_id;
+ struct element_id descr_id;
+ struct element_id srvc_id;
+ struct app_connection *conn;
+ int32_t conn_id = 0;
+ uint8_t primary;
+ uint8_t status;
+
+ DBG("");
+
+ conn_id = cmd->conn_id;
+ primary = cmd->srvc_id.is_primary;
+
+ hal_srvc_id_to_element_id(&cmd->srvc_id, &srvc_id);
+ hal_gatt_id_to_element_id(&cmd->char_id, &char_id);
+ hal_gatt_id_to_element_id(&cmd->descr_id, &descr_id);
+
+ if (!find_service(conn_id, &srvc_id, &conn, &srvc)) {
+ error("gatt: Read descr. could not find service");
+
+ status = HAL_STATUS_FAILED;
+ goto failed;
+ }
+
+ ch = queue_find(srvc->chars, match_char_by_element_id, &char_id);
+ if (!ch) {
+ error("gatt: Read descr. could not find characteristic");
+
+ status = HAL_STATUS_FAILED;
+ goto failed;
+ }
+
+ descr = queue_find(ch->descriptors, match_descr_by_element_id,
+ &descr_id);
+ if (!descr) {
+ error("gatt: Read descr. could not find descriptor");
+
+ status = HAL_STATUS_FAILED;
+ goto failed;
+ }
+
+ cb_data = create_desc_data(conn_id, &srvc->id, &ch->id, &descr->id,
+ primary);
+
+ if (!set_auth_type(conn->device, cmd->auth_req)) {
+ error("gatt: Failed to set security %d", cmd->auth_req);
+ status = HAL_STATUS_FAILED;
+ free(cb_data);
+ goto failed;
+ }
+
+ if (!gatt_read_char(conn->device->attrib, descr->handle, read_desc_cb,
+ cb_data)) {
+ free(cb_data);
+
+ status = HAL_STATUS_FAILED;
+ goto failed;
+ }
+
+ status = HAL_STATUS_SUCCESS;
+
+failed:
+ if (status != HAL_STATUS_SUCCESS)
+ send_client_descr_read_notify(GATT_FAILURE, NULL, 0, conn_id,
+ &srvc_id, &char_id, &descr_id,
+ primary);
+
+ ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT,
+ HAL_OP_GATT_CLIENT_READ_DESCRIPTOR, status);
+}
+
+static void send_client_descr_write_notify(int32_t status, int32_t conn_id,
+ const struct element_id *srvc,
+ const struct element_id *ch,
+ const struct element_id *descr,
+ uint8_t primary) {
+ uint8_t buf[IPC_MTU];
+ struct hal_ev_gatt_client_write_descriptor *ev = (void *) buf;
+
+ memset(buf, 0, sizeof(buf));
+
+ ev->status = status;
+ ev->conn_id = conn_id;
+
+ element_id_to_hal_srvc_id(srvc, primary, &ev->data.srvc_id);
+ element_id_to_hal_gatt_id(ch, &ev->data.char_id);
+ element_id_to_hal_gatt_id(descr, &ev->data.descr_id);
+ ev->data.status = status;
+
+ ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT,
+ HAL_EV_GATT_CLIENT_WRITE_DESCRIPTOR,
+ sizeof(*ev), ev);
+}
+
+static void write_descr_cb(guint8 status, const guint8 *pdu, guint16 len,
+ gpointer user_data)
+{
+ struct desc_data *cb_data = user_data;
+
+ if (status)
+ error("gatt: Write descriptors failed: %s",
+ att_ecode2str(status));
+
+ send_client_descr_write_notify(status, cb_data->conn_id,
+ cb_data->srvc_id, cb_data->char_id,
+ cb_data->descr_id, cb_data->primary);
+
+ free(cb_data);
+}
+
+static void handle_client_write_descriptor(const void *buf, uint16_t len)
+{
+ const struct hal_cmd_gatt_client_write_descriptor *cmd = buf;
+ struct desc_data *cb_data = NULL;
+ struct characteristic *ch;
+ struct descriptor *descr;
+ struct service *srvc;
+ struct element_id srvc_id;
+ struct element_id char_id;
+ struct element_id descr_id;
+ struct app_connection *conn;
+ int32_t conn_id;
+ uint8_t primary;
+ uint8_t status;
+ guint res;
+
+ DBG("");
+
+ if (len != sizeof(*cmd) + cmd->len) {
+ error("Invalid write desriptor command (%u bytes), terminating",
+ len);
+ raise(SIGTERM);
+ return;
+ }
+
+ primary = cmd->srvc_id.is_primary;
+ conn_id = cmd->conn_id;
+
+ hal_srvc_id_to_element_id(&cmd->srvc_id, &srvc_id);
+ hal_gatt_id_to_element_id(&cmd->char_id, &char_id);
+ hal_gatt_id_to_element_id(&cmd->descr_id, &descr_id);
+
+ if (!find_service(cmd->conn_id, &srvc_id, &conn, &srvc)) {
+ error("gatt: Write descr. could not find service");
+
+ status = HAL_STATUS_FAILED;
+ goto failed;
+ }
+
+ ch = queue_find(srvc->chars, match_char_by_element_id, &char_id);
+ if (!ch) {
+ error("gatt: Write descr. could not find characteristic");
+
+ status = HAL_STATUS_FAILED;
+ goto failed;
+ }
+
+ descr = queue_find(ch->descriptors, match_descr_by_element_id,
+ &descr_id);
+ if (!descr) {
+ error("gatt: Write descr. could not find descriptor");
+
+ status = HAL_STATUS_FAILED;
+ goto failed;
+ }
+
+ if (cmd->write_type != GATT_WRITE_TYPE_NO_RESPONSE)
+ cb_data = create_desc_data(conn_id, &srvc->id, &ch->id,
+ &descr->id, primary);
+
+ if (!set_auth_type(conn->device, cmd->auth_req)) {
+ error("gatt: Failed to set security %d", cmd->auth_req);
+ status = HAL_STATUS_FAILED;
+ goto failed;
+ }
+
+ switch (cmd->write_type) {
+ case GATT_WRITE_TYPE_NO_RESPONSE:
+ res = gatt_write_cmd(conn->device->attrib, descr->handle,
+ cmd->value, cmd->len, NULL , NULL);
+ break;
+ case GATT_WRITE_TYPE_PREPARE:
+ res = gatt_reliable_write_char(conn->device->attrib,
+ descr->handle, cmd->value,
+ cmd->len, write_descr_cb,
+ cb_data);
+ break;
+ case GATT_WRITE_TYPE_DEFAULT:
+ res = gatt_write_char(conn->device->attrib, descr->handle,
+ cmd->value, cmd->len,
+ write_descr_cb, cb_data);
+ break;
+ default:
+ error("gatt: Write type %d unsupported", cmd->write_type);
+ status = HAL_STATUS_UNSUPPORTED;
+ goto failed;
+ }
+
+ if (!res) {
+ error("gatt: Write desc, could not write desc");
+ status = HAL_STATUS_FAILED;
+ goto failed;
+ }
+
+ status = HAL_STATUS_SUCCESS;
+
+failed:
+ if (status != HAL_STATUS_SUCCESS ||
+ cmd->write_type == GATT_WRITE_TYPE_NO_RESPONSE) {
+ int32_t gatt_status = (status == HAL_STATUS_SUCCESS) ?
+ GATT_SUCCESS : GATT_FAILURE;
+
+ send_client_descr_write_notify(gatt_status, conn_id, &srvc_id,
+ &char_id, &descr_id, primary);
+ free(cb_data);
+ }
+
+ ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT,
+ HAL_OP_GATT_CLIENT_WRITE_DESCRIPTOR, status);
+}
+
+static void send_client_write_execute_notify(int32_t id, int32_t status)
+{
+ struct hal_ev_gatt_client_exec_write ev;
+
+ ev.conn_id = id;
+ ev.status = status;
+
+ ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT,
+ HAL_EV_GATT_CLIENT_EXEC_WRITE,
+ sizeof(ev), &ev);
+}
+
+static void write_execute_cb(guint8 status, const guint8 *pdu, guint16 len,
+ gpointer user_data)
+{
+ send_client_write_execute_notify(PTR_TO_INT(user_data), status);
+}
+
+static void handle_client_execute_write(const void *buf, uint16_t len)
+{
+ const struct hal_cmd_gatt_client_execute_write *cmd = buf;
+ struct app_connection *conn;
+ uint8_t status;
+ uint8_t flags;
+
+ DBG("");
+
+ conn = find_connection_by_id(cmd->conn_id);
+ if (!conn) {
+ status = HAL_STATUS_FAILED;
+ goto reply;
+ }
+
+ flags = cmd->execute ? ATT_WRITE_ALL_PREP_WRITES :
+ ATT_CANCEL_ALL_PREP_WRITES;
+
+ if (!gatt_execute_write(conn->device->attrib, flags, write_execute_cb,
+ INT_TO_PTR(cmd->conn_id))) {
+ error("gatt: Could not send execute write");
+ status = HAL_STATUS_FAILED;
+ goto reply;
+ }
+
+ status = HAL_STATUS_SUCCESS;
+reply:
+ ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT,
+ HAL_OP_GATT_CLIENT_EXECUTE_WRITE, status);
+
+ /* In case of early error send also notification.*/
+ if (status != HAL_STATUS_SUCCESS)
+ send_client_write_execute_notify(cmd->conn_id, GATT_FAILURE);
+}
+
+static void handle_notification(const uint8_t *pdu, uint16_t len,
+ gpointer user_data)
+{
+ uint8_t buf[IPC_MTU];
+ struct hal_ev_gatt_client_notify *ev = (void *) buf;
+ struct notification_data *notification = user_data;
+ uint8_t data_offset = sizeof(uint8_t) + sizeof(uint16_t);
+
+ if (len < data_offset)
+ return;
+
+ memcpy(&ev->char_id, ¬ification->ch, sizeof(ev->char_id));
+ memcpy(&ev->srvc_id, ¬ification->service, sizeof(ev->srvc_id));
+ bdaddr2android(¬ification->conn->device->bdaddr, &ev->bda);
+ ev->conn_id = notification->conn->id;
+ ev->is_notify = pdu[0] == ATT_OP_HANDLE_NOTIFY;
+
+ /* We have to cut opcode and handle from data */
+ ev->len = len - data_offset;
+ memcpy(ev->value, pdu + data_offset, len - data_offset);
+
+ ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT, HAL_EV_GATT_CLIENT_NOTIFY,
+ sizeof(*ev) + ev->len, ev);
+}
+
+static void send_register_for_notification_ev(int32_t id, int32_t registered,
+ int32_t status,
+ const struct hal_gatt_srvc_id *srvc,
+ const struct hal_gatt_gatt_id *ch)
+{
+ struct hal_ev_gatt_client_reg_for_notif ev;
+
+ ev.conn_id = id;
+ ev.status = status;
+ ev.registered = registered;
+ memcpy(&ev.srvc_id, srvc, sizeof(ev.srvc_id));
+ memcpy(&ev.char_id, ch, sizeof(ev.char_id));
+
+ ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT,
+ HAL_EV_GATT_CLIENT_REGISTER_FOR_NOTIF, sizeof(ev), &ev);
+}
+
+static void handle_client_register_for_notification(const void *buf,
+ uint16_t len)
+{
+ const struct hal_cmd_gatt_client_register_for_notification *cmd = buf;
+ struct notification_data *notification;
+ struct characteristic *c;
+ struct element_id match_id;
+ struct app_connection *conn;
+ int32_t conn_id = 0;
+ struct service *service;
+ uint8_t status;
+ int32_t gatt_status;
+ bdaddr_t addr;
+
+ DBG("");
+
+ android2bdaddr(&cmd->bdaddr, &addr);
+
+ conn = find_conn(&addr, cmd->client_if);
+ if (!conn) {
+ status = HAL_STATUS_FAILED;
+ goto failed;
+ }
+
+ conn_id = conn->id;
+
+ hal_srvc_id_to_element_id(&cmd->srvc_id, &match_id);
+ service = queue_find(conn->device->services, match_srvc_by_element_id,
+ &match_id);
+ if (!service) {
+ status = HAL_STATUS_FAILED;
+ goto failed;
+ }
+
+ hal_gatt_id_to_element_id(&cmd->char_id, &match_id);
+ c = queue_find(service->chars, match_char_by_element_id, &match_id);
+ if (!c) {
+ status = HAL_STATUS_FAILED;
+ goto failed;
+ }
+
+ notification = new0(struct notification_data, 1);
+
+ memcpy(¬ification->ch, &cmd->char_id, sizeof(notification->ch));
+ memcpy(¬ification->service, &cmd->srvc_id,
+ sizeof(notification->service));
+ notification->conn = conn;
+
+ if (queue_find(conn->app->notifications, match_notification,
+ notification)) {
+ free(notification);
+ status = HAL_STATUS_SUCCESS;
+ goto failed;
+ }
+
+ notification->notif_id = g_attrib_register(conn->device->attrib,
+ ATT_OP_HANDLE_NOTIFY,
+ c->ch.value_handle,
+ handle_notification,
+ notification,
+ destroy_notification);
+ if (!notification->notif_id) {
+ free(notification);
+ status = HAL_STATUS_FAILED;
+ goto failed;
+ }
+
+ notification->ind_id = g_attrib_register(conn->device->attrib,
+ ATT_OP_HANDLE_IND,
+ c->ch.value_handle,
+ handle_notification,
+ notification,
+ destroy_notification);
+ if (!notification->ind_id) {
+ g_attrib_unregister(conn->device->attrib,
+ notification->notif_id);
+ free(notification);
+ status = HAL_STATUS_FAILED;
+ goto failed;
+ }
+
+ /*
+ * Because same data - notification - is shared by two handlers, we
+ * introduce ref counter to be sure that data can be freed with no risk.
+ * Counter is decremented in destroy_notification.
+ */
+ notification->ref = 2;
+
+ queue_push_tail(conn->app->notifications, notification);
+
+ status = HAL_STATUS_SUCCESS;
+
+failed:
+ gatt_status = status ? GATT_FAILURE : GATT_SUCCESS;
+ send_register_for_notification_ev(conn_id, 1, gatt_status,
+ &cmd->srvc_id, &cmd->char_id);
+ ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT,
+ HAL_OP_GATT_CLIENT_REGISTER_FOR_NOTIFICATION, status);
+}
+
+static void handle_client_deregister_for_notification(const void *buf,
+ uint16_t len)
+{
+ const struct hal_cmd_gatt_client_deregister_for_notification *cmd = buf;
+ struct notification_data *notification, notif;
+ struct app_connection *conn;
+ int32_t conn_id = 0;
+ uint8_t status;
+ int32_t gatt_status;
+ bdaddr_t addr;
+
+ DBG("");
+
+ android2bdaddr(&cmd->bdaddr, &addr);
+
+ conn = find_conn(&addr, cmd->client_if);
+ if (!conn) {
+ status = HAL_STATUS_FAILED;
+ goto failed;
+ }
+
+ conn_id = conn->id;
+
+ memcpy(¬if.ch, &cmd->char_id, sizeof(notif.ch));
+ memcpy(¬if.service, &cmd->srvc_id, sizeof(notif.service));
+ notif.conn = conn;
+
+ notification = queue_find(conn->app->notifications,
+ match_notification, ¬if);
+ if (!notification) {
+ status = HAL_STATUS_FAILED;
+ goto failed;
+ }
+
+ unregister_notification(notification);
+
+ status = HAL_STATUS_SUCCESS;
+
+failed:
+ gatt_status = status ? GATT_FAILURE : GATT_SUCCESS;
+ send_register_for_notification_ev(conn_id, 0, gatt_status,
+ &cmd->srvc_id, &cmd->char_id);
+
+ ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT,
+ HAL_OP_GATT_CLIENT_DEREGISTER_FOR_NOTIFICATION, status);
+}
+
+static void send_client_remote_rssi_notify(int32_t client_if,
+ const bdaddr_t *addr,
+ int32_t rssi, int32_t status)
+{
+ struct hal_ev_gatt_client_read_remote_rssi ev;
+
+ ev.client_if = client_if;
+ bdaddr2android(addr, &ev.address);
+ ev.rssi = rssi;
+ ev.status = status;
+
+ ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT,
+ HAL_EV_GATT_CLIENT_READ_REMOTE_RSSI, sizeof(ev), &ev);
+}
+
+static void read_remote_rssi_cb(uint8_t status, const bdaddr_t *addr,
+ int8_t rssi, void *user_data)
+{
+ int32_t client_if = PTR_TO_INT(user_data);
+ int32_t gatt_status = status ? GATT_FAILURE : GATT_SUCCESS;
+
+ send_client_remote_rssi_notify(client_if, addr, rssi, gatt_status);
+}
+
+static void handle_client_read_remote_rssi(const void *buf, uint16_t len)
+{
+ const struct hal_cmd_gatt_client_read_remote_rssi *cmd = buf;
+ uint8_t status;
+ bdaddr_t bdaddr;
+
+ DBG("");
+
+ if (!find_app_by_id(cmd->client_if)) {
+ status = HAL_STATUS_FAILED;
+ goto failed;
+ }
+
+ android2bdaddr(cmd->bdaddr, &bdaddr);
+ if (!bt_read_device_rssi(&bdaddr, read_remote_rssi_cb,
+ INT_TO_PTR(cmd->client_if))) {
+ error("gatt: Could not read RSSI");
+ status = HAL_STATUS_FAILED;
+ goto failed;
+ }
+
+ status = HAL_STATUS_SUCCESS;
+
+failed:
+ ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT,
+ HAL_OP_GATT_CLIENT_READ_REMOTE_RSSI, status);
+
+ if (status != HAL_STATUS_SUCCESS)
+ send_client_remote_rssi_notify(cmd->client_if, &bdaddr, 0,
+ GATT_FAILURE);
+}
+
+static void handle_client_get_device_type(const void *buf, uint16_t len)
+{
+ const struct hal_cmd_gatt_client_get_device_type *cmd = buf;
+ struct hal_rsp_gatt_client_get_device_type rsp;
+ bdaddr_t bdaddr;
+
+ DBG("");
+
+ android2bdaddr(cmd->bdaddr, &bdaddr);
+
+ rsp.type = bt_get_device_android_type(&bdaddr);
+
+ ipc_send_rsp_full(hal_ipc, HAL_SERVICE_ID_GATT,
+ HAL_OP_GATT_CLIENT_GET_DEVICE_TYPE,
+ sizeof(rsp), &rsp, -1);
+}
+
+static void handle_client_set_adv_data(const void *buf, uint16_t len)
+{
+ const struct hal_cmd_gatt_client_set_adv_data *cmd = buf;
+ uint8_t status;
+
+ if (len != sizeof(*cmd) + cmd->manufacturer_len) {
+ error("Invalid set adv data command (%u bytes), terminating",
+ len);
+ raise(SIGTERM);
+ return;
+ }
+
+ DBG("scan_rsp=%u name=%u tx=%u min=%d max=%d app=%d",
+ cmd->set_scan_rsp, cmd->include_name, cmd->include_txpower,
+ cmd->min_interval, cmd->max_interval, cmd->appearance);
+
+ DBG("manufacturer=%u service_data=%u service_uuid=%u",
+ cmd->manufacturer_len, cmd->service_data_len,
+ cmd->service_uuid_len);
+
+ /* TODO This should be implemented when kernel supports it */
+ if (cmd->manufacturer_len || cmd->service_data_len ||
+ cmd->service_uuid_len) {
+ error("gatt: Extra advertising data not supported");
+ status = HAL_STATUS_FAILED;
+ goto failed;
+ }
+
+ status = HAL_STATUS_SUCCESS;
+
+failed:
+ ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT,
+ HAL_OP_GATT_CLIENT_SET_ADV_DATA, status);
+}
+
+static void test_command_result(guint8 status, const guint8 *pdu,
+ guint16 len, gpointer user_data)
+{
+ DBG("status: %d", status);
+}
+
+static uint8_t test_read_write(bdaddr_t *bdaddr, bt_uuid_t *uuid, uint16_t op,
+ uint16_t u2, uint16_t u3,
+ uint16_t u4, uint16_t u5)
+{
+ guint16 length = 0;
+ struct gatt_device *dev;
+ uint8_t *pdu;
+ size_t mtu;
+
+ dev = find_device_by_addr(bdaddr);
+ if (!dev || dev->state != DEVICE_CONNECTED)
+ return HAL_STATUS_FAILED;
+
+ pdu = g_attrib_get_buffer(dev->attrib, &mtu);
+ if (!pdu)
+ return HAL_STATUS_FAILED;
+
+ switch (op) {
+ case ATT_OP_READ_REQ:
+ length = enc_read_req(u2, pdu, mtu);
+ break;
+ case ATT_OP_READ_BY_TYPE_REQ:
+ length = enc_read_by_type_req(u2, u3, uuid, pdu, mtu);
+ break;
+ case ATT_OP_READ_BLOB_REQ:
+ length = enc_read_blob_req(u2, u3, pdu, mtu);
+ break;
+ case ATT_OP_READ_BY_GROUP_REQ:
+ length = enc_read_by_grp_req(u2, u3, uuid, pdu, mtu);
+ break;
+ case ATT_OP_READ_MULTI_REQ:
+ return HAL_STATUS_UNSUPPORTED;
+ case ATT_OP_WRITE_REQ:
+ length = enc_write_req(u2, (uint8_t *) &u3, sizeof(u3), pdu,
+ mtu);
+ break;
+ case ATT_OP_WRITE_CMD:
+ length = enc_write_cmd(u2, (uint8_t *) &u3, sizeof(u3), pdu,
+ mtu);
+ break;
+ case ATT_OP_PREP_WRITE_REQ:
+ length = enc_prep_write_req(u2, u3, (uint8_t *) &u4, sizeof(u4),
+ pdu, mtu);
+ break;
+ case ATT_OP_EXEC_WRITE_REQ:
+ length = enc_exec_write_req(u2, pdu, mtu);
+ break;
+ case ATT_OP_SIGNED_WRITE_CMD:
+ if (signed_write_cmd(dev, u2, (uint8_t *) &u3, sizeof(u3)))
+ return HAL_STATUS_SUCCESS;
+ else
+ return HAL_STATUS_FAILED;
+ default:
+ error("gatt: Unknown operation type");
+
+ return HAL_STATUS_UNSUPPORTED;
+ }
+
+ if (!g_attrib_send(dev->attrib, 0, pdu, length, test_command_result,
+ NULL, NULL))
+ return HAL_STATUS_FAILED;
+
+ return HAL_STATUS_SUCCESS;
+}
+
+static uint8_t test_increase_security(bdaddr_t *bdaddr, uint16_t u1)
+{
+ struct gatt_device *device;
+
+ device = find_device_by_addr(bdaddr);
+ if (!device)
+ return HAL_STATUS_FAILED;
+
+ if (!set_auth_type(device, u1))
+ return HAL_STATUS_FAILED;
+
+ return HAL_STATUS_SUCCESS;
+}
+
+static void handle_client_test_command(const void *buf, uint16_t len)
+{
+ const struct hal_cmd_gatt_client_test_command *cmd = buf;
+ struct gatt_app *app;
+ bdaddr_t bdaddr;
+ bt_uuid_t uuid;
+ uint8_t status;
+
+ DBG("");
+
+ android2bdaddr(cmd->bda1, &bdaddr);
+ android2uuid(cmd->uuid1, &uuid);
+
+ switch (cmd->command) {
+ case GATT_CLIENT_TEST_CMD_ENABLE:
+ if (cmd->u1) {
+ if (!test_client_if) {
+ app = register_app(TEST_UUID, GATT_CLIENT);
+ if (app)
+ test_client_if = app->id;
+ }
+
+ if (test_client_if)
+ status = HAL_STATUS_SUCCESS;
+ else
+ status = HAL_STATUS_FAILED;
+ } else {
+ status = unregister_app(test_client_if);
+ test_client_if = 0;
+ }
+ break;
+ case GATT_CLIENT_TEST_CMD_CONNECT:
+ /* TODO u1 holds device type, for now assume BLE */
+ status = handle_connect(test_client_if, &bdaddr, false);
+ break;
+ case GATT_CLIENT_TEST_CMD_DISCONNECT:
+ app = queue_find(gatt_apps, match_app_by_id,
+ INT_TO_PTR(test_client_if));
+ queue_remove_all(app_connections, match_connection_by_app, app,
+ destroy_connection);
+
+ status = HAL_STATUS_SUCCESS;
+ break;
+ case GATT_CLIENT_TEST_CMD_DISCOVER:
+ status = HAL_STATUS_FAILED;
+ break;
+ case GATT_CLIENT_TEST_CMD_READ:
+ case GATT_CLIENT_TEST_CMD_WRITE:
+ status = test_read_write(&bdaddr, &uuid, cmd->u1, cmd->u2,
+ cmd->u3, cmd->u4, cmd->u5);
+ break;
+ case GATT_CLIENT_TEST_CMD_INCREASE_SECURITY:
+ status = test_increase_security(&bdaddr, cmd->u1);
+ break;
+ case GATT_CLIENT_TEST_CMD_PAIRING_CONFIG:
+ default:
+ status = HAL_STATUS_FAILED;
+ break;
+ }
+
+ ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT,
+ HAL_OP_GATT_CLIENT_TEST_COMMAND, status);
+}
+
+static void handle_server_register(const void *buf, uint16_t len)
+{
+ const struct hal_cmd_gatt_server_register *cmd = buf;
+ struct hal_ev_gatt_server_register ev;
+ struct gatt_app *app;
+
+ DBG("");
+
+ memset(&ev, 0, sizeof(ev));
+
+ app = register_app(cmd->uuid, GATT_SERVER);
+
+ if (app) {
+ ev.server_if = app->id;
+ ev.status = GATT_SUCCESS;
+ } else {
+ ev.status = GATT_FAILURE;
+ }
+
+ memcpy(ev.uuid, cmd->uuid, sizeof(ev.uuid));
+
+ ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT,
+ HAL_EV_GATT_SERVER_REGISTER, sizeof(ev), &ev);
+
+ ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT, HAL_OP_GATT_SERVER_REGISTER,
+ HAL_STATUS_SUCCESS);
+}
+
+static void handle_server_unregister(const void *buf, uint16_t len)
+{
+ const struct hal_cmd_gatt_server_unregister *cmd = buf;
+ uint8_t status;
+
+ DBG("");
+
+ status = unregister_app(cmd->server_if);
+
+ ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT,
+ HAL_OP_GATT_SERVER_UNREGISTER, status);
+}
+
+static void handle_server_connect(const void *buf, uint16_t len)
+{
+ const struct hal_cmd_gatt_server_connect *cmd = buf;
+ uint8_t status;
+ bdaddr_t addr;
+
+ DBG("");
+
+ android2bdaddr(&cmd->bdaddr, &addr);
+
+ /* TODO: Handle transport flag */
+
+ status = handle_connect(cmd->server_if, &addr, cmd->is_direct);
+
+ ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT, HAL_OP_GATT_SERVER_CONNECT,
+ status);
+}
+
+static void handle_server_disconnect(const void *buf, uint16_t len)
+{
+ const struct hal_cmd_gatt_server_disconnect *cmd = buf;
+ struct app_connection *conn;
+ uint8_t status;
+
+ DBG("");
+
+ /* TODO: should we care to match also bdaddr when conn_id is unique? */
+ conn = queue_remove_if(app_connections, match_connection_by_id,
+ INT_TO_PTR(cmd->conn_id));
+ destroy_connection(conn);
+
+ status = HAL_STATUS_SUCCESS;
+
+ ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT,
+ HAL_OP_GATT_SERVER_DISCONNECT, status);
+}
+
+static void handle_server_add_service(const void *buf, uint16_t len)
+{
+ const struct hal_cmd_gatt_server_add_service *cmd = buf;
+ struct hal_ev_gatt_server_service_added ev;
+ struct gatt_app *server;
+ struct gatt_db_attribute *service;
+ uint8_t status;
+ bt_uuid_t uuid;
+
+ DBG("");
+
+ memset(&ev, 0, sizeof(ev));
+
+ server = find_app_by_id(cmd->server_if);
+ if (!server) {
+ status = HAL_STATUS_FAILED;
+ goto failed;
+ }
+
+ android2uuid(cmd->srvc_id.uuid, &uuid);
+
+ service = gatt_db_add_service(gatt_db, &uuid, cmd->srvc_id.is_primary,
+ cmd->num_handles);
+ if (!service) {
+ status = HAL_STATUS_FAILED;
+ goto failed;
+ }
+
+ ev.srvc_handle = gatt_db_attribute_get_handle(service);
+ if (!ev.srvc_handle) {
+ status = HAL_STATUS_FAILED;
+ goto failed;
+ }
+
+ status = HAL_STATUS_SUCCESS;
+
+failed:
+ ev.status = status == HAL_STATUS_SUCCESS ? GATT_SUCCESS : GATT_FAILURE;
+ ev.srvc_id = cmd->srvc_id;
+ ev.server_if = cmd->server_if;
+
+ ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT,
+ HAL_EV_GATT_SERVER_SERVICE_ADDED, sizeof(ev), &ev);
+
+ ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT,
+ HAL_OP_GATT_SERVER_ADD_SERVICE, status);
+}
+
+static void handle_server_add_included_service(const void *buf, uint16_t len)
+{
+ const struct hal_cmd_gatt_server_add_inc_service *cmd = buf;
+ struct hal_ev_gatt_server_inc_srvc_added ev;
+ struct gatt_app *server;
+ struct gatt_db_attribute *service, *include;
+ uint8_t status;
+
+ DBG("");
+
+ memset(&ev, 0, sizeof(ev));
+
+ server = find_app_by_id(cmd->server_if);
+ if (!server) {
+ status = HAL_STATUS_FAILED;
+ goto failed;
+ }
+
+ service = gatt_db_get_attribute(gatt_db, cmd->service_handle);
+ if (!service) {
+ status = HAL_STATUS_FAILED;
+ goto failed;
+ }
+
+ include = gatt_db_get_attribute(gatt_db, cmd->included_handle);
+ if (!include) {
+ status = HAL_STATUS_FAILED;
+ goto failed;
+ }
+
+ service = gatt_db_service_add_included(service, include);
+ if (!service) {
+ status = HAL_STATUS_FAILED;
+ goto failed;
+ }
+
+ ev.incl_srvc_handle = gatt_db_attribute_get_handle(service);
+ status = HAL_STATUS_SUCCESS;
+failed:
+ ev.srvc_handle = cmd->service_handle;
+ ev.status = status;
+ ev.server_if = cmd->server_if;
+ ev.status = status == HAL_STATUS_SUCCESS ? GATT_SUCCESS : GATT_FAILURE;
+
+ ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT,
+ HAL_EV_GATT_SERVER_INC_SRVC_ADDED, sizeof(ev), &ev);
+
+ ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT,
+ HAL_OP_GATT_SERVER_ADD_INC_SERVICE, status);
+}
+
+static bool is_service(const bt_uuid_t *type)
+{
+ bt_uuid_t uuid;
+
+ bt_uuid16_create(&uuid, GATT_PRIM_SVC_UUID);
+ if (!bt_uuid_cmp(&uuid, type))
+ return true;
+
+ bt_uuid16_create(&uuid, GATT_SND_SVC_UUID);
+ if (!bt_uuid_cmp(&uuid, type))
+ return true;
+
+ return false;
+}
+
+static bool match_pending_dev_request(const void *data, const void *user_data)
+{
+ const struct pending_request *pending_request = data;
+
+ return !pending_request->completed;
+}
+
+static void send_dev_complete_response(struct gatt_device *device,
+ uint8_t opcode)
+{
+ size_t mtu;
+ uint8_t *rsp = g_attrib_get_buffer(device->attrib, &mtu);
+ struct pending_request *val;
+ uint16_t len = 0;
+ uint8_t error = 0;
+
+ if (queue_isempty(device->pending_requests))
+ return;
+
+ if (queue_find(device->pending_requests, match_pending_dev_request,
+ NULL)) {
+ DBG("Still pending requests");
+ return;
+ }
+
+ val = queue_peek_head(device->pending_requests);
+ if (!val) {
+ error = ATT_ECODE_ATTR_NOT_FOUND;
+ goto done;
+ }
+
+ if (val->error) {
+ error = val->error;
+ goto done;
+ }
+
+ switch (opcode) {
+ case ATT_OP_READ_BY_TYPE_REQ: {
+ struct att_data_list *adl;
+ int iterator = 0;
+ int length;
+ struct queue *temp;
+
+ temp = queue_new();
+
+ val = queue_pop_head(device->pending_requests);
+ if (!val) {
+ queue_destroy(temp, NULL);
+ error = ATT_ECODE_ATTR_NOT_FOUND;
+ goto done;
+ }
+
+ if (val->error) {
+ queue_destroy(temp, NULL);
+ error = val->error;
+ destroy_pending_request(val);
+ goto done;
+ }
+
+ length = val->length;
+
+ while (val && val->length == length && val->error == 0) {
+ queue_push_tail(temp, val);
+ val = queue_pop_head(device->pending_requests);
+ }
+
+ adl = att_data_list_alloc(queue_length(temp),
+ sizeof(uint16_t) + length);
+
+ destroy_pending_request(val);
+
+ val = queue_pop_head(temp);
+ while (val) {
+ uint8_t *value = adl->data[iterator++];
+ uint16_t handle;
+
+ handle = gatt_db_attribute_get_handle(val->attrib);
+
+ put_le16(handle, value);
+ memcpy(&value[2], val->value, val->length);
+
+ destroy_pending_request(val);
+ val = queue_pop_head(temp);
+ }
+
+ len = enc_read_by_type_resp(adl, rsp, mtu);
+
+ att_data_list_free(adl);
+ queue_destroy(temp, destroy_pending_request);
+
+ break;
+ }
+ case ATT_OP_READ_BLOB_REQ:
+ len = enc_read_blob_resp(val->value, val->length, val->offset,
+ rsp, mtu);
+ break;
+ case ATT_OP_READ_REQ:
+ len = enc_read_resp(val->value, val->length, rsp, mtu);
+ break;
+ case ATT_OP_READ_BY_GROUP_REQ: {
+ struct att_data_list *adl;
+ int iterator = 0;
+ int length;
+ struct queue *temp;
+
+ temp = queue_new();
+
+ val = queue_pop_head(device->pending_requests);
+ if (!val) {
+ queue_destroy(temp, NULL);
+ error = ATT_ECODE_ATTR_NOT_FOUND;
+ goto done;
+ }
+
+ length = val->length;
+
+ while (val && val->length == length) {
+ queue_push_tail(temp, val);
+ val = queue_pop_head(device->pending_requests);
+ }
+
+ adl = att_data_list_alloc(queue_length(temp),
+ 2 * sizeof(uint16_t) + length);
+
+ val = queue_pop_head(temp);
+ while (val) {
+ uint8_t *value = adl->data[iterator++];
+ uint16_t start_handle, end_handle;
+
+ gatt_db_attribute_get_service_handles(val->attrib,
+ &start_handle,
+ &end_handle);
+
+ put_le16(start_handle, value);
+ put_le16(end_handle, &value[2]);
+ memcpy(&value[4], val->value, val->length);
+
+ destroy_pending_request(val);
+ val = queue_pop_head(temp);
+ }
+
+ len = enc_read_by_grp_resp(adl, rsp, mtu);
+
+ att_data_list_free(adl);
+ queue_destroy(temp, destroy_pending_request);
+
+ break;
+ }
+ case ATT_OP_FIND_BY_TYPE_REQ: {
+ GSList *list = NULL;
+
+ val = queue_pop_head(device->pending_requests);
+ while (val) {
+ struct att_range *range;
+ const bt_uuid_t *type;
+
+ /* Its find by type and value - filter by value here */
+ if ((val->length != val->filter_vlen) ||
+ memcmp(val->value, val->filter_value,
+ val->length)) {
+
+ destroy_pending_request(val);
+ val = queue_pop_head(device->pending_requests);
+ continue;
+ }
+
+ range = new0(struct att_range, 1);
+ range->start = gatt_db_attribute_get_handle(
+ val->attrib);
+
+ type = gatt_db_attribute_get_type(val->attrib);
+ if (is_service(type))
+ gatt_db_attribute_get_service_handles(
+ val->attrib,
+ NULL,
+ &range->end);
+ else
+ range->end = range->start;
+
+ list = g_slist_append(list, range);
+
+ destroy_pending_request(val);
+ val = queue_pop_head(device->pending_requests);
+ }
+
+ if (list && !error)
+ len = enc_find_by_type_resp(list, rsp, mtu);
+ else
+ error = ATT_ECODE_ATTR_NOT_FOUND;
+
+ g_slist_free_full(list, free);
+
+ break;
+ }
+ case ATT_OP_EXEC_WRITE_REQ:
+ len = enc_exec_write_resp(rsp);
+ break;
+ case ATT_OP_WRITE_REQ:
+ len = enc_write_resp(rsp);
+ break;
+ case ATT_OP_PREP_WRITE_REQ: {
+ uint16_t handle;
+
+ handle = gatt_db_attribute_get_handle(val->attrib);
+ len = enc_prep_write_resp(handle, val->offset, val->value,
+ val->length, rsp, mtu);
+ break;
+ }
+ default:
+ break;
+ }
+
+done:
+ if (!len)
+ len = enc_error_resp(opcode, 0x0000, error, rsp, mtu);
+
+ g_attrib_send(device->attrib, 0, rsp, len, NULL, NULL, NULL);
+
+ queue_remove_all(device->pending_requests, NULL, NULL,
+ destroy_pending_request);
+}
+
+struct request_processing_data {
+ uint8_t opcode;
+ struct gatt_device *device;
+};
+
+static uint8_t check_device_permissions(struct gatt_device *device,
+ uint8_t opcode, uint32_t permissions)
+{
+ GIOChannel *io;
+ int sec_level;
+
+ io = g_attrib_get_channel(device->attrib);
+
+ if (!bt_io_get(io, NULL, BT_IO_OPT_SEC_LEVEL, &sec_level,
+ BT_IO_OPT_INVALID))
+ return ATT_ECODE_UNLIKELY;
+
+ DBG("opcode 0x%02x permissions %u sec_level %u", opcode, permissions,
+ sec_level);
+
+ switch (opcode) {
+ case ATT_OP_SIGNED_WRITE_CMD:
+ if (!(permissions & GATT_PERM_WRITE_SIGNED))
+ return ATT_ECODE_WRITE_NOT_PERM;
+
+ if (permissions & GATT_PERM_WRITE_SIGNED_MITM) {
+ bool auth;
+
+ if (bt_get_csrk(&device->bdaddr, true, NULL, NULL,
+ &auth) && auth)
+ break;
+
+ return ATT_ECODE_AUTHENTICATION;
+ }
+ break;
+ case ATT_OP_READ_BY_TYPE_REQ:
+ case ATT_OP_READ_REQ:
+ case ATT_OP_READ_BLOB_REQ:
+ case ATT_OP_READ_MULTI_REQ:
+ case ATT_OP_READ_BY_GROUP_REQ:
+ case ATT_OP_FIND_BY_TYPE_REQ:
+ case ATT_OP_FIND_INFO_REQ:
+ if (!(permissions & GATT_PERM_READ))
+ return ATT_ECODE_READ_NOT_PERM;
+
+ if ((permissions & GATT_PERM_READ_MITM) &&
+ sec_level < BT_SECURITY_HIGH)
+ return ATT_ECODE_AUTHENTICATION;
+
+ if ((permissions & GATT_PERM_READ_ENCRYPTED) &&
+ sec_level < BT_SECURITY_MEDIUM)
+ return ATT_ECODE_INSUFF_ENC;
+
+ if (permissions & GATT_PERM_READ_AUTHORIZATION)
+ return ATT_ECODE_AUTHORIZATION;
+ break;
+ case ATT_OP_WRITE_REQ:
+ case ATT_OP_WRITE_CMD:
+ case ATT_OP_PREP_WRITE_REQ:
+ case ATT_OP_EXEC_WRITE_REQ:
+ if (!(permissions & GATT_PERM_WRITE))
+ return ATT_ECODE_WRITE_NOT_PERM;
+
+ if ((permissions & GATT_PERM_WRITE_MITM) &&
+ sec_level < BT_SECURITY_HIGH)
+ return ATT_ECODE_AUTHENTICATION;
+
+ if ((permissions & GATT_PERM_WRITE_ENCRYPTED) &&
+ sec_level < BT_SECURITY_MEDIUM)
+ return ATT_ECODE_INSUFF_ENC;
+
+ if (permissions & GATT_PERM_WRITE_AUTHORIZATION)
+ return ATT_ECODE_AUTHORIZATION;
+ break;
+ default:
+ return ATT_ECODE_UNLIKELY;
+ }
+
+ return 0;
+}
+
+static uint8_t err_to_att(int err)
+{
+ if (!err || (err > 0 && err < UINT8_MAX))
+ return err;
+
+ switch (err) {
+ case -ENOENT:
+ return ATT_ECODE_INVALID_HANDLE;
+ case -ENOMEM:
+ return ATT_ECODE_INSUFF_RESOURCES;
+ default:
+ return ATT_ECODE_UNLIKELY;
+ }
+}
+
+static void attribute_read_cb(struct gatt_db_attribute *attrib, int err,
+ const uint8_t *value, size_t length,
+ void *user_data)
+{
+ struct pending_request *resp_data = user_data;
+ uint8_t error = err_to_att(err);
+
+ resp_data->attrib = attrib;
+ resp_data->length = length;
+ resp_data->error = error;
+
+ resp_data->completed = true;
+
+ if (!length)
+ return;
+
+ resp_data->value = malloc0(length);
+ if (!resp_data->value) {
+ resp_data->error = ATT_ECODE_INSUFF_RESOURCES;
+
+ return;
+ }
+
+ memcpy(resp_data->value, value, length);
+}
+
+static void read_requested_attributes(void *data, void *user_data)
+{
+ struct pending_request *resp_data = data;
+ struct request_processing_data *process_data = user_data;
+ struct bt_att *att = g_attrib_get_att(process_data->device->attrib);
+ struct gatt_db_attribute *attrib;
+ uint32_t permissions;
+ uint8_t error;
+
+ attrib = resp_data->attrib;
+ if (!attrib) {
+ resp_data->error = ATT_ECODE_ATTR_NOT_FOUND;
+ resp_data->completed = true;
+ return;
+ }
+
+ permissions = gatt_db_attribute_get_permissions(attrib);
+
+ /*
+ * Check if it is attribute we didn't declare permissions, like service
+ * declaration or included service. Set permissions to read only
+ */
+ if (permissions == 0)
+ permissions = GATT_PERM_READ;
+
+ error = check_device_permissions(process_data->device,
+ process_data->opcode,
+ permissions);
+ if (error != 0) {
+ resp_data->error = error;
+ resp_data->completed = true;
+ return;
+ }
+
+ gatt_db_attribute_read(attrib, resp_data->offset, process_data->opcode,
+ att, attribute_read_cb, resp_data);
+}
+
+static void process_dev_pending_requests(struct gatt_device *device,
+ uint8_t att_opcode)
+{
+ struct request_processing_data process_data;
+
+ if (queue_isempty(device->pending_requests))
+ return;
+
+ process_data.device = device;
+ process_data.opcode = att_opcode;
+
+ /* Process pending requests and prepare response */
+ queue_foreach(device->pending_requests, read_requested_attributes,
+ &process_data);
+
+ send_dev_complete_response(device, att_opcode);
+}
+
+static struct pending_trans_data *conn_add_transact(struct app_connection *conn,
+ uint8_t opcode,
+ struct gatt_db_attribute *attrib,
+ unsigned int serial_id)
+{
+ struct pending_trans_data *transaction;
+ static int32_t trans_id = 1;
+
+ transaction = new0(struct pending_trans_data, 1);
+ transaction->id = trans_id++;
+ transaction->opcode = opcode;
+ transaction->attrib = attrib;
+ transaction->serial_id = serial_id;
+
+ queue_push_tail(conn->transactions, transaction);
+
+ return transaction;
+}
+
+static bool get_dst_addr(struct bt_att *att, bdaddr_t *dst)
+{
+ GIOChannel *io = NULL;
+ GError *gerr = NULL;
+
+ io = g_io_channel_unix_new(bt_att_get_fd(att));
+ if (!io)
+ return false;
+
+ bt_io_get(io, &gerr, BT_IO_OPT_DEST_BDADDR, dst, BT_IO_OPT_INVALID);
+ if (gerr) {
+ error("gatt: bt_io_get: %s", gerr->message);
+ g_error_free(gerr);
+ g_io_channel_unref(io);
+ return false;
+ }
+
+ g_io_channel_unref(io);
+ return true;
+}
+
+static void read_cb(struct gatt_db_attribute *attrib, unsigned int id,
+ uint16_t offset, uint8_t opcode, struct bt_att *att,
+ void *user_data)
+{
+ struct pending_trans_data *transaction;
+ struct hal_ev_gatt_server_request_read ev;
+ struct gatt_app *app;
+ struct app_connection *conn;
+ int32_t app_id = PTR_TO_INT(user_data);
+ bdaddr_t bdaddr;
+
+ DBG("id %u", id);
+
+ app = find_app_by_id(app_id);
+ if (!app) {
+ error("gatt: read_cb, cound not found app id");
+ goto failed;
+ }
+
+ if (!get_dst_addr(att, &bdaddr)) {
+ error("gatt: read_cb, could not obtain dst BDADDR");
+ goto failed;
+ }
+
+ conn = find_conn(&bdaddr, app->id);
+ if (!conn) {
+ error("gatt: read_cb, cound not found connection");
+ goto failed;
+ }
+
+ memset(&ev, 0, sizeof(ev));
+
+ /* Store the request data, complete callback and transaction id */
+ transaction = conn_add_transact(conn, opcode, attrib, id);
+
+ bdaddr2android(&bdaddr, ev.bdaddr);
+ ev.conn_id = conn->id;
+ ev.attr_handle = gatt_db_attribute_get_handle(attrib);
+ ev.offset = offset;
+ ev.is_long = opcode == ATT_OP_READ_BLOB_REQ;
+ ev.trans_id = transaction->id;
+
+ ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT,
+ HAL_EV_GATT_SERVER_REQUEST_READ,
+ sizeof(ev), &ev);
+
+ return;
+
+failed:
+ gatt_db_attribute_read_result(attrib, id, -ENOENT, NULL, 0);
+}
+
+static void write_cb(struct gatt_db_attribute *attrib, unsigned int id,
+ uint16_t offset, const uint8_t *value, size_t len,
+ uint8_t opcode, struct bt_att *att, void *user_data)
+{
+ uint8_t buf[IPC_MTU];
+ struct hal_ev_gatt_server_request_write *ev = (void *) buf;
+ struct pending_trans_data *transaction;
+ struct gatt_app *app;
+ int32_t app_id = PTR_TO_INT(user_data);
+ struct app_connection *conn;
+ bdaddr_t bdaddr;
+
+ DBG("id %u", id);
+
+ app = find_app_by_id(app_id);
+ if (!app) {
+ error("gatt: write_cb could not found app id");
+ goto failed;
+ }
+
+ if (!get_dst_addr(att, &bdaddr)) {
+ error("gatt: write_cb, could not obtain dst BDADDR");
+ goto failed;
+ }
+
+ conn = find_conn(&bdaddr, app->id);
+ if (!conn) {
+ error("gatt: write_cb could not found connection");
+ goto failed;
+ }
+
+ /*
+ * Remember that this application has ongoing prep write
+ * Need it later to find out where to send execute write
+ */
+ if (opcode == ATT_OP_PREP_WRITE_REQ)
+ conn->wait_execute_write = true;
+
+ /* Store the request data, complete callback and transaction id */
+ transaction = conn_add_transact(conn, opcode, attrib, id);
+
+ memset(ev, 0, sizeof(*ev));
+
+ bdaddr2android(&bdaddr, &ev->bdaddr);
+ ev->attr_handle = gatt_db_attribute_get_handle(attrib);
+ ev->offset = offset;
+
+ ev->conn_id = conn->id;
+ ev->trans_id = transaction->id;
+
+ ev->is_prep = opcode == ATT_OP_PREP_WRITE_REQ;
+
+ if (opcode == ATT_OP_WRITE_REQ || opcode == ATT_OP_PREP_WRITE_REQ)
+ ev->need_rsp = 0x01;
+ else
+ gatt_db_attribute_write_result(attrib, id, 0);
+
+ ev->length = len;
+ memcpy(ev->value, value, len);
+
+ ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT,
+ HAL_EV_GATT_SERVER_REQUEST_WRITE,
+ sizeof(*ev) + ev->length , ev);
+ return;
+
+failed:
+ gatt_db_attribute_write_result(attrib, id, ATT_ECODE_UNLIKELY);
+}
+
+static uint32_t android_to_gatt_permissions(int32_t hal_permissions)
+{
+ uint32_t permissions = 0;
+
+ if (hal_permissions & HAL_GATT_PERMISSION_READ)
+ permissions |= GATT_PERM_READ;
+
+ if (hal_permissions & HAL_GATT_PERMISSION_READ_ENCRYPTED)
+ permissions |= GATT_PERM_READ_ENCRYPTED | GATT_PERM_READ;
+
+ if (hal_permissions & HAL_GATT_PERMISSION_READ_ENCRYPTED_MITM)
+ permissions |= GATT_PERM_READ_MITM | GATT_PERM_READ_ENCRYPTED |
+ GATT_PERM_READ;
+
+ if (hal_permissions & HAL_GATT_PERMISSION_WRITE)
+ permissions |= GATT_PERM_WRITE;
+
+ if (hal_permissions & HAL_GATT_PERMISSION_WRITE_ENCRYPTED)
+ permissions |= GATT_PERM_WRITE_ENCRYPTED | GATT_PERM_WRITE;
+
+ if (hal_permissions & HAL_GATT_PERMISSION_WRITE_ENCRYPTED_MITM)
+ permissions |= GATT_PERM_WRITE_MITM |
+ GATT_PERM_WRITE_ENCRYPTED | GATT_PERM_WRITE;
+
+ if (hal_permissions & HAL_GATT_PERMISSION_WRITE_SIGNED)
+ permissions |= GATT_PERM_WRITE_SIGNED;
+
+ if (hal_permissions & HAL_GATT_PERMISSION_WRITE_SIGNED_MITM)
+ permissions |= GATT_PERM_WRITE_SIGNED_MITM |
+ GATT_PERM_WRITE_SIGNED;
+
+ return permissions;
+}
+
+static void handle_server_add_characteristic(const void *buf, uint16_t len)
+{
+ const struct hal_cmd_gatt_server_add_characteristic *cmd = buf;
+ struct hal_ev_gatt_server_characteristic_added ev;
+ struct gatt_app *server;
+ struct gatt_db_attribute *attrib;
+ bt_uuid_t uuid;
+ uint8_t status;
+ uint32_t permissions;
+ int32_t app_id = cmd->server_if;
+
+ DBG("");
+
+ memset(&ev, 0, sizeof(ev));
+
+ server = find_app_by_id(app_id);
+ if (!server) {
+ status = HAL_STATUS_FAILED;
+ goto failed;
+ }
+
+ attrib = gatt_db_get_attribute(gatt_db, cmd->service_handle);
+ if (!attrib) {
+ status = HAL_STATUS_FAILED;
+ goto failed;
+ }
+
+ android2uuid(cmd->uuid, &uuid);
+ permissions = android_to_gatt_permissions(cmd->permissions);
+
+ attrib = gatt_db_service_add_characteristic(attrib,
+ &uuid, permissions,
+ cmd->properties,
+ read_cb, write_cb,
+ INT_TO_PTR(app_id));
+ if (!attrib) {
+ status = HAL_STATUS_FAILED;
+ goto failed;
+ }
+
+ ev.char_handle = gatt_db_attribute_get_handle(attrib);
+ status = HAL_STATUS_SUCCESS;
+
+failed:
+ ev.srvc_handle = cmd->service_handle;
+ ev.status = status;
+ ev.server_if = app_id;
+ ev.status = status == HAL_STATUS_SUCCESS ? GATT_SUCCESS : GATT_FAILURE;
+ memcpy(ev.uuid, cmd->uuid, sizeof(cmd->uuid));
+
+ ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT,
+ HAL_EV_GATT_SERVER_CHAR_ADDED, sizeof(ev), &ev);
+
+ ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT,
+ HAL_OP_GATT_SERVER_ADD_CHARACTERISTIC, status);
+}
+
+static void handle_server_add_descriptor(const void *buf, uint16_t len)
+{
+ const struct hal_cmd_gatt_server_add_descriptor *cmd = buf;
+ struct hal_ev_gatt_server_descriptor_added ev;
+ struct gatt_app *server;
+ struct gatt_db_attribute *attrib;
+ bt_uuid_t uuid;
+ uint8_t status;
+ uint32_t permissions;
+ int32_t app_id = cmd->server_if;
+
+ DBG("");
+
+ memset(&ev, 0, sizeof(ev));
+
+ server = find_app_by_id(app_id);
+ if (!server) {
+ status = HAL_STATUS_FAILED;
+ goto failed;
+ }
+
+ android2uuid(cmd->uuid, &uuid);
+ permissions = android_to_gatt_permissions(cmd->permissions);
+
+ attrib = gatt_db_get_attribute(gatt_db, cmd->service_handle);
+ if (!attrib) {
+ status = HAL_STATUS_FAILED;
+ goto failed;
+ }
+
+ attrib = gatt_db_service_add_descriptor(attrib, &uuid, permissions,
+ read_cb, write_cb,
+ INT_TO_PTR(app_id));
+ if (!attrib) {
+ status = HAL_STATUS_FAILED;
+ goto failed;
+ }
+
+ ev.descr_handle = gatt_db_attribute_get_handle(attrib);
+ status = HAL_STATUS_SUCCESS;
+
+failed:
+ ev.server_if = app_id;
+ ev.srvc_handle = cmd->service_handle;
+ memcpy(ev.uuid, cmd->uuid, sizeof(cmd->uuid));
+ ev.status = status == HAL_STATUS_SUCCESS ? GATT_SUCCESS : GATT_FAILURE;
+
+ ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT,
+ HAL_EV_GATT_SERVER_DESCRIPTOR_ADDED, sizeof(ev), &ev);
+
+ ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT,
+ HAL_OP_GATT_SERVER_ADD_DESCRIPTOR, status);
+}
+
+static void notify_service_change(void *data, void *user_data)
+{
+ struct att_range range;
+ struct gatt_db_attribute *attrib = user_data;
+
+ gatt_db_attribute_get_service_handles(attrib, &range.start, &range.end);
+
+ /* In case of db error */
+ if (!range.end)
+ return;
+
+ notify_att_range_change(data, &range);
+}
+
+static sdp_record_t *get_sdp_record(uuid_t *uuid, uint16_t start, uint16_t end,
+ const char *name)
+{
+ sdp_list_t *svclass_id, *apseq, *proto[2], *root, *aproto;
+ uuid_t root_uuid, proto_uuid, l2cap;
+ sdp_record_t *record;
+ sdp_data_t *psm, *sh, *eh;
+ uint16_t lp = ATT_PSM;
+
+ record = sdp_record_alloc();
+ if (!record)
+ return NULL;
+
+ sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
+ root = sdp_list_append(NULL, &root_uuid);
+ sdp_set_browse_groups(record, root);
+ sdp_list_free(root, NULL);
+
+ svclass_id = sdp_list_append(NULL, uuid);
+ sdp_set_service_classes(record, svclass_id);
+ sdp_list_free(svclass_id, NULL);
+
+ sdp_uuid16_create(&l2cap, L2CAP_UUID);
+ proto[0] = sdp_list_append(NULL, &l2cap);
+ psm = sdp_data_alloc(SDP_UINT16, &lp);
+ proto[0] = sdp_list_append(proto[0], psm);
+ apseq = sdp_list_append(NULL, proto[0]);
+
+ sdp_uuid16_create(&proto_uuid, ATT_UUID);
+ proto[1] = sdp_list_append(NULL, &proto_uuid);
+ sh = sdp_data_alloc(SDP_UINT16, &start);
+ proto[1] = sdp_list_append(proto[1], sh);
+ eh = sdp_data_alloc(SDP_UINT16, &end);
+ proto[1] = sdp_list_append(proto[1], eh);
+ apseq = sdp_list_append(apseq, proto[1]);
+
+ aproto = sdp_list_append(NULL, apseq);
+ sdp_set_access_protos(record, aproto);
+
+ if (name)
+ sdp_set_info_attr(record, name, "BlueZ for Android", NULL);
+
+ sdp_data_free(psm);
+ sdp_data_free(sh);
+ sdp_data_free(eh);
+ sdp_list_free(proto[0], NULL);
+ sdp_list_free(proto[1], NULL);
+ sdp_list_free(apseq, NULL);
+ sdp_list_free(aproto, NULL);
+
+ return record;
+}
+
+static uint32_t add_sdp_record(const bt_uuid_t *uuid, uint16_t start,
+ uint16_t end, const char *name)
+{
+ sdp_record_t *rec;
+ uuid_t u, u32;
+
+ switch (uuid->type) {
+ case BT_UUID16:
+ sdp_uuid16_create(&u, uuid->value.u16);
+ break;
+ case BT_UUID32:
+ sdp_uuid32_create(&u32, uuid->value.u32);
+ sdp_uuid32_to_uuid128(&u, &u32);
+ break;
+ case BT_UUID128:
+ sdp_uuid128_create(&u, &uuid->value.u128);
+ break;
+ case BT_UUID_UNSPEC:
+ default:
+ return 0;
+ }
+
+ rec = get_sdp_record(&u, start, end, name);
+ if (!rec)
+ return 0;
+
+ if (bt_adapter_add_record(rec, 0) < 0) {
+ error("gatt: Failed to register SDP record");
+ sdp_record_free(rec);
+ return 0;
+ }
+
+ return rec->handle;
+}
+
+static bool match_service_sdp(const void *data, const void *user_data)
+{
+ const struct service_sdp *s = data;
+
+ return s->service_handle == PTR_TO_INT(user_data);
+}
+
+static struct service_sdp *new_service_sdp_record(int32_t service_handle)
+{
+ bt_uuid_t uuid;
+ struct service_sdp *s;
+ struct gatt_db_attribute *attrib;
+ uint16_t end_handle;
+
+ attrib = gatt_db_get_attribute(gatt_db, service_handle);
+ if (!attrib)
+ return NULL;
+
+ gatt_db_attribute_get_service_handles(attrib, NULL, &end_handle);
+ if (!end_handle)
+ return NULL;
+
+ if (!gatt_db_attribute_get_service_uuid(attrib, &uuid))
+ return NULL;
+
+ s = new0(struct service_sdp, 1);
+ s->service_handle = service_handle;
+ s->sdp_handle = add_sdp_record(&uuid, service_handle, end_handle, NULL);
+ if (!s->sdp_handle) {
+ free(s);
+ return NULL;
+ }
+
+ return s;
+}
+
+static void free_service_sdp_record(void *data)
+{
+ struct service_sdp *s = data;
+
+ if (!s)
+ return;
+
+ bt_adapter_remove_record(s->sdp_handle);
+ free(s);
+}
+
+static bool add_service_sdp_record(int32_t service_handle)
+{
+ struct service_sdp *s;
+
+ s = queue_find(services_sdp, match_service_sdp,
+ INT_TO_PTR(service_handle));
+ if (s)
+ return true;
+
+ s = new_service_sdp_record(service_handle);
+ if (!s)
+ return false;
+
+ queue_push_tail(services_sdp, s);
+
+ return true;
+}
+
+static void remove_service_sdp_record(int32_t service_handle)
+{
+ struct service_sdp *s;
+
+ s = queue_remove_if(services_sdp, match_service_sdp,
+ INT_TO_PTR(service_handle));
+ if (!s)
+ return;
+
+ free_service_sdp_record(s);
+}
+
+static void handle_server_start_service(const void *buf, uint16_t len)
+{
+ const struct hal_cmd_gatt_server_start_service *cmd = buf;
+ struct hal_ev_gatt_server_service_started ev;
+ struct gatt_app *server;
+ struct gatt_db_attribute *attrib;
+ uint8_t status;
+
+ DBG("transport 0x%02x", cmd->transport);
+
+ memset(&ev, 0, sizeof(ev));
+
+ if (cmd->transport == 0) {
+ status = HAL_STATUS_FAILED;
+ goto failed;
+ }
+
+ server = find_app_by_id(cmd->server_if);
+ if (!server) {
+ status = HAL_STATUS_FAILED;
+ goto failed;
+ }
+
+ if (cmd->transport & GATT_SERVER_TRANSPORT_BREDR_BIT) {
+ if (!add_service_sdp_record(cmd->service_handle)) {
+ status = HAL_STATUS_FAILED;
+ goto failed;
+ }
+ }
+ /* TODO: Handle BREDR only */
+
+ attrib = gatt_db_get_attribute(gatt_db, cmd->service_handle);
+ if (!attrib) {
+ status = HAL_STATUS_FAILED;
+ goto failed;
+ }
+
+ if (!gatt_db_service_set_active(attrib, true)) {
+ /*
+ * no need to clean SDP since this can fail only if service
+ * handle is invalid in which case add_sdp_record() also fails
+ */
+ status = HAL_STATUS_FAILED;
+ goto failed;
+ }
+
+ queue_foreach(gatt_devices, notify_service_change, attrib);
+
+ status = HAL_STATUS_SUCCESS;
+
+failed:
+ ev.status = status == HAL_STATUS_SUCCESS ? GATT_SUCCESS : GATT_FAILURE;
+ ev.server_if = cmd->server_if;
+ ev.srvc_handle = cmd->service_handle;
+
+ ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT,
+ HAL_EV_GATT_SERVER_SERVICE_STARTED, sizeof(ev), &ev);
+
+ ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT,
+ HAL_OP_GATT_SERVER_START_SERVICE, status);
+}
+
+static void handle_server_stop_service(const void *buf, uint16_t len)
+{
+ const struct hal_cmd_gatt_server_stop_service *cmd = buf;
+ struct hal_ev_gatt_server_service_stopped ev;
+ struct gatt_app *server;
+ struct gatt_db_attribute *attrib;
+ uint8_t status;
+
+ DBG("");
+
+ memset(&ev, 0, sizeof(ev));
+
+ server = find_app_by_id(cmd->server_if);
+ if (!server) {
+ status = HAL_STATUS_FAILED;
+ goto failed;
+ }
+
+ attrib = gatt_db_get_attribute(gatt_db, cmd->service_handle);
+ if (!attrib) {
+ status = HAL_STATUS_FAILED;
+ goto failed;
+ }
+
+ if (!gatt_db_service_set_active(attrib, false)) {
+ status = HAL_STATUS_FAILED;
+ goto failed;
+ }
+
+ remove_service_sdp_record(cmd->service_handle);
+
+ status = HAL_STATUS_SUCCESS;
+
+ queue_foreach(gatt_devices, notify_service_change, attrib);
+
+failed:
+ ev.status = status == HAL_STATUS_SUCCESS ? GATT_SUCCESS : GATT_FAILURE;
+ ev.server_if = cmd->server_if;
+ ev.srvc_handle = cmd->service_handle;
+
+ ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT,
+ HAL_EV_GATT_SERVER_SERVICE_STOPPED, sizeof(ev), &ev);
+
+ ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT,
+ HAL_OP_GATT_SERVER_STOP_SERVICE, status);
+}
+
+static void handle_server_delete_service(const void *buf, uint16_t len)
+{
+ const struct hal_cmd_gatt_server_delete_service *cmd = buf;
+ struct hal_ev_gatt_server_service_deleted ev;
+ struct gatt_app *server;
+ struct gatt_db_attribute *attrib;
+ uint8_t status;
+
+ DBG("");
+
+ memset(&ev, 0, sizeof(ev));
+
+ server = find_app_by_id(cmd->server_if);
+ if (!server) {
+ status = HAL_STATUS_FAILED;
+ goto failed;
+ }
+
+ attrib = gatt_db_get_attribute(gatt_db, cmd->service_handle);
+ if (!attrib) {
+ status = HAL_STATUS_FAILED;
+ goto failed;
+ }
+
+ if (!gatt_db_remove_service(gatt_db, attrib)) {
+ status = HAL_STATUS_FAILED;
+ goto failed;
+ }
+
+ remove_service_sdp_record(cmd->service_handle);
+
+ status = HAL_STATUS_SUCCESS;
+
+failed:
+ ev.status = status == HAL_STATUS_SUCCESS ? GATT_SUCCESS : GATT_FAILURE;
+ ev.srvc_handle = cmd->service_handle;
+ ev.server_if = cmd->server_if;
+
+ ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT,
+ HAL_EV_GATT_SERVER_SERVICE_DELETED, sizeof(ev), &ev);
+
+ ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT,
+ HAL_OP_GATT_SERVER_DELETE_SERVICE, status);
+}
+
+static void indication_confirmation_cb(guint8 status, const guint8 *pdu,
+ guint16 len, gpointer user_data)
+{
+ struct hal_ev_gatt_server_indication_sent ev;
+
+ ev.status = status;
+ ev.conn_id = PTR_TO_UINT(user_data);
+
+ ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT,
+ HAL_EV_GATT_SERVER_INDICATION_SENT, sizeof(ev), &ev);
+}
+
+static void handle_server_send_indication(const void *buf, uint16_t len)
+{
+ const struct hal_cmd_gatt_server_send_indication *cmd = buf;
+ struct app_connection *conn;
+ uint8_t status;
+ uint16_t length;
+ uint8_t *pdu;
+ size_t mtu;
+ GAttribResultFunc confirmation_cb = NULL;
+
+ DBG("");
+
+ conn = find_connection_by_id(cmd->conn_id);
+ if (!conn) {
+ error("gatt: Could not find connection");
+ status = HAL_STATUS_FAILED;
+ goto reply;
+ }
+
+ pdu = g_attrib_get_buffer(conn->device->attrib, &mtu);
+
+ if (cmd->confirm) {
+ length = enc_indication(cmd->attribute_handle,
+ (uint8_t *) cmd->value, cmd->len, pdu,
+ mtu);
+ confirmation_cb = indication_confirmation_cb;
+ } else {
+ length = enc_notification(cmd->attribute_handle,
+ (uint8_t *) cmd->value,
+ cmd->len, pdu, mtu);
+ }
+
+ if (!g_attrib_send(conn->device->attrib, 0, pdu, length,
+ confirmation_cb, UINT_TO_PTR(conn->id), NULL)) {
+ error("gatt: Failed to send indication");
+ status = HAL_STATUS_FAILED;
+ } else {
+ status = HAL_STATUS_SUCCESS;
+ }
+
+ /* Here we confirm failed indications and all notifications */
+ if (status || !confirmation_cb)
+ indication_confirmation_cb(status, NULL, 0,
+ UINT_TO_PTR(conn->id));
+
+reply:
+ ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT,
+ HAL_OP_GATT_SERVER_SEND_INDICATION, status);
+}
+
+static bool match_trans_id(const void *data, const void *user_data)
+{
+ const struct pending_trans_data *transaction = data;
+
+ return transaction->id == PTR_TO_UINT(user_data);
+}
+
+static bool find_conn_waiting_exec_write(const void *data,
+ const void *user_data)
+{
+ const struct app_connection *conn = data;
+
+ return conn->wait_execute_write;
+}
+
+static bool pending_execute_write(void)
+{
+ return queue_find(app_connections, find_conn_waiting_exec_write, NULL);
+}
+
+static void handle_server_send_response(const void *buf, uint16_t len)
+{
+ const struct hal_cmd_gatt_server_send_response *cmd = buf;
+ struct pending_trans_data *transaction;
+ struct app_connection *conn;
+ uint8_t status;
+
+ DBG("");
+
+ conn = find_connection_by_id(cmd->conn_id);
+ if (!conn) {
+ error("gatt: could not found connection");
+ status = HAL_STATUS_FAILED;
+ goto reply;
+ }
+
+ transaction = queue_remove_if(conn->transactions, match_trans_id,
+ UINT_TO_PTR(cmd->trans_id));
+ if (!transaction) {
+ error("gatt: transaction ID = %d not found", cmd->trans_id);
+ status = HAL_STATUS_FAILED;
+ goto reply;
+ }
+
+ if (transaction->opcode == ATT_OP_EXEC_WRITE_REQ) {
+ struct pending_request *req;
+
+ conn->wait_execute_write = false;
+
+ /* Check for execute response from all server applications */
+ if (pending_execute_write())
+ goto done;
+
+ /*
+ * This is usually done through db write callback but for
+ * execute write we dont have the attribute or handle to call
+ * gatt_db_attribute_write().
+ */
+ req = queue_peek_head(conn->device->pending_requests);
+ if (!req)
+ goto done;
+
+ /* Cast status to uint8_t, due to (byte) cast in java layer. */
+ req->error = err_to_att((uint8_t) cmd->status);
+ req->completed = true;
+
+ /*
+ * FIXME: Handle situation when not all server applications
+ * respond with a success.
+ */
+ }
+
+ /* Cast status to uint8_t, due to (byte) cast in java layer. */
+ if (transaction->opcode < ATT_OP_WRITE_REQ)
+ gatt_db_attribute_read_result(transaction->attrib,
+ transaction->serial_id,
+ err_to_att((uint8_t) cmd->status),
+ cmd->data, cmd->len);
+ else
+ gatt_db_attribute_write_result(transaction->attrib,
+ transaction->serial_id,
+ err_to_att((uint8_t) cmd->status));
+
+ send_dev_complete_response(conn->device, transaction->opcode);
+
+done:
+ /* Clean request data */
+ free(transaction);
+
+ status = HAL_STATUS_SUCCESS;
+
+reply:
+ ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT,
+ HAL_OP_GATT_SERVER_SEND_RESPONSE, status);
+}
+
+static void handle_client_scan_filter_setup(const void *buf, uint16_t len)
+{
+ const struct hal_cmd_gatt_client_scan_filter_setup *cmd = buf;
+
+ DBG("client_if %u", cmd->client_if);
+
+ /* TODO */
+
+ ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT,
+ HAL_OP_GATT_CLIENT_SCAN_FILTER_SETUP,
+ HAL_STATUS_UNSUPPORTED);
+}
+
+static void handle_client_scan_filter_add_remove(const void *buf, uint16_t len)
+{
+ const struct hal_cmd_gatt_client_scan_filter_add_remove *cmd = buf;
+
+ DBG("client_if %u", cmd->client_if);
+
+ /* TODO */
+
+ ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT,
+ HAL_OP_GATT_CLIENT_SCAN_FILTER_ADD_REMOVE,
+ HAL_STATUS_UNSUPPORTED);
+}
+
+static void handle_client_scan_filter_clear(const void *buf, uint16_t len)
+{
+ const struct hal_cmd_gatt_client_scan_filter_clear *cmd = buf;
+
+ DBG("client_if %u", cmd->client_if);
+
+ /* TODO */
+
+ ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT,
+ HAL_OP_GATT_CLIENT_SCAN_FILTER_CLEAR,
+ HAL_STATUS_UNSUPPORTED);
+}
+
+static void handle_client_scan_filter_enable(const void *buf, uint16_t len)
+{
+ const struct hal_cmd_gatt_client_scan_filter_enable *cmd = buf;
+
+ DBG("client_if %u", cmd->client_if);
+
+ /* TODO */
+
+ ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT,
+ HAL_OP_GATT_CLIENT_SCAN_FILTER_ENABLE,
+ HAL_STATUS_UNSUPPORTED);
+}
+
+static void handle_client_configure_mtu(const void *buf, uint16_t len)
+{
+ const struct hal_cmd_gatt_client_configure_mtu *cmd = buf;
+ static struct app_connection *conn;
+ uint8_t status;
+
+ DBG("conn_id %u mtu %d", cmd->conn_id, cmd->mtu);
+
+ conn = find_connection_by_id(cmd->conn_id);
+ if (!conn) {
+ status = HAL_STATUS_FAILED;
+ goto failed;
+ }
+
+ /*
+ * currently MTU is always exchanged on connection, just report current
+ * value
+ *
+ * TODO figure out when send failed status in notification
+ * TODO should we fail for BR/EDR?
+ */
+ notify_client_mtu_change(conn, false);
+ status = HAL_STATUS_SUCCESS;
+
+failed:
+ ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT,
+ HAL_OP_GATT_CLIENT_CONFIGURE_MTU,
+ status);
+}
+
+static void handle_client_conn_param_update(const void *buf, uint16_t len)
+{
+ const struct hal_cmd_gatt_client_conn_param_update *cmd = buf;
+ char address[18];
+ bdaddr_t bdaddr;
+
+ android2bdaddr(cmd->address, &bdaddr);
+ ba2str(&bdaddr, address);
+
+ DBG("%s", address);
+
+ /* TODO */
+
+ ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT,
+ HAL_OP_GATT_CLIENT_CONN_PARAM_UPDATE,
+ HAL_STATUS_UNSUPPORTED);
+}
+
+static void handle_client_set_scan_param(const void *buf, uint16_t len)
+{
+ const struct hal_cmd_gatt_client_set_scan_param *cmd = buf;
+
+ DBG("interval %d window %d", cmd->interval, cmd->window);
+
+ /* TODO */
+
+ ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT,
+ HAL_OP_GATT_CLIENT_SET_SCAN_PARAM,
+ HAL_STATUS_UNSUPPORTED);
+}
+
+static void handle_client_setup_multi_adv(const void *buf, uint16_t len)
+{
+ const struct hal_cmd_gatt_client_setup_multi_adv *cmd = buf;
+
+ DBG("client_if %d", cmd->client_if);
+
+ /* TODO */
+
+ ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT,
+ HAL_OP_GATT_CLIENT_SETUP_MULTI_ADV,
+ HAL_STATUS_UNSUPPORTED);
+}
+
+static void handle_client_update_multi_adv(const void *buf, uint16_t len)
+{
+ const struct hal_cmd_gatt_client_update_multi_adv *cmd = buf;
+
+ DBG("client_if %d", cmd->client_if);
+
+ /* TODO */
+
+ ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT,
+ HAL_OP_GATT_CLIENT_UPDATE_MULTI_ADV,
+ HAL_STATUS_UNSUPPORTED);
+}
+
+static void handle_client_setup_multi_adv_inst(const void *buf, uint16_t len)
+{
+ const struct hal_cmd_gatt_client_setup_multi_adv_inst *cmd = buf;
+
+ DBG("client_if %d", cmd->client_if);
+
+ /* TODO */
+
+ ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT,
+ HAL_OP_GATT_CLIENT_SETUP_MULTI_ADV_INST,
+ HAL_STATUS_UNSUPPORTED);
+}
+
+static void handle_client_disable_multi_adv_inst(const void *buf, uint16_t len)
+{
+ const struct hal_cmd_gatt_client_disable_multi_adv_inst *cmd = buf;
+
+ DBG("client_if %d", cmd->client_if);
+
+ /* TODO */
+
+ ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT,
+ HAL_OP_GATT_CLIENT_DISABLE_MULTI_ADV_INST,
+ HAL_STATUS_UNSUPPORTED);
+}
+
+static void handle_client_configure_batchscan(const void *buf, uint16_t len)
+{
+ const struct hal_cmd_gatt_client_configure_batchscan *cmd = buf;
+
+ DBG("client_if %d", cmd->client_if);
+
+ /* TODO */
+
+ ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT,
+ HAL_OP_GATT_CLIENT_CONFIGURE_BATCHSCAN,
+ HAL_STATUS_UNSUPPORTED);
+}
+
+static void handle_client_enable_batchscan(const void *buf, uint16_t len)
+{
+ const struct hal_cmd_gatt_client_enable_batchscan *cmd = buf;
+
+ DBG("client_if %d", cmd->client_if);
+
+ /* TODO */
+
+ ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT,
+ HAL_OP_GATT_CLIENT_ENABLE_BATCHSCAN,
+ HAL_STATUS_UNSUPPORTED);
+}
+
+static void handle_client_disable_batchscan(const void *buf, uint16_t len)
+{
+ const struct hal_cmd_gatt_client_disable_batchscan *cmd = buf;
+
+ DBG("client_if %d", cmd->client_if);
+
+ /* TODO */
+
+ ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT,
+ HAL_OP_GATT_CLIENT_DISABLE_BATCHSCAN,
+ HAL_STATUS_UNSUPPORTED);
+}
+
+static void handle_client_read_batchscan_reports(const void *buf, uint16_t len)
+{
+ const struct hal_cmd_gatt_client_read_batchscan_reports *cmd = buf;
+
+ DBG("client_if %d", cmd->client_if);
+
+ /* TODO */
+
+ ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT,
+ HAL_OP_GATT_CLIENT_READ_BATCHSCAN_REPORTS,
+ HAL_STATUS_UNSUPPORTED);
+}
+
+static const struct ipc_handler cmd_handlers[] = {
+ /* HAL_OP_GATT_CLIENT_REGISTER */
+ { handle_client_register, false,
+ sizeof(struct hal_cmd_gatt_client_register) },
+ /* HAL_OP_GATT_CLIENT_UNREGISTER */
+ { handle_client_unregister, false,
+ sizeof(struct hal_cmd_gatt_client_unregister) },
+ /* HAL_OP_GATT_CLIENT_SCAN */
+ { handle_client_scan, false,
+ sizeof(struct hal_cmd_gatt_client_scan) },
+ /* HAL_OP_GATT_CLIENT_CONNECT */
+ { handle_client_connect, false,
+ sizeof(struct hal_cmd_gatt_client_connect) },
+ /* HAL_OP_GATT_CLIENT_DISCONNECT */
+ { handle_client_disconnect, false,
+ sizeof(struct hal_cmd_gatt_client_disconnect) },
+ /* HAL_OP_GATT_CLIENT_LISTEN */
+ { handle_client_listen, false,
+ sizeof(struct hal_cmd_gatt_client_listen) },
+ /* HAL_OP_GATT_CLIENT_REFRESH */
+ { handle_client_refresh, false,
+ sizeof(struct hal_cmd_gatt_client_refresh) },
+ /* HAL_OP_GATT_CLIENT_SEARCH_SERVICE */
+ { handle_client_search_service, true,
+ sizeof(struct hal_cmd_gatt_client_search_service) },
+ /* HAL_OP_GATT_CLIENT_GET_INCLUDED_SERVICE */
+ { handle_client_get_included_service, true,
+ sizeof(struct hal_cmd_gatt_client_get_included_service) },
+ /* HAL_OP_GATT_CLIENT_GET_CHARACTERISTIC */
+ { handle_client_get_characteristic, true,
+ sizeof(struct hal_cmd_gatt_client_get_characteristic) },
+ /* HAL_OP_GATT_CLIENT_GET_DESCRIPTOR */
+ { handle_client_get_descriptor, true,
+ sizeof(struct hal_cmd_gatt_client_get_descriptor) },
+ /* HAL_OP_GATT_CLIENT_READ_CHARACTERISTIC */
+ { handle_client_read_characteristic, false,
+ sizeof(struct hal_cmd_gatt_client_read_characteristic) },
+ /* HAL_OP_GATT_CLIENT_WRITE_CHARACTERISTIC */
+ { handle_client_write_characteristic, true,
+ sizeof(struct hal_cmd_gatt_client_write_characteristic) },
+ /* HAL_OP_GATT_CLIENT_READ_DESCRIPTOR */
+ { handle_client_read_descriptor, false,
+ sizeof(struct hal_cmd_gatt_client_read_descriptor) },
+ /* HAL_OP_GATT_CLIENT_WRITE_DESCRIPTOR */
+ { handle_client_write_descriptor, true,
+ sizeof(struct hal_cmd_gatt_client_write_descriptor) },
+ /* HAL_OP_GATT_CLIENT_EXECUTE_WRITE */
+ { handle_client_execute_write, false,
+ sizeof(struct hal_cmd_gatt_client_execute_write)},
+ /* HAL_OP_GATT_CLIENT_REGISTER_FOR_NOTIFICATION */
+ { handle_client_register_for_notification, false,
+ sizeof(struct hal_cmd_gatt_client_register_for_notification) },
+ /* HAL_OP_GATT_CLIENT_DEREGISTER_FOR_NOTIFICATION */
+ { handle_client_deregister_for_notification, false,
+ sizeof(struct hal_cmd_gatt_client_deregister_for_notification) },
+ /* HAL_OP_GATT_CLIENT_READ_REMOTE_RSSI */
+ { handle_client_read_remote_rssi, false,
+ sizeof(struct hal_cmd_gatt_client_read_remote_rssi) },
+ /* HAL_OP_GATT_CLIENT_GET_DEVICE_TYPE */
+ { handle_client_get_device_type, false,
+ sizeof(struct hal_cmd_gatt_client_get_device_type) },
+ /* HAL_OP_GATT_CLIENT_SET_ADV_DATA */
+ { handle_client_set_adv_data, true,
+ sizeof(struct hal_cmd_gatt_client_set_adv_data) },
+ /* HAL_OP_GATT_CLIENT_TEST_COMMAND */
+ { handle_client_test_command, false,
+ sizeof(struct hal_cmd_gatt_client_test_command) },
+ /* HAL_OP_GATT_SERVER_REGISTER */
+ { handle_server_register, false,
+ sizeof(struct hal_cmd_gatt_server_register) },
+ /* HAL_OP_GATT_SERVER_UNREGISTER */
+ { handle_server_unregister, false,
+ sizeof(struct hal_cmd_gatt_server_unregister) },
+ /* HAL_OP_GATT_SERVER_CONNECT */
+ { handle_server_connect, false,
+ sizeof(struct hal_cmd_gatt_server_connect) },
+ /* HAL_OP_GATT_SERVER_DISCONNECT */
+ { handle_server_disconnect, false,
+ sizeof(struct hal_cmd_gatt_server_disconnect) },
+ /* HAL_OP_GATT_SERVER_ADD_SERVICE */
+ { handle_server_add_service, false,
+ sizeof(struct hal_cmd_gatt_server_add_service) },
+ /* HAL_OP_GATT_SERVER_ADD_INC_SERVICE */
+ { handle_server_add_included_service, false,
+ sizeof(struct hal_cmd_gatt_server_add_inc_service) },
+ /* HAL_OP_GATT_SERVER_ADD_CHARACTERISTIC */
+ { handle_server_add_characteristic, false,
+ sizeof(struct hal_cmd_gatt_server_add_characteristic) },
+ /* HAL_OP_GATT_SERVER_ADD_DESCRIPTOR */
+ { handle_server_add_descriptor, false,
+ sizeof(struct hal_cmd_gatt_server_add_descriptor) },
+ /* HAL_OP_GATT_SERVER_START_SERVICE */
+ { handle_server_start_service, false,
+ sizeof(struct hal_cmd_gatt_server_start_service) },
+ /* HAL_OP_GATT_SERVER_STOP_SERVICE */
+ { handle_server_stop_service, false,
+ sizeof(struct hal_cmd_gatt_server_stop_service) },
+ /* HAL_OP_GATT_SERVER_DELETE_SERVICE */
+ { handle_server_delete_service, false,
+ sizeof(struct hal_cmd_gatt_server_delete_service) },
+ /* HAL_OP_GATT_SERVER_SEND_INDICATION */
+ { handle_server_send_indication, true,
+ sizeof(struct hal_cmd_gatt_server_send_indication) },
+ /* HAL_OP_GATT_SERVER_SEND_RESPONSE */
+ { handle_server_send_response, true,
+ sizeof(struct hal_cmd_gatt_server_send_response) },
+ /* HAL_OP_GATT_CLIENT_SCAN_FILTER_SETUP */
+ { handle_client_scan_filter_setup, false,
+ sizeof(struct hal_cmd_gatt_client_scan_filter_setup) },
+ /* HAL_OP_GATT_CLIENT_SCAN_FILTER_ADD_REMOVE */
+ { handle_client_scan_filter_add_remove, true,
+ sizeof(struct hal_cmd_gatt_client_scan_filter_add_remove) },
+ /* HAL_OP_GATT_CLIENT_SCAN_FILTER_CLEAR */
+ { handle_client_scan_filter_clear, false,
+ sizeof(struct hal_cmd_gatt_client_scan_filter_clear) },
+ /* HAL_OP_GATT_CLIENT_SCAN_FILTER_ENABLE */
+ { handle_client_scan_filter_enable, false,
+ sizeof(struct hal_cmd_gatt_client_scan_filter_enable) },
+ /* HAL_OP_GATT_CLIENT_CONFIGURE_MTU */
+ { handle_client_configure_mtu, false,
+ sizeof(struct hal_cmd_gatt_client_configure_mtu) },
+ /* HAL_OP_GATT_CLIENT_CONN_PARAM_UPDATE */
+ { handle_client_conn_param_update, false,
+ sizeof(struct hal_cmd_gatt_client_conn_param_update) },
+ /* HAL_OP_GATT_CLIENT_SET_SCAN_PARAM */
+ { handle_client_set_scan_param, false,
+ sizeof(struct hal_cmd_gatt_client_set_scan_param) },
+ /* HAL_OP_GATT_CLIENT_SETUP_MULTI_ADV */
+ { handle_client_setup_multi_adv, false,
+ sizeof(struct hal_cmd_gatt_client_setup_multi_adv) },
+ /* HAL_OP_GATT_CLIENT_UPDATE_MULTI_ADV */
+ { handle_client_update_multi_adv, false,
+ sizeof(struct hal_cmd_gatt_client_update_multi_adv) },
+ /* HAL_OP_GATT_CLIENT_SETUP_MULTI_ADV_INST */
+ { handle_client_setup_multi_adv_inst, false,
+ sizeof(struct hal_cmd_gatt_client_setup_multi_adv_inst) },
+ /* HAL_OP_GATT_CLIENT_DISABLE_MULTI_ADV_INST */
+ { handle_client_disable_multi_adv_inst, false,
+ sizeof(struct hal_cmd_gatt_client_disable_multi_adv_inst) },
+ /* HAL_OP_GATT_CLIENT_CONFIGURE_BATCHSCAN */
+ { handle_client_configure_batchscan, false,
+ sizeof(struct hal_cmd_gatt_client_configure_batchscan) },
+ /* HAL_OP_GATT_CLIENT_ENABLE_BATCHSCAN */
+ { handle_client_enable_batchscan, false,
+ sizeof(struct hal_cmd_gatt_client_enable_batchscan) },
+ /* HAL_OP_GATT_CLIENT_DISABLE_BATCHSCAN */
+ { handle_client_disable_batchscan, false,
+ sizeof(struct hal_cmd_gatt_client_disable_batchscan) },
+ /* HAL_OP_GATT_CLIENT_READ_BATCHSCAN_REPORTS */
+ { handle_client_read_batchscan_reports, false,
+ sizeof(struct hal_cmd_gatt_client_read_batchscan_reports) },
+};
+
+static uint8_t read_by_type(const uint8_t *cmd, uint16_t cmd_len,
+ struct gatt_device *device)
+{
+ uint16_t start, end;
+ uint16_t len = 0;
+ bt_uuid_t uuid;
+ struct queue *q;
+
+ DBG("");
+
+ switch (cmd[0]) {
+ case ATT_OP_READ_BY_TYPE_REQ:
+ len = dec_read_by_type_req(cmd, cmd_len, &start, &end, &uuid);
+ break;
+ case ATT_OP_READ_BY_GROUP_REQ:
+ len = dec_read_by_grp_req(cmd, cmd_len, &start, &end, &uuid);
+ break;
+ default:
+ break;
+ }
+
+ if (!len)
+ return ATT_ECODE_INVALID_PDU;
+
+ if (start > end || start == 0)
+ return ATT_ECODE_INVALID_HANDLE;
+
+ q = queue_new();
+
+ switch (cmd[0]) {
+ case ATT_OP_READ_BY_TYPE_REQ:
+ gatt_db_read_by_type(gatt_db, start, end, uuid, q);
+ break;
+ case ATT_OP_READ_BY_GROUP_REQ:
+ gatt_db_read_by_group_type(gatt_db, start, end, uuid, q);
+ break;
+ default:
+ break;
+ }
+
+ if (queue_isempty(q)) {
+ queue_destroy(q, NULL);
+ return ATT_ECODE_ATTR_NOT_FOUND;
+ }
+
+ while (queue_peek_head(q)) {
+ struct pending_request *data;
+ struct gatt_db_attribute *attrib = queue_pop_head(q);
+
+ data = new0(struct pending_request, 1);
+ data->attrib = attrib;
+ queue_push_tail(device->pending_requests, data);
+ }
+
+ queue_destroy(q, NULL);
+ process_dev_pending_requests(device, cmd[0]);
+
+ return 0;
+}
+
+static uint8_t read_request(const uint8_t *cmd, uint16_t cmd_len,
+ struct gatt_device *dev)
+{
+ struct gatt_db_attribute *attrib;
+ uint16_t handle;
+ uint16_t len;
+ uint16_t offset;
+ struct pending_request *data;
+
+ DBG("");
+
+ switch (cmd[0]) {
+ case ATT_OP_READ_BLOB_REQ:
+ len = dec_read_blob_req(cmd, cmd_len, &handle, &offset);
+ if (!len)
+ return ATT_ECODE_INVALID_PDU;
+ break;
+ case ATT_OP_READ_REQ:
+ len = dec_read_req(cmd, cmd_len, &handle);
+ if (!len)
+ return ATT_ECODE_INVALID_PDU;
+ offset = 0;
+ break;
+ default:
+ error("gatt: Unexpected read type 0x%02x", cmd[0]);
+ return ATT_ECODE_REQ_NOT_SUPP;
+ }
+
+ attrib = gatt_db_get_attribute(gatt_db, handle);
+ if (attrib == 0)
+ return ATT_ECODE_INVALID_HANDLE;
+
+ data = new0(struct pending_request, 1);
+ data->offset = offset;
+ data->attrib = attrib;
+ queue_push_tail(dev->pending_requests, data);
+
+ process_dev_pending_requests(dev, cmd[0]);
+
+ return 0;
+}
+
+static uint8_t mtu_att_handle(const uint8_t *cmd, uint16_t cmd_len,
+ struct gatt_device *dev)
+{
+ uint16_t rmtu, mtu, len;
+ size_t length;
+ uint8_t *rsp;
+
+ DBG("");
+
+ len = dec_mtu_req(cmd, cmd_len, &rmtu);
+ if (!len)
+ return ATT_ECODE_INVALID_PDU;
+
+ /* MTU exchange shall not be used on BR/EDR - Vol 3. Part G. 4.3.1 */
+ if (get_cid(dev) != ATT_CID)
+ return ATT_ECODE_UNLIKELY;
+
+ if (!get_local_mtu(dev, &mtu))
+ return ATT_ECODE_UNLIKELY;
+
+ if (!update_mtu(dev, rmtu))
+ return ATT_ECODE_UNLIKELY;
+
+ rsp = g_attrib_get_buffer(dev->attrib, &length);
+
+ /* Respond with our MTU */
+ len = enc_mtu_resp(mtu, rsp, length);
+ if (!g_attrib_send(dev->attrib, 0, rsp, len, NULL, NULL, NULL))
+ return ATT_ECODE_UNLIKELY;
+
+ return 0;
+}
+
+static uint8_t find_info_handle(const uint8_t *cmd, uint16_t cmd_len,
+ uint8_t *rsp, size_t rsp_size, uint16_t *length)
+{
+ struct gatt_db_attribute *attrib;
+ struct queue *q, *temp;
+ struct att_data_list *adl;
+ int iterator = 0;
+ uint16_t start, end;
+ uint16_t len, queue_len;
+ uint8_t format;
+ uint8_t ret = 0;
+
+ DBG("");
+
+ len = dec_find_info_req(cmd, cmd_len, &start, &end);
+ if (!len)
+ return ATT_ECODE_INVALID_PDU;
+
+ if (start > end || start == 0)
+ return ATT_ECODE_INVALID_HANDLE;
+
+ q = queue_new();
+
+ gatt_db_find_information(gatt_db, start, end, q);
+
+ if (queue_isempty(q)) {
+ queue_destroy(q, NULL);
+ return ATT_ECODE_ATTR_NOT_FOUND;
+ }
+
+ temp = queue_new();
+
+ attrib = queue_peek_head(q);
+ /* UUIDS can be only 128 bit and 16 bit */
+ len = bt_uuid_len(gatt_db_attribute_get_type(attrib));
+ if (len != 2 && len != 16) {
+ queue_destroy(q, NULL);
+ queue_destroy(temp, NULL);
+ return ATT_ECODE_UNLIKELY;
+ }
+
+ while (attrib) {
+ const bt_uuid_t *type;
+
+ type = gatt_db_attribute_get_type(attrib);
+ if (bt_uuid_len(type) != len)
+ break;
+
+ queue_push_tail(temp, queue_pop_head(q));
+ attrib = queue_peek_head(q);
+ }
+
+ queue_destroy(q, NULL);
+
+ queue_len = queue_length(temp);
+ adl = att_data_list_alloc(queue_len, len + sizeof(uint16_t));
+ if (!adl) {
+ queue_destroy(temp, NULL);
+ return ATT_ECODE_INSUFF_RESOURCES;
+ }
+
+ while (queue_peek_head(temp)) {
+ uint8_t *value;
+ const bt_uuid_t *type;
+ struct gatt_db_attribute *attrib = queue_pop_head(temp);
+ uint16_t handle;
+
+ type = gatt_db_attribute_get_type(attrib);
+ if (!type)
+ break;
+
+ value = adl->data[iterator++];
+
+ handle = gatt_db_attribute_get_handle(attrib);
+ put_le16(handle, value);
+ memcpy(&value[2], &type->value, len);
+ }
+
+ if (len == 2)
+ format = ATT_FIND_INFO_RESP_FMT_16BIT;
+ else
+ format = ATT_FIND_INFO_RESP_FMT_128BIT;
+
+ len = enc_find_info_resp(format, adl, rsp, rsp_size);
+ if (!len)
+ ret = ATT_ECODE_UNLIKELY;
+
+ *length = len;
+ att_data_list_free(adl);
+ queue_destroy(temp, NULL);
+
+ return ret;
+}
+
+struct find_by_type_request_data {
+ struct gatt_device *device;
+ uint8_t *search_value;
+ size_t search_vlen;
+ uint8_t error;
+};
+
+static void find_by_type_request_cb(struct gatt_db_attribute *attrib,
+ void *user_data)
+{
+ struct find_by_type_request_data *find_data = user_data;
+ struct pending_request *request_data;
+
+ if (find_data->error)
+ return;
+
+ request_data = new0(struct pending_request, 1);
+ request_data->filter_value = malloc0(find_data->search_vlen);
+ if (!request_data->filter_value) {
+ destroy_pending_request(request_data);
+ find_data->error = ATT_ECODE_INSUFF_RESOURCES;
+ return;
+ }
+
+ request_data->attrib = attrib;
+ request_data->filter_vlen = find_data->search_vlen;
+ memcpy(request_data->filter_value, find_data->search_value,
+ find_data->search_vlen);
+
+ queue_push_tail(find_data->device->pending_requests, request_data);
+}
+
+static uint8_t find_by_type_request(const uint8_t *cmd, uint16_t cmd_len,
+ struct gatt_device *device)
+{
+ uint8_t search_value[cmd_len];
+ size_t search_vlen;
+ uint16_t start, end;
+ bt_uuid_t uuid;
+ uint16_t len;
+ struct find_by_type_request_data data;
+
+ DBG("");
+
+ len = dec_find_by_type_req(cmd, cmd_len, &start, &end, &uuid,
+ search_value, &search_vlen);
+ if (!len)
+ return ATT_ECODE_INVALID_PDU;
+
+ if (start > end || start == 0)
+ return ATT_ECODE_INVALID_HANDLE;
+
+ data.error = 0;
+ data.search_vlen = search_vlen;
+ data.search_value = search_value;
+ data.device = device;
+
+ if (gatt_db_find_by_type(gatt_db, start, end, &uuid,
+ find_by_type_request_cb, &data) == 0) {
+ size_t mtu;
+ uint8_t *rsp = g_attrib_get_buffer(device->attrib, &mtu);
+
+ len = enc_error_resp(ATT_OP_FIND_BY_TYPE_REQ, start,
+ ATT_ECODE_ATTR_NOT_FOUND, rsp, mtu);
+ g_attrib_send(device->attrib, 0, rsp, len, NULL, NULL, NULL);
+ return 0;
+ }
+
+ if (!data.error)
+ process_dev_pending_requests(device, ATT_OP_FIND_BY_TYPE_REQ);
+
+ return data.error;
+}
+
+static void write_confirm(struct gatt_db_attribute *attrib,
+ int err, void *user_data)
+{
+ if (!err)
+ return;
+
+ error("Error writting attribute %p", attrib);
+}
+
+static void write_cmd_request(const uint8_t *cmd, uint16_t cmd_len,
+ struct gatt_device *dev)
+{
+ uint8_t value[cmd_len];
+ struct gatt_db_attribute *attrib;
+ uint32_t permissions;
+ uint16_t handle;
+ uint16_t len;
+ size_t vlen;
+
+ len = dec_write_cmd(cmd, cmd_len, &handle, value, &vlen);
+ if (!len)
+ return;
+
+ if (handle == 0)
+ return;
+
+ attrib = gatt_db_get_attribute(gatt_db, handle);
+ if (!attrib)
+ return;
+
+ permissions = gatt_db_attribute_get_permissions(attrib);
+
+ if (check_device_permissions(dev, cmd[0], permissions))
+ return;
+
+ gatt_db_attribute_write(attrib, 0, value, vlen, cmd[0],
+ g_attrib_get_att(dev->attrib),
+ write_confirm, NULL);
+}
+
+static void write_signed_cmd_request(const uint8_t *cmd, uint16_t cmd_len,
+ struct gatt_device *dev)
+{
+ uint8_t value[cmd_len];
+ uint8_t s[ATT_SIGNATURE_LEN];
+ struct gatt_db_attribute *attrib;
+ uint32_t permissions;
+ uint16_t handle;
+ uint16_t len;
+ size_t vlen;
+ uint8_t csrk[16];
+ uint32_t sign_cnt;
+
+ if (get_cid(dev) != ATT_CID) {
+ error("gatt: Remote tries write signed on BR/EDR bearer");
+ connection_cleanup(dev);
+ return;
+ }
+
+ if (get_sec_level(dev) != BT_SECURITY_LOW) {
+ error("gatt: Remote tries write signed on encrypted link");
+ connection_cleanup(dev);
+ return;
+ }
+
+ if (!bt_get_csrk(&dev->bdaddr, false, csrk, &sign_cnt, NULL)) {
+ error("gatt: No valid csrk from remote device");
+ return;
+ }
+
+ len = dec_signed_write_cmd(cmd, cmd_len, &handle, value, &vlen, s);
+
+ if (handle == 0)
+ return;
+
+ attrib = gatt_db_get_attribute(gatt_db, handle);
+ if (!attrib)
+ return;
+
+ permissions = gatt_db_attribute_get_permissions(attrib);
+
+ if (check_device_permissions(dev, cmd[0], permissions))
+ return;
+
+ if (len) {
+ uint8_t t[ATT_SIGNATURE_LEN];
+ uint32_t r_sign_cnt = get_le32(s);
+
+ if (r_sign_cnt < sign_cnt) {
+ error("gatt: Invalid sign counter (%d<%d)",
+ r_sign_cnt, sign_cnt);
+ return;
+ }
+
+ /* Generate signature and verify it */
+ if (!bt_crypto_sign_att(crypto, csrk, cmd,
+ cmd_len - ATT_SIGNATURE_LEN,
+ r_sign_cnt, t)) {
+ error("gatt: Error when generating att signature");
+ return;
+ }
+
+ if (memcmp(t, s, ATT_SIGNATURE_LEN)) {
+ error("gatt: signature does not match");
+ return;
+ }
+ /* Signature OK, proceed with write */
+ bt_update_sign_counter(&dev->bdaddr, false, r_sign_cnt);
+ gatt_db_attribute_write(attrib, 0, value, vlen, cmd[0],
+ g_attrib_get_att(dev->attrib),
+ write_confirm, NULL);
+ }
+}
+
+static void attribute_write_cb(struct gatt_db_attribute *attrib, int err,
+ void *user_data)
+{
+ struct pending_request *data = user_data;
+ uint8_t error = err_to_att(err);
+
+ DBG("");
+
+ data->attrib = attrib;
+ data->error = error;
+ data->completed = true;
+}
+
+static uint8_t write_req_request(const uint8_t *cmd, uint16_t cmd_len,
+ struct gatt_device *dev)
+{
+ uint8_t value[cmd_len];
+ struct pending_request *data;
+ struct gatt_db_attribute *attrib;
+ uint32_t permissions;
+ uint16_t handle;
+ uint16_t len;
+ uint8_t error;
+ size_t vlen;
+
+ len = dec_write_req(cmd, cmd_len, &handle, value, &vlen);
+ if (!len)
+ return ATT_ECODE_INVALID_PDU;
+
+ if (handle == 0)
+ return ATT_ECODE_INVALID_HANDLE;
+
+ attrib = gatt_db_get_attribute(gatt_db, handle);
+ if (!attrib)
+ return ATT_ECODE_ATTR_NOT_FOUND;
+
+ permissions = gatt_db_attribute_get_permissions(attrib);
+
+ error = check_device_permissions(dev, cmd[0], permissions);
+ if (error)
+ return error;
+
+ data = new0(struct pending_request, 1);
+ data->attrib = attrib;
+
+ queue_push_tail(dev->pending_requests, data);
+
+ if (!gatt_db_attribute_write(attrib, 0, value, vlen, cmd[0],
+ g_attrib_get_att(dev->attrib),
+ attribute_write_cb, data)) {
+ queue_remove(dev->pending_requests, data);
+ free(data);
+ return ATT_ECODE_UNLIKELY;
+ }
+
+ send_dev_complete_response(dev, cmd[0]);
+
+ return 0;
+}
+
+static uint8_t write_prep_request(const uint8_t *cmd, uint16_t cmd_len,
+ struct gatt_device *dev)
+{
+ uint8_t value[cmd_len];
+ struct pending_request *data;
+ struct gatt_db_attribute *attrib;
+ uint32_t permissions;
+ uint16_t handle;
+ uint16_t offset;
+ uint8_t error;
+ uint16_t len;
+ size_t vlen;
+
+ len = dec_prep_write_req(cmd, cmd_len, &handle, &offset,
+ value, &vlen);
+ if (!len)
+ return ATT_ECODE_INVALID_PDU;
+
+ if (handle == 0)
+ return ATT_ECODE_INVALID_HANDLE;
+
+ attrib = gatt_db_get_attribute(gatt_db, handle);
+ if (!attrib)
+ return ATT_ECODE_ATTR_NOT_FOUND;
+
+ permissions = gatt_db_attribute_get_permissions(attrib);
+
+ error = check_device_permissions(dev, cmd[0], permissions);
+ if (error)
+ return error;
+
+ data = new0(struct pending_request, 1);
+ data->attrib = attrib;
+ data->offset = offset;
+
+ queue_push_tail(dev->pending_requests, data);
+
+ data->value = g_memdup(value, vlen);
+ data->length = vlen;
+
+ if (!gatt_db_attribute_write(attrib, offset, value, vlen, cmd[0],
+ g_attrib_get_att(dev->attrib),
+ attribute_write_cb, data)) {
+ queue_remove(dev->pending_requests, data);
+ g_free(data->value);
+ free(data);
+
+ return ATT_ECODE_UNLIKELY;
+ }
+
+ send_dev_complete_response(dev, cmd[0]);
+
+ return 0;
+}
+
+static void send_server_write_execute_notify(void *data, void *user_data)
+{
+ struct hal_ev_gatt_server_request_exec_write *ev = user_data;
+ struct pending_trans_data *transaction;
+ struct app_connection *conn = data;
+
+ if (!conn->wait_execute_write)
+ return;
+
+ ev->conn_id = conn->id;
+
+ transaction = conn_add_transact(conn, ATT_OP_EXEC_WRITE_REQ, NULL, 0);
+
+ ev->trans_id = transaction->id;
+
+ ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT,
+ HAL_EV_GATT_SERVER_REQUEST_EXEC_WRITE, sizeof(*ev), ev);
+}
+
+static uint8_t write_execute_request(const uint8_t *cmd, uint16_t cmd_len,
+ struct gatt_device *dev)
+{
+ struct hal_ev_gatt_server_request_exec_write ev;
+ uint8_t value;
+ struct pending_request *data;
+
+ /*
+ * Check if there was any write prep before.
+ * TODO: Try to find better error code if possible
+ */
+ if (!pending_execute_write())
+ return ATT_ECODE_UNLIKELY;
+
+ if (!dec_exec_write_req(cmd, cmd_len, &value))
+ return ATT_ECODE_INVALID_PDU;
+
+ memset(&ev, 0, sizeof(ev));
+ bdaddr2android(&dev->bdaddr, &ev.bdaddr);
+ ev.exec_write = value;
+
+ data = new0(struct pending_request, 1);
+
+ queue_push_tail(dev->pending_requests, data);
+
+ queue_foreach(app_connections, send_server_write_execute_notify, &ev);
+ send_dev_complete_response(dev, cmd[0]);
+
+ return 0;
+}
+
+static void att_handler(const uint8_t *ipdu, uint16_t len, gpointer user_data)
+{
+ struct gatt_device *dev = user_data;
+ uint8_t status;
+ uint16_t resp_length = 0;
+ size_t length;
+ uint8_t *opdu = g_attrib_get_buffer(dev->attrib, &length);
+
+ DBG("op 0x%02x", ipdu[0]);
+
+ if (len > length) {
+ error("gatt: Too much data on ATT socket %p", opdu);
+ status = ATT_ECODE_INVALID_PDU;
+ goto done;
+ }
+
+ switch (ipdu[0]) {
+ case ATT_OP_READ_BY_GROUP_REQ:
+ case ATT_OP_READ_BY_TYPE_REQ:
+ status = read_by_type(ipdu, len, dev);
+ break;
+ case ATT_OP_READ_REQ:
+ case ATT_OP_READ_BLOB_REQ:
+ status = read_request(ipdu, len, dev);
+ break;
+ case ATT_OP_MTU_REQ:
+ status = mtu_att_handle(ipdu, len, dev);
+ break;
+ case ATT_OP_FIND_INFO_REQ:
+ status = find_info_handle(ipdu, len, opdu, length,
+ &resp_length);
+ break;
+ case ATT_OP_WRITE_REQ:
+ status = write_req_request(ipdu, len, dev);
+ break;
+ case ATT_OP_WRITE_CMD:
+ write_cmd_request(ipdu, len, dev);
+ /* No response on write cmd */
+ return;
+ case ATT_OP_SIGNED_WRITE_CMD:
+ write_signed_cmd_request(ipdu, len, dev);
+ /* No response on write signed cmd */
+ return;
+ case ATT_OP_PREP_WRITE_REQ:
+ status = write_prep_request(ipdu, len, dev);
+ break;
+ case ATT_OP_FIND_BY_TYPE_REQ:
+ status = find_by_type_request(ipdu, len, dev);
+ break;
+ case ATT_OP_EXEC_WRITE_REQ:
+ status = write_execute_request(ipdu, len, dev);
+ break;
+ case ATT_OP_READ_MULTI_REQ:
+ default:
+ DBG("Unsupported request 0x%02x", ipdu[0]);
+ status = ATT_ECODE_REQ_NOT_SUPP;
+ break;
+ }
+
+done:
+ if (status)
+ resp_length = enc_error_resp(ipdu[0], 0x0000, status, opdu,
+ length);
+
+ g_attrib_send(dev->attrib, 0, opdu, resp_length, NULL, NULL, NULL);
+}
+
+static void connect_confirm(GIOChannel *io, void *user_data)
+{
+ struct gatt_device *dev;
+ bdaddr_t dst;
+ GError *gerr = NULL;
+
+ DBG("");
+
+ bt_io_get(io, &gerr, BT_IO_OPT_DEST_BDADDR, &dst, BT_IO_OPT_INVALID);
+ if (gerr) {
+ error("gatt: bt_io_get: %s", gerr->message);
+ g_error_free(gerr);
+ return;
+ }
+
+ /* TODO Handle collision */
+ dev = find_device_by_addr(&dst);
+ if (!dev) {
+ dev = create_device(&dst);
+ } else {
+ if ((dev->state != DEVICE_DISCONNECTED) &&
+ !(dev->state == DEVICE_CONNECT_INIT &&
+ bt_kernel_conn_control())) {
+ char addr[18];
+
+ ba2str(&dst, addr);
+ info("gatt: Rejecting incoming connection from %s",
+ addr);
+ goto drop;
+ }
+ }
+
+ if (!bt_io_accept(io, connect_cb, device_ref(dev), NULL, NULL)) {
+ error("gatt: failed to accept connection");
+ device_unref(dev);
+ goto drop;
+ }
+
+ queue_foreach(listen_apps, create_app_connection, dev);
+ device_set_state(dev, DEVICE_CONNECT_READY);
+
+ return;
+
+drop:
+ g_io_channel_shutdown(io, TRUE, NULL);
+}
+
+struct gap_srvc_handles {
+ struct gatt_db_attribute *srvc;
+
+ /* Characteristics */
+ struct gatt_db_attribute *dev_name;
+ struct gatt_db_attribute *appear;
+ struct gatt_db_attribute *priv;
+};
+
+static struct gap_srvc_handles gap_srvc_data;
+
+#define APPEARANCE_GENERIC_PHONE 0x0040
+#define PERIPHERAL_PRIVACY_DISABLE 0x00
+
+static void device_name_read_cb(struct gatt_db_attribute *attrib,
+ unsigned int id, uint16_t offset,
+ uint8_t opcode, struct bt_att *att,
+ void *user_data)
+{
+ const char *name = bt_get_adapter_name();
+
+ gatt_db_attribute_read_result(attrib, id, 0, (void *) name,
+ strlen(name));
+}
+
+static void register_gap_service(void)
+{
+ uint16_t start, end;
+ bt_uuid_t uuid;
+
+ /* GAP UUID */
+ bt_uuid16_create(&uuid, 0x1800);
+ gap_srvc_data.srvc = gatt_db_add_service(gatt_db, &uuid, true, 7);
+
+ /* Device name characteristic */
+ bt_uuid16_create(&uuid, GATT_CHARAC_DEVICE_NAME);
+ gap_srvc_data.dev_name =
+ gatt_db_service_add_characteristic(gap_srvc_data.srvc,
+ &uuid, GATT_PERM_READ,
+ GATT_CHR_PROP_READ,
+ device_name_read_cb,
+ NULL, NULL);
+
+ /* Appearance */
+ bt_uuid16_create(&uuid, GATT_CHARAC_APPEARANCE);
+
+ gap_srvc_data.appear =
+ gatt_db_service_add_characteristic(gap_srvc_data.srvc,
+ &uuid, GATT_PERM_READ,
+ GATT_CHR_PROP_READ,
+ NULL, NULL, NULL);
+ if (gap_srvc_data.appear) {
+ uint16_t value;
+ /* Store appearance into db */
+ value = cpu_to_le16(APPEARANCE_GENERIC_PHONE);
+ gatt_db_attribute_write(gap_srvc_data.appear, 0,
+ (void *) &value, sizeof(value),
+ ATT_OP_WRITE_REQ, NULL,
+ write_confirm, NULL);
+ }
+
+ /* Pripheral privacy flag */
+ bt_uuid16_create(&uuid, GATT_CHARAC_PERIPHERAL_PRIV_FLAG);
+ gap_srvc_data.priv =
+ gatt_db_service_add_characteristic(gap_srvc_data.srvc,
+ &uuid, GATT_PERM_READ,
+ GATT_CHR_PROP_READ,
+ NULL, NULL, NULL);
+ if (gap_srvc_data.priv) {
+ uint8_t value;
+
+ /* Store privacy into db */
+ value = PERIPHERAL_PRIVACY_DISABLE;
+ gatt_db_attribute_write(gap_srvc_data.priv, 0,
+ &value, sizeof(value),
+ ATT_OP_WRITE_REQ, NULL,
+ write_confirm, NULL);
+ }
+
+ gatt_db_service_set_active(gap_srvc_data.srvc , true);
+
+ /* SDP */
+ bt_uuid16_create(&uuid, 0x1800);
+ gatt_db_attribute_get_service_handles(gap_srvc_data.srvc, &start, &end);
+ gap_sdp_handle = add_sdp_record(&uuid, start, end,
+ "Generic Access Profile");
+ if (!gap_sdp_handle)
+ error("gatt: Failed to register GAP SDP record");
+}
+
+static void device_info_read_cb(struct gatt_db_attribute *attrib,
+ unsigned int id, uint16_t offset,
+ uint8_t opcode, struct bt_att *att,
+ void *user_data)
+{
+ char *buf = user_data;
+
+ gatt_db_attribute_read_result(attrib, id, 0, user_data, strlen(buf));
+}
+
+static void device_info_read_system_id_cb(struct gatt_db_attribute *attrib,
+ unsigned int id, uint16_t offset,
+ uint8_t opcode, struct bt_att *att,
+ void *user_data)
+{
+ uint8_t pdu[8];
+
+ put_le64(bt_config_get_system_id(), pdu);
+
+ gatt_db_attribute_read_result(attrib, id, 0, pdu, sizeof(pdu));
+}
+
+static void device_info_read_pnp_id_cb(struct gatt_db_attribute *attrib,
+ unsigned int id, uint16_t offset,
+ uint8_t opcode, struct bt_att *att,
+ void *user_data)
+{
+ uint8_t pdu[7];
+
+ pdu[0] = bt_config_get_pnp_source();
+ put_le16(bt_config_get_pnp_vendor(), &pdu[1]);
+ put_le16(bt_config_get_pnp_product(), &pdu[3]);
+ put_le16(bt_config_get_pnp_version(), &pdu[5]);
+
+ gatt_db_attribute_read_result(attrib, id, 0, pdu, sizeof(pdu));
+}
+
+static void register_device_info_service(void)
+{
+ bt_uuid_t uuid;
+ struct gatt_db_attribute *service;
+ uint16_t start_handle, end_handle;
+ const char *data;
+ uint32_t enc_perm = GATT_PERM_READ | GATT_PERM_READ_ENCRYPTED;
+
+ DBG("");
+
+ /* Device Information Service */
+ bt_uuid16_create(&uuid, 0x180a);
+ service = gatt_db_add_service(gatt_db, &uuid, true, 17);
+
+ /* User data are not const hence (void *) cast is used */
+ data = bt_config_get_name();
+ if (data) {
+ bt_uuid16_create(&uuid, GATT_CHARAC_MODEL_NUMBER_STRING);
+ gatt_db_service_add_characteristic(service, &uuid,
+ GATT_PERM_READ,
+ GATT_CHR_PROP_READ,
+ device_info_read_cb, NULL,
+ (void *) data);
+ }
+
+ data = bt_config_get_serial();
+ if (data) {
+ bt_uuid16_create(&uuid, GATT_CHARAC_SERIAL_NUMBER_STRING);
+ gatt_db_service_add_characteristic(service, &uuid,
+ enc_perm, GATT_CHR_PROP_READ,
+ device_info_read_cb, NULL,
+ (void *) data);
+ }
+
+ if (bt_config_get_system_id()) {
+ bt_uuid16_create(&uuid, GATT_CHARAC_SYSTEM_ID);
+ gatt_db_service_add_characteristic(service, &uuid,
+ enc_perm, GATT_CHR_PROP_READ,
+ device_info_read_system_id_cb,
+ NULL, NULL);
+ }
+
+ data = bt_config_get_fw_rev();
+ if (data) {
+ bt_uuid16_create(&uuid, GATT_CHARAC_FIRMWARE_REVISION_STRING);
+ gatt_db_service_add_characteristic(service, &uuid,
+ GATT_PERM_READ,
+ GATT_CHR_PROP_READ,
+ device_info_read_cb, NULL,
+ (void *) data);
+ }
+
+ data = bt_config_get_hw_rev();
+ if (data) {
+ bt_uuid16_create(&uuid, GATT_CHARAC_HARDWARE_REVISION_STRING);
+ gatt_db_service_add_characteristic(service, &uuid,
+ GATT_PERM_READ,
+ GATT_CHR_PROP_READ,
+ device_info_read_cb, NULL,
+ (void *) data);
+ }
+
+ bt_uuid16_create(&uuid, GATT_CHARAC_SOFTWARE_REVISION_STRING);
+ gatt_db_service_add_characteristic(service, &uuid, GATT_PERM_READ,
+ GATT_CHR_PROP_READ, device_info_read_cb,
+ NULL, VERSION);
+
+ data = bt_config_get_vendor();
+ if (data) {
+ bt_uuid16_create(&uuid, GATT_CHARAC_MANUFACTURER_NAME_STRING);
+ gatt_db_service_add_characteristic(service, &uuid,
+ GATT_PERM_READ,
+ GATT_CHR_PROP_READ,
+ device_info_read_cb, NULL,
+ (void *) data);
+ }
+
+ if (bt_config_get_pnp_source()) {
+ bt_uuid16_create(&uuid, GATT_CHARAC_PNP_ID);
+ gatt_db_service_add_characteristic(service, &uuid,
+ GATT_PERM_READ,
+ GATT_CHR_PROP_READ,
+ device_info_read_pnp_id_cb,
+ NULL, NULL);
+ }
+
+ gatt_db_service_set_active(service, true);
+
+ /* SDP */
+ bt_uuid16_create(&uuid, 0x180a);
+ gatt_db_attribute_get_service_handles(service, &start_handle,
+ &end_handle);
+ dis_sdp_handle = add_sdp_record(&uuid, start_handle, end_handle,
+ "Device Information Service");
+ if (!dis_sdp_handle)
+ error("gatt: Failed to register DIS SDP record");
+}
+
+static void gatt_srvc_change_write_cb(struct gatt_db_attribute *attrib,
+ unsigned int id, uint16_t offset,
+ const uint8_t *value, size_t len,
+ uint8_t opcode, struct bt_att *att,
+ void *user_data)
+{
+ struct gatt_device *dev;
+ bdaddr_t bdaddr;
+
+ if (!get_dst_addr(att, &bdaddr)) {
+ error("gatt: srvc_change_write_cb, could not obtain BDADDR");
+ return;
+ }
+
+ dev = find_device_by_addr(&bdaddr);
+ if (!dev) {
+ error("gatt: Could not find device ?!");
+ return;
+ }
+
+ if (!bt_device_is_bonded(&bdaddr)) {
+ gatt_db_attribute_write_result(attrib, id,
+ ATT_ECODE_AUTHORIZATION);
+ return;
+ }
+
+ /* 2 octets are expected as CCC value */
+ if (len != 2) {
+ gatt_db_attribute_write_result(attrib, id,
+ ATT_ECODE_INVAL_ATTR_VALUE_LEN);
+ return;
+ }
+
+ /* Set services changed indication value */
+ bt_store_gatt_ccc(&bdaddr, get_le16(value));
+
+ gatt_db_attribute_write_result(attrib, id, 0);
+}
+
+static void gatt_srvc_change_read_cb(struct gatt_db_attribute *attrib,
+ unsigned int id, uint16_t offset,
+ uint8_t opcode, struct bt_att *att,
+ void *user_data)
+{
+ struct gatt_device *dev;
+ uint8_t pdu[2];
+ bdaddr_t bdaddr;
+
+ if (!get_dst_addr(att, &bdaddr)) {
+ error("gatt: srvc_change_read_cb, could not obtain BDADDR");
+ return;
+ }
+
+ dev = find_device_by_addr(&bdaddr);
+ if (!dev) {
+ error("gatt: Could not find device ?!");
+ return;
+ }
+
+ put_le16(bt_get_gatt_ccc(&dev->bdaddr), pdu);
+
+ gatt_db_attribute_read_result(attrib, id, 0, pdu, sizeof(pdu));
+}
+
+static void register_gatt_service(void)
+{
+ struct gatt_db_attribute *service;
+ uint16_t start_handle, end_handle;
+ bt_uuid_t uuid;
+
+ DBG("");
+
+ bt_uuid16_create(&uuid, 0x1801);
+ service = gatt_db_add_service(gatt_db, &uuid, true, 4);
+
+ bt_uuid16_create(&uuid, GATT_CHARAC_SERVICE_CHANGED);
+ service_changed_attrib = gatt_db_service_add_characteristic(service,
+ &uuid, GATT_PERM_NONE,
+ GATT_CHR_PROP_INDICATE,
+ NULL, NULL, NULL);
+
+ bt_uuid16_create(&uuid, GATT_CLIENT_CHARAC_CFG_UUID);
+ gatt_db_service_add_descriptor(service, &uuid,
+ GATT_PERM_READ | GATT_PERM_WRITE,
+ gatt_srvc_change_read_cb,
+ gatt_srvc_change_write_cb, NULL);
+
+ gatt_db_service_set_active(service, true);
+
+ /* SDP */
+ bt_uuid16_create(&uuid, 0x1801);
+ gatt_db_attribute_get_service_handles(service, &start_handle,
+ &end_handle);
+ gatt_sdp_handle = add_sdp_record(&uuid, start_handle, end_handle,
+ "Generic Attribute Profile");
+
+ if (!gatt_sdp_handle)
+ error("gatt: Failed to register GATT SDP record");
+}
+
+static bool start_listening(void)
+{
+ /* BR/EDR socket */
+ bredr_io = bt_io_listen(NULL, connect_confirm, NULL, NULL, NULL,
+ BT_IO_OPT_SOURCE_TYPE, BDADDR_BREDR,
+ BT_IO_OPT_PSM, ATT_PSM,
+ BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_LOW,
+ BT_IO_OPT_INVALID);
+
+ /* LE socket */
+ le_io = bt_io_listen(NULL, connect_confirm, NULL, NULL, NULL,
+ BT_IO_OPT_SOURCE_TYPE, BDADDR_LE_PUBLIC,
+ BT_IO_OPT_CID, ATT_CID,
+ BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_LOW,
+ BT_IO_OPT_INVALID);
+
+ if (!le_io && !bredr_io) {
+ error("gatt: Failed to start listening IO");
+ return false;
+ }
+
+ return true;
+}
+
+static void gatt_paired_cb(const bdaddr_t *addr)
+{
+ struct gatt_device *dev;
+ char address[18];
+ struct app_connection *conn;
+
+ dev = find_device_by_addr(addr);
+ if (!dev)
+ return;
+
+ ba2str(addr, address);
+ DBG("Paired device %s", address);
+
+ /* conn without app is internal one used for search primary services */
+ conn = find_conn_without_app(dev);
+ if (!conn)
+ return;
+
+ if (conn->timeout_id > 0) {
+ g_source_remove(conn->timeout_id);
+ conn->timeout_id = 0;
+ }
+
+ search_dev_for_srvc(conn, NULL);
+}
+
+static void gatt_unpaired_cb(const bdaddr_t *addr)
+{
+ struct gatt_device *dev;
+ char address[18];
+
+ dev = find_device_by_addr(addr);
+ if (!dev)
+ return;
+
+ ba2str(addr, address);
+ DBG("Unpaired device %s", address);
+
+ queue_remove(gatt_devices, dev);
+ destroy_device(dev);
+}
+
+bool bt_gatt_register(struct ipc *ipc, const bdaddr_t *addr)
+{
+ DBG("");
+
+ if (!bt_paired_register(gatt_paired_cb)) {
+ error("gatt: Could not register paired callback");
+ return false;
+ }
+
+ if (!bt_unpaired_register(gatt_unpaired_cb)) {
+ error("gatt: Could not register unpaired callback");
+ return false;
+ }
+
+ if (!start_listening())
+ return false;
+
+ crypto = bt_crypto_new();
+ if (!crypto) {
+ error("gatt: Failed to setup crypto");
+ goto failed;
+ }
+
+ gatt_devices = queue_new();
+ gatt_apps = queue_new();
+ app_connections = queue_new();
+ listen_apps = queue_new();
+ services_sdp = queue_new();
+ gatt_db = gatt_db_new();
+
+ if (!gatt_db) {
+ error("gatt: Failed to allocate memory for database");
+ goto failed;
+ }
+
+ if (!bt_le_register(le_device_found_handler)) {
+ error("gatt: bt_le_register failed");
+ goto failed;
+ }
+
+ bacpy(&adapter_addr, addr);
+
+ hal_ipc = ipc;
+
+ ipc_register(hal_ipc, HAL_SERVICE_ID_GATT, cmd_handlers,
+ G_N_ELEMENTS(cmd_handlers));
+
+ register_gap_service();
+ register_device_info_service();
+ register_gatt_service();
+
+ info("gatt: LE: %s BR/EDR: %s", le_io ? "enabled" : "disabled",
+ bredr_io ? "enabled" : "disabled");
+
+ return true;
+
+failed:
+
+ bt_paired_unregister(gatt_paired_cb);
+ bt_unpaired_unregister(gatt_unpaired_cb);
+
+ queue_destroy(gatt_apps, NULL);
+ gatt_apps = NULL;
+
+ queue_destroy(gatt_devices, NULL);
+ gatt_devices = NULL;
+
+ queue_destroy(app_connections, NULL);
+ app_connections = NULL;
+
+ queue_destroy(listen_apps, NULL);
+ listen_apps = NULL;
+
+ queue_destroy(services_sdp, NULL);
+ services_sdp = NULL;
+
+ gatt_db_unref(gatt_db);
+ gatt_db = NULL;
+
+ bt_crypto_unref(crypto);
+ crypto = NULL;
+
+ if (le_io) {
+ g_io_channel_unref(le_io);
+ le_io = NULL;
+ }
+
+ if (bredr_io) {
+ g_io_channel_unref(bredr_io);
+ bredr_io = NULL;
+ }
+
+ return false;
+}
+
+void bt_gatt_unregister(void)
+{
+ DBG("");
+
+ ipc_unregister(hal_ipc, HAL_SERVICE_ID_GATT);
+ hal_ipc = NULL;
+
+ queue_destroy(app_connections, destroy_connection);
+ app_connections = NULL;
+
+ queue_destroy(gatt_apps, destroy_gatt_app);
+ gatt_apps = NULL;
+
+ queue_destroy(gatt_devices, destroy_device);
+ gatt_devices = NULL;
+
+ queue_destroy(services_sdp, free_service_sdp_record);
+ services_sdp = NULL;
+
+ queue_destroy(listen_apps, NULL);
+ listen_apps = NULL;
+
+ gatt_db_unref(gatt_db);
+ gatt_db = NULL;
+
+ if (le_io) {
+ g_io_channel_unref(le_io);
+ le_io = NULL;
+ }
+
+ if (bredr_io) {
+ g_io_channel_unref(bredr_io);
+ bredr_io = NULL;
+ }
+
+ if (gap_sdp_handle) {
+ bt_adapter_remove_record(gap_sdp_handle);
+ gap_sdp_handle = 0;
+ }
+
+ if (gatt_sdp_handle) {
+ bt_adapter_remove_record(gatt_sdp_handle);
+ gatt_sdp_handle = 0;
+ }
+
+ if (dis_sdp_handle) {
+ bt_adapter_remove_record(dis_sdp_handle);
+ dis_sdp_handle = 0;
+ }
+
+ bt_crypto_unref(crypto);
+ crypto = NULL;
+
+ bt_le_unregister();
+ bt_unpaired_unregister(gatt_unpaired_cb);
+}
+
+unsigned int bt_gatt_register_app(const char *uuid, gatt_type_t type,
+ gatt_conn_cb_t func)
+{
+ struct gatt_app *app;
+ bt_uuid_t u, u128;
+
+ bt_string_to_uuid(&u, uuid);
+ bt_uuid_to_uuid128(&u, &u128);
+ app = register_app((void *) &u128.value.u128, type);
+ if (!app)
+ return 0;
+
+ app->func = func;
+
+ return app->id;
+}
+
+bool bt_gatt_unregister_app(unsigned int id)
+{
+ uint8_t status;
+
+ status = unregister_app(id);
+
+ return status != HAL_STATUS_FAILED;
+}
+
+bool bt_gatt_connect_app(unsigned int id, const bdaddr_t *addr)
+{
+ uint8_t status;
+
+ status = handle_connect(id, addr, false);
+
+ return status != HAL_STATUS_FAILED;
+}
+
+bool bt_gatt_disconnect_app(unsigned int id, const bdaddr_t *addr)
+{
+ struct app_connection match;
+ struct app_connection *conn;
+ struct gatt_device *device;
+ struct gatt_app *app;
+
+ app = find_app_by_id(id);
+ if (!app)
+ return false;
+
+ device = find_device_by_addr(addr);
+ if (!device)
+ return false;
+
+ match.device = device;
+ match.app = app;
+
+ conn = queue_remove_if(app_connections,
+ match_connection_by_device_and_app, &match);
+ if (!conn)
+ return false;
+
+ destroy_connection(conn);
+
+ return true;
+}
+
+bool bt_gatt_add_autoconnect(unsigned int id, const bdaddr_t *addr)
+{
+ struct gatt_device *dev;
+ struct gatt_app *app;
+
+ DBG("");
+
+ app = find_app_by_id(id);
+ if (!app) {
+ error("gatt: App ID=%d not found", id);
+ return false;
+ }
+
+ dev = find_device_by_addr(addr);
+ if (!dev) {
+ error("gatt: Device not found");
+ return false;
+ }
+
+ /* Take reference of device for auto connect purpose */
+ if (queue_isempty(dev->autoconnect_apps))
+ device_ref(dev);
+
+ if (!queue_find(dev->autoconnect_apps, NULL, INT_TO_PTR(id)))
+ return queue_push_head(dev->autoconnect_apps, INT_TO_PTR(id));
+
+ return true;
+}
+
+void bt_gatt_remove_autoconnect(unsigned int id, const bdaddr_t *addr)
+{
+ struct gatt_device *dev;
+
+ DBG("");
+
+ dev = find_device_by_addr(addr);
+ if (!dev) {
+ error("gatt: Device not found");
+ return;
+ }
+
+ queue_remove(dev->autoconnect_apps, INT_TO_PTR(id));
+
+ if (queue_isempty(dev->autoconnect_apps))
+ remove_autoconnect_device(dev);
+}
diff --git a/repo/android/gatt.h b/repo/android/gatt.h
new file mode 100644
index 0000000..3382df9
--- /dev/null
+++ b/repo/android/gatt.h
@@ -0,0 +1,43 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2014 Intel Corporation. All rights reserved.
+ *
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+bool bt_gatt_register(struct ipc *ipc, const bdaddr_t *addr);
+void bt_gatt_unregister(void);
+
+
+typedef enum {
+ GATT_CLIENT,
+ GATT_SERVER,
+} gatt_type_t;
+
+typedef void (*gatt_conn_cb_t)(const bdaddr_t *addr, int err, void *attrib);
+
+unsigned int bt_gatt_register_app(const char *uuid, gatt_type_t type,
+ gatt_conn_cb_t func);
+bool bt_gatt_unregister_app(unsigned int id);
+
+bool bt_gatt_connect_app(unsigned int id, const bdaddr_t *addr);
+bool bt_gatt_disconnect_app(unsigned int id, const bdaddr_t *addr);
+bool bt_gatt_set_security(const bdaddr_t *bdaddr, int sec_level);
+bool bt_gatt_add_autoconnect(unsigned int id, const bdaddr_t *addr);
+void bt_gatt_remove_autoconnect(unsigned int id, const bdaddr_t *addr);
diff --git a/repo/android/hal-a2dp-sink.c b/repo/android/hal-a2dp-sink.c
new file mode 100644
index 0000000..a0b7ed1
--- /dev/null
+++ b/repo/android/hal-a2dp-sink.c
@@ -0,0 +1,163 @@
+/*
+ * Copyright (C) 2014 Intel Corporation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include <stdbool.h>
+#include <stddef.h>
+#include <string.h>
+
+#include "hal-log.h"
+#include "hal.h"
+#include "hal-msg.h"
+#include "hal-ipc.h"
+
+static const btav_callbacks_t *cbs = NULL;
+
+static bool interface_ready(void)
+{
+ return cbs != NULL;
+}
+
+static void handle_conn_state(void *buf, uint16_t len, int fd)
+{
+ struct hal_ev_a2dp_conn_state *ev = buf;
+
+ if (cbs->connection_state_cb)
+ cbs->connection_state_cb(ev->state,
+ (bt_bdaddr_t *) (ev->bdaddr));
+}
+
+static void handle_audio_state(void *buf, uint16_t len, int fd)
+{
+ struct hal_ev_a2dp_audio_state *ev = buf;
+
+ if (cbs->audio_state_cb)
+ cbs->audio_state_cb(ev->state, (bt_bdaddr_t *)(ev->bdaddr));
+}
+
+static void handle_audio_config(void *buf, uint16_t len, int fd)
+{
+ struct hal_ev_a2dp_audio_config *ev = buf;
+
+ if (cbs->audio_config_cb)
+ cbs->audio_config_cb((bt_bdaddr_t *)(ev->bdaddr),
+ ev->sample_rate, ev->channel_count);
+}
+
+/*
+ * handlers will be called from notification thread context,
+ * index in table equals to 'opcode - HAL_MINIMUM_EVENT'
+ */
+static const struct hal_ipc_handler ev_handlers[] = {
+ /* HAL_EV_A2DP_CONN_STATE */
+ { handle_conn_state, false, sizeof(struct hal_ev_a2dp_conn_state) },
+ /* HAL_EV_A2DP_AUDIO_STATE */
+ { handle_audio_state, false, sizeof(struct hal_ev_a2dp_audio_state) },
+ /* HAL_EV_A2DP_AUDIO_CONFIG */
+ { handle_audio_config, false, sizeof(struct hal_ev_a2dp_audio_config) },
+};
+
+static bt_status_t a2dp_connect(bt_bdaddr_t *bd_addr)
+{
+ struct hal_cmd_a2dp_connect cmd;
+
+ DBG("");
+
+ if (!interface_ready())
+ return BT_STATUS_NOT_READY;
+
+ memcpy(cmd.bdaddr, bd_addr, sizeof(cmd.bdaddr));
+
+ return hal_ipc_cmd(HAL_SERVICE_ID_A2DP_SINK, HAL_OP_A2DP_CONNECT,
+ sizeof(cmd), &cmd, NULL, NULL, NULL);
+}
+
+static bt_status_t disconnect(bt_bdaddr_t *bd_addr)
+{
+ struct hal_cmd_a2dp_disconnect cmd;
+
+ DBG("");
+
+ if (!interface_ready())
+ return BT_STATUS_NOT_READY;
+
+ memcpy(cmd.bdaddr, bd_addr, sizeof(cmd.bdaddr));
+
+ return hal_ipc_cmd(HAL_SERVICE_ID_A2DP_SINK, HAL_OP_A2DP_DISCONNECT,
+ sizeof(cmd), &cmd, NULL, NULL, NULL);
+}
+
+static bt_status_t init(btav_callbacks_t *callbacks)
+{
+ struct hal_cmd_register_module cmd;
+ int ret;
+
+ DBG("");
+
+ if (interface_ready())
+ return BT_STATUS_DONE;
+
+ cbs = callbacks;
+
+ hal_ipc_register(HAL_SERVICE_ID_A2DP_SINK, ev_handlers,
+ sizeof(ev_handlers)/sizeof(ev_handlers[0]));
+
+ cmd.service_id = HAL_SERVICE_ID_A2DP_SINK;
+ cmd.mode = HAL_MODE_DEFAULT;
+ cmd.max_clients = 1;
+
+ ret = hal_ipc_cmd(HAL_SERVICE_ID_CORE, HAL_OP_REGISTER_MODULE,
+ sizeof(cmd), &cmd, NULL, NULL, NULL);
+
+ if (ret != BT_STATUS_SUCCESS) {
+ cbs = NULL;
+ hal_ipc_unregister(HAL_SERVICE_ID_A2DP_SINK);
+ }
+
+ return ret;
+}
+
+static void cleanup(void)
+{
+ struct hal_cmd_unregister_module cmd;
+
+ DBG("");
+
+ if (!interface_ready())
+ return;
+
+ cmd.service_id = HAL_SERVICE_ID_A2DP_SINK;
+
+ hal_ipc_cmd(HAL_SERVICE_ID_CORE, HAL_OP_UNREGISTER_MODULE,
+ sizeof(cmd), &cmd, NULL, NULL, NULL);
+
+ hal_ipc_unregister(HAL_SERVICE_ID_A2DP_SINK);
+
+ cbs = NULL;
+}
+
+static btav_interface_t iface = {
+ .size = sizeof(iface),
+ .init = init,
+ .connect = a2dp_connect,
+ .disconnect = disconnect,
+ .cleanup = cleanup
+};
+
+btav_interface_t *bt_get_a2dp_sink_interface(void)
+{
+ return &iface;
+}
diff --git a/repo/android/hal-a2dp.c b/repo/android/hal-a2dp.c
new file mode 100644
index 0000000..f572875
--- /dev/null
+++ b/repo/android/hal-a2dp.c
@@ -0,0 +1,165 @@
+/*
+ * Copyright (C) 2013 Intel Corporation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include <stdbool.h>
+#include <stddef.h>
+#include <string.h>
+
+#include "hal-log.h"
+#include "hal.h"
+#include "hal-msg.h"
+#include "hal-ipc.h"
+
+static const btav_callbacks_t *cbs = NULL;
+
+static bool interface_ready(void)
+{
+ return cbs != NULL;
+}
+
+static void handle_conn_state(void *buf, uint16_t len, int fd)
+{
+ struct hal_ev_a2dp_conn_state *ev = buf;
+
+ if (cbs->connection_state_cb)
+ cbs->connection_state_cb(ev->state,
+ (bt_bdaddr_t *) (ev->bdaddr));
+}
+
+static void handle_audio_state(void *buf, uint16_t len, int fd)
+{
+ struct hal_ev_a2dp_audio_state *ev = buf;
+
+ if (cbs->audio_state_cb)
+ cbs->audio_state_cb(ev->state, (bt_bdaddr_t *)(ev->bdaddr));
+}
+
+static void handle_audio_config(void *buf, uint16_t len, int fd)
+{
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+ struct hal_ev_a2dp_audio_config *ev = buf;
+
+ if (cbs->audio_config_cb)
+ cbs->audio_config_cb((bt_bdaddr_t *)(ev->bdaddr),
+ ev->sample_rate, ev->channel_count);
+#endif
+}
+
+/*
+ * handlers will be called from notification thread context,
+ * index in table equals to 'opcode - HAL_MINIMUM_EVENT'
+ */
+static const struct hal_ipc_handler ev_handlers[] = {
+ /* HAL_EV_A2DP_CONN_STATE */
+ { handle_conn_state, false, sizeof(struct hal_ev_a2dp_conn_state) },
+ /* HAL_EV_A2DP_AUDIO_STATE */
+ { handle_audio_state, false, sizeof(struct hal_ev_a2dp_audio_state) },
+ /* HAL_EV_A2DP_AUDIO_CONFIG */
+ { handle_audio_config, false, sizeof(struct hal_ev_a2dp_audio_config) },
+};
+
+static bt_status_t a2dp_connect(bt_bdaddr_t *bd_addr)
+{
+ struct hal_cmd_a2dp_connect cmd;
+
+ DBG("");
+
+ if (!interface_ready())
+ return BT_STATUS_NOT_READY;
+
+ memcpy(cmd.bdaddr, bd_addr, sizeof(cmd.bdaddr));
+
+ return hal_ipc_cmd(HAL_SERVICE_ID_A2DP, HAL_OP_A2DP_CONNECT,
+ sizeof(cmd), &cmd, NULL, NULL, NULL);
+}
+
+static bt_status_t disconnect(bt_bdaddr_t *bd_addr)
+{
+ struct hal_cmd_a2dp_disconnect cmd;
+
+ DBG("");
+
+ if (!interface_ready())
+ return BT_STATUS_NOT_READY;
+
+ memcpy(cmd.bdaddr, bd_addr, sizeof(cmd.bdaddr));
+
+ return hal_ipc_cmd(HAL_SERVICE_ID_A2DP, HAL_OP_A2DP_DISCONNECT,
+ sizeof(cmd), &cmd, NULL, NULL, NULL);
+}
+
+static bt_status_t init(btav_callbacks_t *callbacks)
+{
+ struct hal_cmd_register_module cmd;
+ int ret;
+
+ DBG("");
+
+ if (interface_ready())
+ return BT_STATUS_DONE;
+
+ cbs = callbacks;
+
+ hal_ipc_register(HAL_SERVICE_ID_A2DP, ev_handlers,
+ sizeof(ev_handlers)/sizeof(ev_handlers[0]));
+
+ cmd.service_id = HAL_SERVICE_ID_A2DP;
+ cmd.mode = HAL_MODE_DEFAULT;
+ cmd.max_clients = 1;
+
+ ret = hal_ipc_cmd(HAL_SERVICE_ID_CORE, HAL_OP_REGISTER_MODULE,
+ sizeof(cmd), &cmd, NULL, NULL, NULL);
+
+ if (ret != BT_STATUS_SUCCESS) {
+ cbs = NULL;
+ hal_ipc_unregister(HAL_SERVICE_ID_A2DP);
+ }
+
+ return ret;
+}
+
+static void cleanup(void)
+{
+ struct hal_cmd_unregister_module cmd;
+
+ DBG("");
+
+ if (!interface_ready())
+ return;
+
+ cmd.service_id = HAL_SERVICE_ID_A2DP;
+
+ hal_ipc_cmd(HAL_SERVICE_ID_CORE, HAL_OP_UNREGISTER_MODULE,
+ sizeof(cmd), &cmd, NULL, NULL, NULL);
+
+ hal_ipc_unregister(HAL_SERVICE_ID_A2DP);
+
+ cbs = NULL;
+}
+
+static btav_interface_t iface = {
+ .size = sizeof(iface),
+ .init = init,
+ .connect = a2dp_connect,
+ .disconnect = disconnect,
+ .cleanup = cleanup
+};
+
+btav_interface_t *bt_get_a2dp_interface(void)
+{
+ return &iface;
+}
diff --git a/repo/android/hal-audio-aptx.c b/repo/android/hal-audio-aptx.c
new file mode 100644
index 0000000..a800075
--- /dev/null
+++ b/repo/android/hal-audio-aptx.c
@@ -0,0 +1,276 @@
+/*
+ * Copyright (C) 2014 Tieto Poland
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include <stdint.h>
+#include <stdbool.h>
+#include <string.h>
+#include <malloc.h>
+#include <dlfcn.h>
+
+#include "audio-msg.h"
+#include "hal-audio.h"
+#include "hal-log.h"
+#include "profiles/audio/a2dp-codecs.h"
+
+#define APTX_SO_NAME "libbt-aptx.so"
+
+struct aptx_data {
+ a2dp_aptx_t aptx;
+
+ void *enc;
+};
+
+static const a2dp_aptx_t aptx_presets[] = {
+ {
+ .info = {
+ .vendor_id = APTX_VENDOR_ID,
+ .codec_id = APTX_CODEC_ID,
+ },
+ .frequency = APTX_SAMPLING_FREQ_44100 |
+ APTX_SAMPLING_FREQ_48000,
+ .channel_mode = APTX_CHANNEL_MODE_STEREO,
+ },
+ {
+ .info = {
+ .vendor_id = APTX_VENDOR_ID,
+ .codec_id = APTX_CODEC_ID,
+ },
+ .frequency = APTX_SAMPLING_FREQ_48000,
+ .channel_mode = APTX_CHANNEL_MODE_STEREO,
+ },
+ {
+ .info = {
+ .vendor_id = APTX_VENDOR_ID,
+ .codec_id = APTX_CODEC_ID,
+ },
+ .frequency = APTX_SAMPLING_FREQ_44100,
+ .channel_mode = APTX_CHANNEL_MODE_STEREO,
+ },
+};
+
+static void *aptx_handle;
+static int aptx_btencsize;
+static int (*aptx_init)(void *, short);
+static int (*aptx_encode)(void *, void *, void *, void *);
+
+static bool aptx_load(void)
+{
+ const char * (*aptx_version)(void);
+ const char * (*aptx_build)(void);
+ int (*aptx_sizeofenc)(void);
+
+ aptx_handle = dlopen(APTX_SO_NAME, RTLD_LAZY);
+ if (!aptx_handle) {
+ error("APTX: failed to open library %s (%s)", APTX_SO_NAME,
+ dlerror());
+ return false;
+ }
+
+ aptx_version = dlsym(aptx_handle, "aptxbtenc_version");
+ aptx_build = dlsym(aptx_handle, "aptxbtenc_build");
+
+ if (aptx_version && aptx_build)
+ info("APTX: using library version %s build %s", aptx_version(),
+ aptx_build());
+ else
+ warn("APTX: cannot retrieve library version");
+
+ aptx_sizeofenc = dlsym(aptx_handle, "SizeofAptxbtenc");
+ aptx_init = dlsym(aptx_handle, "aptxbtenc_init");
+ aptx_encode = dlsym(aptx_handle, "aptxbtenc_encodestereo");
+ if (!aptx_sizeofenc || !aptx_init || !aptx_encode) {
+ error("APTX: failed initialize library");
+ dlclose(aptx_handle);
+ aptx_handle = NULL;
+ return false;
+ }
+ aptx_btencsize = aptx_sizeofenc();
+
+ info("APTX: codec library initialized (size=%d)", aptx_btencsize);
+
+ return true;
+}
+
+static void aptx_unload(void)
+{
+ if (!aptx_handle)
+ return;
+
+ dlclose(aptx_handle);
+ aptx_handle = NULL;
+}
+
+static int aptx_get_presets(struct audio_preset *preset, size_t *len)
+{
+ int i;
+ int count;
+ size_t new_len = 0;
+ uint8_t *ptr = (uint8_t *) preset;
+ size_t preset_size = sizeof(*preset) + sizeof(a2dp_aptx_t);
+
+ DBG("");
+
+ count = sizeof(aptx_presets) / sizeof(aptx_presets[0]);
+
+ for (i = 0; i < count; i++) {
+ preset = (struct audio_preset *) ptr;
+
+ if (new_len + preset_size > *len)
+ break;
+
+ preset->len = sizeof(a2dp_aptx_t);
+ memcpy(preset->data, &aptx_presets[i], preset->len);
+
+ new_len += preset_size;
+ ptr += preset_size;
+ }
+
+ *len = new_len;
+
+ return i;
+}
+
+static bool aptx_codec_init(struct audio_preset *preset, uint16_t payload_len,
+ void **codec_data)
+{
+ struct aptx_data *aptx_data;
+
+ DBG("");
+
+ if (preset->len != sizeof(a2dp_aptx_t)) {
+ error("APTX: preset size mismatch");
+ return false;
+ }
+
+ aptx_data = malloc(sizeof(*aptx_data));
+ if (!aptx_data)
+ return false;
+
+ memset(aptx_data, 0, sizeof(*aptx_data));
+ memcpy(&aptx_data->aptx, preset->data, preset->len);
+
+ aptx_data->enc = calloc(1, aptx_btencsize);
+ if (!aptx_data->enc) {
+ error("APTX: failed to create encoder");
+ free(aptx_data);
+ return false;
+ }
+
+ /* 1 = big-endian, this is what devices are using */
+ aptx_init(aptx_data->enc, 1);
+
+ *codec_data = aptx_data;
+
+ return true;
+}
+
+static bool aptx_cleanup(void *codec_data)
+{
+ struct aptx_data *aptx_data = (struct aptx_data *) codec_data;
+
+ free(aptx_data->enc);
+ free(codec_data);
+
+ return true;
+}
+
+static bool aptx_get_config(void *codec_data, struct audio_input_config *config)
+{
+ struct aptx_data *aptx_data = (struct aptx_data *) codec_data;
+
+ config->rate = aptx_data->aptx.frequency & APTX_SAMPLING_FREQ_48000 ?
+ 48000 : 44100;
+ config->channels = AUDIO_CHANNEL_OUT_STEREO;
+ config->format = AUDIO_FORMAT_PCM_16_BIT;
+
+ return true;
+}
+
+static size_t aptx_get_buffer_size(void *codec_data)
+{
+ /* TODO: return actual value */
+ return 0;
+}
+
+static size_t aptx_get_mediapacket_duration(void *codec_data)
+{
+ /* TODO: return actual value */
+ return 0;
+}
+
+static ssize_t aptx_encode_mediapacket(void *codec_data, const uint8_t *buffer,
+ size_t len, struct media_packet *mp,
+ size_t mp_data_len, size_t *written)
+{
+ struct aptx_data *aptx_data = (struct aptx_data *) codec_data;
+ const int16_t *ptr = (const void *) buffer;
+ size_t bytes_in = 0;
+ size_t bytes_out = 0;
+
+ while ((len - bytes_in) >= 16 && (mp_data_len - bytes_out) >= 4) {
+ int pcm_l[4], pcm_r[4];
+ int i;
+
+ for (i = 0; i < 4; i++) {
+ pcm_l[i] = ptr[0];
+ pcm_r[i] = ptr[1];
+ ptr += 2;
+ }
+
+ aptx_encode(aptx_data->enc, pcm_l, pcm_r, &mp->data[bytes_out]);
+
+ bytes_in += 16;
+ bytes_out += 4;
+ }
+
+ *written = bytes_out;
+
+ return bytes_in;
+}
+
+static bool aptx_update_qos(void *codec_data, uint8_t op)
+{
+ /*
+ * aptX has constant bitrate of 352kbps (with constant 4:1 compression
+ * ratio) thus QoS is not possible here.
+ */
+
+ return false;
+}
+
+static const struct audio_codec codec = {
+ .type = A2DP_CODEC_VENDOR,
+ .use_rtp = false,
+
+ .load = aptx_load,
+ .unload = aptx_unload,
+
+ .get_presets = aptx_get_presets,
+
+ .init = aptx_codec_init,
+ .cleanup = aptx_cleanup,
+ .get_config = aptx_get_config,
+ .get_buffer_size = aptx_get_buffer_size,
+ .get_mediapacket_duration = aptx_get_mediapacket_duration,
+ .encode_mediapacket = aptx_encode_mediapacket,
+ .update_qos = aptx_update_qos,
+};
+
+const struct audio_codec *codec_aptx(void)
+{
+ return &codec;
+}
diff --git a/repo/android/hal-audio-sbc.c b/repo/android/hal-audio-sbc.c
new file mode 100644
index 0000000..7ad3a6a
--- /dev/null
+++ b/repo/android/hal-audio-sbc.c
@@ -0,0 +1,428 @@
+/*
+ * Copyright (C) 2013 Intel Corporation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include <stdbool.h>
+#include <string.h>
+#include <malloc.h>
+#include <stdlib.h>
+
+#include <sbc/sbc.h>
+#include "audio-msg.h"
+#include "hal-audio.h"
+#include "hal-log.h"
+#include "../profiles/audio/a2dp-codecs.h"
+
+#define MAX_FRAMES_IN_PAYLOAD 15
+
+#define SBC_QUALITY_MIN_BITPOOL 33
+#define SBC_QUALITY_STEP 5
+
+
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+
+struct rtp_payload {
+ unsigned frame_count:4;
+ unsigned rfa0:1;
+ unsigned is_last_fragment:1;
+ unsigned is_first_fragment:1;
+ unsigned is_fragmented:1;
+} __attribute__ ((packed));
+
+#elif __BYTE_ORDER == __BIG_ENDIAN
+
+struct rtp_payload {
+ unsigned is_fragmented:1;
+ unsigned is_first_fragment:1;
+ unsigned is_last_fragment:1;
+ unsigned rfa0:1;
+ unsigned frame_count:4;
+} __attribute__ ((packed));
+
+#else
+#error "Unknown byte order"
+#endif
+
+struct media_packet_sbc {
+ struct media_packet_rtp hdr;
+ struct rtp_payload payload;
+ uint8_t data[0];
+};
+
+struct sbc_data {
+ a2dp_sbc_t sbc;
+
+ sbc_t enc;
+
+ uint16_t payload_len;
+
+ size_t in_frame_len;
+ size_t in_buf_size;
+
+ size_t out_frame_len;
+
+ unsigned frame_duration;
+ unsigned frames_per_packet;
+};
+
+static const a2dp_sbc_t sbc_presets[] = {
+ {
+ .frequency = SBC_SAMPLING_FREQ_44100 | SBC_SAMPLING_FREQ_48000,
+ .channel_mode = SBC_CHANNEL_MODE_MONO |
+ SBC_CHANNEL_MODE_DUAL_CHANNEL |
+ SBC_CHANNEL_MODE_STEREO |
+ SBC_CHANNEL_MODE_JOINT_STEREO,
+ .subbands = SBC_SUBBANDS_4 | SBC_SUBBANDS_8,
+ .allocation_method = SBC_ALLOCATION_SNR |
+ SBC_ALLOCATION_LOUDNESS,
+ .block_length = SBC_BLOCK_LENGTH_4 | SBC_BLOCK_LENGTH_8 |
+ SBC_BLOCK_LENGTH_12 | SBC_BLOCK_LENGTH_16,
+ .min_bitpool = MIN_BITPOOL,
+ .max_bitpool = MAX_BITPOOL
+ },
+ {
+ .frequency = SBC_SAMPLING_FREQ_44100,
+ .channel_mode = SBC_CHANNEL_MODE_JOINT_STEREO,
+ .subbands = SBC_SUBBANDS_8,
+ .allocation_method = SBC_ALLOCATION_LOUDNESS,
+ .block_length = SBC_BLOCK_LENGTH_16,
+ .min_bitpool = MIN_BITPOOL,
+ .max_bitpool = MAX_BITPOOL
+ },
+ {
+ .frequency = SBC_SAMPLING_FREQ_48000,
+ .channel_mode = SBC_CHANNEL_MODE_JOINT_STEREO,
+ .subbands = SBC_SUBBANDS_8,
+ .allocation_method = SBC_ALLOCATION_LOUDNESS,
+ .block_length = SBC_BLOCK_LENGTH_16,
+ .min_bitpool = MIN_BITPOOL,
+ .max_bitpool = MAX_BITPOOL
+ },
+};
+
+static int sbc_get_presets(struct audio_preset *preset, size_t *len)
+{
+ int i;
+ int count;
+ size_t new_len = 0;
+ uint8_t *ptr = (uint8_t *) preset;
+ size_t preset_size = sizeof(*preset) + sizeof(a2dp_sbc_t);
+
+ count = sizeof(sbc_presets) / sizeof(sbc_presets[0]);
+
+ for (i = 0; i < count; i++) {
+ preset = (struct audio_preset *) ptr;
+
+ if (new_len + preset_size > *len)
+ break;
+
+ preset->len = sizeof(a2dp_sbc_t);
+ memcpy(preset->data, &sbc_presets[i], preset->len);
+
+ new_len += preset_size;
+ ptr += preset_size;
+ }
+
+ *len = new_len;
+
+ return i;
+}
+
+static int sbc_freq2int(uint8_t freq)
+{
+ switch (freq) {
+ case SBC_SAMPLING_FREQ_16000:
+ return 16000;
+ case SBC_SAMPLING_FREQ_32000:
+ return 32000;
+ case SBC_SAMPLING_FREQ_44100:
+ return 44100;
+ case SBC_SAMPLING_FREQ_48000:
+ return 48000;
+ default:
+ return 0;
+ }
+}
+
+static const char *sbc_mode2str(uint8_t mode)
+{
+ switch (mode) {
+ case SBC_CHANNEL_MODE_MONO:
+ return "Mono";
+ case SBC_CHANNEL_MODE_DUAL_CHANNEL:
+ return "DualChannel";
+ case SBC_CHANNEL_MODE_STEREO:
+ return "Stereo";
+ case SBC_CHANNEL_MODE_JOINT_STEREO:
+ return "JointStereo";
+ default:
+ return "(unknown)";
+ }
+}
+
+static int sbc_blocks2int(uint8_t blocks)
+{
+ switch (blocks) {
+ case SBC_BLOCK_LENGTH_4:
+ return 4;
+ case SBC_BLOCK_LENGTH_8:
+ return 8;
+ case SBC_BLOCK_LENGTH_12:
+ return 12;
+ case SBC_BLOCK_LENGTH_16:
+ return 16;
+ default:
+ return 0;
+ }
+}
+
+static int sbc_subbands2int(uint8_t subbands)
+{
+ switch (subbands) {
+ case SBC_SUBBANDS_4:
+ return 4;
+ case SBC_SUBBANDS_8:
+ return 8;
+ default:
+ return 0;
+ }
+}
+
+static const char *sbc_allocation2str(uint8_t allocation)
+{
+ switch (allocation) {
+ case SBC_ALLOCATION_SNR:
+ return "SNR";
+ case SBC_ALLOCATION_LOUDNESS:
+ return "Loudness";
+ default:
+ return "(unknown)";
+ }
+}
+
+static void sbc_init_encoder(struct sbc_data *sbc_data)
+{
+ a2dp_sbc_t *in = &sbc_data->sbc;
+ sbc_t *out = &sbc_data->enc;
+
+ sbc_init_a2dp(out, 0L, in, sizeof(*in));
+
+ out->endian = SBC_LE;
+ out->bitpool = in->max_bitpool;
+
+ DBG("frequency=%d channel_mode=%s block_length=%d subbands=%d allocation=%s bitpool=%d-%d",
+ sbc_freq2int(in->frequency),
+ sbc_mode2str(in->channel_mode),
+ sbc_blocks2int(in->block_length),
+ sbc_subbands2int(in->subbands),
+ sbc_allocation2str(in->allocation_method),
+ in->min_bitpool, in->max_bitpool);
+}
+
+static void sbc_codec_calculate(struct sbc_data *sbc_data)
+{
+ size_t in_frame_len;
+ size_t out_frame_len;
+ size_t num_frames;
+
+ in_frame_len = sbc_get_codesize(&sbc_data->enc);
+ out_frame_len = sbc_get_frame_length(&sbc_data->enc);
+ num_frames = sbc_data->payload_len / out_frame_len;
+
+ if (num_frames > MAX_FRAMES_IN_PAYLOAD)
+ num_frames = MAX_FRAMES_IN_PAYLOAD;
+
+ sbc_data->in_frame_len = in_frame_len;
+ sbc_data->in_buf_size = num_frames * in_frame_len;
+
+ sbc_data->out_frame_len = out_frame_len;
+
+ sbc_data->frame_duration = sbc_get_frame_duration(&sbc_data->enc);
+ sbc_data->frames_per_packet = num_frames;
+
+ DBG("in_frame_len=%zu out_frame_len=%zu frames_per_packet=%zu",
+ in_frame_len, out_frame_len, num_frames);
+}
+
+static bool sbc_codec_init(struct audio_preset *preset, uint16_t payload_len,
+ void **codec_data)
+{
+ struct sbc_data *sbc_data;
+
+ if (preset->len != sizeof(a2dp_sbc_t)) {
+ error("SBC: preset size mismatch");
+ return false;
+ }
+
+ sbc_data = calloc(sizeof(struct sbc_data), 1);
+ if (!sbc_data)
+ return false;
+
+ memcpy(&sbc_data->sbc, preset->data, preset->len);
+
+ sbc_init_encoder(sbc_data);
+
+ sbc_data->payload_len = payload_len;
+
+ sbc_codec_calculate(sbc_data);
+
+ *codec_data = sbc_data;
+
+ return true;
+}
+
+static bool sbc_cleanup(void *codec_data)
+{
+ struct sbc_data *sbc_data = (struct sbc_data *) codec_data;
+
+ sbc_finish(&sbc_data->enc);
+ free(codec_data);
+
+ return true;
+}
+
+static bool sbc_get_config(void *codec_data, struct audio_input_config *config)
+{
+ struct sbc_data *sbc_data = (struct sbc_data *) codec_data;
+
+ switch (sbc_data->sbc.frequency) {
+ case SBC_SAMPLING_FREQ_16000:
+ config->rate = 16000;
+ break;
+ case SBC_SAMPLING_FREQ_32000:
+ config->rate = 32000;
+ break;
+ case SBC_SAMPLING_FREQ_44100:
+ config->rate = 44100;
+ break;
+ case SBC_SAMPLING_FREQ_48000:
+ config->rate = 48000;
+ break;
+ default:
+ return false;
+ }
+ config->channels = sbc_data->sbc.channel_mode == SBC_CHANNEL_MODE_MONO ?
+ AUDIO_CHANNEL_OUT_MONO :
+ AUDIO_CHANNEL_OUT_STEREO;
+ config->format = AUDIO_FORMAT_PCM_16_BIT;
+
+ return true;
+}
+
+static size_t sbc_get_buffer_size(void *codec_data)
+{
+ struct sbc_data *sbc_data = (struct sbc_data *) codec_data;
+
+ return sbc_data->in_buf_size;
+}
+
+static size_t sbc_get_mediapacket_duration(void *codec_data)
+{
+ struct sbc_data *sbc_data = (struct sbc_data *) codec_data;
+
+ return sbc_data->frame_duration * sbc_data->frames_per_packet;
+}
+
+static ssize_t sbc_encode_mediapacket(void *codec_data, const uint8_t *buffer,
+ size_t len, struct media_packet *mp,
+ size_t mp_data_len, size_t *written)
+{
+ struct sbc_data *sbc_data = (struct sbc_data *) codec_data;
+ struct media_packet_sbc *mp_sbc = (struct media_packet_sbc *) mp;
+ size_t consumed = 0;
+ size_t encoded = 0;
+ uint8_t frame_count = 0;
+
+ mp_data_len -= sizeof(mp_sbc->payload);
+
+ while (len - consumed >= sbc_data->in_frame_len &&
+ mp_data_len - encoded >= sbc_data->out_frame_len &&
+ frame_count < sbc_data->frames_per_packet) {
+ ssize_t read;
+ ssize_t written = 0;
+
+ read = sbc_encode(&sbc_data->enc, buffer + consumed,
+ sbc_data->in_frame_len, mp_sbc->data + encoded,
+ mp_data_len - encoded, &written);
+
+ if (read < 0) {
+ error("SBC: failed to encode block at frame %d (%zd)",
+ frame_count, read);
+ break;
+ }
+
+ frame_count++;
+ consumed += read;
+ encoded += written;
+ }
+
+ *written = encoded + sizeof(mp_sbc->payload);
+ mp_sbc->payload.frame_count = frame_count;
+
+ return consumed;
+}
+
+static bool sbc_update_qos(void *codec_data, uint8_t op)
+{
+ struct sbc_data *sbc_data = (struct sbc_data *) codec_data;
+ uint8_t curr_bitpool = sbc_data->enc.bitpool;
+ uint8_t new_bitpool = curr_bitpool;
+
+ switch (op) {
+ case QOS_POLICY_DEFAULT:
+ new_bitpool = sbc_data->sbc.max_bitpool;
+ break;
+
+ case QOS_POLICY_DECREASE:
+ if (curr_bitpool > SBC_QUALITY_MIN_BITPOOL) {
+ new_bitpool = curr_bitpool - SBC_QUALITY_STEP;
+ if (new_bitpool < SBC_QUALITY_MIN_BITPOOL)
+ new_bitpool = SBC_QUALITY_MIN_BITPOOL;
+ }
+ break;
+ }
+
+ if (new_bitpool == curr_bitpool)
+ return false;
+
+ sbc_data->enc.bitpool = new_bitpool;
+
+ sbc_codec_calculate(sbc_data);
+
+ info("SBC: bitpool changed: %d -> %d", curr_bitpool, new_bitpool);
+
+ return true;
+}
+
+static const struct audio_codec codec = {
+ .type = A2DP_CODEC_SBC,
+ .use_rtp = true,
+
+ .get_presets = sbc_get_presets,
+
+ .init = sbc_codec_init,
+ .cleanup = sbc_cleanup,
+ .get_config = sbc_get_config,
+ .get_buffer_size = sbc_get_buffer_size,
+ .get_mediapacket_duration = sbc_get_mediapacket_duration,
+ .encode_mediapacket = sbc_encode_mediapacket,
+ .update_qos = sbc_update_qos,
+};
+
+const struct audio_codec *codec_sbc(void)
+{
+ return &codec;
+}
diff --git a/repo/android/hal-audio.c b/repo/android/hal-audio.c
new file mode 100644
index 0000000..207101f
--- /dev/null
+++ b/repo/android/hal-audio.c
@@ -0,0 +1,1641 @@
+/*
+ * Copyright (C) 2013 Intel Corporation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include <errno.h>
+#include <pthread.h>
+#include <poll.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <unistd.h>
+#include <arpa/inet.h>
+#include <fcntl.h>
+
+#include <hardware/audio.h>
+#include <hardware/hardware.h>
+
+#include "audio-msg.h"
+#include "ipc-common.h"
+#include "hal-log.h"
+#include "hal-msg.h"
+#include "hal-audio.h"
+#include "hal-utils.h"
+#include "hal.h"
+
+#define FIXED_A2DP_PLAYBACK_LATENCY_MS 25
+
+#define FIXED_BUFFER_SIZE (20 * 512)
+
+#define MAX_DELAY 100000 /* 100ms */
+
+static const uint8_t a2dp_src_uuid[] = {
+ 0x00, 0x00, 0x11, 0x0a, 0x00, 0x00, 0x10, 0x00,
+ 0x80, 0x00, 0x00, 0x80, 0x5f, 0x9b, 0x34, 0xfb };
+
+static int listen_sk = -1;
+static int audio_sk = -1;
+
+static pthread_t ipc_th = 0;
+static pthread_mutex_t sk_mutex = PTHREAD_MUTEX_INITIALIZER;
+
+static void timespec_add(struct timespec *base, uint64_t time_us,
+ struct timespec *res)
+{
+ res->tv_sec = base->tv_sec + time_us / 1000000;
+ res->tv_nsec = base->tv_nsec + (time_us % 1000000) * 1000;
+
+ if (res->tv_nsec >= 1000000000) {
+ res->tv_sec++;
+ res->tv_nsec -= 1000000000;
+ }
+}
+
+static void timespec_diff(struct timespec *a, struct timespec *b,
+ struct timespec *res)
+{
+ res->tv_sec = a->tv_sec - b->tv_sec;
+ res->tv_nsec = a->tv_nsec - b->tv_nsec;
+
+ if (res->tv_nsec < 0) {
+ res->tv_sec--;
+ res->tv_nsec += 1000000000; /* 1sec */
+ }
+}
+
+static uint64_t timespec_diff_us(struct timespec *a, struct timespec *b)
+{
+ struct timespec res;
+
+ timespec_diff(a, b, &res);
+
+ return res.tv_sec * 1000000ll + res.tv_nsec / 1000ll;
+}
+
+#if defined(ANDROID)
+/*
+ * Bionic does not have clock_nanosleep() prototype in time.h even though
+ * it provides its implementation.
+ */
+extern int clock_nanosleep(clockid_t clock_id, int flags,
+ const struct timespec *request,
+ struct timespec *remain);
+#endif
+
+static struct {
+ const audio_codec_get_t get_codec;
+ bool loaded;
+} audio_codecs[] = {
+ { .get_codec = codec_aptx, .loaded = false },
+ { .get_codec = codec_sbc, .loaded = false },
+};
+
+#define NUM_CODECS (sizeof(audio_codecs) / sizeof(audio_codecs[0]))
+
+#define MAX_AUDIO_ENDPOINTS NUM_CODECS
+
+struct audio_endpoint {
+ uint8_t id;
+ const struct audio_codec *codec;
+ void *codec_data;
+ int fd;
+
+ struct media_packet *mp;
+ size_t mp_data_len;
+
+ uint16_t seq;
+ uint32_t samples;
+ struct timespec start;
+
+ bool resync;
+};
+
+static struct audio_endpoint audio_endpoints[MAX_AUDIO_ENDPOINTS];
+
+enum a2dp_state_t {
+ AUDIO_A2DP_STATE_NONE,
+ AUDIO_A2DP_STATE_STANDBY,
+ AUDIO_A2DP_STATE_SUSPENDED,
+ AUDIO_A2DP_STATE_STARTED
+};
+
+struct a2dp_stream_out {
+ struct audio_stream_out stream;
+
+ struct audio_endpoint *ep;
+ enum a2dp_state_t audio_state;
+ struct audio_input_config cfg;
+
+ uint8_t *downmix_buf;
+};
+
+struct a2dp_audio_dev {
+ struct audio_hw_device dev;
+ struct a2dp_stream_out *out;
+};
+
+static int audio_ipc_cmd(uint8_t service_id, uint8_t opcode, uint16_t len,
+ void *param, size_t *rsp_len, void *rsp, int *fd)
+{
+ ssize_t ret;
+ struct msghdr msg;
+ struct iovec iv[2];
+ struct ipc_hdr cmd;
+ char cmsgbuf[CMSG_SPACE(sizeof(int))];
+ struct ipc_status s;
+ size_t s_len = sizeof(s);
+
+ pthread_mutex_lock(&sk_mutex);
+
+ if (audio_sk < 0) {
+ error("audio: Invalid cmd socket passed to audio_ipc_cmd");
+ goto failed;
+ }
+
+ if (!rsp || !rsp_len) {
+ memset(&s, 0, s_len);
+ rsp_len = &s_len;
+ rsp = &s;
+ }
+
+ memset(&msg, 0, sizeof(msg));
+ memset(&cmd, 0, sizeof(cmd));
+
+ cmd.service_id = service_id;
+ cmd.opcode = opcode;
+ cmd.len = len;
+
+ iv[0].iov_base = &cmd;
+ iv[0].iov_len = sizeof(cmd);
+
+ iv[1].iov_base = param;
+ iv[1].iov_len = len;
+
+ msg.msg_iov = iv;
+ msg.msg_iovlen = 2;
+
+ ret = sendmsg(audio_sk, &msg, 0);
+ if (ret < 0) {
+ error("audio: Sending command failed:%s", strerror(errno));
+ goto failed;
+ }
+
+ /* socket was shutdown */
+ if (ret == 0) {
+ error("audio: Command socket closed");
+ goto failed;
+ }
+
+ memset(&msg, 0, sizeof(msg));
+ memset(&cmd, 0, sizeof(cmd));
+
+ iv[0].iov_base = &cmd;
+ iv[0].iov_len = sizeof(cmd);
+
+ iv[1].iov_base = rsp;
+ iv[1].iov_len = *rsp_len;
+
+ msg.msg_iov = iv;
+ msg.msg_iovlen = 2;
+
+ if (fd) {
+ memset(cmsgbuf, 0, sizeof(cmsgbuf));
+ msg.msg_control = cmsgbuf;
+ msg.msg_controllen = sizeof(cmsgbuf);
+ }
+
+ ret = recvmsg(audio_sk, &msg, 0);
+ if (ret < 0) {
+ error("audio: Receiving command response failed:%s",
+ strerror(errno));
+ goto failed;
+ }
+
+ if (ret < (ssize_t) sizeof(cmd)) {
+ error("audio: Too small response received(%zd bytes)", ret);
+ goto failed;
+ }
+
+ if (cmd.service_id != service_id) {
+ error("audio: Invalid service id (%u vs %u)", cmd.service_id,
+ service_id);
+ goto failed;
+ }
+
+ if (ret != (ssize_t) (sizeof(cmd) + cmd.len)) {
+ error("audio: Malformed response received(%zd bytes)", ret);
+ goto failed;
+ }
+
+ if (cmd.opcode != opcode && cmd.opcode != AUDIO_OP_STATUS) {
+ error("audio: Invalid opcode received (%u vs %u)",
+ cmd.opcode, opcode);
+ goto failed;
+ }
+
+ if (cmd.opcode == AUDIO_OP_STATUS) {
+ struct ipc_status *s = rsp;
+
+ if (sizeof(*s) != cmd.len) {
+ error("audio: Invalid status length");
+ goto failed;
+ }
+
+ if (s->code == AUDIO_STATUS_SUCCESS) {
+ error("audio: Invalid success status response");
+ goto failed;
+ }
+
+ pthread_mutex_unlock(&sk_mutex);
+
+ return s->code;
+ }
+
+ pthread_mutex_unlock(&sk_mutex);
+
+ /* Receive auxiliary data in msg */
+ if (fd) {
+ struct cmsghdr *cmsg;
+
+ *fd = -1;
+
+ for (cmsg = CMSG_FIRSTHDR(&msg); cmsg;
+ cmsg = CMSG_NXTHDR(&msg, cmsg)) {
+ if (cmsg->cmsg_level == SOL_SOCKET
+ && cmsg->cmsg_type == SCM_RIGHTS) {
+ memcpy(fd, CMSG_DATA(cmsg), sizeof(int));
+ break;
+ }
+ }
+
+ if (*fd < 0)
+ goto failed;
+ }
+
+ *rsp_len = cmd.len;
+
+ return AUDIO_STATUS_SUCCESS;
+
+failed:
+ /* Some serious issue happen on IPC - recover */
+ shutdown(audio_sk, SHUT_RDWR);
+ pthread_mutex_unlock(&sk_mutex);
+
+ return AUDIO_STATUS_FAILED;
+}
+
+static int ipc_open_cmd(const struct audio_codec *codec)
+{
+ uint8_t buf[BLUEZ_AUDIO_MTU];
+ struct audio_cmd_open *cmd = (struct audio_cmd_open *) buf;
+ struct audio_rsp_open rsp;
+ size_t cmd_len = sizeof(buf) - sizeof(*cmd);
+ size_t rsp_len = sizeof(rsp);
+ int result;
+
+ DBG("");
+
+ memcpy(cmd->uuid, a2dp_src_uuid, sizeof(a2dp_src_uuid));
+
+ cmd->codec = codec->type;
+ cmd->presets = codec->get_presets(cmd->preset, &cmd_len);
+
+ cmd_len += sizeof(*cmd);
+
+ result = audio_ipc_cmd(AUDIO_SERVICE_ID, AUDIO_OP_OPEN, cmd_len, cmd,
+ &rsp_len, &rsp, NULL);
+
+ if (result != AUDIO_STATUS_SUCCESS)
+ return 0;
+
+ return rsp.id;
+}
+
+static int ipc_close_cmd(uint8_t endpoint_id)
+{
+ struct audio_cmd_close cmd;
+ int result;
+
+ DBG("");
+
+ cmd.id = endpoint_id;
+
+ result = audio_ipc_cmd(AUDIO_SERVICE_ID, AUDIO_OP_CLOSE,
+ sizeof(cmd), &cmd, NULL, NULL, NULL);
+
+ return result;
+}
+
+static int ipc_open_stream_cmd(uint8_t *endpoint_id, uint16_t *mtu, int *fd,
+ struct audio_preset **caps)
+{
+ char buf[BLUEZ_AUDIO_MTU];
+ struct audio_cmd_open_stream cmd;
+ struct audio_rsp_open_stream *rsp =
+ (struct audio_rsp_open_stream *) &buf;
+ size_t rsp_len = sizeof(buf);
+ int result;
+
+ DBG("");
+
+ if (!caps)
+ return AUDIO_STATUS_FAILED;
+
+ cmd.id = *endpoint_id;
+
+ result = audio_ipc_cmd(AUDIO_SERVICE_ID, AUDIO_OP_OPEN_STREAM,
+ sizeof(cmd), &cmd, &rsp_len, rsp, fd);
+ if (result == AUDIO_STATUS_SUCCESS) {
+ size_t buf_len = sizeof(struct audio_preset) +
+ rsp->preset[0].len;
+ *endpoint_id = rsp->id;
+ *mtu = rsp->mtu;
+ *caps = malloc(buf_len);
+ memcpy(*caps, &rsp->preset, buf_len);
+ } else {
+ *caps = NULL;
+ }
+
+ return result;
+}
+
+static int ipc_close_stream_cmd(uint8_t endpoint_id)
+{
+ struct audio_cmd_close_stream cmd;
+ int result;
+
+ DBG("");
+
+ cmd.id = endpoint_id;
+
+ result = audio_ipc_cmd(AUDIO_SERVICE_ID, AUDIO_OP_CLOSE_STREAM,
+ sizeof(cmd), &cmd, NULL, NULL, NULL);
+
+ return result;
+}
+
+static int ipc_resume_stream_cmd(uint8_t endpoint_id)
+{
+ struct audio_cmd_resume_stream cmd;
+ int result;
+
+ DBG("");
+
+ cmd.id = endpoint_id;
+
+ result = audio_ipc_cmd(AUDIO_SERVICE_ID, AUDIO_OP_RESUME_STREAM,
+ sizeof(cmd), &cmd, NULL, NULL, NULL);
+
+ return result;
+}
+
+static int ipc_suspend_stream_cmd(uint8_t endpoint_id)
+{
+ struct audio_cmd_suspend_stream cmd;
+ int result;
+
+ DBG("");
+
+ cmd.id = endpoint_id;
+
+ result = audio_ipc_cmd(AUDIO_SERVICE_ID, AUDIO_OP_SUSPEND_STREAM,
+ sizeof(cmd), &cmd, NULL, NULL, NULL);
+
+ return result;
+}
+
+struct register_state {
+ struct audio_endpoint *ep;
+ bool error;
+};
+
+static void register_endpoint(const struct audio_codec *codec,
+ struct register_state *state)
+{
+ struct audio_endpoint *ep = state->ep;
+
+ /* don't even try to register more endpoints if one failed */
+ if (state->error)
+ return;
+
+ ep->id = ipc_open_cmd(codec);
+
+ if (!ep->id) {
+ state->error = true;
+ error("Failed to register endpoint");
+ return;
+ }
+
+ ep->codec = codec;
+ ep->codec_data = NULL;
+ ep->fd = -1;
+
+ state->ep++;
+}
+
+static int register_endpoints(void)
+{
+ struct register_state state;
+ unsigned int i;
+
+ state.ep = &audio_endpoints[0];
+ state.error = false;
+
+ for (i = 0; i < NUM_CODECS; i++) {
+ const struct audio_codec *codec = audio_codecs[i].get_codec();
+
+ if (!audio_codecs[i].loaded)
+ continue;
+
+ register_endpoint(codec, &state);
+ }
+
+ return state.error ? AUDIO_STATUS_FAILED : AUDIO_STATUS_SUCCESS;
+}
+
+static void unregister_endpoints(void)
+{
+ size_t i;
+
+ for (i = 0; i < MAX_AUDIO_ENDPOINTS; i++) {
+ struct audio_endpoint *ep = &audio_endpoints[i];
+
+ if (ep->id) {
+ ipc_close_cmd(ep->id);
+ memset(ep, 0, sizeof(*ep));
+ }
+ }
+}
+
+static bool open_endpoint(struct audio_endpoint **epp,
+ struct audio_input_config *cfg)
+{
+ struct audio_preset *preset;
+ struct audio_endpoint *ep = *epp;
+ const struct audio_codec *codec;
+ uint16_t mtu;
+ uint16_t payload_len;
+ int fd;
+ size_t i;
+ uint8_t ep_id = 0;
+
+ if (ep)
+ ep_id = ep->id;
+
+ if (ipc_open_stream_cmd(&ep_id, &mtu, &fd, &preset) !=
+ AUDIO_STATUS_SUCCESS)
+ return false;
+
+ DBG("ep_id=%d mtu=%u", ep_id, mtu);
+
+ for (i = 0; i < MAX_AUDIO_ENDPOINTS; i++)
+ if (audio_endpoints[i].id == ep_id) {
+ ep = &audio_endpoints[i];
+ break;
+ }
+
+ if (!ep) {
+ error("Cound not find opened endpoint");
+ goto failed;
+ }
+
+ *epp = ep;
+
+ payload_len = mtu;
+ if (ep->codec->use_rtp)
+ payload_len -= sizeof(struct rtp_header);
+
+ ep->fd = fd;
+
+ codec = ep->codec;
+ codec->init(preset, payload_len, &ep->codec_data);
+ codec->get_config(ep->codec_data, cfg);
+
+ ep->mp = calloc(mtu, 1);
+ if (!ep->mp)
+ goto failed;
+
+ if (ep->codec->use_rtp) {
+ struct media_packet_rtp *mp_rtp =
+ (struct media_packet_rtp *) ep->mp;
+ mp_rtp->hdr.v = 2;
+ mp_rtp->hdr.pt = 0x60;
+ mp_rtp->hdr.ssrc = htonl(1);
+ }
+
+ ep->mp_data_len = payload_len;
+
+ free(preset);
+
+ return true;
+
+failed:
+ close(fd);
+ free(preset);
+
+ return false;
+}
+
+static void close_endpoint(struct audio_endpoint *ep)
+{
+ ipc_close_stream_cmd(ep->id);
+ if (ep->fd >= 0) {
+ close(ep->fd);
+ ep->fd = -1;
+ }
+
+ free(ep->mp);
+
+ ep->codec->cleanup(ep->codec_data);
+ ep->codec_data = NULL;
+}
+
+static bool resume_endpoint(struct audio_endpoint *ep)
+{
+ if (ipc_resume_stream_cmd(ep->id) != AUDIO_STATUS_SUCCESS)
+ return false;
+
+ ep->samples = 0;
+ ep->resync = false;
+
+ ep->codec->update_qos(ep->codec_data, QOS_POLICY_DEFAULT);
+
+ return true;
+}
+
+static void downmix_to_mono(struct a2dp_stream_out *out, const uint8_t *buffer,
+ size_t bytes)
+{
+ const int16_t *input = (const void *) buffer;
+ int16_t *output = (void *) out->downmix_buf;
+ size_t i, frames;
+
+ /* PCM 16bit stereo */
+ frames = bytes / (2 * sizeof(int16_t));
+
+ for (i = 0; i < frames; i++) {
+ int16_t l = get_le16(&input[i * 2]);
+ int16_t r = get_le16(&input[i * 2 + 1]);
+
+ put_le16((l + r) / 2, &output[i]);
+ }
+}
+
+static bool wait_for_endpoint(struct audio_endpoint *ep, bool *writable)
+{
+ int ret;
+
+ while (true) {
+ struct pollfd pollfd;
+
+ pollfd.fd = ep->fd;
+ pollfd.events = POLLOUT;
+ pollfd.revents = 0;
+
+ ret = poll(&pollfd, 1, 500);
+
+ if (ret >= 0) {
+ *writable = !!(pollfd.revents & POLLOUT);
+ break;
+ }
+
+ if (errno != EINTR) {
+ ret = errno;
+ error("poll failed (%d)", ret);
+ return false;
+ }
+ }
+
+ return true;
+}
+
+static bool write_to_endpoint(struct audio_endpoint *ep, size_t bytes)
+{
+ struct media_packet *mp = (struct media_packet *) ep->mp;
+ int ret;
+
+ while (true) {
+ ret = write(ep->fd, mp, bytes);
+
+ if (ret >= 0)
+ break;
+
+ /*
+ * this should not happen so let's issue warning, but do not
+ * fail, we can try to write next packet
+ */
+ if (errno == EAGAIN) {
+ ret = errno;
+ warn("write failed (%d)", ret);
+ break;
+ }
+
+ if (errno != EINTR) {
+ ret = errno;
+ error("write failed (%d)", ret);
+ return false;
+ }
+ }
+
+ return true;
+}
+
+static bool write_data(struct a2dp_stream_out *out, const void *buffer,
+ size_t bytes)
+{
+ struct audio_endpoint *ep = out->ep;
+ struct media_packet *mp = (struct media_packet *) ep->mp;
+ struct media_packet_rtp *mp_rtp = (struct media_packet_rtp *) ep->mp;
+ size_t free_space = ep->mp_data_len;
+ size_t consumed = 0;
+
+ while (consumed < bytes) {
+ size_t written = 0;
+ ssize_t read;
+ uint32_t samples;
+ int ret;
+ struct timespec current;
+ uint64_t audio_sent, audio_passed;
+ bool do_write = false;
+
+ /*
+ * prepare media packet in advance so we don't waste time after
+ * wakeup
+ */
+ if (ep->codec->use_rtp) {
+ mp_rtp->hdr.sequence_number = htons(ep->seq++);
+ mp_rtp->hdr.timestamp = htonl(ep->samples);
+ }
+ read = ep->codec->encode_mediapacket(ep->codec_data,
+ buffer + consumed,
+ bytes - consumed, mp,
+ free_space, &written);
+
+ /*
+ * not much we can do here, let's just ignore remaining
+ * data and continue
+ */
+ if (read <= 0)
+ return true;
+
+ /* calculate where are we and where we should be */
+ clock_gettime(CLOCK_MONOTONIC, ¤t);
+ if (!ep->samples)
+ memcpy(&ep->start, ¤t, sizeof(ep->start));
+ audio_sent = ep->samples * 1000000ll / out->cfg.rate;
+ audio_passed = timespec_diff_us(¤t, &ep->start);
+
+ /*
+ * if we're ahead of stream then wait for next write point,
+ * if we're lagging more than 100ms then stop writing and just
+ * skip data until we're back in sync
+ */
+ if (audio_sent > audio_passed) {
+ struct timespec anchor;
+
+ ep->resync = false;
+
+ timespec_add(&ep->start, audio_sent, &anchor);
+
+ while (true) {
+ ret = clock_nanosleep(CLOCK_MONOTONIC,
+ TIMER_ABSTIME, &anchor,
+ NULL);
+
+ if (!ret)
+ break;
+
+ if (ret != EINTR) {
+ error("clock_nanosleep failed (%d)",
+ ret);
+ return false;
+ }
+ }
+ } else if (!ep->resync) {
+ uint64_t diff = audio_passed - audio_sent;
+
+ if (diff > MAX_DELAY) {
+ warn("lag is %jums, resyncing", diff / 1000);
+
+ ep->codec->update_qos(ep->codec_data,
+ QOS_POLICY_DECREASE);
+ ep->resync = true;
+ }
+ }
+
+ /* we send data only in case codec encoded some data, i.e. some
+ * codecs do internal buffering and output data only if full
+ * frame can be encoded
+ * in resync mode we'll just drop mediapackets
+ */
+ if (written > 0 && !ep->resync) {
+ /* wait some time for socket to be ready for write,
+ * but we'll just skip writing data if timeout occurs
+ */
+ if (!wait_for_endpoint(ep, &do_write))
+ return false;
+
+ if (do_write) {
+ if (ep->codec->use_rtp)
+ written += sizeof(struct rtp_header);
+
+ if (!write_to_endpoint(ep, written))
+ return false;
+ }
+ }
+
+ /*
+ * AudioFlinger provides 16bit PCM, so sample size is 2 bytes
+ * multiplied by number of channels. Number of channels is
+ * simply number of bits set in channels mask.
+ */
+ samples = read / (2 * popcount(out->cfg.channels));
+ ep->samples += samples;
+ consumed += read;
+ }
+
+ return true;
+}
+
+static ssize_t out_write(struct audio_stream_out *stream, const void *buffer,
+ size_t bytes)
+{
+ struct a2dp_stream_out *out = (struct a2dp_stream_out *) stream;
+ const void *in_buf = buffer;
+ size_t in_len = bytes;
+
+ /* just return in case we're closing */
+ if (out->audio_state == AUDIO_A2DP_STATE_NONE)
+ return -1;
+
+ /* We can auto-start only from standby */
+ if (out->audio_state == AUDIO_A2DP_STATE_STANDBY) {
+ DBG("stream in standby, auto-start");
+
+ if (!resume_endpoint(out->ep))
+ return -1;
+
+ out->audio_state = AUDIO_A2DP_STATE_STARTED;
+ }
+
+ if (out->audio_state != AUDIO_A2DP_STATE_STARTED) {
+ error("audio: stream not started");
+ return -1;
+ }
+
+ if (out->ep->fd < 0) {
+ error("audio: no transport socket");
+ return -1;
+ }
+
+ /*
+ * currently Android audioflinger is not able to provide mono stream on
+ * A2DP output so down mixing needs to be done in hal-audio plugin.
+ *
+ * for reference see
+ * AudioFlinger::PlaybackThread::readOutputParameters()
+ * frameworks/av/services/audioflinger/Threads.cpp:1631
+ */
+ if (out->cfg.channels == AUDIO_CHANNEL_OUT_MONO) {
+ if (!out->downmix_buf) {
+ error("audio: downmix buffer not initialized");
+ return -1;
+ }
+
+ downmix_to_mono(out, buffer, bytes);
+
+ in_buf = out->downmix_buf;
+ in_len = bytes / 2;
+ }
+
+ if (!write_data(out, in_buf, in_len))
+ return -1;
+
+ return bytes;
+}
+
+static uint32_t out_get_sample_rate(const struct audio_stream *stream)
+{
+ struct a2dp_stream_out *out = (struct a2dp_stream_out *) stream;
+
+ DBG("");
+
+ return out->cfg.rate;
+}
+
+static int out_set_sample_rate(struct audio_stream *stream, uint32_t rate)
+{
+ struct a2dp_stream_out *out = (struct a2dp_stream_out *) stream;
+
+ DBG("");
+
+ if (rate != out->cfg.rate) {
+ warn("audio: cannot set sample rate to %d", rate);
+ return -1;
+ }
+
+ return 0;
+}
+
+static size_t out_get_buffer_size(const struct audio_stream *stream)
+{
+ DBG("");
+
+ /*
+ * We should return proper buffer size calculated by codec (so each
+ * input buffer is encoded into single media packed) but this does not
+ * work well with AudioFlinger and causes problems. For this reason we
+ * use magic value here and out_write code takes care of splitting
+ * input buffer into multiple media packets.
+ */
+ return FIXED_BUFFER_SIZE;
+}
+
+static uint32_t out_get_channels(const struct audio_stream *stream)
+{
+ DBG("");
+
+ /*
+ * AudioFlinger can only provide stereo stream, so we return it here and
+ * later we'll downmix this to mono in case codec requires it
+ */
+
+ return AUDIO_CHANNEL_OUT_STEREO;
+}
+
+static audio_format_t out_get_format(const struct audio_stream *stream)
+{
+ struct a2dp_stream_out *out = (struct a2dp_stream_out *) stream;
+
+ DBG("");
+
+ return out->cfg.format;
+}
+
+static int out_set_format(struct audio_stream *stream, audio_format_t format)
+{
+ DBG("");
+ return -ENOSYS;
+}
+
+static int out_standby(struct audio_stream *stream)
+{
+ struct a2dp_stream_out *out = (struct a2dp_stream_out *) stream;
+
+ DBG("");
+
+ if (out->audio_state == AUDIO_A2DP_STATE_STARTED) {
+ if (ipc_suspend_stream_cmd(out->ep->id) != AUDIO_STATUS_SUCCESS)
+ return -1;
+ out->audio_state = AUDIO_A2DP_STATE_STANDBY;
+ }
+
+ return 0;
+}
+
+static int out_dump(const struct audio_stream *stream, int fd)
+{
+ DBG("");
+ return -ENOSYS;
+}
+
+static int out_set_parameters(struct audio_stream *stream, const char *kvpairs)
+{
+ struct a2dp_stream_out *out = (struct a2dp_stream_out *) stream;
+ char *kvpair;
+ char *str;
+ char *saveptr;
+ bool enter_suspend = false;
+ bool exit_suspend = false;
+
+ DBG("%s", kvpairs);
+
+ str = strdup(kvpairs);
+ if (!str)
+ return -ENOMEM;
+
+ kvpair = strtok_r(str, ";", &saveptr);
+
+ for (; kvpair && *kvpair; kvpair = strtok_r(NULL, ";", &saveptr)) {
+ char *keyval;
+
+ keyval = strchr(kvpair, '=');
+ if (!keyval)
+ continue;
+
+ *keyval = '\0';
+ keyval++;
+
+ if (!strcmp(kvpair, "closing")) {
+ if (!strcmp(keyval, "true"))
+ out->audio_state = AUDIO_A2DP_STATE_NONE;
+ } else if (!strcmp(kvpair, "A2dpSuspended")) {
+ if (!strcmp(keyval, "true"))
+ enter_suspend = true;
+ else
+ exit_suspend = true;
+ }
+ }
+
+ free(str);
+
+ if (enter_suspend && out->audio_state == AUDIO_A2DP_STATE_STARTED) {
+ if (ipc_suspend_stream_cmd(out->ep->id) != AUDIO_STATUS_SUCCESS)
+ return -1;
+ out->audio_state = AUDIO_A2DP_STATE_SUSPENDED;
+ }
+
+ if (exit_suspend && out->audio_state == AUDIO_A2DP_STATE_SUSPENDED)
+ out->audio_state = AUDIO_A2DP_STATE_STANDBY;
+
+ return 0;
+}
+
+static char *out_get_parameters(const struct audio_stream *stream,
+ const char *keys)
+{
+ DBG("");
+ return strdup("");
+}
+
+static uint32_t out_get_latency(const struct audio_stream_out *stream)
+{
+ struct a2dp_stream_out *out = (struct a2dp_stream_out *) stream;
+ struct audio_endpoint *ep = out->ep;
+ size_t pkt_duration;
+
+ DBG("");
+
+ pkt_duration = ep->codec->get_mediapacket_duration(ep->codec_data);
+
+ return FIXED_A2DP_PLAYBACK_LATENCY_MS + pkt_duration / 1000;
+}
+
+static int out_set_volume(struct audio_stream_out *stream, float left,
+ float right)
+{
+ DBG("");
+ /* volume controlled in audioflinger mixer (digital) */
+ return -ENOSYS;
+}
+
+static int out_get_render_position(const struct audio_stream_out *stream,
+ uint32_t *dsp_frames)
+{
+ DBG("");
+ return -ENOSYS;
+}
+
+static int out_add_audio_effect(const struct audio_stream *stream,
+ effect_handle_t effect)
+{
+ DBG("");
+ return -ENOSYS;
+}
+
+static int out_remove_audio_effect(const struct audio_stream *stream,
+ effect_handle_t effect)
+{
+ DBG("");
+ return -ENOSYS;
+}
+
+static uint32_t in_get_sample_rate(const struct audio_stream *stream)
+{
+ DBG("");
+ return -ENOSYS;
+}
+
+static int in_set_sample_rate(struct audio_stream *stream, uint32_t rate)
+{
+ DBG("");
+ return -ENOSYS;
+}
+
+static size_t in_get_buffer_size(const struct audio_stream *stream)
+{
+ DBG("");
+ return -ENOSYS;
+}
+
+static uint32_t in_get_channels(const struct audio_stream *stream)
+{
+ DBG("");
+ return -ENOSYS;
+}
+
+static audio_format_t in_get_format(const struct audio_stream *stream)
+{
+ DBG("");
+ return -ENOSYS;
+}
+
+static int in_set_format(struct audio_stream *stream, audio_format_t format)
+{
+ DBG("");
+ return -ENOSYS;
+}
+
+static int in_standby(struct audio_stream *stream)
+{
+ DBG("");
+ return -ENOSYS;
+}
+
+static int in_dump(const struct audio_stream *stream, int fd)
+{
+ DBG("");
+ return -ENOSYS;
+}
+
+static int in_set_parameters(struct audio_stream *stream, const char *kvpairs)
+{
+ DBG("");
+ return -ENOSYS;
+}
+
+static char *in_get_parameters(const struct audio_stream *stream,
+ const char *keys)
+{
+ DBG("");
+ return strdup("");
+}
+
+static int in_set_gain(struct audio_stream_in *stream, float gain)
+{
+ DBG("");
+ return -ENOSYS;
+}
+
+static ssize_t in_read(struct audio_stream_in *stream, void *buffer,
+ size_t bytes)
+{
+ DBG("");
+ return -ENOSYS;
+}
+
+static uint32_t in_get_input_frames_lost(struct audio_stream_in *stream)
+{
+ DBG("");
+ return -ENOSYS;
+}
+
+static int in_add_audio_effect(const struct audio_stream *stream,
+ effect_handle_t effect)
+{
+ DBG("");
+ return -ENOSYS;
+}
+
+static int in_remove_audio_effect(const struct audio_stream *stream,
+ effect_handle_t effect)
+{
+ DBG("");
+ return -ENOSYS;
+}
+
+static int audio_open_output_stream_real(struct audio_hw_device *dev,
+ audio_io_handle_t handle,
+ audio_devices_t devices,
+ audio_output_flags_t flags,
+ struct audio_config *config,
+ struct audio_stream_out **stream_out,
+ const char *address)
+{
+ struct a2dp_audio_dev *a2dp_dev = (struct a2dp_audio_dev *) dev;
+ struct a2dp_stream_out *out;
+
+ out = calloc(1, sizeof(struct a2dp_stream_out));
+ if (!out)
+ return -ENOMEM;
+
+ DBG("");
+
+ out->stream.common.get_sample_rate = out_get_sample_rate;
+ out->stream.common.set_sample_rate = out_set_sample_rate;
+ out->stream.common.get_buffer_size = out_get_buffer_size;
+ out->stream.common.get_channels = out_get_channels;
+ out->stream.common.get_format = out_get_format;
+ out->stream.common.set_format = out_set_format;
+ out->stream.common.standby = out_standby;
+ out->stream.common.dump = out_dump;
+ out->stream.common.set_parameters = out_set_parameters;
+ out->stream.common.get_parameters = out_get_parameters;
+ out->stream.common.add_audio_effect = out_add_audio_effect;
+ out->stream.common.remove_audio_effect = out_remove_audio_effect;
+ out->stream.get_latency = out_get_latency;
+ out->stream.set_volume = out_set_volume;
+ out->stream.write = out_write;
+ out->stream.get_render_position = out_get_render_position;
+
+ /* We want to autoselect opened endpoint */
+ out->ep = NULL;
+
+ if (!open_endpoint(&out->ep, &out->cfg))
+ goto fail;
+
+ DBG("rate=%d channels=%d format=%d", out->cfg.rate,
+ out->cfg.channels, out->cfg.format);
+
+ if (out->cfg.channels == AUDIO_CHANNEL_OUT_MONO) {
+ out->downmix_buf = malloc(FIXED_BUFFER_SIZE / 2);
+ if (!out->downmix_buf)
+ goto fail;
+ }
+
+ *stream_out = &out->stream;
+ a2dp_dev->out = out;
+
+ out->audio_state = AUDIO_A2DP_STATE_STANDBY;
+
+ return 0;
+
+fail:
+ error("audio: cannot open output stream");
+ free(out);
+ *stream_out = NULL;
+ return -EIO;
+}
+
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+static int audio_open_output_stream(struct audio_hw_device *dev,
+ audio_io_handle_t handle,
+ audio_devices_t devices,
+ audio_output_flags_t flags,
+ struct audio_config *config,
+ struct audio_stream_out **stream_out,
+ const char *address)
+{
+ return audio_open_output_stream_real(dev, handle, devices, flags,
+ config, stream_out, address);
+}
+#else
+static int audio_open_output_stream(struct audio_hw_device *dev,
+ audio_io_handle_t handle,
+ audio_devices_t devices,
+ audio_output_flags_t flags,
+ struct audio_config *config,
+ struct audio_stream_out **stream_out)
+{
+ return audio_open_output_stream_real(dev, handle, devices, flags,
+ config, stream_out, NULL);
+}
+#endif
+
+static void audio_close_output_stream(struct audio_hw_device *dev,
+ struct audio_stream_out *stream)
+{
+ struct a2dp_audio_dev *a2dp_dev = (struct a2dp_audio_dev *) dev;
+ struct a2dp_stream_out *out = (struct a2dp_stream_out *) stream;
+
+ DBG("");
+
+ close_endpoint(a2dp_dev->out->ep);
+
+ free(out->downmix_buf);
+
+ free(stream);
+ a2dp_dev->out = NULL;
+}
+
+static int audio_set_parameters(struct audio_hw_device *dev,
+ const char *kvpairs)
+{
+ struct a2dp_audio_dev *a2dp_dev = (struct a2dp_audio_dev *) dev;
+ struct a2dp_stream_out *out = a2dp_dev->out;
+
+ DBG("");
+
+ if (!out)
+ return 0;
+
+ return out->stream.common.set_parameters((struct audio_stream *) out,
+ kvpairs);
+}
+
+static char *audio_get_parameters(const struct audio_hw_device *dev,
+ const char *keys)
+{
+ DBG("");
+ return strdup("");
+}
+
+static int audio_init_check(const struct audio_hw_device *dev)
+{
+ DBG("");
+ return 0;
+}
+
+static int audio_set_voice_volume(struct audio_hw_device *dev, float volume)
+{
+ DBG("");
+ return -ENOSYS;
+}
+
+static int audio_set_master_volume(struct audio_hw_device *dev, float volume)
+{
+ DBG("");
+ return -ENOSYS;
+}
+
+static int audio_set_mode(struct audio_hw_device *dev, int mode)
+{
+ DBG("");
+ return -ENOSYS;
+}
+
+static int audio_set_mic_mute(struct audio_hw_device *dev, bool state)
+{
+ DBG("");
+ return -ENOSYS;
+}
+
+static int audio_get_mic_mute(const struct audio_hw_device *dev, bool *state)
+{
+ DBG("");
+ return -ENOSYS;
+}
+
+static size_t audio_get_input_buffer_size(const struct audio_hw_device *dev,
+ const struct audio_config *config)
+{
+ DBG("");
+ return -ENOSYS;
+}
+
+static int audio_open_input_stream_real(struct audio_hw_device *dev,
+ audio_io_handle_t handle,
+ audio_devices_t devices,
+ struct audio_config *config,
+ struct audio_stream_in **stream_in,
+ audio_input_flags_t flags,
+ const char *address,
+ audio_source_t source)
+{
+ struct audio_stream_in *in;
+
+ DBG("");
+
+ in = calloc(1, sizeof(struct audio_stream_in));
+ if (!in)
+ return -ENOMEM;
+
+ in->common.get_sample_rate = in_get_sample_rate;
+ in->common.set_sample_rate = in_set_sample_rate;
+ in->common.get_buffer_size = in_get_buffer_size;
+ in->common.get_channels = in_get_channels;
+ in->common.get_format = in_get_format;
+ in->common.set_format = in_set_format;
+ in->common.standby = in_standby;
+ in->common.dump = in_dump;
+ in->common.set_parameters = in_set_parameters;
+ in->common.get_parameters = in_get_parameters;
+ in->common.add_audio_effect = in_add_audio_effect;
+ in->common.remove_audio_effect = in_remove_audio_effect;
+ in->set_gain = in_set_gain;
+ in->read = in_read;
+ in->get_input_frames_lost = in_get_input_frames_lost;
+
+ *stream_in = in;
+
+ return 0;
+}
+
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+static int audio_open_input_stream(struct audio_hw_device *dev,
+ audio_io_handle_t handle,
+ audio_devices_t devices,
+ struct audio_config *config,
+ struct audio_stream_in **stream_in,
+ audio_input_flags_t flags,
+ const char *address,
+ audio_source_t source)
+{
+ return audio_open_input_stream_real(dev, handle, devices, config,
+ stream_in, flags, address,
+ source);
+}
+#else
+static int audio_open_input_stream(struct audio_hw_device *dev,
+ audio_io_handle_t handle,
+ audio_devices_t devices,
+ struct audio_config *config,
+ struct audio_stream_in **stream_in)
+{
+ return audio_open_input_stream_real(dev, handle, devices, config,
+ stream_in, 0, NULL, 0);
+}
+#endif
+
+static void audio_close_input_stream(struct audio_hw_device *dev,
+ struct audio_stream_in *stream_in)
+{
+ DBG("");
+ free(stream_in);
+}
+
+static int audio_dump(const audio_hw_device_t *device, int fd)
+{
+ DBG("");
+ return -ENOSYS;
+}
+
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+static int set_master_mute(struct audio_hw_device *dev, bool mute)
+{
+ DBG("");
+ return -ENOSYS;
+}
+
+static int get_master_mute(struct audio_hw_device *dev, bool *mute)
+{
+ DBG("");
+ return -ENOSYS;
+}
+
+static int create_audio_patch(struct audio_hw_device *dev,
+ unsigned int num_sources,
+ const struct audio_port_config *sources,
+ unsigned int num_sinks,
+ const struct audio_port_config *sinks,
+ audio_patch_handle_t *handle)
+{
+ DBG("");
+ return -ENOSYS;
+}
+
+static int release_audio_patch(struct audio_hw_device *dev,
+ audio_patch_handle_t handle)
+{
+ DBG("");
+ return -ENOSYS;
+}
+
+static int get_audio_port(struct audio_hw_device *dev, struct audio_port *port)
+{
+ DBG("");
+ return -ENOSYS;
+}
+
+static int set_audio_port_config(struct audio_hw_device *dev,
+ const struct audio_port_config *config)
+{
+ DBG("");
+ return -ENOSYS;
+}
+#endif
+
+static int audio_close(hw_device_t *device)
+{
+ struct a2dp_audio_dev *a2dp_dev = (struct a2dp_audio_dev *)device;
+ unsigned int i;
+
+ DBG("");
+
+ unregister_endpoints();
+
+ for (i = 0; i < NUM_CODECS; i++) {
+ const struct audio_codec *codec = audio_codecs[i].get_codec();
+
+ if (!audio_codecs[i].loaded)
+ continue;
+
+ if (codec->unload)
+ codec->unload();
+
+ audio_codecs[i].loaded = false;
+ }
+
+ shutdown(listen_sk, SHUT_RDWR);
+ shutdown(audio_sk, SHUT_RDWR);
+
+ pthread_join(ipc_th, NULL);
+
+ close(listen_sk);
+ listen_sk = -1;
+
+ free(a2dp_dev);
+ return 0;
+}
+
+static void *ipc_handler(void *data)
+{
+ bool done = false;
+ struct pollfd pfd;
+ int sk;
+
+ DBG("");
+
+ while (!done) {
+ DBG("Waiting for connection ...");
+
+ sk = accept(listen_sk, NULL, NULL);
+ if (sk < 0) {
+ int err = errno;
+
+ if (err == EINTR)
+ continue;
+
+ if (err != ECONNABORTED && err != EINVAL)
+ error("audio: Failed to accept socket: %d (%s)",
+ err, strerror(err));
+
+ break;
+ }
+
+ pthread_mutex_lock(&sk_mutex);
+ audio_sk = sk;
+ pthread_mutex_unlock(&sk_mutex);
+
+ DBG("Audio IPC: Connected");
+
+ if (register_endpoints() != AUDIO_STATUS_SUCCESS) {
+ error("audio: Failed to register endpoints");
+
+ unregister_endpoints();
+
+ pthread_mutex_lock(&sk_mutex);
+ shutdown(audio_sk, SHUT_RDWR);
+ close(audio_sk);
+ audio_sk = -1;
+ pthread_mutex_unlock(&sk_mutex);
+
+ continue;
+ }
+
+ memset(&pfd, 0, sizeof(pfd));
+ pfd.fd = audio_sk;
+ pfd.events = POLLHUP | POLLERR | POLLNVAL;
+
+ /* Check if socket is still alive. Empty while loop.*/
+ while (poll(&pfd, 1, -1) < 0 && errno == EINTR);
+
+ info("Audio HAL: Socket closed");
+
+ pthread_mutex_lock(&sk_mutex);
+ close(audio_sk);
+ audio_sk = -1;
+ pthread_mutex_unlock(&sk_mutex);
+ }
+
+ /* audio_sk is closed at this point, just cleanup endpoints states */
+ memset(audio_endpoints, 0, sizeof(audio_endpoints));
+
+ info("Closing Audio IPC thread");
+ return NULL;
+}
+
+static int audio_ipc_init(void)
+{
+ struct sockaddr_un addr;
+ int err;
+ int sk;
+
+ DBG("");
+
+ sk = socket(PF_LOCAL, SOCK_SEQPACKET, 0);
+ if (sk < 0) {
+ err = -errno;
+ error("audio: Failed to create socket: %d (%s)", -err,
+ strerror(-err));
+ return err;
+ }
+
+ memset(&addr, 0, sizeof(addr));
+ addr.sun_family = AF_UNIX;
+
+ memcpy(addr.sun_path, BLUEZ_AUDIO_SK_PATH,
+ sizeof(BLUEZ_AUDIO_SK_PATH));
+
+ if (bind(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+ err = -errno;
+ error("audio: Failed to bind socket: %d (%s)", -err,
+ strerror(-err));
+ goto failed;
+ }
+
+ if (listen(sk, 1) < 0) {
+ err = -errno;
+ error("audio: Failed to listen on the socket: %d (%s)", -err,
+ strerror(-err));
+ goto failed;
+ }
+
+ listen_sk = sk;
+
+ err = pthread_create(&ipc_th, NULL, ipc_handler, NULL);
+ if (err) {
+ err = -err;
+ ipc_th = 0;
+ error("audio: Failed to start Audio IPC thread: %d (%s)",
+ -err, strerror(-err));
+ goto failed;
+ }
+
+ return 0;
+
+failed:
+ close(sk);
+ return err;
+}
+
+static int audio_open(const hw_module_t *module, const char *name,
+ hw_device_t **device)
+{
+ struct a2dp_audio_dev *a2dp_dev;
+ size_t i;
+ int err;
+
+ DBG("");
+
+ if (strcmp(name, AUDIO_HARDWARE_INTERFACE)) {
+ error("audio: interface %s not matching [%s]", name,
+ AUDIO_HARDWARE_INTERFACE);
+ return -EINVAL;
+ }
+
+ err = audio_ipc_init();
+ if (err < 0)
+ return err;
+
+ a2dp_dev = calloc(1, sizeof(struct a2dp_audio_dev));
+ if (!a2dp_dev)
+ return -ENOMEM;
+
+ a2dp_dev->dev.common.tag = HARDWARE_DEVICE_TAG;
+ a2dp_dev->dev.common.version = AUDIO_DEVICE_API_VERSION_CURRENT;
+ a2dp_dev->dev.common.module = (struct hw_module_t *) module;
+ a2dp_dev->dev.common.close = audio_close;
+
+ a2dp_dev->dev.init_check = audio_init_check;
+ a2dp_dev->dev.set_voice_volume = audio_set_voice_volume;
+ a2dp_dev->dev.set_master_volume = audio_set_master_volume;
+ a2dp_dev->dev.set_mode = audio_set_mode;
+ a2dp_dev->dev.set_mic_mute = audio_set_mic_mute;
+ a2dp_dev->dev.get_mic_mute = audio_get_mic_mute;
+ a2dp_dev->dev.set_parameters = audio_set_parameters;
+ a2dp_dev->dev.get_parameters = audio_get_parameters;
+ a2dp_dev->dev.get_input_buffer_size = audio_get_input_buffer_size;
+ a2dp_dev->dev.open_output_stream = audio_open_output_stream;
+ a2dp_dev->dev.close_output_stream = audio_close_output_stream;
+ a2dp_dev->dev.open_input_stream = audio_open_input_stream;
+ a2dp_dev->dev.close_input_stream = audio_close_input_stream;
+ a2dp_dev->dev.dump = audio_dump;
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+ a2dp_dev->dev.set_master_mute = set_master_mute;
+ a2dp_dev->dev.get_master_mute = get_master_mute;
+ a2dp_dev->dev.create_audio_patch = create_audio_patch;
+ a2dp_dev->dev.release_audio_patch = release_audio_patch;
+ a2dp_dev->dev.get_audio_port = get_audio_port;
+ a2dp_dev->dev.set_audio_port_config = set_audio_port_config;
+#endif
+
+ for (i = 0; i < NUM_CODECS; i++) {
+ const struct audio_codec *codec = audio_codecs[i].get_codec();
+
+ if (codec->load && !codec->load())
+ continue;
+
+ audio_codecs[i].loaded = true;
+ }
+
+ /*
+ * Note that &a2dp_dev->dev.common is the same pointer as a2dp_dev.
+ * This results from the structure of following structs:a2dp_audio_dev,
+ * audio_hw_device. We will rely on this later in the code.
+ */
+ *device = &a2dp_dev->dev.common;
+
+ return 0;
+}
+
+static struct hw_module_methods_t hal_module_methods = {
+ .open = audio_open,
+};
+
+struct audio_module HAL_MODULE_INFO_SYM = {
+ .common = {
+ .tag = HARDWARE_MODULE_TAG,
+ .version_major = 1,
+ .version_minor = 0,
+ .id = AUDIO_HARDWARE_MODULE_ID,
+ .name = "A2DP Bluez HW HAL",
+ .author = "Intel Corporation",
+ .methods = &hal_module_methods,
+ },
+};
diff --git a/repo/android/hal-audio.h b/repo/android/hal-audio.h
new file mode 100644
index 0000000..2b47412
--- /dev/null
+++ b/repo/android/hal-audio.h
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 2013 Intel Corporation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include <time.h>
+#include <hardware/audio.h>
+
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+
+struct rtp_header {
+ unsigned cc:4;
+ unsigned x:1;
+ unsigned p:1;
+ unsigned v:2;
+
+ unsigned pt:7;
+ unsigned m:1;
+
+ uint16_t sequence_number;
+ uint32_t timestamp;
+ uint32_t ssrc;
+ uint32_t csrc[0];
+} __attribute__ ((packed));
+
+#elif __BYTE_ORDER == __BIG_ENDIAN
+
+struct rtp_header {
+ unsigned v:2;
+ unsigned p:1;
+ unsigned x:1;
+ unsigned cc:4;
+
+ unsigned m:1;
+ unsigned pt:7;
+
+ uint16_t sequence_number;
+ uint32_t timestamp;
+ uint32_t ssrc;
+ uint32_t csrc[0];
+} __attribute__ ((packed));
+
+#else
+#error "Unknown byte order"
+#endif
+
+struct media_packet {
+ uint8_t data[0];
+};
+
+struct media_packet_rtp {
+ struct rtp_header hdr;
+ uint8_t data[0];
+};
+
+struct audio_input_config {
+ uint32_t rate;
+ uint32_t channels;
+ audio_format_t format;
+};
+
+struct audio_codec {
+ uint8_t type;
+ bool use_rtp;
+
+ bool (*load) (void);
+ void (*unload) (void);
+
+ int (*get_presets) (struct audio_preset *preset, size_t *len);
+
+ bool (*init) (struct audio_preset *preset, uint16_t mtu,
+ void **codec_data);
+ bool (*cleanup) (void *codec_data);
+ bool (*get_config) (void *codec_data,
+ struct audio_input_config *config);
+ size_t (*get_buffer_size) (void *codec_data);
+ size_t (*get_mediapacket_duration) (void *codec_data);
+ ssize_t (*encode_mediapacket) (void *codec_data, const uint8_t *buffer,
+ size_t len, struct media_packet *mp,
+ size_t mp_data_len, size_t *written);
+ bool (*update_qos) (void *codec_data, uint8_t op);
+};
+
+#define QOS_POLICY_DEFAULT 0x00
+#define QOS_POLICY_DECREASE 0x01
+
+typedef const struct audio_codec * (*audio_codec_get_t) (void);
+
+const struct audio_codec *codec_sbc(void);
+const struct audio_codec *codec_aptx(void);
diff --git a/repo/android/hal-avrcp-ctrl.c b/repo/android/hal-avrcp-ctrl.c
new file mode 100644
index 0000000..46b77fd
--- /dev/null
+++ b/repo/android/hal-avrcp-ctrl.c
@@ -0,0 +1,145 @@
+/*
+ * Copyright (C) 2014 Intel Corporation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include <stdbool.h>
+#include <stddef.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include "hal-utils.h"
+#include "hal-log.h"
+#include "hal.h"
+#include "hal-msg.h"
+#include "ipc-common.h"
+#include "hal-ipc.h"
+
+static const btrc_ctrl_callbacks_t *cbs = NULL;
+
+static bool interface_ready(void)
+{
+ return cbs != NULL;
+}
+
+static void handle_connection_state(void *buf, uint16_t len, int fd)
+{
+ struct hal_ev_avrcp_ctrl_conn_state *ev = buf;
+
+ if (cbs->connection_state_cb)
+ cbs->connection_state_cb(ev->state,
+ (bt_bdaddr_t *) (ev->bdaddr));
+}
+
+static void handle_passthrough_rsp(void *buf, uint16_t len, int fd)
+{
+ struct hal_ev_avrcp_ctrl_passthrough_rsp *ev = buf;
+
+ if (cbs->passthrough_rsp_cb)
+ cbs->passthrough_rsp_cb(ev->id, ev->key_state);
+}
+
+/*
+ * handlers will be called from notification thread context,
+ * index in table equals to 'opcode - HAL_MINIMUM_EVENT'
+ */
+static const struct hal_ipc_handler ev_handlers[] = {
+ /* HAL_EV_AVRCP_CTRL_CONN_STATE */
+ { handle_connection_state, false,
+ sizeof(struct hal_ev_avrcp_ctrl_conn_state) },
+ /* HAL_EV_AVRCP_CTRL_PASSTHROUGH_RSP */
+ { handle_passthrough_rsp, false,
+ sizeof(struct hal_ev_avrcp_ctrl_passthrough_rsp) },
+};
+
+static bt_status_t init(btrc_ctrl_callbacks_t *callbacks)
+{
+ struct hal_cmd_register_module cmd;
+ int ret;
+
+ DBG("");
+
+ if (interface_ready())
+ return BT_STATUS_DONE;
+
+ cbs = callbacks;
+
+ hal_ipc_register(HAL_SERVICE_ID_AVRCP_CTRL, ev_handlers,
+ sizeof(ev_handlers) / sizeof(ev_handlers[0]));
+
+ cmd.service_id = HAL_SERVICE_ID_AVRCP_CTRL;
+ cmd.mode = HAL_MODE_DEFAULT;
+ cmd.max_clients = 1;
+
+ ret = hal_ipc_cmd(HAL_SERVICE_ID_CORE, HAL_OP_REGISTER_MODULE,
+ sizeof(cmd), &cmd, NULL, NULL, NULL);
+
+ if (ret != BT_STATUS_SUCCESS) {
+ cbs = NULL;
+ hal_ipc_unregister(HAL_SERVICE_ID_AVRCP_CTRL);
+ }
+
+ return ret;
+}
+
+static bt_status_t send_pass_through_cmd(bt_bdaddr_t *bd_addr, uint8_t key_code,
+ uint8_t key_state)
+{
+ struct hal_cmd_avrcp_ctrl_send_passthrough cmd;
+
+ DBG("");
+
+ if (!interface_ready())
+ return BT_STATUS_NOT_READY;
+
+ memcpy(cmd.bdaddr, bd_addr, sizeof(cmd.bdaddr));
+ cmd.key_code = key_code;
+ cmd.key_state = key_state;
+
+ return hal_ipc_cmd(HAL_SERVICE_ID_AVRCP_CTRL,
+ HAL_OP_AVRCP_CTRL_SEND_PASSTHROUGH,
+ sizeof(cmd), &cmd, NULL, NULL, NULL);
+}
+
+static void cleanup(void)
+{
+ struct hal_cmd_unregister_module cmd;
+
+ DBG("");
+
+ if (!interface_ready())
+ return;
+
+ cmd.service_id = HAL_SERVICE_ID_AVRCP_CTRL;
+
+ hal_ipc_cmd(HAL_SERVICE_ID_CORE, HAL_OP_UNREGISTER_MODULE,
+ sizeof(cmd), &cmd, NULL, NULL, NULL);
+
+ hal_ipc_unregister(HAL_SERVICE_ID_AVRCP_CTRL);
+
+ cbs = NULL;
+}
+
+static btrc_ctrl_interface_t iface = {
+ .size = sizeof(iface),
+ .init = init,
+ .send_pass_through_cmd = send_pass_through_cmd,
+ .cleanup = cleanup
+};
+
+btrc_ctrl_interface_t *bt_get_avrcp_ctrl_interface(void)
+{
+ return &iface;
+}
diff --git a/repo/android/hal-avrcp.c b/repo/android/hal-avrcp.c
new file mode 100644
index 0000000..f935eda
--- /dev/null
+++ b/repo/android/hal-avrcp.c
@@ -0,0 +1,688 @@
+/*
+ * Copyright (C) 2014 Intel Corporation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include <stdbool.h>
+#include <stddef.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include "hal-utils.h"
+#include "hal-log.h"
+#include "hal.h"
+#include "hal-msg.h"
+#include "ipc-common.h"
+#include "hal-ipc.h"
+
+static const btrc_callbacks_t *cbs = NULL;
+
+static bool interface_ready(void)
+{
+ return cbs != NULL;
+}
+
+static void handle_remote_features(void *buf, uint16_t len, int fd)
+{
+ struct hal_ev_avrcp_remote_features *ev = buf;
+
+ if (cbs->remote_features_cb)
+ cbs->remote_features_cb((bt_bdaddr_t *) (ev->bdaddr),
+ ev->features);
+}
+
+static void handle_get_play_status(void *buf, uint16_t len, int fd)
+{
+ if (cbs->get_play_status_cb)
+ cbs->get_play_status_cb();
+}
+
+static void handle_list_player_attrs(void *buf, uint16_t len, int fd)
+{
+ if (cbs->list_player_app_attr_cb)
+ cbs->list_player_app_attr_cb();
+}
+
+static void handle_list_player_values(void *buf, uint16_t len, int fd)
+{
+ struct hal_ev_avrcp_list_player_values *ev = buf;
+
+ if (cbs->list_player_app_values_cb)
+ cbs->list_player_app_values_cb(ev->attr);
+}
+
+static void handle_get_player_values(void *buf, uint16_t len, int fd)
+{
+ struct hal_ev_avrcp_get_player_values *ev = buf;
+ btrc_player_attr_t attrs[4];
+ int i;
+
+ if (!cbs->get_player_app_value_cb)
+ return;
+
+ /* Convert uint8_t array to btrc_player_attr_t array */
+ for (i = 0; i < ev->number; i++)
+ attrs[i] = ev->attrs[i];
+
+ cbs->get_player_app_value_cb(ev->number, attrs);
+}
+
+static void handle_get_player_attrs_text(void *buf, uint16_t len, int fd)
+{
+ struct hal_ev_avrcp_get_player_attrs_text *ev = buf;
+ btrc_player_attr_t attrs[4];
+ int i;
+
+ if (!cbs->get_player_app_attrs_text_cb)
+ return;
+
+ /* Convert uint8_t array to btrc_player_attr_t array */
+ for (i = 0; i < ev->number; i++)
+ attrs[i] = ev->attrs[i];
+
+ cbs->get_player_app_attrs_text_cb(ev->number, attrs);
+}
+
+static void handle_get_player_values_text(void *buf, uint16_t len, int fd)
+{
+ struct hal_ev_avrcp_get_player_values_text *ev = buf;
+
+ if (cbs->get_player_app_values_text_cb)
+ cbs->get_player_app_values_text_cb(ev->attr, ev->number,
+ ev->values);
+}
+
+static void handle_set_player_value(void *buf, uint16_t len, int fd)
+{
+ struct hal_ev_avrcp_set_player_values *ev = buf;
+ struct hal_avrcp_player_attr_value *attrs;
+ btrc_player_settings_t values;
+ int i;
+
+ if (!cbs->set_player_app_value_cb)
+ return;
+
+ attrs = (struct hal_avrcp_player_attr_value *) ev->attrs;
+
+ /* Convert to btrc_player_settings_t */
+ values.num_attr = ev->number;
+ for (i = 0; i < ev->number; i++) {
+ values.attr_ids[i] = attrs[i].attr;
+ values.attr_values[i] = attrs[i].value;
+ }
+
+ cbs->set_player_app_value_cb(&values);
+}
+
+static void handle_get_element_attrs(void *buf, uint16_t len, int fd)
+{
+ struct hal_ev_avrcp_get_element_attrs *ev = buf;
+ btrc_media_attr_t attrs[BTRC_MAX_APP_SETTINGS];
+ int i;
+
+ if (!cbs->get_element_attr_cb)
+ return;
+
+ /* Convert uint8_t array to btrc_media_attr_t array */
+ for (i = 0; i < ev->number; i++)
+ attrs[i] = ev->attrs[i];
+
+ cbs->get_element_attr_cb(ev->number, attrs);
+}
+
+static void handle_register_notification(void *buf, uint16_t len, int fd)
+{
+ struct hal_ev_avrcp_register_notification *ev = buf;
+
+ if (cbs->register_notification_cb)
+ cbs->register_notification_cb(ev->event, ev->param);
+}
+
+static void handle_volume_changed(void *buf, uint16_t len, int fd)
+{
+ struct hal_ev_avrcp_volume_changed *ev = buf;
+
+ if (cbs->volume_change_cb)
+ cbs->volume_change_cb(ev->volume, ev->type);
+}
+
+static void handle_passthrough_cmd(void *buf, uint16_t len, int fd)
+{
+ struct hal_ev_avrcp_passthrough_cmd *ev = buf;
+
+ if (cbs->passthrough_cmd_cb)
+ cbs->passthrough_cmd_cb(ev->id, ev->state);
+}
+
+/*
+ * handlers will be called from notification thread context,
+ * index in table equals to 'opcode - HAL_MINIMUM_EVENT'
+ */
+static const struct hal_ipc_handler ev_handlers[] = {
+ /* HAL_EV_AVRCP_REMOTE_FEATURES */
+ { handle_remote_features, false,
+ sizeof(struct hal_ev_avrcp_remote_features) },
+ /* HAL_EV_AVRCP_GET_PLAY_STATUS */
+ { handle_get_play_status, false, 0 },
+ /* HAL_EV_AVRCP_LIST_PLAYER_ATTRS */
+ { handle_list_player_attrs, false, 0 },
+ /* HAL_EV_AVRCP_LIST_PLAYER_VALUES */
+ { handle_list_player_values, false,
+ sizeof(struct hal_ev_avrcp_list_player_values) },
+ /* HAL_EV_AVRCP_GET_PLAYER_VALUES */
+ { handle_get_player_values, true,
+ sizeof(struct hal_ev_avrcp_get_player_values) },
+ /* HAL_EV_AVRCP_GET_PLAYER_ATTRS_TEXT */
+ { handle_get_player_attrs_text, true,
+ sizeof(struct hal_ev_avrcp_get_player_attrs_text) },
+ /* HAL_EV_AVRCP_GET_PLAYER_VALUES_TEXT */
+ { handle_get_player_values_text, true,
+ sizeof(struct hal_ev_avrcp_get_player_values_text) },
+ /* HAL_EV_AVRCP_SET_PLAYER_VALUES */
+ { handle_set_player_value, true,
+ sizeof(struct hal_ev_avrcp_set_player_values) },
+ /* HAL_EV_AVRCP_GET_ELEMENT_ATTRS */
+ { handle_get_element_attrs, true,
+ sizeof(struct hal_ev_avrcp_get_element_attrs) },
+ /* HAL_EV_AVRCP_REGISTER_NOTIFICATION */
+ { handle_register_notification, false,
+ sizeof(struct hal_ev_avrcp_register_notification) },
+ /* HAL_EV_AVRCP_VOLUME_CHANGED */
+ { handle_volume_changed, false,
+ sizeof(struct hal_ev_avrcp_volume_changed) },
+ /* HAL_EV_AVRCP_PASSTHROUGH_CMD */
+ { handle_passthrough_cmd, false,
+ sizeof(struct hal_ev_avrcp_passthrough_cmd) },
+};
+
+static bt_status_t init(btrc_callbacks_t *callbacks)
+{
+ struct hal_cmd_register_module cmd;
+ int ret;
+
+ DBG("");
+
+ if (interface_ready())
+ return BT_STATUS_DONE;
+
+ cbs = callbacks;
+
+ hal_ipc_register(HAL_SERVICE_ID_AVRCP, ev_handlers,
+ sizeof(ev_handlers) / sizeof(ev_handlers[0]));
+
+ cmd.service_id = HAL_SERVICE_ID_AVRCP;
+ cmd.mode = HAL_MODE_DEFAULT;
+ cmd.max_clients = 1;
+
+ ret = hal_ipc_cmd(HAL_SERVICE_ID_CORE, HAL_OP_REGISTER_MODULE,
+ sizeof(cmd), &cmd, NULL, NULL, NULL);
+
+ if (ret != BT_STATUS_SUCCESS) {
+ cbs = NULL;
+ hal_ipc_unregister(HAL_SERVICE_ID_AVRCP);
+ }
+
+ return ret;
+}
+
+static bt_status_t get_play_status_rsp(btrc_play_status_t status,
+ uint32_t song_len, uint32_t song_pos)
+{
+ struct hal_cmd_avrcp_get_play_status cmd;
+
+ DBG("");
+
+ if (!interface_ready())
+ return BT_STATUS_NOT_READY;
+
+ cmd.status = status;
+ cmd.duration = song_len;
+ cmd.position = song_pos;
+
+ return hal_ipc_cmd(HAL_SERVICE_ID_AVRCP, HAL_OP_AVRCP_GET_PLAY_STATUS,
+ sizeof(cmd), &cmd, NULL, NULL, NULL);
+}
+
+static bt_status_t list_player_app_attr_rsp(int num_attr,
+ btrc_player_attr_t *p_attrs)
+{
+ char buf[IPC_MTU];
+ struct hal_cmd_avrcp_list_player_attrs *cmd = (void *) buf;
+ size_t len;
+
+ DBG("");
+
+ if (!interface_ready())
+ return BT_STATUS_NOT_READY;
+
+ if (num_attr < 0)
+ return BT_STATUS_PARM_INVALID;
+
+ len = sizeof(*cmd) + num_attr;
+ if (len > IPC_MTU)
+ return BT_STATUS_PARM_INVALID;
+
+ cmd->number = num_attr;
+ memcpy(cmd->attrs, p_attrs, num_attr);
+
+ return hal_ipc_cmd(HAL_SERVICE_ID_AVRCP,
+ HAL_OP_AVRCP_LIST_PLAYER_ATTRS,
+ len, cmd, NULL, NULL, NULL);
+}
+
+static bt_status_t list_player_app_value_rsp(int num_val, uint8_t *p_vals)
+{
+ char buf[IPC_MTU];
+ struct hal_cmd_avrcp_list_player_values *cmd = (void *) buf;
+ size_t len;
+
+ DBG("");
+
+ if (!interface_ready())
+ return BT_STATUS_NOT_READY;
+
+ if (num_val < 0)
+ return BT_STATUS_PARM_INVALID;
+
+ len = sizeof(*cmd) + num_val;
+
+ if (len > IPC_MTU)
+ return BT_STATUS_PARM_INVALID;
+
+ cmd->number = num_val;
+ memcpy(cmd->values, p_vals, num_val);
+
+ return hal_ipc_cmd(HAL_SERVICE_ID_AVRCP,
+ HAL_OP_AVRCP_LIST_PLAYER_VALUES,
+ len, cmd, NULL, NULL, NULL);
+}
+
+static bt_status_t get_player_app_value_rsp(btrc_player_settings_t *p_vals)
+{
+ char buf[IPC_MTU];
+ struct hal_cmd_avrcp_get_player_attrs *cmd = (void *) buf;
+ size_t len, attrs_len;
+ int i;
+
+ DBG("");
+
+ if (!interface_ready())
+ return BT_STATUS_NOT_READY;
+
+ if (!p_vals)
+ return BT_STATUS_PARM_INVALID;
+
+ attrs_len = p_vals->num_attr *
+ sizeof(struct hal_avrcp_player_attr_value);
+ len = sizeof(*cmd) + attrs_len;
+
+ if (len > IPC_MTU)
+ return BT_STATUS_PARM_INVALID;
+
+ cmd->number = p_vals->num_attr;
+
+ for (i = 0; i < p_vals->num_attr; i++) {
+ cmd->attrs[i].attr = p_vals->attr_ids[i];
+ cmd->attrs[i].value = p_vals->attr_values[i];
+ }
+
+ return hal_ipc_cmd(HAL_SERVICE_ID_AVRCP,
+ HAL_OP_AVRCP_GET_PLAYER_ATTRS,
+ len, cmd, NULL, NULL, NULL);
+}
+
+static int write_text(uint8_t *ptr, uint8_t id, uint8_t *text, size_t *len)
+{
+ struct hal_avrcp_player_setting_text *value = (void *) ptr;
+ size_t attr_len = sizeof(*value);
+
+ if (attr_len + *len > IPC_MTU)
+ return 0;
+
+ value->id = id;
+ value->len = strnlen((const char *) text, BTRC_MAX_ATTR_STR_LEN);
+
+ *len += attr_len;
+
+ if (value->len + *len > IPC_MTU)
+ value->len = IPC_MTU - *len;
+
+ memcpy(value->text, text, value->len);
+
+ *len += value->len;
+
+ return attr_len + value->len;
+}
+
+static uint8_t write_player_setting_text(uint8_t *ptr, uint8_t num_attr,
+ btrc_player_setting_text_t *p_attrs,
+ size_t *len)
+{
+ int i;
+
+ for (i = 0; i < num_attr && *len < IPC_MTU; i++) {
+ int ret;
+
+ ret = write_text(ptr, p_attrs[i].id, p_attrs[i].text, len);
+ if (ret == 0)
+ break;
+
+ ptr += ret;
+ }
+
+ return i;
+}
+
+static bt_status_t get_player_app_attr_text_rsp(int num_attr,
+ btrc_player_setting_text_t *p_attrs)
+{
+ char buf[IPC_MTU];
+ struct hal_cmd_avrcp_get_player_attrs_text *cmd = (void *) buf;
+ uint8_t *ptr;
+ size_t len;
+
+ DBG("");
+
+ if (!interface_ready())
+ return BT_STATUS_NOT_READY;
+
+ if (num_attr < 0 || num_attr > BTRC_MAX_APP_SETTINGS)
+ return BT_STATUS_PARM_INVALID;
+
+ len = sizeof(*cmd);
+ ptr = (uint8_t *) &cmd->attrs[0];
+ cmd->number = write_player_setting_text(ptr, num_attr, p_attrs, &len);
+
+ return hal_ipc_cmd(HAL_SERVICE_ID_AVRCP,
+ HAL_OP_AVRCP_GET_PLAYER_ATTRS_TEXT,
+ len, cmd, NULL, NULL, NULL);
+}
+
+static bt_status_t get_player_app_value_text_rsp(int num_val,
+ btrc_player_setting_text_t *p_vals)
+{
+ char buf[IPC_MTU];
+ struct hal_cmd_avrcp_get_player_values_text *cmd = (void *) buf;
+ uint8_t *ptr;
+ size_t len;
+
+ DBG("");
+
+ if (!interface_ready())
+ return BT_STATUS_NOT_READY;
+
+ if (num_val < 0)
+ return BT_STATUS_PARM_INVALID;
+
+ len = sizeof(*cmd);
+ ptr = (uint8_t *) &cmd->values[0];
+ cmd->number = write_player_setting_text(ptr, num_val, p_vals, &len);
+
+ return hal_ipc_cmd(HAL_SERVICE_ID_AVRCP,
+ HAL_OP_AVRCP_GET_PLAYER_VALUES_TEXT,
+ len, cmd, NULL, NULL, NULL);
+}
+
+static uint8_t write_element_attr_text(uint8_t *ptr, uint8_t num_attr,
+ btrc_element_attr_val_t *p_attrs,
+ size_t *len)
+{
+ int i;
+
+ for (i = 0; i < num_attr && *len < IPC_MTU; i++) {
+ int ret;
+
+ ret = write_text(ptr, p_attrs[i].attr_id, p_attrs[i].text, len);
+ if (ret == 0)
+ break;
+
+ ptr += ret;
+ }
+
+ return i;
+}
+
+static bt_status_t get_element_attr_rsp(uint8_t num_attr,
+ btrc_element_attr_val_t *p_attrs)
+{
+ char buf[IPC_MTU];
+ struct hal_cmd_avrcp_get_element_attrs_text *cmd = (void *) buf;
+ size_t len;
+ uint8_t *ptr;
+
+ DBG("");
+
+ if (!interface_ready())
+ return BT_STATUS_NOT_READY;
+
+ len = sizeof(*cmd);
+ ptr = (uint8_t *) &cmd->values[0];
+ cmd->number = write_element_attr_text(ptr, num_attr, p_attrs, &len);
+
+ return hal_ipc_cmd(HAL_SERVICE_ID_AVRCP,
+ HAL_OP_AVRCP_GET_ELEMENT_ATTRS_TEXT,
+ len, cmd, NULL, NULL, NULL);
+}
+
+static bt_status_t set_player_app_value_rsp(btrc_status_t rsp_status)
+{
+ struct hal_cmd_avrcp_set_player_attrs_value cmd;
+
+ DBG("");
+
+ if (!interface_ready())
+ return BT_STATUS_NOT_READY;
+
+ cmd.status = rsp_status;
+
+ return hal_ipc_cmd(HAL_SERVICE_ID_AVRCP,
+ HAL_OP_AVRCP_SET_PLAYER_ATTRS_VALUE,
+ sizeof(cmd), &cmd, NULL, NULL, NULL);
+}
+
+static bt_status_t play_status_changed_rsp(btrc_notification_type_t type,
+ btrc_play_status_t *play_status)
+{
+ char buf[IPC_MTU];
+ struct hal_cmd_avrcp_register_notification *cmd = (void *) buf;
+ size_t len;
+
+ cmd->event = BTRC_EVT_PLAY_STATUS_CHANGED;
+ cmd->type = type;
+ cmd->len = 1;
+ memcpy(cmd->data, play_status, cmd->len);
+
+ len = sizeof(*cmd) + cmd->len;
+
+ return hal_ipc_cmd(HAL_SERVICE_ID_AVRCP,
+ HAL_OP_AVRCP_REGISTER_NOTIFICATION,
+ len, cmd, NULL, NULL, NULL);
+}
+
+static bt_status_t track_change_rsp(btrc_notification_type_t type,
+ btrc_uid_t *track)
+{
+ char buf[IPC_MTU];
+ struct hal_cmd_avrcp_register_notification *cmd = (void *) buf;
+ size_t len;
+
+ cmd->event = BTRC_EVT_TRACK_CHANGE;
+ cmd->type = type;
+ cmd->len = sizeof(*track);
+ memcpy(cmd->data, track, cmd->len);
+
+ len = sizeof(*cmd) + cmd->len;
+
+ return hal_ipc_cmd(HAL_SERVICE_ID_AVRCP,
+ HAL_OP_AVRCP_REGISTER_NOTIFICATION,
+ len, cmd, NULL, NULL, NULL);
+}
+
+static bt_status_t track_reached_end_rsp(btrc_notification_type_t type)
+{
+ struct hal_cmd_avrcp_register_notification cmd;
+
+ cmd.event = BTRC_EVT_TRACK_REACHED_END;
+ cmd.type = type;
+ cmd.len = 0;
+
+ return hal_ipc_cmd(HAL_SERVICE_ID_AVRCP,
+ HAL_OP_AVRCP_REGISTER_NOTIFICATION,
+ sizeof(cmd), &cmd, NULL, NULL, NULL);
+}
+
+static bt_status_t track_reached_start_rsp(btrc_notification_type_t type)
+{
+ struct hal_cmd_avrcp_register_notification cmd;
+
+ cmd.event = BTRC_EVT_TRACK_REACHED_START;
+ cmd.type = type;
+ cmd.len = 0;
+
+ return hal_ipc_cmd(HAL_SERVICE_ID_AVRCP,
+ HAL_OP_AVRCP_REGISTER_NOTIFICATION,
+ sizeof(cmd), &cmd, NULL, NULL, NULL);
+}
+
+static bt_status_t play_pos_changed_rsp(btrc_notification_type_t type,
+ uint32_t *song_pos)
+{
+ char buf[IPC_MTU];
+ struct hal_cmd_avrcp_register_notification *cmd = (void *) buf;
+ size_t len;
+
+ cmd->event = BTRC_EVT_PLAY_POS_CHANGED;
+ cmd->type = type;
+ cmd->len = sizeof(*song_pos);
+ memcpy(cmd->data, song_pos, cmd->len);
+
+ len = sizeof(*cmd) + cmd->len;
+
+ return hal_ipc_cmd(HAL_SERVICE_ID_AVRCP,
+ HAL_OP_AVRCP_REGISTER_NOTIFICATION,
+ len, cmd, NULL, NULL, NULL);
+}
+
+static bt_status_t settings_changed_rsp(btrc_notification_type_t type,
+ btrc_player_settings_t *player_setting)
+{
+ char buf[IPC_MTU];
+ struct hal_cmd_avrcp_register_notification *cmd = (void *) buf;
+ struct hal_avrcp_player_attr_value *attrs;
+ size_t len, param_len;
+ int i;
+
+ param_len = player_setting->num_attr * sizeof(*attrs);
+ len = sizeof(*cmd) + param_len;
+
+ if (len > IPC_MTU)
+ return BT_STATUS_PARM_INVALID;
+
+ cmd->event = BTRC_EVT_APP_SETTINGS_CHANGED;
+ cmd->type = type;
+ cmd->len = param_len;
+
+ attrs = (struct hal_avrcp_player_attr_value *) &cmd->data[0];
+ for (i = 0; i < player_setting->num_attr; i++) {
+ attrs[i].attr = player_setting->attr_ids[i];
+ attrs[i].value = player_setting->attr_values[i];
+ }
+
+ return hal_ipc_cmd(HAL_SERVICE_ID_AVRCP,
+ HAL_OP_AVRCP_REGISTER_NOTIFICATION,
+ len, cmd, NULL, NULL, NULL);
+}
+
+static bt_status_t register_notification_rsp(btrc_event_id_t event_id,
+ btrc_notification_type_t type,
+ btrc_register_notification_t *p_param)
+{
+ DBG("");
+
+ if (!interface_ready())
+ return BT_STATUS_NOT_READY;
+
+ switch (event_id) {
+ case BTRC_EVT_PLAY_STATUS_CHANGED:
+ return play_status_changed_rsp(type, &p_param->play_status);
+ case BTRC_EVT_TRACK_CHANGE:
+ return track_change_rsp(type, &p_param->track);
+ case BTRC_EVT_TRACK_REACHED_END:
+ return track_reached_end_rsp(type);
+ case BTRC_EVT_TRACK_REACHED_START:
+ return track_reached_start_rsp(type);
+ case BTRC_EVT_PLAY_POS_CHANGED:
+ return play_pos_changed_rsp(type, &p_param->song_pos);
+ case BTRC_EVT_APP_SETTINGS_CHANGED:
+ return settings_changed_rsp(type, &p_param->player_setting);
+ default:
+ return BT_STATUS_PARM_INVALID;
+ }
+}
+
+static bt_status_t set_volume(uint8_t volume)
+{
+ struct hal_cmd_avrcp_set_volume cmd;
+
+ DBG("");
+
+ if (!interface_ready())
+ return BT_STATUS_NOT_READY;
+
+ cmd.value = volume;
+
+ return hal_ipc_cmd(HAL_SERVICE_ID_AVRCP, HAL_OP_AVRCP_SET_VOLUME,
+ sizeof(cmd), &cmd, NULL, NULL, NULL);
+}
+
+static void cleanup(void)
+{
+ struct hal_cmd_unregister_module cmd;
+
+ DBG("");
+
+ if (!interface_ready())
+ return;
+
+ cmd.service_id = HAL_SERVICE_ID_AVRCP;
+
+ hal_ipc_cmd(HAL_SERVICE_ID_CORE, HAL_OP_UNREGISTER_MODULE,
+ sizeof(cmd), &cmd, NULL, NULL, NULL);
+
+ hal_ipc_unregister(HAL_SERVICE_ID_AVRCP);
+
+ cbs = NULL;
+}
+
+static btrc_interface_t iface = {
+ .size = sizeof(iface),
+ .init = init,
+ .get_play_status_rsp = get_play_status_rsp,
+ .list_player_app_attr_rsp = list_player_app_attr_rsp,
+ .list_player_app_value_rsp = list_player_app_value_rsp,
+ .get_player_app_value_rsp = get_player_app_value_rsp,
+ .get_player_app_attr_text_rsp = get_player_app_attr_text_rsp,
+ .get_player_app_value_text_rsp = get_player_app_value_text_rsp,
+ .get_element_attr_rsp = get_element_attr_rsp,
+ .set_player_app_value_rsp = set_player_app_value_rsp,
+ .register_notification_rsp = register_notification_rsp,
+ .set_volume = set_volume,
+ .cleanup = cleanup
+};
+
+btrc_interface_t *bt_get_avrcp_interface(void)
+{
+ return &iface;
+}
diff --git a/repo/android/hal-bluetooth.c b/repo/android/hal-bluetooth.c
new file mode 100644
index 0000000..66f4a37
--- /dev/null
+++ b/repo/android/hal-bluetooth.c
@@ -0,0 +1,1136 @@
+/*
+ * Copyright (C) 2013 Intel Corporation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <string.h>
+#include <errno.h>
+
+#include <cutils/properties.h>
+
+#include "hal-log.h"
+#include "hal.h"
+#include "hal-msg.h"
+#include "ipc-common.h"
+#include "hal-ipc.h"
+#include "hal-utils.h"
+
+static const bt_callbacks_t *bt_hal_cbacks = NULL;
+
+#define enum_prop_to_hal(prop, hal_prop, type) do { \
+ static type e; \
+ prop.val = &e; \
+ prop.len = sizeof(e); \
+ e = *((uint8_t *) (hal_prop->val)); \
+} while (0)
+
+#define enum_prop_from_hal(prop, hal_len, hal_val, enum_type) do { \
+ enum_type e; \
+ if (prop->len != sizeof(e)) { \
+ error("invalid HAL property %u (%u vs %zu), aborting ", \
+ prop->type, prop->len, sizeof(e)); \
+ exit(EXIT_FAILURE); \
+ } \
+ memcpy(&e, prop->val, sizeof(e)); \
+ *((uint8_t *) hal_val) = e; /* enums are mapped to 1 byte */ \
+ *hal_len = 1; \
+} while (0)
+
+static void handle_adapter_state_changed(void *buf, uint16_t len, int fd)
+{
+ struct hal_ev_adapter_state_changed *ev = buf;
+
+ DBG("state: %s", bt_state_t2str(ev->state));
+
+ if (bt_hal_cbacks->adapter_state_changed_cb)
+ bt_hal_cbacks->adapter_state_changed_cb(ev->state);
+}
+
+static void adapter_props_to_hal(bt_property_t *send_props,
+ struct hal_property *prop,
+ uint8_t num_props, uint16_t len)
+{
+ void *buf = prop;
+ uint8_t i;
+
+ for (i = 0; i < num_props; i++) {
+ if (sizeof(*prop) + prop->len > len) {
+ error("invalid adapter properties(%zu > %u), aborting",
+ sizeof(*prop) + prop->len, len);
+ exit(EXIT_FAILURE);
+ }
+
+ send_props[i].type = prop->type;
+
+ switch (prop->type) {
+ case HAL_PROP_ADAPTER_TYPE:
+ enum_prop_to_hal(send_props[i], prop,
+ bt_device_type_t);
+ break;
+ case HAL_PROP_ADAPTER_SCAN_MODE:
+ enum_prop_to_hal(send_props[i], prop,
+ bt_scan_mode_t);
+ break;
+ case HAL_PROP_ADAPTER_SERVICE_REC:
+ default:
+ send_props[i].len = prop->len;
+ send_props[i].val = prop->val;
+ break;
+ }
+
+ DBG("prop[%d]: %s", i, btproperty2str(&send_props[i]));
+
+ len -= sizeof(*prop) + prop->len;
+ buf += sizeof(*prop) + prop->len;
+ prop = buf;
+ }
+
+ if (!len)
+ return;
+
+ error("invalid adapter properties (%u bytes left), aborting", len);
+ exit(EXIT_FAILURE);
+}
+
+static void adapter_prop_from_hal(const bt_property_t *property, uint8_t *type,
+ uint16_t *len, void *val)
+{
+ /* type match IPC type */
+ *type = property->type;
+
+ switch (property->type) {
+ case HAL_PROP_ADAPTER_SCAN_MODE:
+ enum_prop_from_hal(property, len, val, bt_scan_mode_t);
+ break;
+ case BT_PROPERTY_BDNAME:
+ case BT_PROPERTY_BDADDR:
+ case BT_PROPERTY_UUIDS:
+ case BT_PROPERTY_CLASS_OF_DEVICE:
+ case BT_PROPERTY_TYPE_OF_DEVICE:
+ case BT_PROPERTY_SERVICE_RECORD:
+ case BT_PROPERTY_ADAPTER_BONDED_DEVICES:
+ case BT_PROPERTY_ADAPTER_DISCOVERY_TIMEOUT:
+ case BT_PROPERTY_REMOTE_FRIENDLY_NAME:
+ case BT_PROPERTY_REMOTE_RSSI:
+ case BT_PROPERTY_REMOTE_VERSION_INFO:
+ case BT_PROPERTY_REMOTE_DEVICE_TIMESTAMP:
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+ case BT_PROPERTY_LOCAL_LE_FEATURES:
+#endif
+ default:
+ *len = property->len;
+ memcpy(val, property->val, property->len);
+ break;
+ }
+}
+
+static void device_props_to_hal(bt_property_t *send_props,
+ struct hal_property *prop, uint8_t num_props,
+ uint16_t len)
+{
+ void *buf = prop;
+ uint8_t i;
+
+ for (i = 0; i < num_props; i++) {
+ if (sizeof(*prop) + prop->len > len) {
+ error("invalid device properties (%zu > %u), aborting",
+ sizeof(*prop) + prop->len, len);
+ exit(EXIT_FAILURE);
+ }
+
+ send_props[i].type = prop->type;
+
+ switch (prop->type) {
+ case HAL_PROP_DEVICE_TYPE:
+ enum_prop_to_hal(send_props[i], prop,
+ bt_device_type_t);
+ break;
+ case HAL_PROP_DEVICE_VERSION_INFO:
+ {
+ static bt_remote_version_t e;
+ const struct hal_prop_device_info *p;
+
+ send_props[i].val = &e;
+ send_props[i].len = sizeof(e);
+
+ p = (struct hal_prop_device_info *) prop->val;
+
+ e.manufacturer = p->manufacturer;
+ e.sub_ver = p->sub_version;
+ e.version = p->version;
+ }
+ break;
+ case HAL_PROP_DEVICE_SERVICE_REC:
+ {
+ static bt_service_record_t e;
+ const struct hal_prop_device_service_rec *p;
+
+ send_props[i].val = &e;
+ send_props[i].len = sizeof(e);
+
+ p = (struct hal_prop_device_service_rec *) prop->val;
+
+ memset(&e, 0, sizeof(e));
+ memcpy(&e.channel, &p->channel, sizeof(e.channel));
+ memcpy(e.uuid.uu, p->uuid, sizeof(e.uuid.uu));
+ memcpy(e.name, p->name, p->name_len);
+ }
+ break;
+ default:
+ send_props[i].len = prop->len;
+ send_props[i].val = prop->val;
+ break;
+ }
+
+ len -= sizeof(*prop) + prop->len;
+ buf += sizeof(*prop) + prop->len;
+ prop = buf;
+
+ DBG("prop[%d]: %s", i, btproperty2str(&send_props[i]));
+ }
+
+ if (!len)
+ return;
+
+ error("invalid device properties (%u bytes left), aborting", len);
+ exit(EXIT_FAILURE);
+}
+
+static void handle_adapter_props_changed(void *buf, uint16_t len, int fd)
+{
+ struct hal_ev_adapter_props_changed *ev = buf;
+ bt_property_t props[ev->num_props];
+
+ DBG("");
+
+ if (!bt_hal_cbacks->adapter_properties_cb)
+ return;
+
+ len -= sizeof(*ev);
+ adapter_props_to_hal(props, ev->props, ev->num_props, len);
+
+ bt_hal_cbacks->adapter_properties_cb(ev->status, ev->num_props, props);
+}
+
+static void handle_bond_state_change(void *buf, uint16_t len, int fd)
+{
+ struct hal_ev_bond_state_changed *ev = buf;
+ bt_bdaddr_t *addr = (bt_bdaddr_t *) ev->bdaddr;
+
+ DBG("state %u", ev->state);
+
+ if (bt_hal_cbacks->bond_state_changed_cb)
+ bt_hal_cbacks->bond_state_changed_cb(ev->status, addr,
+ ev->state);
+}
+
+static void handle_pin_request(void *buf, uint16_t len, int fd)
+{
+ struct hal_ev_pin_request *ev = buf;
+ /* Those are declared as packed, so it's safe to assign pointers */
+ bt_bdaddr_t *addr = (bt_bdaddr_t *) ev->bdaddr;
+ bt_bdname_t *name = (bt_bdname_t *) ev->name;
+
+ DBG("");
+
+ if (bt_hal_cbacks->pin_request_cb)
+ bt_hal_cbacks->pin_request_cb(addr, name, ev->class_of_dev);
+}
+
+static void handle_ssp_request(void *buf, uint16_t len, int fd)
+{
+ struct hal_ev_ssp_request *ev = buf;
+ /* Those are declared as packed, so it's safe to assign pointers */
+ bt_bdaddr_t *addr = (bt_bdaddr_t *) ev->bdaddr;
+ bt_bdname_t *name = (bt_bdname_t *) ev->name;
+
+ DBG("");
+
+ if (bt_hal_cbacks->ssp_request_cb)
+ bt_hal_cbacks->ssp_request_cb(addr, name, ev->class_of_dev,
+ ev->pairing_variant,
+ ev->passkey);
+}
+
+void bt_thread_associate(void)
+{
+ if (bt_hal_cbacks->thread_evt_cb)
+ bt_hal_cbacks->thread_evt_cb(ASSOCIATE_JVM);
+}
+
+void bt_thread_disassociate(void)
+{
+ if (bt_hal_cbacks->thread_evt_cb)
+ bt_hal_cbacks->thread_evt_cb(DISASSOCIATE_JVM);
+}
+
+static bool interface_ready(void)
+{
+ return bt_hal_cbacks != NULL;
+}
+
+static void handle_discovery_state_changed(void *buf, uint16_t len, int fd)
+{
+ struct hal_ev_discovery_state_changed *ev = buf;
+
+ DBG("");
+
+ if (bt_hal_cbacks->discovery_state_changed_cb)
+ bt_hal_cbacks->discovery_state_changed_cb(ev->state);
+}
+
+static void handle_device_found(void *buf, uint16_t len, int fd)
+{
+ struct hal_ev_device_found *ev = buf;
+ bt_property_t props[ev->num_props];
+
+ DBG("");
+
+ if (!bt_hal_cbacks->device_found_cb)
+ return;
+
+ len -= sizeof(*ev);
+ device_props_to_hal(props, ev->props, ev->num_props, len);
+
+ bt_hal_cbacks->device_found_cb(ev->num_props, props);
+}
+
+static void handle_device_state_changed(void *buf, uint16_t len, int fd)
+{
+ struct hal_ev_remote_device_props *ev = buf;
+ bt_property_t props[ev->num_props];
+
+ DBG("");
+
+ if (!bt_hal_cbacks->remote_device_properties_cb)
+ return;
+
+ len -= sizeof(*ev);
+ device_props_to_hal(props, ev->props, ev->num_props, len);
+
+ bt_hal_cbacks->remote_device_properties_cb(ev->status,
+ (bt_bdaddr_t *)ev->bdaddr,
+ ev->num_props, props);
+}
+
+static void handle_acl_state_changed(void *buf, uint16_t len, int fd)
+{
+ struct hal_ev_acl_state_changed *ev = buf;
+ bt_bdaddr_t *addr = (bt_bdaddr_t *) ev->bdaddr;
+
+ DBG("state %u", ev->state);
+
+ if (bt_hal_cbacks->acl_state_changed_cb)
+ bt_hal_cbacks->acl_state_changed_cb(ev->status, addr,
+ ev->state);
+}
+
+static void handle_dut_mode_receive(void *buf, uint16_t len, int fd)
+{
+ struct hal_ev_dut_mode_receive *ev = buf;
+
+ DBG("");
+
+ if (len != sizeof(*ev) + ev->len) {
+ error("invalid dut mode receive event (%u), aborting", len);
+ exit(EXIT_FAILURE);
+ }
+
+ if (bt_hal_cbacks->dut_mode_recv_cb)
+ bt_hal_cbacks->dut_mode_recv_cb(ev->opcode, ev->data, ev->len);
+}
+
+static void handle_le_test_mode(void *buf, uint16_t len, int fd)
+{
+ struct hal_ev_le_test_mode *ev = buf;
+
+ DBG("");
+
+ if (bt_hal_cbacks->le_test_mode_cb)
+ bt_hal_cbacks->le_test_mode_cb(ev->status, ev->num_packets);
+}
+
+static void handle_energy_info(void *buf, uint16_t len, int fd)
+{
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+ struct hal_ev_energy_info *ev = buf;
+ bt_activity_energy_info info;
+
+ DBG("");
+
+ info.ctrl_state = ev->ctrl_state;
+ info.energy_used = ev->energy_used;
+ info.idle_time = ev->idle_time;
+ info.rx_time = ev->rx_time;
+ info.status = ev->status;
+ info.tx_time = ev->status;
+
+ if (bt_hal_cbacks->energy_info_cb)
+ bt_hal_cbacks->energy_info_cb(&info);
+#endif
+}
+
+/*
+ * handlers will be called from notification thread context,
+ * index in table equals to 'opcode - HAL_MINIMUM_EVENT'
+ */
+static const struct hal_ipc_handler ev_handlers[] = {
+ /* HAL_EV_ADAPTER_STATE_CHANGED */
+ { handle_adapter_state_changed, false,
+ sizeof(struct hal_ev_adapter_state_changed) },
+ /* HAL_EV_ADAPTER_PROPS_CHANGED */
+ { handle_adapter_props_changed, true,
+ sizeof(struct hal_ev_adapter_props_changed) +
+ sizeof(struct hal_property) },
+ /* HAL_EV_REMOTE_DEVICE_PROPS */
+ { handle_device_state_changed, true,
+ sizeof(struct hal_ev_remote_device_props) +
+ sizeof(struct hal_property) },
+ /* HAL_EV_DEVICE_FOUND */
+ { handle_device_found, true, sizeof(struct hal_ev_device_found) +
+ sizeof(struct hal_property) },
+ /* HAL_EV_DISCOVERY_STATE_CHANGED */
+ { handle_discovery_state_changed, false,
+ sizeof(struct hal_ev_discovery_state_changed) },
+ /* HAL_EV_PIN_REQUEST */
+ { handle_pin_request, false, sizeof(struct hal_ev_pin_request) },
+ /* HAL_EV_SSP_REQUEST */
+ { handle_ssp_request, false, sizeof(struct hal_ev_ssp_request) },
+ /* HAL_EV_BOND_STATE_CHANGED */
+ { handle_bond_state_change, false,
+ sizeof(struct hal_ev_bond_state_changed) },
+ /* HAL_EV_ACL_STATE_CHANGED */
+ { handle_acl_state_changed, false,
+ sizeof(struct hal_ev_acl_state_changed) },
+ /* HAL_EV_DUT_MODE_RECEIVE */
+ { handle_dut_mode_receive, true,
+ sizeof(struct hal_ev_dut_mode_receive) },
+ /* HAL_EV_LE_TEST_MODE */
+ { handle_le_test_mode, false, sizeof(struct hal_ev_le_test_mode) },
+ /* HAL_EV_ENERGY_INFO */
+ { handle_energy_info, false, sizeof(struct hal_ev_energy_info) },
+};
+
+static uint8_t get_mode(void)
+{
+ char value[PROPERTY_VALUE_MAX];
+
+ if (get_config("mode", value, NULL) > 0) {
+ if (!strcasecmp(value, "bredr"))
+ return HAL_MODE_BREDR;
+
+ if (!strcasecmp(value, "le"))
+ return HAL_MODE_LE;
+ }
+
+ return HAL_MODE_DEFAULT;
+}
+
+static uint16_t add_prop(const char *prop, uint8_t type, void *buf)
+{
+ struct hal_config_prop *hal_prop = buf;
+
+ hal_prop->type = type;
+ hal_prop->len = strlen(prop) + 1;
+ memcpy(hal_prop->val, prop, hal_prop->len);
+
+ return sizeof(*hal_prop) + hal_prop->len;
+}
+
+static int send_configuration(void)
+{
+ char buf[IPC_MTU];
+ struct hal_cmd_configuration *cmd = (void *) buf;
+ char prop[PROPERTY_VALUE_MAX];
+ uint16_t len = sizeof(*cmd);
+
+ cmd->num = 0;
+
+ if (get_config("vendor", prop, "ro.product.manufacturer") > 0) {
+ len += add_prop(prop, HAL_CONFIG_VENDOR, buf + len);
+ cmd->num++;
+ }
+
+ if (get_config("name", prop, "ro.product.name") > 0) {
+ len += add_prop(prop, HAL_CONFIG_NAME, buf + len);
+ cmd->num++;
+ }
+
+ if (get_config("model", prop, "ro.product.model") > 0) {
+ len += add_prop(prop, HAL_CONFIG_MODEL, buf + len);
+ cmd->num++;
+ }
+
+ if (get_config("serialno", prop, "ro.serialno") > 0) {
+ len += add_prop(prop, HAL_CONFIG_SERIAL_NUMBER, buf + len);
+ cmd->num++;
+ }
+
+ if (get_config("systemid", prop, NULL) > 0) {
+ len += add_prop(prop, HAL_CONFIG_SYSTEM_ID, buf + len);
+ cmd->num++;
+ }
+
+ if (get_config("pnpid", prop, NULL) > 0) {
+ len += add_prop(prop, HAL_CONFIG_PNP_ID, buf + len);
+ cmd->num++;
+ }
+
+ if (get_config("fwrev", prop, "ro.build.version.release") > 0) {
+ len += add_prop(prop, HAL_CONFIG_FW_REV, buf + len);
+ cmd->num++;
+ }
+
+ if (get_config("hwrev", prop, "ro.board.platform") > 0) {
+ len += add_prop(prop, HAL_CONFIG_HW_REV, buf + len);
+ cmd->num++;
+ }
+
+ return hal_ipc_cmd(HAL_SERVICE_ID_CORE, HAL_OP_CONFIGURATION, len, cmd,
+ NULL, NULL, NULL);
+}
+
+static int init(bt_callbacks_t *callbacks)
+{
+ struct hal_cmd_register_module cmd;
+ int status;
+
+ DBG("");
+
+ if (interface_ready())
+ return BT_STATUS_DONE;
+
+ hal_ipc_register(HAL_SERVICE_ID_BLUETOOTH, ev_handlers,
+ sizeof(ev_handlers)/sizeof(ev_handlers[0]));
+
+ if (!hal_ipc_init(BLUEZ_HAL_SK_PATH, sizeof(BLUEZ_HAL_SK_PATH)))
+ return BT_STATUS_FAIL;
+
+ bt_hal_cbacks = callbacks;
+
+ /* Start Android Bluetooth daemon service */
+ if (property_set("bluetooth.start", "daemon") < 0) {
+ error("Failed to set bluetooth.start=daemon");
+ hal_ipc_cleanup();
+ bt_hal_cbacks = NULL;
+ return BT_STATUS_FAIL;
+ }
+
+ if (!hal_ipc_accept()) {
+ hal_ipc_cleanup();
+ bt_hal_cbacks = NULL;
+ return BT_STATUS_FAIL;
+ }
+
+ status = send_configuration();
+ if (status != BT_STATUS_SUCCESS) {
+ error("Failed to send configuration");
+ goto fail;
+ }
+
+ cmd.service_id = HAL_SERVICE_ID_BLUETOOTH;
+ cmd.mode = get_mode();
+ cmd.max_clients = 1;
+
+ status = hal_ipc_cmd(HAL_SERVICE_ID_CORE, HAL_OP_REGISTER_MODULE,
+ sizeof(cmd), &cmd, NULL, NULL, NULL);
+ if (status != BT_STATUS_SUCCESS) {
+ error("Failed to register 'bluetooth' service");
+ goto fail;
+ }
+
+ cmd.service_id = HAL_SERVICE_ID_SOCKET;
+ cmd.max_clients = 1;
+
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+ cmd.mode = HAL_MODE_SOCKET_DYNAMIC_MAP;
+#else
+ cmd.mode = HAL_MODE_DEFAULT;
+#endif
+
+ status = hal_ipc_cmd(HAL_SERVICE_ID_CORE, HAL_OP_REGISTER_MODULE,
+ sizeof(cmd), &cmd, NULL, NULL, NULL);
+ if (status != BT_STATUS_SUCCESS) {
+ error("Failed to register 'socket' service");
+ goto fail;
+ }
+
+ return status;
+
+fail:
+ hal_ipc_cleanup();
+ bt_hal_cbacks = NULL;
+
+ hal_ipc_unregister(HAL_SERVICE_ID_BLUETOOTH);
+
+ return status;
+}
+
+static int enable(void)
+{
+ DBG("");
+
+ if (!interface_ready())
+ return BT_STATUS_NOT_READY;
+
+ return hal_ipc_cmd(HAL_SERVICE_ID_BLUETOOTH, HAL_OP_ENABLE, 0, NULL,
+ NULL, NULL, NULL);
+}
+
+static int disable(void)
+{
+ DBG("");
+
+ if (!interface_ready())
+ return BT_STATUS_NOT_READY;
+
+ return hal_ipc_cmd(HAL_SERVICE_ID_BLUETOOTH, HAL_OP_DISABLE, 0, NULL,
+ NULL, NULL, NULL);
+}
+
+static void cleanup(void)
+{
+ DBG("");
+
+ if (!interface_ready())
+ return;
+
+ hal_ipc_cleanup();
+
+ hal_ipc_unregister(HAL_SERVICE_ID_BLUETOOTH);
+
+ bt_hal_cbacks = NULL;
+}
+
+static int get_adapter_properties(void)
+{
+ DBG("");
+
+ if (!interface_ready())
+ return BT_STATUS_NOT_READY;
+
+ return hal_ipc_cmd(HAL_SERVICE_ID_BLUETOOTH, HAL_OP_GET_ADAPTER_PROPS,
+ 0, NULL, NULL, NULL, NULL);
+}
+
+static int get_adapter_property(bt_property_type_t type)
+{
+ struct hal_cmd_get_adapter_prop cmd;
+
+ DBG("prop: %s (%d)", bt_property_type_t2str(type), type);
+
+ if (!interface_ready())
+ return BT_STATUS_NOT_READY;
+
+ /* type match IPC type */
+ cmd.type = type;
+
+ return hal_ipc_cmd(HAL_SERVICE_ID_BLUETOOTH, HAL_OP_GET_ADAPTER_PROP,
+ sizeof(cmd), &cmd, NULL, NULL, NULL);
+}
+
+static int set_adapter_property(const bt_property_t *property)
+{
+ char buf[IPC_MTU];
+ struct hal_cmd_set_adapter_prop *cmd = (void *) buf;
+ size_t len;
+
+ DBG("prop: %s", btproperty2str(property));
+
+ if (!interface_ready())
+ return BT_STATUS_NOT_READY;
+
+ adapter_prop_from_hal(property, &cmd->type, &cmd->len, cmd->val);
+
+ len = sizeof(*cmd) + cmd->len;
+
+ return hal_ipc_cmd(HAL_SERVICE_ID_BLUETOOTH, HAL_OP_SET_ADAPTER_PROP,
+ len, cmd, NULL, NULL, NULL);
+}
+
+static int get_remote_device_properties(bt_bdaddr_t *remote_addr)
+{
+ struct hal_cmd_get_remote_device_props cmd;
+
+ DBG("bdaddr: %s", bdaddr2str(remote_addr));
+
+ if (!interface_ready())
+ return BT_STATUS_NOT_READY;
+
+ memcpy(cmd.bdaddr, remote_addr, sizeof(cmd.bdaddr));
+
+ return hal_ipc_cmd(HAL_SERVICE_ID_BLUETOOTH,
+ HAL_OP_GET_REMOTE_DEVICE_PROPS,
+ sizeof(cmd), &cmd, NULL, NULL, NULL);
+}
+
+static int get_remote_device_property(bt_bdaddr_t *remote_addr,
+ bt_property_type_t type)
+{
+ struct hal_cmd_get_remote_device_prop cmd;
+
+ DBG("bdaddr: %s prop: %s", bdaddr2str(remote_addr),
+ bt_property_type_t2str(type));
+
+ if (!interface_ready())
+ return BT_STATUS_NOT_READY;
+
+ memcpy(cmd.bdaddr, remote_addr, sizeof(cmd.bdaddr));
+
+ /* type match IPC type */
+ cmd.type = type;
+
+ return hal_ipc_cmd(HAL_SERVICE_ID_BLUETOOTH,
+ HAL_OP_GET_REMOTE_DEVICE_PROP,
+ sizeof(cmd), &cmd, NULL, NULL, NULL);
+}
+
+static int set_remote_device_property(bt_bdaddr_t *remote_addr,
+ const bt_property_t *property)
+{
+ char buf[IPC_MTU];
+ struct hal_cmd_set_remote_device_prop *cmd = (void *) buf;
+ size_t len;
+
+ DBG("bdaddr: %s prop: %s", bdaddr2str(remote_addr),
+ bt_property_type_t2str(property->type));
+
+ if (!interface_ready())
+ return BT_STATUS_NOT_READY;
+
+ memcpy(cmd->bdaddr, remote_addr, sizeof(cmd->bdaddr));
+
+ /* type match IPC type */
+ cmd->type = property->type;
+ cmd->len = property->len;
+ memcpy(cmd->val, property->val, property->len);
+
+ len = sizeof(*cmd) + cmd->len;
+
+ return hal_ipc_cmd(HAL_SERVICE_ID_BLUETOOTH,
+ HAL_OP_SET_REMOTE_DEVICE_PROP,
+ len, cmd, NULL, NULL, NULL);
+}
+
+static int get_remote_service_record(bt_bdaddr_t *remote_addr, bt_uuid_t *uuid)
+{
+ struct hal_cmd_get_remote_service_rec cmd;
+
+ DBG("bdaddr: %s", bdaddr2str(remote_addr));
+
+ if (!interface_ready())
+ return BT_STATUS_NOT_READY;
+
+ memcpy(cmd.bdaddr, remote_addr, sizeof(cmd.bdaddr));
+ memcpy(cmd.uuid, uuid, sizeof(cmd.uuid));
+
+ return hal_ipc_cmd(HAL_SERVICE_ID_BLUETOOTH,
+ HAL_OP_GET_REMOTE_SERVICE_REC,
+ sizeof(cmd), &cmd, NULL, NULL, NULL);
+}
+
+static int get_remote_services(bt_bdaddr_t *remote_addr)
+{
+ struct hal_cmd_get_remote_services cmd;
+
+ DBG("bdaddr: %s", bdaddr2str(remote_addr));
+
+ if (!interface_ready())
+ return BT_STATUS_NOT_READY;
+
+ memcpy(cmd.bdaddr, remote_addr, sizeof(cmd.bdaddr));
+
+ return hal_ipc_cmd(HAL_SERVICE_ID_BLUETOOTH, HAL_OP_GET_REMOTE_SERVICES,
+ sizeof(cmd), &cmd, NULL, NULL, NULL);
+}
+
+static int start_discovery(void)
+{
+ DBG("");
+
+ if (!interface_ready())
+ return BT_STATUS_NOT_READY;
+
+ return hal_ipc_cmd(HAL_SERVICE_ID_BLUETOOTH, HAL_OP_START_DISCOVERY, 0,
+ NULL, NULL, NULL, NULL);
+}
+
+static int cancel_discovery(void)
+{
+ DBG("");
+
+ if (!interface_ready())
+ return BT_STATUS_NOT_READY;
+
+ return hal_ipc_cmd(HAL_SERVICE_ID_BLUETOOTH, HAL_OP_CANCEL_DISCOVERY, 0,
+ NULL, NULL, NULL, NULL);
+}
+
+static int create_bond_real(const bt_bdaddr_t *bd_addr, int transport)
+{
+ struct hal_cmd_create_bond cmd;
+
+ DBG("bdaddr: %s", bdaddr2str(bd_addr));
+
+ if (!interface_ready())
+ return BT_STATUS_NOT_READY;
+
+ cmd.transport = transport;
+
+ memcpy(cmd.bdaddr, bd_addr, sizeof(cmd.bdaddr));
+
+ return hal_ipc_cmd(HAL_SERVICE_ID_BLUETOOTH, HAL_OP_CREATE_BOND,
+ sizeof(cmd), &cmd, NULL, NULL, NULL);
+}
+
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+static int create_bond(const bt_bdaddr_t *bd_addr, int transport)
+{
+ return create_bond_real(bd_addr, transport);
+}
+#else
+static int create_bond(const bt_bdaddr_t *bd_addr)
+{
+ return create_bond_real(bd_addr, BT_TRANSPORT_UNKNOWN);
+}
+#endif
+
+static int cancel_bond(const bt_bdaddr_t *bd_addr)
+{
+ struct hal_cmd_cancel_bond cmd;
+
+ DBG("bdaddr: %s", bdaddr2str(bd_addr));
+
+ if (!interface_ready())
+ return BT_STATUS_NOT_READY;
+
+ memcpy(cmd.bdaddr, bd_addr, sizeof(cmd.bdaddr));
+
+ return hal_ipc_cmd(HAL_SERVICE_ID_BLUETOOTH, HAL_OP_CANCEL_BOND,
+ sizeof(cmd), &cmd, NULL, NULL, NULL);
+}
+
+static int remove_bond(const bt_bdaddr_t *bd_addr)
+{
+ struct hal_cmd_remove_bond cmd;
+
+ DBG("bdaddr: %s", bdaddr2str(bd_addr));
+
+ if (!interface_ready())
+ return BT_STATUS_NOT_READY;
+
+ memcpy(cmd.bdaddr, bd_addr, sizeof(cmd.bdaddr));
+
+ return hal_ipc_cmd(HAL_SERVICE_ID_BLUETOOTH, HAL_OP_REMOVE_BOND,
+ sizeof(cmd), &cmd, NULL, NULL, NULL);
+}
+
+static int pin_reply(const bt_bdaddr_t *bd_addr, uint8_t accept,
+ uint8_t pin_len, bt_pin_code_t *pin_code)
+{
+ struct hal_cmd_pin_reply cmd;
+
+ DBG("bdaddr: %s", bdaddr2str(bd_addr));
+
+ if (!interface_ready())
+ return BT_STATUS_NOT_READY;
+
+ memcpy(cmd.bdaddr, bd_addr, sizeof(cmd.bdaddr));
+ cmd.accept = accept;
+ cmd.pin_len = pin_len;
+ memcpy(cmd.pin_code, pin_code, sizeof(cmd.pin_code));
+
+ return hal_ipc_cmd(HAL_SERVICE_ID_BLUETOOTH, HAL_OP_PIN_REPLY,
+ sizeof(cmd), &cmd, NULL, NULL, NULL);
+}
+
+static int ssp_reply(const bt_bdaddr_t *bd_addr, bt_ssp_variant_t variant,
+ uint8_t accept, uint32_t passkey)
+{
+ struct hal_cmd_ssp_reply cmd;
+
+ DBG("bdaddr: %s", bdaddr2str(bd_addr));
+
+ if (!interface_ready())
+ return BT_STATUS_NOT_READY;
+
+ memcpy(cmd.bdaddr, bd_addr, sizeof(cmd.bdaddr));
+ /* type match IPC type */
+ cmd.ssp_variant = variant;
+ cmd.accept = accept;
+ cmd.passkey = passkey;
+
+ return hal_ipc_cmd(HAL_SERVICE_ID_BLUETOOTH, HAL_OP_SSP_REPLY,
+ sizeof(cmd), &cmd, NULL, NULL, NULL);
+}
+
+static const void *get_profile_interface(const char *profile_id)
+{
+ DBG("%s", profile_id);
+
+ if (!interface_ready())
+ return NULL;
+
+ if (!strcmp(profile_id, BT_PROFILE_SOCKETS_ID))
+ return bt_get_socket_interface();
+
+ if (!strcmp(profile_id, BT_PROFILE_HIDHOST_ID))
+ return bt_get_hidhost_interface();
+
+ if (!strcmp(profile_id, BT_PROFILE_PAN_ID))
+ return bt_get_pan_interface();
+
+ if (!strcmp(profile_id, BT_PROFILE_ADVANCED_AUDIO_ID))
+ return bt_get_a2dp_interface();
+
+ if (!strcmp(profile_id, BT_PROFILE_AV_RC_ID))
+ return bt_get_avrcp_interface();
+
+ if (!strcmp(profile_id, BT_PROFILE_HANDSFREE_ID))
+ return bt_get_handsfree_interface();
+
+ if (!strcmp(profile_id, BT_PROFILE_GATT_ID))
+ return bt_get_gatt_interface();
+
+ if (!strcmp(profile_id, BT_PROFILE_HEALTH_ID))
+ return bt_get_health_interface();
+
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+ if (!strcmp(profile_id, BT_PROFILE_AV_RC_CTRL_ID))
+ return bt_get_avrcp_ctrl_interface();
+
+ if (!strcmp(profile_id, BT_PROFILE_HANDSFREE_CLIENT_ID))
+ return bt_get_hf_client_interface();
+
+ if (!strcmp(profile_id, BT_PROFILE_MAP_CLIENT_ID))
+ return bt_get_map_client_interface();
+
+ if (!strcmp(profile_id, BT_PROFILE_ADVANCED_AUDIO_SINK_ID))
+ return bt_get_a2dp_sink_interface();
+#endif
+
+ return NULL;
+}
+
+static int dut_mode_configure(uint8_t enable)
+{
+ struct hal_cmd_dut_mode_conf cmd;
+
+ DBG("enable %u", enable);
+
+ if (!interface_ready())
+ return BT_STATUS_NOT_READY;
+
+ cmd.enable = enable;
+
+ return hal_ipc_cmd(HAL_SERVICE_ID_BLUETOOTH, HAL_OP_DUT_MODE_CONF,
+ sizeof(cmd), &cmd, NULL, NULL, NULL);
+}
+
+static int dut_mode_send(uint16_t opcode, uint8_t *buf, uint8_t buf_len)
+{
+ char cmd_buf[IPC_MTU];
+ struct hal_cmd_dut_mode_send *cmd = (void *) cmd_buf;
+ size_t len;
+
+ DBG("opcode %u len %u", opcode, buf_len);
+
+ if (!interface_ready())
+ return BT_STATUS_NOT_READY;
+
+ cmd->opcode = opcode;
+ cmd->len = buf_len;
+ memcpy(cmd->data, buf, cmd->len);
+
+ len = sizeof(*cmd) + cmd->len;
+
+ return hal_ipc_cmd(HAL_SERVICE_ID_BLUETOOTH, HAL_OP_DUT_MODE_SEND,
+ len, cmd, NULL, NULL, NULL);
+}
+
+static int le_test_mode(uint16_t opcode, uint8_t *buf, uint8_t buf_len)
+{
+ char cmd_buf[IPC_MTU];
+ struct hal_cmd_le_test_mode *cmd = (void *) cmd_buf;
+ size_t len;
+
+ DBG("opcode %u len %u", opcode, buf_len);
+
+ if (!interface_ready())
+ return BT_STATUS_NOT_READY;
+
+ cmd->opcode = opcode;
+ cmd->len = buf_len;
+ memcpy(cmd->data, buf, cmd->len);
+
+ len = sizeof(*cmd) + cmd->len;
+
+ return hal_ipc_cmd(HAL_SERVICE_ID_BLUETOOTH, HAL_OP_LE_TEST_MODE,
+ len, cmd, NULL, NULL, NULL);
+}
+
+static int config_hci_snoop_log(uint8_t enable)
+{
+ const char *property;
+
+ DBG("enable %u", enable);
+
+ property = enable ? "bluetooth.start" : "bluetooth.stop";
+
+ if (property_set(property, "snoop") < 0) {
+ error("Failed to set %s=snoop", property);
+ return BT_STATUS_FAIL;
+ }
+
+ return BT_STATUS_SUCCESS;
+}
+
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+static int get_connection_state(const bt_bdaddr_t *bd_addr)
+{
+ struct hal_cmd_get_connection_state cmd;
+ struct hal_rsp_get_connection_state rsp;
+ size_t rsp_len = sizeof(rsp);
+ bt_status_t status;
+
+ DBG("bdaddr: %s", bdaddr2str(bd_addr));
+
+ if (!interface_ready())
+ return 0;
+
+ memcpy(cmd.bdaddr, bd_addr, sizeof(cmd.bdaddr));
+
+ status = hal_ipc_cmd(HAL_SERVICE_ID_BLUETOOTH,
+ HAL_OP_GET_CONNECTION_STATE, sizeof(cmd), &cmd,
+ &rsp_len, &rsp, NULL);
+
+ if (status != BT_STATUS_SUCCESS)
+ return 0;
+
+ return rsp.connection_state;
+}
+
+static int set_os_callouts(bt_os_callouts_t *callouts)
+{
+ DBG("callouts: %p", callouts);
+
+ /* TODO: implement */
+
+ return BT_STATUS_SUCCESS;
+}
+
+static int read_energy_info(void)
+{
+ DBG("");
+
+ if (!interface_ready())
+ return BT_STATUS_NOT_READY;
+
+ return hal_ipc_cmd(HAL_SERVICE_ID_BLUETOOTH, HAL_OP_READ_ENERGY_INFO, 0,
+ NULL, NULL, NULL, NULL);
+}
+#endif
+
+static const bt_interface_t bluetooth_if = {
+ .size = sizeof(bt_interface_t),
+ .init = init,
+ .enable = enable,
+ .disable = disable,
+ .cleanup = cleanup,
+ .get_adapter_properties = get_adapter_properties,
+ .get_adapter_property = get_adapter_property,
+ .set_adapter_property = set_adapter_property,
+ .get_remote_device_properties = get_remote_device_properties,
+ .get_remote_device_property = get_remote_device_property,
+ .set_remote_device_property = set_remote_device_property,
+ .get_remote_service_record = get_remote_service_record,
+ .get_remote_services = get_remote_services,
+ .start_discovery = start_discovery,
+ .cancel_discovery = cancel_discovery,
+ .create_bond = create_bond,
+ .remove_bond = remove_bond,
+ .cancel_bond = cancel_bond,
+ .pin_reply = pin_reply,
+ .ssp_reply = ssp_reply,
+ .get_profile_interface = get_profile_interface,
+ .dut_mode_configure = dut_mode_configure,
+ .dut_mode_send = dut_mode_send,
+ .le_test_mode = le_test_mode,
+ .config_hci_snoop_log = config_hci_snoop_log,
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+ .get_connection_state = get_connection_state,
+ .set_os_callouts = set_os_callouts,
+ .read_energy_info = read_energy_info,
+#endif
+};
+
+static const bt_interface_t *get_bluetooth_interface(void)
+{
+ DBG("");
+
+ return &bluetooth_if;
+}
+
+static int close_bluetooth(struct hw_device_t *device)
+{
+ DBG("");
+
+ cleanup();
+
+ free(device);
+
+ return 0;
+}
+
+static int open_bluetooth(const struct hw_module_t *module, char const *name,
+ struct hw_device_t **device)
+{
+ bluetooth_device_t *dev = malloc(sizeof(bluetooth_device_t));
+
+ DBG("");
+
+ if (!dev) {
+ error("Failed to allocate memory for device");
+ return -ENOMEM;
+ }
+
+ memset(dev, 0, sizeof(bluetooth_device_t));
+ dev->common.tag = HARDWARE_DEVICE_TAG;
+ dev->common.version = 0;
+ dev->common.module = (struct hw_module_t *) module;
+ dev->common.close = close_bluetooth;
+ dev->get_bluetooth_interface = get_bluetooth_interface;
+
+ *device = (struct hw_device_t *) dev;
+
+ return 0;
+}
+
+static struct hw_module_methods_t bluetooth_module_methods = {
+ .open = open_bluetooth,
+};
+
+struct hw_module_t HAL_MODULE_INFO_SYM = {
+ .tag = HARDWARE_MODULE_TAG,
+ .version_major = 1,
+ .version_minor = 0,
+ .id = BT_HARDWARE_MODULE_ID,
+ .name = "BlueZ Bluetooth stack",
+ .author = "Intel Corporation",
+ .methods = &bluetooth_module_methods
+};
diff --git a/repo/android/hal-gatt.c b/repo/android/hal-gatt.c
new file mode 100644
index 0000000..f7217c7
--- /dev/null
+++ b/repo/android/hal-gatt.c
@@ -0,0 +1,2103 @@
+/*
+ * Copyright (C) 2014 Intel Corporation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include <stdbool.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include "hal-log.h"
+#include "hal.h"
+#include "hal-msg.h"
+#include "ipc-common.h"
+#include "hal-ipc.h"
+#include "hal-utils.h"
+
+static const btgatt_callbacks_t *cbs = NULL;
+
+static bool interface_ready(void)
+{
+ return cbs != NULL;
+}
+
+static void gatt_id_from_hal(btgatt_gatt_id_t *to,
+ struct hal_gatt_gatt_id *from)
+{
+ memcpy(&to->uuid, from->uuid, sizeof(to->uuid));
+ to->inst_id = from->inst_id;
+}
+
+static void gatt_id_to_hal(struct hal_gatt_gatt_id *to, btgatt_gatt_id_t *from)
+{
+ memcpy(to->uuid, &from->uuid, sizeof(from->uuid));
+ to->inst_id = from->inst_id;
+}
+
+static void srvc_id_from_hal(btgatt_srvc_id_t *to,
+ struct hal_gatt_srvc_id *from)
+{
+ memcpy(&to->id.uuid, from->uuid, sizeof(to->id.uuid));
+ to->id.inst_id = from->inst_id;
+ to->is_primary = from->is_primary;
+}
+
+static void srvc_id_to_hal(struct hal_gatt_srvc_id *to, btgatt_srvc_id_t *from)
+{
+ memcpy(to->uuid, &from->id.uuid, sizeof(from->id.uuid));
+ to->inst_id = from->id.inst_id;
+ to->is_primary = from->is_primary;
+}
+
+/* Client Event Handlers */
+
+static void handle_register_client(void *buf, uint16_t len, int fd)
+{
+ struct hal_ev_gatt_client_register_client *ev = buf;
+
+ if (cbs->client->register_client_cb)
+ cbs->client->register_client_cb(ev->status, ev->client_if,
+ (bt_uuid_t *) ev->app_uuid);
+}
+
+static void handle_scan_result(void *buf, uint16_t len, int fd)
+{
+ struct hal_ev_gatt_client_scan_result *ev = buf;
+ uint8_t ad[62];
+
+ if (len != sizeof(*ev) + ev->len) {
+ error("gatt: invalid scan result event, aborting");
+ exit(EXIT_FAILURE);
+ }
+
+ /* Java assumes that passed data has 62 bytes */
+ memset(ad, 0, sizeof(ad));
+ memcpy(ad, ev->adv_data, ev->len > sizeof(ad) ? sizeof(ad) : ev->len);
+
+ if (cbs->client->scan_result_cb)
+ cbs->client->scan_result_cb((bt_bdaddr_t *) ev->bda, ev->rssi,
+ ad);
+}
+
+static void handle_connect(void *buf, uint16_t len, int fd)
+{
+ struct hal_ev_gatt_client_connect *ev = buf;
+
+ if (cbs->client->open_cb)
+ cbs->client->open_cb(ev->conn_id, ev->status, ev->client_if,
+ (bt_bdaddr_t *) ev->bda);
+}
+
+static void handle_disconnect(void *buf, uint16_t len, int fd)
+{
+ struct hal_ev_gatt_client_disconnect *ev = buf;
+
+ if (cbs->client->close_cb)
+ cbs->client->close_cb(ev->conn_id, ev->status, ev->client_if,
+ (bt_bdaddr_t *) ev->bda);
+}
+
+static void handle_search_complete(void *buf, uint16_t len, int fd)
+{
+ struct hal_ev_gatt_client_search_complete *ev = buf;
+
+ if (cbs->client->search_complete_cb)
+ cbs->client->search_complete_cb(ev->conn_id, ev->status);
+}
+
+static void handle_search_result(void *buf, uint16_t len, int fd)
+{
+ struct hal_ev_gatt_client_search_result *ev = buf;
+ btgatt_srvc_id_t srvc_id;
+
+ srvc_id_from_hal(&srvc_id, &ev->srvc_id);
+
+ if (cbs->client->search_result_cb)
+ cbs->client->search_result_cb(ev->conn_id, &srvc_id);
+}
+
+static void handle_get_characteristic(void *buf, uint16_t len, int fd)
+{
+ struct hal_ev_gatt_client_get_characteristic *ev = buf;
+ btgatt_gatt_id_t char_id;
+ btgatt_srvc_id_t srvc_id;
+
+ srvc_id_from_hal(&srvc_id, &ev->srvc_id);
+ gatt_id_from_hal(&char_id, &ev->char_id);
+
+ if (cbs->client->get_characteristic_cb)
+ cbs->client->get_characteristic_cb(ev->conn_id, ev->status,
+ &srvc_id, &char_id,
+ ev->char_prop);
+}
+
+static void handle_get_descriptor(void *buf, uint16_t len, int fd)
+{
+ struct hal_ev_gatt_client_get_descriptor *ev = buf;
+ btgatt_gatt_id_t descr_id;
+ btgatt_gatt_id_t char_id;
+ btgatt_srvc_id_t srvc_id;
+
+ srvc_id_from_hal(&srvc_id, &ev->srvc_id);
+ gatt_id_from_hal(&char_id, &ev->char_id);
+ gatt_id_from_hal(&descr_id, &ev->descr_id);
+
+ if (cbs->client->get_descriptor_cb)
+ cbs->client->get_descriptor_cb(ev->conn_id, ev->status,
+ &srvc_id, &char_id, &descr_id);
+}
+
+static void handle_get_included_service(void *buf, uint16_t len, int fd)
+{
+ struct hal_ev_gatt_client_get_inc_service *ev = buf;
+ btgatt_srvc_id_t srvc_id;
+ btgatt_srvc_id_t incl_srvc_id;
+
+ srvc_id_from_hal(&srvc_id, &ev->srvc_id);
+ srvc_id_from_hal(&incl_srvc_id, &ev->incl_srvc_id);
+
+ if (cbs->client->get_included_service_cb)
+ cbs->client->get_included_service_cb(ev->conn_id, ev->status,
+ &srvc_id,
+ &incl_srvc_id);
+}
+
+static void handle_register_for_notification(void *buf, uint16_t len, int fd)
+{
+ struct hal_ev_gatt_client_reg_for_notif *ev = buf;
+ btgatt_gatt_id_t char_id;
+ btgatt_srvc_id_t srvc_id;
+
+ srvc_id_from_hal(&srvc_id, &ev->srvc_id);
+ gatt_id_from_hal(&char_id, &ev->char_id);
+
+ if (cbs->client->register_for_notification_cb)
+ cbs->client->register_for_notification_cb(ev->conn_id,
+ ev->registered,
+ ev->status,
+ &srvc_id,
+ &char_id);
+}
+
+static void handle_notify(void *buf, uint16_t len, int fd)
+{
+ struct hal_ev_gatt_client_notify *ev = buf;
+ btgatt_notify_params_t params;
+
+ if (len != sizeof(*ev) + ev->len) {
+ error("gatt: invalid notify event, aborting");
+ exit(EXIT_FAILURE);
+ }
+
+ memset(¶ms, 0, sizeof(params));
+ memcpy(params.value, ev->value, ev->len);
+ memcpy(¶ms.bda, ev->bda, sizeof(params.bda));
+
+ srvc_id_from_hal(¶ms.srvc_id, &ev->srvc_id);
+ gatt_id_from_hal(¶ms.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, ¶ms);
+}
+
+static void handle_read_characteristic(void *buf, uint16_t len, int fd)
+{
+ struct hal_ev_gatt_client_read_characteristic *ev = buf;
+ btgatt_read_params_t params;
+
+ if (len != sizeof(*ev) + ev->data.len) {
+ error("gatt: invalid read characteristic event, aborting");
+ exit(EXIT_FAILURE);
+ }
+
+ memset(¶ms, 0, sizeof(params));
+
+ srvc_id_from_hal(¶ms.srvc_id, &ev->data.srvc_id);
+ gatt_id_from_hal(¶ms.char_id, &ev->data.char_id);
+ gatt_id_from_hal(¶ms.descr_id, &ev->data.descr_id);
+
+ memcpy(¶ms.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,
+ ¶ms);
+}
+
+static void handle_write_characteristic(void *buf, uint16_t len, int fd)
+{
+ struct hal_ev_gatt_client_write_characteristic *ev = buf;
+ btgatt_write_params_t params;
+
+ memset(¶ms, 0, sizeof(params));
+
+ srvc_id_from_hal(¶ms.srvc_id, &ev->data.srvc_id);
+ gatt_id_from_hal(¶ms.char_id, &ev->data.char_id);
+ gatt_id_from_hal(¶ms.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,
+ ¶ms);
+}
+
+static void handle_read_descriptor(void *buf, uint16_t len, int fd)
+{
+ struct hal_ev_gatt_client_read_descriptor *ev = buf;
+ btgatt_read_params_t params;
+
+ if (len != sizeof(*ev) + ev->data.len) {
+ error("gatt: invalid read descriptor event, aborting");
+ exit(EXIT_FAILURE);
+ }
+
+ memset(¶ms, 0, sizeof(params));
+
+ srvc_id_from_hal(¶ms.srvc_id, &ev->data.srvc_id);
+ gatt_id_from_hal(¶ms.char_id, &ev->data.char_id);
+ gatt_id_from_hal(¶ms.descr_id, &ev->data.descr_id);
+
+ memcpy(¶ms.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,
+ ¶ms);
+}
+
+static void handle_write_descriptor(void *buf, uint16_t len, int fd)
+{
+ struct hal_ev_gatt_client_write_descriptor *ev = buf;
+ btgatt_write_params_t params;
+
+ memset(¶ms, 0, sizeof(params));
+
+ srvc_id_from_hal(¶ms.srvc_id, &ev->data.srvc_id);
+ gatt_id_from_hal(¶ms.char_id, &ev->data.char_id);
+ gatt_id_from_hal(¶ms.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,
+ ¶ms);
+}
+
+static void handle_execute_write(void *buf, uint16_t len, int fd)
+{
+ struct hal_ev_gatt_client_exec_write *ev = buf;
+
+ if (cbs->client->execute_write_cb)
+ cbs->client->execute_write_cb(ev->conn_id, ev->status);
+}
+
+static void handle_read_remote_rssi(void *buf, uint16_t len, int fd)
+{
+ struct hal_ev_gatt_client_read_remote_rssi *ev = buf;
+
+ if (cbs->client->read_remote_rssi_cb)
+ cbs->client->read_remote_rssi_cb(ev->client_if,
+ (bt_bdaddr_t *) ev->address,
+ ev->rssi, ev->status);
+}
+
+static void handle_listen(void *buf, uint16_t len, int fd)
+{
+ struct hal_ev_gatt_client_listen *ev = buf;
+
+ if (cbs->client->listen_cb)
+ cbs->client->listen_cb(ev->status, ev->server_if);
+}
+
+/* Server Event Handlers */
+
+static void handle_register_server(void *buf, uint16_t len, int fd)
+{
+ struct hal_ev_gatt_server_register *ev = buf;
+
+ if (cbs->server->register_server_cb)
+ cbs->server->register_server_cb(ev->status, ev->server_if,
+ (bt_uuid_t *) &ev->uuid);
+}
+
+static void handle_connection(void *buf, uint16_t len, int fd)
+{
+ struct hal_ev_gatt_server_connection *ev = buf;
+
+ if (cbs->server->connection_cb)
+ cbs->server->connection_cb(ev->conn_id, ev->server_if,
+ ev->connected,
+ (bt_bdaddr_t *) &ev->bdaddr);
+}
+
+static void handle_service_added(void *buf, uint16_t len, int fd)
+{
+ struct hal_ev_gatt_server_service_added *ev = buf;
+ btgatt_srvc_id_t srvc_id;
+
+ srvc_id_from_hal(&srvc_id, &ev->srvc_id);
+
+ if (cbs->server->service_added_cb)
+ cbs->server->service_added_cb(ev->status, ev->server_if,
+ &srvc_id, ev->srvc_handle);
+}
+
+static void handle_included_service_added(void *buf, uint16_t len, int fd)
+{
+ struct hal_ev_gatt_server_inc_srvc_added *ev = buf;
+
+ if (cbs->server->included_service_added_cb)
+ cbs->server->included_service_added_cb(ev->status,
+ ev->server_if,
+ ev->srvc_handle,
+ ev->incl_srvc_handle);
+}
+
+static void handle_characteristic_added(void *buf, uint16_t len, int fd)
+{
+ struct hal_ev_gatt_server_characteristic_added *ev = buf;
+
+ if (cbs->server->characteristic_added_cb)
+ cbs->server->characteristic_added_cb(ev->status, ev->server_if,
+ (bt_uuid_t *) &ev->uuid,
+ ev->srvc_handle,
+ ev->char_handle);
+}
+
+static void handle_descriptor_added(void *buf, uint16_t len, int fd)
+{
+ struct hal_ev_gatt_server_descriptor_added *ev = buf;
+
+ if (cbs->server->descriptor_added_cb)
+ cbs->server->descriptor_added_cb(ev->status, ev->server_if,
+ (bt_uuid_t *) &ev->uuid,
+ ev->srvc_handle,
+ ev->descr_handle);
+}
+
+static void handle_service_started(void *buf, uint16_t len, int fd)
+{
+ struct hal_ev_gatt_server_service_started *ev = buf;
+
+ if (cbs->server->service_started_cb)
+ cbs->server->service_started_cb(ev->status, ev->server_if,
+ ev->srvc_handle);
+}
+
+static void handle_service_stopped(void *buf, uint16_t len, int fd)
+{
+ struct hal_ev_gatt_server_service_stopped *ev = buf;
+
+ if (cbs->server->service_stopped_cb)
+ cbs->server->service_stopped_cb(ev->status, ev->server_if,
+ ev->srvc_handle);
+}
+
+static void handle_service_deleted(void *buf, uint16_t len, int fd)
+{
+ struct hal_ev_gatt_server_service_deleted *ev = buf;
+
+ if (cbs->server->service_deleted_cb)
+ cbs->server->service_deleted_cb(ev->status, ev->server_if,
+ ev->srvc_handle);
+}
+
+static void handle_request_read(void *buf, uint16_t len, int fd)
+{
+ struct hal_ev_gatt_server_request_read *ev = buf;
+
+ if (cbs->server->request_read_cb)
+ cbs->server->request_read_cb(ev->conn_id, ev->trans_id,
+ (bt_bdaddr_t *) &ev->bdaddr,
+ ev->attr_handle, ev->offset,
+ ev->is_long);
+}
+
+static void handle_request_write(void *buf, uint16_t len, int fd)
+{
+ struct hal_ev_gatt_server_request_write *ev = buf;
+
+ if (len != sizeof(*ev) + ev->length) {
+ error("gatt: invalid request write event, aborting");
+ exit(EXIT_FAILURE);
+ }
+
+ if (cbs->server->request_write_cb)
+ cbs->server->request_write_cb(ev->conn_id, ev->trans_id,
+ (bt_bdaddr_t *) ev->bdaddr,
+ ev->attr_handle, ev->offset,
+ ev->length, ev->need_rsp,
+ ev->is_prep, ev->value);
+}
+
+static void handle_request_exec_write(void *buf, uint16_t len, int fd)
+{
+ struct hal_ev_gatt_server_request_exec_write *ev = buf;
+
+ if (cbs->server->request_exec_write_cb)
+ cbs->server->request_exec_write_cb(ev->conn_id, ev->trans_id,
+ (bt_bdaddr_t *) ev->bdaddr,
+ ev->exec_write);
+}
+
+static void handle_response_confirmation(void *buf, uint16_t len, int fd)
+{
+ struct hal_ev_gatt_server_rsp_confirmation *ev = buf;
+
+ if (cbs->server->response_confirmation_cb)
+ cbs->server->response_confirmation_cb(ev->status, ev->handle);
+}
+
+static void handle_configure_mtu(void *buf, uint16_t len, int fd)
+{
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+ struct hal_ev_gatt_client_configure_mtu *ev = buf;
+
+ if (cbs->client->configure_mtu_cb)
+ cbs->client->configure_mtu_cb(ev->conn_id, ev->status, ev->mtu);
+#endif
+}
+
+static void handle_filter_config(void *buf, uint16_t len, int fd)
+{
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+ struct hal_ev_gatt_client_filter_config *ev = buf;
+
+ if (cbs->client->scan_filter_cfg_cb)
+ cbs->client->scan_filter_cfg_cb(ev->action, ev->client_if,
+ ev->status, ev->type,
+ ev->space);
+#endif
+}
+
+static void handle_filter_params(void *buf, uint16_t len, int fd)
+{
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+ struct hal_ev_gatt_client_filter_params *ev = buf;
+
+ if (cbs->client->scan_filter_param_cb)
+ cbs->client->scan_filter_param_cb(ev->action, ev->client_if,
+ ev->status, ev->space);
+#endif
+}
+
+static void handle_filter_status(void *buf, uint16_t len, int fd)
+{
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+ struct hal_ev_gatt_client_filter_status *ev = buf;
+
+ if (cbs->client->scan_filter_status_cb)
+ cbs->client->scan_filter_status_cb(ev->enable, ev->client_if,
+ ev->status);
+#endif
+}
+
+static void handle__multi_adv_enable(void *buf, uint16_t len, int fd)
+{
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+ struct hal_ev_gatt_client_multi_adv_enable *ev = buf;
+
+ if (cbs->client->multi_adv_enable_cb)
+ cbs->client->multi_adv_enable_cb(ev->client_if, ev->status);
+#endif
+}
+
+static void handle_multi_adv_update(void *buf, uint16_t len, int fd)
+{
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+ struct hal_ev_gatt_client_multi_adv_update *ev = buf;
+
+ if (cbs->client->multi_adv_update_cb)
+ cbs->client->multi_adv_update_cb(ev->client_if, ev->status);
+#endif
+}
+
+static void handle_multi_adv_data(void *buf, uint16_t len, int fd)
+{
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+ struct hal_ev_gatt_client_multi_adv_data *ev = buf;
+
+ if (cbs->client->multi_adv_data_cb)
+ cbs->client->multi_adv_data_cb(ev->client_if, ev->status);
+#endif
+}
+
+static void handle_multi_adv_disable(void *buf, uint16_t len, int fd)
+{
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+ struct hal_ev_gatt_client_multi_adv_disable *ev = buf;
+
+ if (cbs->client->multi_adv_disable_cb)
+ cbs->client->multi_adv_disable_cb(ev->client_if, ev->status);
+#endif
+}
+
+static void handle_client_congestion(void *buf, uint16_t len, int fd)
+{
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+ struct hal_ev_gatt_client_congestion *ev = buf;
+
+ if (cbs->client->congestion_cb)
+ cbs->client->congestion_cb(ev->conn_id, ev->congested);
+#endif
+}
+
+static void handle_config_batchscan(void *buf, uint16_t len, int fd)
+{
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+ struct hal_ev_gatt_client_config_batchscan *ev = buf;
+
+ if (cbs->client->batchscan_cfg_storage_cb)
+ cbs->client->batchscan_cfg_storage_cb(ev->client_if,
+ ev->status);
+#endif
+}
+
+static void handle_enable_batchscan(void *buf, uint16_t len, int fd)
+{
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+ struct hal_ev_gatt_client_enable_batchscan *ev = buf;
+
+ if (cbs->client->batchscan_enb_disable_cb)
+ cbs->client->batchscan_enb_disable_cb(ev->action, ev->client_if,
+ ev->status);
+#endif
+}
+
+static void handle_client_batchscan_reports(void *buf, uint16_t len, int fd)
+{
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+ struct hal_ev_gatt_client_batchscan_reports *ev = buf;
+
+ if (cbs->client->batchscan_reports_cb)
+ cbs->client->batchscan_reports_cb(ev->client_if, ev->status,
+ ev->format, ev->num,
+ ev->data_len, ev->data);
+#endif
+}
+
+static void handle_batchscan_threshold(void *buf, uint16_t len, int fd)
+{
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+ struct hal_ev_gatt_client_batchscan_threshold *ev = buf;
+
+ if (cbs->client->batchscan_threshold_cb)
+ cbs->client->batchscan_threshold_cb(ev->client_if);
+#endif
+}
+
+static void handle_track_adv(void *buf, uint16_t len, int fd)
+{
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+ struct hal_ev_gatt_client_track_adv *ev = buf;
+
+ if (cbs->client->track_adv_event_cb)
+ cbs->client->track_adv_event_cb(ev->client_if, ev->filetr_index,
+ ev->address_type,
+ (bt_bdaddr_t *) ev->address,
+ ev->state);
+#endif
+}
+
+static void handle_indication_send(void *buf, uint16_t len, int fd)
+{
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+ struct hal_ev_gatt_server_indication_sent *ev = buf;
+
+ if (cbs->server->indication_sent_cb)
+ cbs->server->indication_sent_cb(ev->conn_id, ev->status);
+#endif
+}
+
+static void handle_server_congestion(void *buf, uint16_t len, int fd)
+{
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+ struct hal_ev_gatt_server_congestion *ev = buf;
+
+ if (cbs->server->congestion_cb)
+ cbs->server->congestion_cb(ev->conn_id, ev->congested);
+#endif
+}
+
+static void handle_server_mtu_changed(void *buf, uint16_t len, int fd)
+{
+#if ANDROID_VERSION >= PLATFORM_VER(5, 1, 0)
+ struct hal_ev_gatt_server_mtu_changed *ev = buf;
+
+ if (cbs->server->mtu_changed_cb)
+ cbs->server->mtu_changed_cb(ev->conn_id, ev->mtu);
+#endif
+}
+
+/*
+ * handlers will be called from notification thread context,
+ * index in table equals to 'opcode - HAL_MINIMUM_EVENT'
+ */
+static const struct hal_ipc_handler ev_handlers[] = {
+ /* HAL_EV_GATT_CLIENT_REGISTER_CLIENT */
+ { handle_register_client, false,
+ sizeof(struct hal_ev_gatt_client_register_client) },
+ /* HAL_EV_GATT_CLIENT_SCAN_RESULT */
+ { handle_scan_result, true,
+ sizeof(struct hal_ev_gatt_client_scan_result) },
+ /* HAL_EV_GATT_CLIENT_CONNECT */
+ { handle_connect, false, sizeof(struct hal_ev_gatt_client_connect) },
+ /* HAL_EV_GATT_CLIENT_DISCONNECT */
+ { handle_disconnect, false,
+ sizeof(struct hal_ev_gatt_client_disconnect) },
+ /* HAL_EV_GATT_CLIENT_SEARCH_COMPLETE */
+ { handle_search_complete, false,
+ sizeof(struct hal_ev_gatt_client_search_complete) },
+ /* HAL_EV_GATT_CLIENT_SEARCH_RESULT */
+ { handle_search_result, false,
+ sizeof(struct hal_ev_gatt_client_search_result) },
+ /* HAL_EV_GATT_CLIENT_GET_CHARACTERISTIC */
+ { handle_get_characteristic, false,
+ sizeof(struct hal_ev_gatt_client_get_characteristic) },
+ /* HAL_EV_GATT_CLIENT_GET_DESCRIPTOR */
+ { handle_get_descriptor, false,
+ sizeof(struct hal_ev_gatt_client_get_descriptor) },
+ /* HAL_EV_GATT_CLIENT_GET_INC_SERVICE */
+ { handle_get_included_service, false,
+ sizeof(struct hal_ev_gatt_client_get_inc_service) },
+ /* HAL_EV_GATT_CLIENT_REGISTER_FOR_NOTIF */
+ { handle_register_for_notification, false,
+ sizeof(struct hal_ev_gatt_client_reg_for_notif) },
+ /* HAL_EV_GATT_CLIENT_NOTIFY */
+ { handle_notify, true, sizeof(struct hal_ev_gatt_client_notify) },
+ /* HAL_EV_GATT_CLIENT_READ_CHARACTERISTIC */
+ { handle_read_characteristic, true,
+ sizeof(struct hal_ev_gatt_client_read_characteristic) },
+ /* HAL_EV_GATT_CLIENT_WRITE_CHARACTERISTIC */
+ { handle_write_characteristic, false,
+ sizeof(struct hal_ev_gatt_client_write_characteristic) },
+ /* HAL_EV_GATT_CLIENT_READ_DESCRIPTOR */
+ { handle_read_descriptor, true,
+ sizeof(struct hal_ev_gatt_client_read_descriptor) },
+ /* HAL_EV_GATT_CLIENT_WRITE_DESCRIPTOR */
+ { handle_write_descriptor, false,
+ sizeof(struct hal_ev_gatt_client_write_descriptor) },
+ /* HAL_EV_GATT_CLIENT_EXEC_WRITE */
+ { handle_execute_write, false,
+ sizeof(struct hal_ev_gatt_client_exec_write) },
+ /* HAL_EV_GATT_CLIENT_READ_REMOTE_RSSI */
+ { handle_read_remote_rssi, false,
+ sizeof(struct hal_ev_gatt_client_read_remote_rssi) },
+ /* HAL_EV_GATT_CLIENT_LISTEN */
+ { handle_listen, false, sizeof(struct hal_ev_gatt_client_listen) },
+ /* HAL_EV_GATT_SERVER_REGISTER */
+ { handle_register_server, false,
+ sizeof(struct hal_ev_gatt_server_register) },
+ /* HAL_EV_GATT_SERVER_CONNECTION */
+ { handle_connection, false,
+ sizeof(struct hal_ev_gatt_server_connection) },
+ /* HAL_EV_GATT_SERVER_SERVICE_ADDED */
+ { handle_service_added, false,
+ sizeof(struct hal_ev_gatt_server_service_added) },
+ /* HAL_EV_GATT_SERVER_INC_SRVC_ADDED */
+ { handle_included_service_added, false,
+ sizeof(struct hal_ev_gatt_server_inc_srvc_added) },
+ /* HAL_EV_GATT_SERVER_CHAR_ADDED */
+ { handle_characteristic_added, false,
+ sizeof(struct hal_ev_gatt_server_characteristic_added) },
+ /* HAL_EV_GATT_SERVER_DESCRIPTOR_ADDED */
+ { handle_descriptor_added, false,
+ sizeof(struct hal_ev_gatt_server_descriptor_added) },
+ /* HAL_EV_GATT_SERVER_SERVICE_STARTED */
+ { handle_service_started, false,
+ sizeof(struct hal_ev_gatt_server_service_started) },
+ /* HAL_EV_GATT_SERVER_SERVICE_STOPPED */
+ { handle_service_stopped, false,
+ sizeof(struct hal_ev_gatt_server_service_stopped) },
+ /* HAL_EV_GATT_SERVER_SERVICE_DELETED */
+ { handle_service_deleted, false,
+ sizeof(struct hal_ev_gatt_server_service_deleted) },
+ /* HAL_EV_GATT_SERVER_REQUEST_READ */
+ { handle_request_read, false,
+ sizeof(struct hal_ev_gatt_server_request_read) },
+ /* HAL_EV_GATT_SERVER_REQUEST_WRITE */
+ { handle_request_write, true,
+ sizeof(struct hal_ev_gatt_server_request_write) },
+ /* HAL_EV_GATT_SERVER_REQUEST_EXEC_WRITE */
+ { handle_request_exec_write, false,
+ sizeof(struct hal_ev_gatt_server_request_exec_write) },
+ /* HAL_EV_GATT_SERVER_RSP_CONFIRMATION */
+ { handle_response_confirmation, false,
+ sizeof(struct hal_ev_gatt_server_rsp_confirmation) },
+ /* HAL_EV_GATT_CLIENT_CONFIGURE_MTU */
+ { handle_configure_mtu, false,
+ sizeof(struct hal_ev_gatt_client_configure_mtu) },
+ /* HAL_EV_GATT_CLIENT_FILTER_CONFIG */
+ { handle_filter_config, false,
+ sizeof(struct hal_ev_gatt_client_filter_config) },
+ /* HAL_EV_GATT_CLIENT_FILTER_PARAMS */
+ { handle_filter_params, false,
+ sizeof(struct hal_ev_gatt_client_filter_params) },
+ /* HAL_EV_GATT_CLIENT_FILTER_STATUS */
+ { handle_filter_status, false,
+ sizeof(struct hal_ev_gatt_client_filter_status) },
+ /* HAL_EV_GATT_CLIENT_MULTI_ADV_ENABLE */
+ { handle__multi_adv_enable, false,
+ sizeof(struct hal_ev_gatt_client_multi_adv_enable) },
+ /* HAL_EV_GATT_CLIENT_MULTI_ADV_UPDATE */
+ { handle_multi_adv_update, false,
+ sizeof(struct hal_ev_gatt_client_multi_adv_update) },
+ /* HAL_EV_GATT_CLIENT_MULTI_ADV_DATA */
+ { handle_multi_adv_data, false,
+ sizeof(struct hal_ev_gatt_client_multi_adv_data) },
+ /* HAL_EV_GATT_CLIENT_MULTI_ADV_DISABLE */
+ { handle_multi_adv_disable, false,
+ sizeof(struct hal_ev_gatt_client_multi_adv_disable) },
+ /* HAL_EV_GATT_CLIENT_CONGESTION */
+ { handle_client_congestion, false,
+ sizeof(struct hal_ev_gatt_client_congestion) },
+ /* HAL_EV_GATT_CLIENT_CONFIG_BATCHSCAN */
+ { handle_config_batchscan, false,
+ sizeof(struct hal_ev_gatt_client_config_batchscan) },
+ /* HAL_EV_GATT_CLIENT_ENABLE_BATCHSCAN */
+ { handle_enable_batchscan, false,
+ sizeof(struct hal_ev_gatt_client_enable_batchscan) },
+ /* HAL_EV_GATT_CLIENT_BATCHSCAN_REPORTS */
+ { handle_client_batchscan_reports, true,
+ sizeof(struct hal_ev_gatt_client_batchscan_reports) },
+ /* HAL_EV_GATT_CLIENT_BATCHSCAN_THRESHOLD */
+ { handle_batchscan_threshold, false,
+ sizeof(struct hal_ev_gatt_client_batchscan_threshold) },
+ /* HAL_EV_GATT_CLIENT_TRACK_ADV */
+ { handle_track_adv, false,
+ sizeof(struct hal_ev_gatt_client_track_adv) },
+ /* HAL_EV_GATT_SERVER_INDICATION_SENT */
+ { handle_indication_send, false,
+ sizeof(struct hal_ev_gatt_server_indication_sent) },
+ /* HAL_EV_GATT_SERVER_CONGESTION */
+ { handle_server_congestion, false,
+ sizeof(struct hal_ev_gatt_server_congestion) },
+ /* HAL_EV_GATT_SERVER_MTU_CHANGED */
+ { handle_server_mtu_changed, false,
+ sizeof(struct hal_ev_gatt_server_mtu_changed) },
+ };
+
+/* Client API */
+
+static bt_status_t register_client(bt_uuid_t *uuid)
+{
+ struct hal_cmd_gatt_client_register cmd;
+
+ if (!interface_ready())
+ return BT_STATUS_NOT_READY;
+
+ memcpy(cmd.uuid, uuid, sizeof(*uuid));
+
+ return hal_ipc_cmd(HAL_SERVICE_ID_GATT, HAL_OP_GATT_CLIENT_REGISTER,
+ sizeof(cmd), &cmd, NULL, NULL, NULL);
+}
+
+static bt_status_t unregister_client(int client_if)
+{
+ struct hal_cmd_gatt_client_unregister cmd;
+
+ if (!interface_ready())
+ return BT_STATUS_NOT_READY;
+
+ cmd.client_if = client_if;
+
+ return hal_ipc_cmd(HAL_SERVICE_ID_GATT, HAL_OP_GATT_CLIENT_UNREGISTER,
+ sizeof(cmd), &cmd, NULL, NULL, NULL);
+}
+
+static bt_status_t scan_real(int client_if, bool start)
+{
+ struct hal_cmd_gatt_client_scan cmd;
+
+ if (!interface_ready())
+ return BT_STATUS_NOT_READY;
+
+ cmd.client_if = client_if;
+ cmd.start = start;
+
+ return hal_ipc_cmd(HAL_SERVICE_ID_GATT, HAL_OP_GATT_CLIENT_SCAN,
+ sizeof(cmd), &cmd, NULL, NULL, NULL);
+}
+
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+static bt_status_t scan(bool start)
+{
+ return scan_real(0, start);
+}
+#else
+static bt_status_t scan(int client_if, bool start)
+{
+ return scan_real(client_if, start);
+}
+#endif
+
+static bt_status_t connect_real(int client_if, const bt_bdaddr_t *bd_addr,
+ bool is_direct, int transport)
+{
+ struct hal_cmd_gatt_client_connect cmd;
+
+ if (!interface_ready())
+ return BT_STATUS_NOT_READY;
+
+ cmd.client_if = client_if;
+ cmd.is_direct = is_direct;
+ cmd.transport = transport;
+
+ memcpy(cmd.bdaddr, bd_addr, sizeof(*bd_addr));
+
+ return hal_ipc_cmd(HAL_SERVICE_ID_GATT, HAL_OP_GATT_CLIENT_CONNECT,
+ sizeof(cmd), &cmd, NULL, NULL, NULL);
+}
+
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+static bt_status_t connect(int client_if, const bt_bdaddr_t *bd_addr,
+ bool is_direct, int transport)
+{
+ return connect_real(client_if, bd_addr, is_direct, transport);
+}
+#else
+static bt_status_t connect(int client_if, const bt_bdaddr_t *bd_addr,
+ bool is_direct)
+{
+ return connect_real(client_if, bd_addr, is_direct,
+ BT_TRANSPORT_UNKNOWN);
+}
+#endif
+
+static bt_status_t disconnect(int client_if, const bt_bdaddr_t *bd_addr,
+ int conn_id)
+{
+ struct hal_cmd_gatt_client_disconnect cmd;
+
+ if (!interface_ready())
+ return BT_STATUS_NOT_READY;
+
+ cmd.client_if = client_if;
+ cmd.conn_id = conn_id;
+
+ memcpy(cmd.bdaddr, bd_addr, sizeof(*bd_addr));
+
+ return hal_ipc_cmd(HAL_SERVICE_ID_GATT, HAL_OP_GATT_CLIENT_DISCONNECT,
+ sizeof(cmd), &cmd, NULL, NULL, NULL);
+}
+
+static bt_status_t listen(int client_if, bool start)
+{
+ struct hal_cmd_gatt_client_listen cmd;
+
+ if (!interface_ready())
+ return BT_STATUS_NOT_READY;
+
+ cmd.client_if = client_if;
+ cmd.start = start;
+
+ return hal_ipc_cmd(HAL_SERVICE_ID_GATT, HAL_OP_GATT_CLIENT_LISTEN,
+ sizeof(cmd), &cmd, NULL, NULL, NULL);
+}
+
+static bt_status_t refresh(int client_if, const bt_bdaddr_t *bd_addr)
+{
+ struct hal_cmd_gatt_client_refresh cmd;
+
+ if (!interface_ready())
+ return BT_STATUS_NOT_READY;
+
+ cmd.client_if = client_if;
+
+ memcpy(cmd.bdaddr, bd_addr, sizeof(*bd_addr));
+
+ return hal_ipc_cmd(HAL_SERVICE_ID_GATT, HAL_OP_GATT_CLIENT_REFRESH,
+ sizeof(cmd), &cmd, NULL, NULL, NULL);
+}
+
+static bt_status_t search_service(int conn_id, bt_uuid_t *filter_uuid)
+{
+ char buf[IPC_MTU];
+ struct hal_cmd_gatt_client_search_service *cmd = (void *) buf;
+ size_t len = sizeof(*cmd);
+
+ if (!interface_ready())
+ return BT_STATUS_NOT_READY;
+
+ memset(cmd, 0, sizeof(*cmd));
+
+ cmd->conn_id = conn_id;
+
+ if (filter_uuid) {
+ memcpy(cmd->filter_uuid, filter_uuid, sizeof(*filter_uuid));
+ len += sizeof(*filter_uuid);
+ cmd->filtered = 1;
+ }
+
+ return hal_ipc_cmd(HAL_SERVICE_ID_GATT,
+ HAL_OP_GATT_CLIENT_SEARCH_SERVICE,
+ len, cmd, NULL, NULL, NULL);
+}
+
+static bt_status_t get_included_service(int conn_id, btgatt_srvc_id_t *srvc_id,
+ btgatt_srvc_id_t *start_incl_srvc_id)
+{
+ char buf[IPC_MTU];
+ struct hal_cmd_gatt_client_get_included_service *cmd = (void *) buf;
+ size_t len = sizeof(*cmd);
+
+ if (!interface_ready())
+ return BT_STATUS_NOT_READY;
+
+ cmd->conn_id = conn_id;
+
+ srvc_id_to_hal(&cmd->srvc_id, srvc_id);
+ cmd->continuation = 0;
+
+ if (start_incl_srvc_id) {
+ srvc_id_to_hal(&cmd->incl_srvc_id[0], start_incl_srvc_id);
+ len += sizeof(cmd->incl_srvc_id[0]);
+ cmd->continuation = 1;
+ }
+
+ return hal_ipc_cmd(HAL_SERVICE_ID_GATT,
+ HAL_OP_GATT_CLIENT_GET_INCLUDED_SERVICE,
+ len, cmd, NULL, NULL, NULL);
+}
+
+static bt_status_t get_characteristic(int conn_id, btgatt_srvc_id_t *srvc_id,
+ btgatt_gatt_id_t *start_char_id)
+{
+ char buf[IPC_MTU];
+ struct hal_cmd_gatt_client_get_characteristic *cmd = (void *) buf;
+ size_t len = sizeof(*cmd);
+
+ if (!interface_ready())
+ return BT_STATUS_NOT_READY;
+
+ cmd->conn_id = conn_id;
+
+ srvc_id_to_hal(&cmd->srvc_id, srvc_id);
+ cmd->continuation = 0;
+
+ if (start_char_id) {
+ gatt_id_to_hal(&cmd->char_id[0], start_char_id);
+ len += sizeof(cmd->char_id[0]);
+ cmd->continuation = 1;
+ }
+
+ return hal_ipc_cmd(HAL_SERVICE_ID_GATT,
+ HAL_OP_GATT_CLIENT_GET_CHARACTERISTIC,
+ len, cmd, NULL, NULL, NULL);
+}
+
+static bt_status_t get_descriptor(int conn_id, btgatt_srvc_id_t *srvc_id,
+ btgatt_gatt_id_t *char_id,
+ btgatt_gatt_id_t *start_descr_id)
+{
+ char buf[IPC_MTU];
+ struct hal_cmd_gatt_client_get_descriptor *cmd = (void *) buf;
+ size_t len = sizeof(*cmd);
+
+ if (!interface_ready())
+ return BT_STATUS_NOT_READY;
+
+ cmd->conn_id = conn_id;
+
+ srvc_id_to_hal(&cmd->srvc_id, srvc_id);
+ gatt_id_to_hal(&cmd->char_id, char_id);
+ cmd->continuation = 0;
+
+ if (start_descr_id) {
+ gatt_id_to_hal(&cmd->descr_id[0], start_descr_id);
+ len += sizeof(cmd->descr_id[0]);
+ cmd->continuation = 1;
+ }
+
+ return hal_ipc_cmd(HAL_SERVICE_ID_GATT,
+ HAL_OP_GATT_CLIENT_GET_DESCRIPTOR,
+ len, cmd, NULL , NULL, NULL);
+}
+
+static bt_status_t read_characteristic(int conn_id, btgatt_srvc_id_t *srvc_id,
+ btgatt_gatt_id_t *char_id,
+ int auth_req)
+{
+ struct hal_cmd_gatt_client_read_characteristic cmd;
+
+ if (!interface_ready())
+ return BT_STATUS_NOT_READY;
+
+ cmd.conn_id = conn_id;
+ cmd.auth_req = auth_req;
+
+ srvc_id_to_hal(&cmd.srvc_id, srvc_id);
+ gatt_id_to_hal(&cmd.char_id, char_id);
+
+ return hal_ipc_cmd(HAL_SERVICE_ID_GATT,
+ HAL_OP_GATT_CLIENT_READ_CHARACTERISTIC,
+ sizeof(cmd), &cmd, NULL, NULL, NULL);
+}
+
+static bt_status_t write_characteristic(int conn_id, btgatt_srvc_id_t *srvc_id,
+ btgatt_gatt_id_t *char_id,
+ int write_type, int len, int auth_req,
+ char *p_value)
+{
+ char buf[IPC_MTU];
+ struct hal_cmd_gatt_client_write_characteristic *cmd = (void *) buf;
+ size_t cmd_len = sizeof(*cmd) + len;
+
+ if (!interface_ready())
+ return BT_STATUS_NOT_READY;
+
+ cmd->conn_id = conn_id;
+ cmd->write_type = write_type;
+ cmd->len = len;
+ cmd->auth_req = auth_req;
+
+ srvc_id_to_hal(&cmd->srvc_id, srvc_id);
+ gatt_id_to_hal(&cmd->char_id, char_id);
+
+ memcpy(cmd->value, p_value, len);
+
+ return hal_ipc_cmd(HAL_SERVICE_ID_GATT,
+ HAL_OP_GATT_CLIENT_WRITE_CHARACTERISTIC,
+ cmd_len, cmd, NULL, NULL, NULL);
+}
+
+static bt_status_t read_descriptor(int conn_id, btgatt_srvc_id_t *srvc_id,
+ btgatt_gatt_id_t *char_id,
+ btgatt_gatt_id_t *descr_id,
+ int auth_req)
+{
+ struct hal_cmd_gatt_client_read_descriptor cmd;
+
+ if (!interface_ready())
+ return BT_STATUS_NOT_READY;
+
+ cmd.conn_id = conn_id;
+ cmd.auth_req = auth_req;
+
+ srvc_id_to_hal(&cmd.srvc_id, srvc_id);
+ gatt_id_to_hal(&cmd.char_id, char_id);
+ gatt_id_to_hal(&cmd.descr_id, descr_id);
+
+ return hal_ipc_cmd(HAL_SERVICE_ID_GATT,
+ HAL_OP_GATT_CLIENT_READ_DESCRIPTOR,
+ sizeof(cmd), &cmd, NULL, NULL, NULL);
+}
+
+static bt_status_t write_descriptor(int conn_id, btgatt_srvc_id_t *srvc_id,
+ btgatt_gatt_id_t *char_id,
+ btgatt_gatt_id_t *descr_id,
+ int write_type, int len, int auth_req,
+ char *p_value)
+{
+ char buf[IPC_MTU];
+ struct hal_cmd_gatt_client_write_descriptor *cmd = (void *) buf;
+ size_t cmd_len = sizeof(*cmd) + len;
+
+ if (!interface_ready())
+ return BT_STATUS_NOT_READY;
+
+ cmd->conn_id = conn_id;
+ cmd->write_type = write_type;
+ cmd->len = len;
+ cmd->auth_req = auth_req;
+
+ srvc_id_to_hal(&cmd->srvc_id, srvc_id);
+ gatt_id_to_hal(&cmd->char_id, char_id);
+ gatt_id_to_hal(&cmd->descr_id, descr_id);
+
+ memcpy(cmd->value, p_value, len);
+
+ return hal_ipc_cmd(HAL_SERVICE_ID_GATT,
+ HAL_OP_GATT_CLIENT_WRITE_DESCRIPTOR,
+ cmd_len, cmd, NULL, NULL, NULL);
+}
+
+static bt_status_t execute_write(int conn_id, int execute)
+{
+ struct hal_cmd_gatt_client_execute_write cmd;
+
+ if (!interface_ready())
+ return BT_STATUS_NOT_READY;
+
+ cmd.conn_id = conn_id;
+ cmd.execute = execute;
+
+ return hal_ipc_cmd(HAL_SERVICE_ID_GATT,
+ HAL_OP_GATT_CLIENT_EXECUTE_WRITE,
+ sizeof(cmd), &cmd, NULL, NULL, NULL);
+}
+
+static bt_status_t register_for_notification(int client_if,
+ const bt_bdaddr_t *bd_addr,
+ btgatt_srvc_id_t *srvc_id,
+ btgatt_gatt_id_t *char_id)
+{
+ struct hal_cmd_gatt_client_register_for_notification cmd;
+
+ if (!interface_ready())
+ return BT_STATUS_NOT_READY;
+
+ cmd.client_if = client_if;
+
+ memcpy(cmd.bdaddr, bd_addr, sizeof(*bd_addr));
+
+ srvc_id_to_hal(&cmd.srvc_id, srvc_id);
+ gatt_id_to_hal(&cmd.char_id, char_id);
+
+ return hal_ipc_cmd(HAL_SERVICE_ID_GATT,
+ HAL_OP_GATT_CLIENT_REGISTER_FOR_NOTIFICATION,
+ sizeof(cmd), &cmd, NULL, NULL, NULL);
+}
+
+static bt_status_t deregister_for_notification(int client_if,
+ const bt_bdaddr_t *bd_addr,
+ btgatt_srvc_id_t *srvc_id,
+ btgatt_gatt_id_t *char_id)
+{
+ struct hal_cmd_gatt_client_deregister_for_notification cmd;
+
+ if (!interface_ready())
+ return BT_STATUS_NOT_READY;
+
+ cmd.client_if = client_if;
+
+ memcpy(cmd.bdaddr, bd_addr, sizeof(*bd_addr));
+
+ srvc_id_to_hal(&cmd.srvc_id, srvc_id);
+ gatt_id_to_hal(&cmd.char_id, char_id);
+
+ return hal_ipc_cmd(HAL_SERVICE_ID_GATT,
+ HAL_OP_GATT_CLIENT_DEREGISTER_FOR_NOTIFICATION,
+ sizeof(cmd), &cmd, NULL, NULL, NULL);
+}
+
+static bt_status_t read_remote_rssi(int client_if, const bt_bdaddr_t *bd_addr)
+{
+ struct hal_cmd_gatt_client_read_remote_rssi cmd;
+
+ if (!interface_ready())
+ return BT_STATUS_NOT_READY;
+
+ cmd.client_if = client_if;
+
+ memcpy(cmd.bdaddr, bd_addr, sizeof(*bd_addr));
+
+ return hal_ipc_cmd(HAL_SERVICE_ID_GATT,
+ HAL_OP_GATT_CLIENT_READ_REMOTE_RSSI,
+ sizeof(cmd), &cmd, NULL, NULL, NULL);
+}
+
+static int get_device_type(const bt_bdaddr_t *bd_addr)
+{
+ struct hal_cmd_gatt_client_get_device_type cmd;
+ uint8_t dev_type;
+ size_t resp_len = sizeof(dev_type);
+ bt_status_t status;
+
+ if (!interface_ready())
+ return BT_STATUS_NOT_READY;
+
+ memcpy(cmd.bdaddr, bd_addr, sizeof(*bd_addr));
+
+ status = hal_ipc_cmd(HAL_SERVICE_ID_GATT,
+ HAL_OP_GATT_CLIENT_GET_DEVICE_TYPE,
+ sizeof(cmd), &cmd, &resp_len, &dev_type, NULL);
+
+ if (status != BT_STATUS_SUCCESS || resp_len != sizeof(dev_type))
+ return 0;
+
+ return dev_type;
+}
+
+static bt_status_t set_adv_data_real(int server_if, bool set_scan_rsp,
+ bool include_name, bool include_txpower,
+ int min_interval, int max_interval,
+ int appearance, uint16_t manufacturer_len,
+ char *manufacturer_data,
+ uint16_t service_data_len, char *service_data,
+ uint16_t service_uuid_len, char *service_uuid)
+{
+ char buf[IPC_MTU];
+ struct hal_cmd_gatt_client_set_adv_data *cmd = (void *) buf;
+ size_t cmd_len;
+ uint8_t *data;
+
+ if (!interface_ready())
+ return BT_STATUS_NOT_READY;
+
+ cmd_len = sizeof(*cmd) + manufacturer_len + service_data_len +
+ service_uuid_len;
+
+ if (cmd_len > IPC_MTU)
+ return BT_STATUS_FAIL;
+
+ cmd->server_if = server_if;
+ cmd->set_scan_rsp = set_scan_rsp;
+ cmd->include_name = include_name;
+ cmd->include_txpower = include_txpower;
+ cmd->min_interval = min_interval;
+ cmd->max_interval = max_interval;
+ cmd->appearance = appearance;
+ cmd->manufacturer_len = manufacturer_len;
+ cmd->service_data_len = service_data_len;
+ cmd->service_uuid_len = service_uuid_len;
+
+ data = cmd->data;
+
+ if (manufacturer_data && manufacturer_len) {
+ memcpy(data, manufacturer_data, manufacturer_len);
+ data += manufacturer_len;
+ }
+
+ if (service_data && service_data_len) {
+ memcpy(data, service_data, service_data_len);
+ data += service_data_len;
+ }
+
+ if (service_uuid && service_uuid_len)
+ memcpy(data, service_uuid, service_uuid_len);
+
+ return hal_ipc_cmd(HAL_SERVICE_ID_GATT, HAL_OP_GATT_CLIENT_SET_ADV_DATA,
+ cmd_len, cmd, NULL, NULL, NULL);
+}
+
+/*
+ * This is temporary solution and support for older Android versions might
+ * be removed at any time.
+ */
+#if ANDROID_VERSION < PLATFORM_VER(4, 4, 3)
+static bt_status_t set_adv_data(int server_if, bool set_scan_rsp,
+ bool include_name, bool include_txpower,
+ int min_interval, int max_interval,
+ int appearance, uint16_t manufacturer_len,
+ char *manufacturer_data)
+{
+ return set_adv_data_real(server_if, set_scan_rsp, include_name,
+ include_txpower, min_interval,
+ max_interval, appearance,
+ manufacturer_len, manufacturer_data,
+ 0, NULL, 0, NULL);
+}
+#else
+static bt_status_t set_adv_data(int server_if, bool set_scan_rsp,
+ bool include_name, bool include_txpower,
+ int min_interval, int max_interval,
+ int appearance, uint16_t manufacturer_len,
+ char *manufacturer_data,
+ uint16_t service_data_len, char *service_data,
+ uint16_t service_uuid_len, char *service_uuid)
+{
+ return set_adv_data_real(server_if, set_scan_rsp, include_name,
+ include_txpower, min_interval,
+ max_interval, appearance,
+ manufacturer_len, manufacturer_data,
+ service_data_len, service_data,
+ service_uuid_len, service_uuid);
+}
+#endif
+
+static bt_status_t test_command(int command, btgatt_test_params_t *params)
+{
+ struct hal_cmd_gatt_client_test_command cmd;
+
+ if (!interface_ready())
+ return BT_STATUS_NOT_READY;
+
+ cmd.command = command;
+
+ memcpy(cmd.bda1, params->bda1, sizeof(*params->bda1));
+ memcpy(cmd.uuid1, params->uuid1, sizeof(*params->uuid1));
+
+ cmd.u1 = params->u1;
+ cmd.u2 = params->u2;
+ cmd.u3 = params->u3;
+ cmd.u4 = params->u4;
+ cmd.u5 = params->u5;
+
+ return hal_ipc_cmd(HAL_SERVICE_ID_GATT, HAL_OP_GATT_CLIENT_TEST_COMMAND,
+ sizeof(cmd), &cmd, NULL, NULL, NULL);
+}
+
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+static bt_status_t scan_filter_param_setup(int client_if, int action,
+ int filt_index, int feat_seln,
+ int list_logic_type,
+ int filt_logic_type,
+ int rssi_high_thres,
+ int rssi_low_thres,
+ int dely_mode,
+ int found_timeout,
+ int lost_timeout,
+ int found_timeout_cnt)
+{
+ struct hal_cmd_gatt_client_scan_filter_setup cmd;
+
+ if (!interface_ready())
+ return BT_STATUS_NOT_READY;
+
+ cmd.client_if = client_if;
+ cmd.action = action;
+ cmd.filter_index = filt_index;
+ cmd.features = feat_seln;
+ cmd.list_type = list_logic_type;
+ cmd.filter_type = filt_logic_type;
+ cmd.rssi_hi = rssi_high_thres;
+ cmd.rssi_lo = rssi_low_thres;
+ cmd.delivery_mode = dely_mode;
+ cmd.found_timeout = found_timeout;
+ cmd.lost_timeout = lost_timeout;
+ cmd.found_timeout_cnt = found_timeout_cnt;
+
+ return hal_ipc_cmd(HAL_SERVICE_ID_GATT,
+ HAL_OP_GATT_CLIENT_SCAN_FILTER_SETUP,
+ sizeof(cmd), &cmd, NULL, NULL, NULL);
+}
+
+static bt_status_t scan_filter_add_remove(int client_if, int action,
+ int filt_type, int filt_index,
+ int company_id,
+ int company_id_mask,
+ const bt_uuid_t *p_uuid,
+ const bt_uuid_t *p_uuid_mask,
+ const bt_bdaddr_t *bd_addr,
+ char addr_type,
+ int data_len, char *p_data,
+ int mask_len, char *p_mask)
+{
+ char buf[IPC_MTU];
+ struct hal_cmd_gatt_client_scan_filter_add_remove *cmd = (void *) buf;
+ size_t cmd_len;
+
+ if (!interface_ready())
+ return BT_STATUS_NOT_READY;
+
+ if (!p_uuid || !p_uuid_mask || !bd_addr)
+ return BT_STATUS_PARM_INVALID;
+
+ cmd_len = sizeof(*cmd) + data_len + mask_len;
+ if (cmd_len > IPC_MTU)
+ return BT_STATUS_FAIL;
+
+ cmd->client_if = client_if;
+ cmd->action = action;
+ cmd->filter_type = filt_type;
+ cmd->filter_index = filt_index;
+ cmd->company_id = company_id;
+ cmd->company_id_mask = company_id_mask;
+ memcpy(cmd->uuid, p_uuid, sizeof(*p_uuid));
+ memcpy(cmd->uuid_mask, p_uuid_mask, sizeof(*p_uuid_mask));
+ memcpy(cmd->address, bd_addr, sizeof(*bd_addr));
+ cmd->address_type = addr_type;
+
+ cmd->data_len = data_len;
+ memcpy(cmd->data_mask, p_data, data_len);
+
+ cmd->mask_len = mask_len;
+ memcpy(cmd->data_mask + data_len, p_mask, mask_len);
+
+ return hal_ipc_cmd(HAL_SERVICE_ID_GATT,
+ HAL_OP_GATT_CLIENT_SCAN_FILTER_ADD_REMOVE,
+ cmd_len, cmd, NULL, NULL, NULL);
+}
+
+static bt_status_t scan_filter_clear(int client_if, int filt_index)
+{
+ struct hal_cmd_gatt_client_scan_filter_clear cmd;
+
+ if (!interface_ready())
+ return BT_STATUS_NOT_READY;
+
+ cmd.client_if = client_if;
+ cmd.index = filt_index;
+
+ return hal_ipc_cmd(HAL_SERVICE_ID_GATT,
+ HAL_OP_GATT_CLIENT_SCAN_FILTER_CLEAR,
+ sizeof(cmd), &cmd, NULL, NULL, NULL);
+}
+
+static bt_status_t scan_filter_enable(int client_if, bool enable)
+{
+ struct hal_cmd_gatt_client_scan_filter_enable cmd;
+
+ if (!interface_ready())
+ return BT_STATUS_NOT_READY;
+
+ cmd.client_if = client_if;
+ cmd.enable = enable;
+
+ return hal_ipc_cmd(HAL_SERVICE_ID_GATT,
+ HAL_OP_GATT_CLIENT_SCAN_FILTER_ENABLE,
+ sizeof(cmd), &cmd, NULL, NULL, NULL);
+}
+
+static bt_status_t configure_mtu(int conn_id, int mtu)
+{
+ struct hal_cmd_gatt_client_configure_mtu cmd;
+
+ if (!interface_ready())
+ return BT_STATUS_NOT_READY;
+
+ cmd.conn_id = conn_id;
+ cmd.mtu = mtu;
+
+ return hal_ipc_cmd(HAL_SERVICE_ID_GATT,
+ HAL_OP_GATT_CLIENT_CONFIGURE_MTU,
+ sizeof(cmd), &cmd, NULL, NULL, NULL);
+}
+
+static bt_status_t conn_parameter_update(const bt_bdaddr_t *bd_addr,
+ int min_interval,
+ int max_interval, int latency,
+ int timeout)
+{
+ struct hal_cmd_gatt_client_conn_param_update cmd;
+
+ if (!interface_ready())
+ return BT_STATUS_NOT_READY;
+
+ if (!bd_addr)
+ return BT_STATUS_PARM_INVALID;
+
+ memcpy(cmd.address, bd_addr, sizeof(*bd_addr));
+ cmd.min_interval = min_interval;
+ cmd.max_interval = max_interval;
+ cmd.latency = latency;
+ cmd.timeout = timeout;
+
+ return hal_ipc_cmd(HAL_SERVICE_ID_GATT,
+ HAL_OP_GATT_CLIENT_CONN_PARAM_UPDATE,
+ sizeof(cmd), &cmd, NULL, NULL, NULL);
+}
+
+static bt_status_t set_scan_parameters(int scan_interval, int scan_window)
+{
+ struct hal_cmd_gatt_client_set_scan_param cmd;
+
+ if (!interface_ready())
+ return BT_STATUS_NOT_READY;
+
+ cmd.interval = scan_interval;
+ cmd.window = scan_window;
+
+ return hal_ipc_cmd(HAL_SERVICE_ID_GATT,
+ HAL_OP_GATT_CLIENT_SET_SCAN_PARAM,
+ sizeof(cmd), &cmd, NULL, NULL, NULL);
+}
+
+static bt_status_t multi_adv_enable(int client_if, int min_interval,
+ int max_interval, int adv_type,
+ int chnl_map, int tx_power,
+ int timeout_s)
+{
+ struct hal_cmd_gatt_client_setup_multi_adv cmd;
+
+ if (!interface_ready())
+ return BT_STATUS_NOT_READY;
+
+ cmd.client_if = client_if;
+ cmd.min_interval = min_interval;
+ cmd.max_interval = max_interval;
+ cmd.type = adv_type;
+ cmd.channel_map = chnl_map;
+ cmd.tx_power = tx_power;
+ cmd.timeout = timeout_s;
+
+ return hal_ipc_cmd(HAL_SERVICE_ID_GATT,
+ HAL_OP_GATT_CLIENT_SETUP_MULTI_ADV,
+ sizeof(cmd), &cmd, NULL, NULL, NULL);
+}
+
+static bt_status_t multi_adv_update(int client_if, int min_interval,
+ int max_interval, int adv_type,
+ int chnl_map, int tx_power,
+ int timeout_s)
+{
+ struct hal_cmd_gatt_client_update_multi_adv cmd;
+
+ if (!interface_ready())
+ return BT_STATUS_NOT_READY;
+
+ cmd.client_if = client_if;
+ cmd.min_interval = min_interval;
+ cmd.max_interval = max_interval;
+ cmd.type = adv_type;
+ cmd.channel_map = chnl_map;
+ cmd.tx_power = tx_power;
+ cmd.timeout = timeout_s;
+
+ return hal_ipc_cmd(HAL_SERVICE_ID_GATT,
+ HAL_OP_GATT_CLIENT_UPDATE_MULTI_ADV,
+ sizeof(cmd), &cmd, NULL, NULL, NULL);
+}
+
+static bt_status_t multi_adv_set_inst_data(int client_if, bool set_scan_rsp,
+ bool include_name,
+ bool incl_txpower,
+ int appearance,
+ int manufacturer_len,
+ char *manufacturer_data,
+ int service_data_len,
+ char *service_data,
+ int service_uuid_len,
+ char *service_uuid)
+{
+ char buf[IPC_MTU];
+ struct hal_cmd_gatt_client_setup_multi_adv_inst *cmd = (void *) buf;
+ int off = 0;
+
+ if (!interface_ready())
+ return BT_STATUS_NOT_READY;
+
+ if (manufacturer_len > 0 && !manufacturer_data)
+ return BT_STATUS_PARM_INVALID;
+
+ if (service_data_len > 0 && !service_data)
+ return BT_STATUS_PARM_INVALID;
+
+ if (service_uuid_len > 0 && !service_uuid)
+ return BT_STATUS_PARM_INVALID;
+
+ if (sizeof(*cmd) + manufacturer_len + service_data_len
+ + service_uuid_len > IPC_MTU)
+ return BT_STATUS_FAIL;
+
+ cmd->client_if = client_if;
+ cmd->set_scan_rsp = set_scan_rsp;
+ cmd->include_name = include_name;
+ cmd->include_tx_power = incl_txpower;
+ cmd->appearance = appearance;
+ cmd->manufacturer_data_len = manufacturer_len;
+ cmd->service_data_len = service_data_len;
+ cmd->service_uuid_len = service_uuid_len;
+
+ if (manufacturer_len > 0) {
+ memcpy(cmd->data_service_uuid, manufacturer_data,
+ manufacturer_len);
+ off += manufacturer_len;
+ }
+
+ if (service_data_len > 0) {
+ memcpy(cmd->data_service_uuid + off, service_data,
+ service_data_len);
+ off += service_data_len;
+ }
+
+ if (service_uuid_len > 0) {
+ memcpy(cmd->data_service_uuid + off, service_uuid,
+ service_uuid_len);
+ off += service_uuid_len;
+ }
+
+ return hal_ipc_cmd(HAL_SERVICE_ID_GATT,
+ HAL_OP_GATT_CLIENT_SETUP_MULTI_ADV_INST,
+ sizeof(*cmd) + off, cmd, NULL, NULL, NULL);
+}
+
+static bt_status_t multi_adv_disable(int client_if)
+{
+ struct hal_cmd_gatt_client_disable_multi_adv_inst cmd;
+
+ if (!interface_ready())
+ return BT_STATUS_NOT_READY;
+
+ cmd.client_if = client_if;
+
+ return hal_ipc_cmd(HAL_SERVICE_ID_GATT,
+ HAL_OP_GATT_CLIENT_DISABLE_MULTI_ADV_INST,
+ sizeof(cmd), &cmd, NULL, NULL, NULL);
+}
+
+static bt_status_t batchscan_cfg_storage(int client_if, int batch_scan_full_max,
+ int batch_scan_trunc_max,
+ int batch_scan_notify_threshold)
+{
+ struct hal_cmd_gatt_client_configure_batchscan cmd;
+
+ if (!interface_ready())
+ return BT_STATUS_NOT_READY;
+
+ cmd.client_if = client_if;
+ cmd.full_max = batch_scan_full_max;
+ cmd.trunc_max = batch_scan_trunc_max;
+ cmd.notify_threshold = batch_scan_notify_threshold;
+
+ return hal_ipc_cmd(HAL_SERVICE_ID_GATT,
+ HAL_OP_GATT_CLIENT_CONFIGURE_BATCHSCAN,
+ sizeof(cmd), &cmd, NULL, NULL, NULL);
+}
+
+static bt_status_t batchscan_enb_batch_scan(int client_if, int scan_mode,
+ int scan_interval,
+ int scan_window, int addr_type,
+ int discard_rule)
+{
+ struct hal_cmd_gatt_client_enable_batchscan cmd;
+
+ if (!interface_ready())
+ return BT_STATUS_NOT_READY;
+
+ cmd.client_if = client_if;
+ cmd.mode = scan_mode;
+ cmd.interval = scan_interval;
+ cmd.window = scan_window;
+ cmd.address_type = addr_type;
+ cmd.discard_rule = discard_rule;
+
+ return hal_ipc_cmd(HAL_SERVICE_ID_GATT,
+ HAL_OP_GATT_CLIENT_ENABLE_BATCHSCAN,
+ sizeof(cmd), &cmd, NULL, NULL, NULL);
+}
+
+static bt_status_t batchscan_dis_batch_scan(int client_if)
+{
+ struct hal_cmd_gatt_client_disable_batchscan cmd;
+
+ if (!interface_ready())
+ return BT_STATUS_NOT_READY;
+
+ cmd.client_if = client_if;
+
+ return hal_ipc_cmd(HAL_SERVICE_ID_GATT,
+ HAL_OP_GATT_CLIENT_DISABLE_BATCHSCAN,
+ sizeof(cmd), &cmd, NULL, NULL, NULL);
+}
+
+static bt_status_t batchscan_read_reports(int client_if, int scan_mode)
+{
+ struct hal_cmd_gatt_client_read_batchscan_reports cmd;
+
+ if (!interface_ready())
+ return BT_STATUS_NOT_READY;
+
+ cmd.client_if = client_if;
+ cmd.scan_mode = scan_mode;
+
+ return hal_ipc_cmd(HAL_SERVICE_ID_GATT,
+ HAL_OP_GATT_CLIENT_READ_BATCHSCAN_REPORTS,
+ sizeof(cmd), &cmd, NULL, NULL, NULL);
+}
+#endif
+
+/* Server API */
+
+static bt_status_t register_server(bt_uuid_t *uuid)
+{
+ struct hal_cmd_gatt_server_register cmd;
+
+ if (!interface_ready())
+ return BT_STATUS_NOT_READY;
+
+ memcpy(cmd.uuid, uuid, sizeof(*uuid));
+
+ return hal_ipc_cmd(HAL_SERVICE_ID_GATT, HAL_OP_GATT_SERVER_REGISTER,
+ sizeof(cmd), &cmd, NULL, NULL, NULL);
+}
+
+static bt_status_t unregister_server(int server_if)
+{
+ struct hal_cmd_gatt_server_unregister cmd;
+
+ if (!interface_ready())
+ return BT_STATUS_NOT_READY;
+
+ cmd.server_if = server_if;
+
+ return hal_ipc_cmd(HAL_SERVICE_ID_GATT, HAL_OP_GATT_SERVER_UNREGISTER,
+ sizeof(cmd), &cmd, NULL, NULL, NULL);
+}
+
+static bt_status_t server_connect_real(int server_if,
+ const bt_bdaddr_t *bd_addr,
+ bool is_direct, int transport)
+{
+ struct hal_cmd_gatt_server_connect cmd;
+
+ if (!interface_ready())
+ return BT_STATUS_NOT_READY;
+
+ cmd.server_if = server_if;
+ cmd.is_direct = is_direct;
+ cmd.transport = transport;
+
+ memcpy(cmd.bdaddr, bd_addr, sizeof(*bd_addr));
+
+ return hal_ipc_cmd(HAL_SERVICE_ID_GATT, HAL_OP_GATT_SERVER_CONNECT,
+ sizeof(cmd), &cmd, NULL, NULL, NULL);
+}
+
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+static bt_status_t server_connect(int server_if, const bt_bdaddr_t *bd_addr,
+ bool is_direct, int transport)
+{
+ return server_connect_real(server_if, bd_addr, is_direct, transport);
+}
+#else
+static bt_status_t server_connect(int server_if, const bt_bdaddr_t *bd_addr,
+ bool is_direct)
+{
+ return server_connect_real(server_if, bd_addr, is_direct,
+ BT_TRANSPORT_UNKNOWN);
+}
+#endif
+
+static bt_status_t server_disconnect(int server_if, const bt_bdaddr_t *bd_addr,
+ int conn_id)
+{
+ struct hal_cmd_gatt_server_disconnect cmd;
+
+ if (!interface_ready())
+ return BT_STATUS_NOT_READY;
+
+ cmd.server_if = server_if;
+ cmd.conn_id = conn_id;
+
+ memcpy(cmd.bdaddr, bd_addr, sizeof(*bd_addr));
+
+ return hal_ipc_cmd(HAL_SERVICE_ID_GATT, HAL_OP_GATT_SERVER_DISCONNECT,
+ sizeof(cmd), &cmd, NULL, NULL, NULL);
+}
+
+static bt_status_t add_service(int server_if, btgatt_srvc_id_t *srvc_id,
+ int num_handles)
+{
+ struct hal_cmd_gatt_server_add_service cmd;
+
+ if (!interface_ready())
+ return BT_STATUS_NOT_READY;
+
+ cmd.server_if = server_if;
+ cmd.num_handles = num_handles;
+
+ srvc_id_to_hal(&cmd.srvc_id, srvc_id);
+
+ return hal_ipc_cmd(HAL_SERVICE_ID_GATT, HAL_OP_GATT_SERVER_ADD_SERVICE,
+ sizeof(cmd), &cmd, NULL, NULL, NULL);
+}
+
+static bt_status_t add_included_service(int server_if, int service_handle,
+ int included_handle)
+{
+ struct hal_cmd_gatt_server_add_inc_service cmd;
+
+ if (!interface_ready())
+ return BT_STATUS_NOT_READY;
+
+ cmd.server_if = server_if;
+ cmd.service_handle = service_handle;
+ cmd.included_handle = included_handle;
+
+ return hal_ipc_cmd(HAL_SERVICE_ID_GATT,
+ HAL_OP_GATT_SERVER_ADD_INC_SERVICE,
+ sizeof(cmd), &cmd, NULL, NULL, NULL);
+}
+
+static bt_status_t add_characteristic(int server_if, int service_handle,
+ bt_uuid_t *uuid, int properties,
+ int permissions)
+{
+ struct hal_cmd_gatt_server_add_characteristic cmd;
+
+ if (!interface_ready())
+ return BT_STATUS_NOT_READY;
+
+ cmd.server_if = server_if;
+ cmd.service_handle = service_handle;
+ cmd.properties = properties;
+ cmd.permissions = permissions;
+
+ memcpy(cmd.uuid, uuid, sizeof(*uuid));
+
+ return hal_ipc_cmd(HAL_SERVICE_ID_GATT,
+ HAL_OP_GATT_SERVER_ADD_CHARACTERISTIC,
+ sizeof(cmd), &cmd, NULL, NULL, NULL);
+}
+
+static bt_status_t add_descriptor(int server_if, int service_handle,
+ bt_uuid_t *uuid, int permissions)
+{
+ struct hal_cmd_gatt_server_add_descriptor cmd;
+
+ if (!interface_ready())
+ return BT_STATUS_NOT_READY;
+
+ cmd.server_if = server_if;
+ cmd.service_handle = service_handle;
+ cmd.permissions = permissions;
+
+ memcpy(cmd.uuid, uuid, sizeof(*uuid));
+
+ return hal_ipc_cmd(HAL_SERVICE_ID_GATT,
+ HAL_OP_GATT_SERVER_ADD_DESCRIPTOR,
+ sizeof(cmd), &cmd, NULL, NULL, NULL);
+}
+
+static bt_status_t start_service_real(int server_if, int service_handle,
+ int transport)
+{
+ struct hal_cmd_gatt_server_start_service cmd;
+
+ if (!interface_ready())
+ return BT_STATUS_NOT_READY;
+
+ cmd.server_if = server_if;
+ cmd.service_handle = service_handle;
+ cmd.transport = transport;
+
+ return hal_ipc_cmd(HAL_SERVICE_ID_GATT,
+ HAL_OP_GATT_SERVER_START_SERVICE,
+ sizeof(cmd), &cmd, NULL, NULL, NULL);
+}
+
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+static bt_status_t start_service(int server_if, int service_handle,
+ int transport)
+{
+ return start_service_real(server_if, service_handle, transport);
+}
+#else
+static bt_status_t start_service(int server_if, int service_handle,
+ int transport)
+{
+ int transport_mask = 0;
+
+ /* Android 5 changes transport enum to bit mask. */
+ switch (transport) {
+ case 0:
+ transport_mask = GATT_SERVER_TRANSPORT_LE_BIT;
+ break;
+ case 1:
+ transport_mask = GATT_SERVER_TRANSPORT_BREDR_BIT;
+ break;
+ case 2:
+ transport_mask = GATT_SERVER_TRANSPORT_LE_BIT |
+ GATT_SERVER_TRANSPORT_BREDR_BIT;
+ break;
+ }
+
+ return start_service_real(server_if, service_handle, transport_mask);
+}
+#endif
+
+static bt_status_t stop_service(int server_if, int service_handle)
+{
+ struct hal_cmd_gatt_server_stop_service cmd;
+
+ if (!interface_ready())
+ return BT_STATUS_NOT_READY;
+
+ cmd.server_if = server_if;
+ cmd.service_handle = service_handle;
+
+ return hal_ipc_cmd(HAL_SERVICE_ID_GATT, HAL_OP_GATT_SERVER_STOP_SERVICE,
+ sizeof(cmd), &cmd, NULL, NULL, NULL);
+}
+
+static bt_status_t delete_service(int server_if, int service_handle)
+{
+ struct hal_cmd_gatt_server_delete_service cmd;
+
+ if (!interface_ready())
+ return BT_STATUS_NOT_READY;
+
+ cmd.server_if = server_if;
+ cmd.service_handle = service_handle;
+
+ return hal_ipc_cmd(HAL_SERVICE_ID_GATT,
+ HAL_OP_GATT_SERVER_DELETE_SERVICE,
+ sizeof(cmd), &cmd, NULL, NULL, NULL);
+}
+
+static bt_status_t send_indication(int server_if, int attribute_handle,
+ int conn_id, int len, int confirm,
+ char *p_value)
+{
+ char buf[IPC_MTU];
+ struct hal_cmd_gatt_server_send_indication *cmd = (void *) buf;
+ size_t cmd_len = sizeof(*cmd) + len;
+
+ if (!interface_ready())
+ return BT_STATUS_NOT_READY;
+
+ cmd->server_if = server_if;
+ cmd->attribute_handle = attribute_handle;
+ cmd->conn_id = conn_id;
+ cmd->len = len;
+ cmd->confirm = confirm;
+
+ memcpy(cmd->value, p_value, len);
+
+ return hal_ipc_cmd(HAL_SERVICE_ID_GATT,
+ HAL_OP_GATT_SERVER_SEND_INDICATION,
+ cmd_len, cmd, NULL, NULL, NULL);
+}
+
+static bt_status_t send_response(int conn_id, int trans_id, int status,
+ btgatt_response_t *response)
+{
+ char buf[IPC_MTU];
+ struct hal_cmd_gatt_server_send_response *cmd = (void *) buf;
+ size_t cmd_len = sizeof(*cmd) + sizeof(*response);
+
+ memset(buf, 0 , IPC_MTU);
+
+ if (!interface_ready())
+ return BT_STATUS_NOT_READY;
+
+ cmd->conn_id = conn_id;
+ cmd->trans_id = trans_id;
+ cmd->status = status;
+ cmd->handle = response->attr_value.handle;
+ cmd->offset = response->attr_value.offset;
+ cmd->auth_req = response->attr_value.auth_req;
+ cmd->len = response->attr_value.len;
+
+ memcpy(cmd->data, response->attr_value.value, cmd->len);
+
+ return hal_ipc_cmd(HAL_SERVICE_ID_GATT,
+ HAL_OP_GATT_SERVER_SEND_RESPONSE,
+ cmd_len, cmd, NULL, NULL, NULL);
+}
+
+static bt_status_t init(const btgatt_callbacks_t *callbacks)
+{
+ struct hal_cmd_register_module cmd;
+ int ret;
+
+ DBG("");
+
+ if (interface_ready())
+ return BT_STATUS_DONE;
+
+ cbs = callbacks;
+
+ hal_ipc_register(HAL_SERVICE_ID_GATT, ev_handlers,
+ sizeof(ev_handlers)/sizeof(ev_handlers[0]));
+
+ cmd.service_id = HAL_SERVICE_ID_GATT;
+ cmd.mode = HAL_MODE_DEFAULT;
+ cmd.max_clients = 1;
+
+ ret = hal_ipc_cmd(HAL_SERVICE_ID_CORE, HAL_OP_REGISTER_MODULE,
+ sizeof(cmd), &cmd, NULL, NULL, NULL);
+
+ if (ret != BT_STATUS_SUCCESS) {
+ cbs = NULL;
+ hal_ipc_unregister(HAL_SERVICE_ID_GATT);
+ }
+
+ return ret;
+}
+
+static void cleanup(void)
+{
+ struct hal_cmd_unregister_module cmd;
+
+ DBG("");
+
+ if (!interface_ready())
+ return;
+
+ cmd.service_id = HAL_SERVICE_ID_GATT;
+
+ hal_ipc_cmd(HAL_SERVICE_ID_CORE, HAL_OP_UNREGISTER_MODULE,
+ sizeof(cmd), &cmd, NULL, NULL, NULL);
+
+ hal_ipc_unregister(HAL_SERVICE_ID_GATT);
+
+ cbs = NULL;
+}
+
+static btgatt_client_interface_t client_iface = {
+ .register_client = register_client,
+ .unregister_client = unregister_client,
+ .scan = scan,
+ .connect = connect,
+ .disconnect = disconnect,
+ .listen = listen,
+ .refresh = refresh,
+ .search_service = search_service,
+ .get_included_service = get_included_service,
+ .get_characteristic = get_characteristic,
+ .get_descriptor = get_descriptor,
+ .read_characteristic = read_characteristic,
+ .write_characteristic = write_characteristic,
+ .read_descriptor = read_descriptor,
+ .write_descriptor = write_descriptor,
+ .execute_write = execute_write,
+ .register_for_notification = register_for_notification,
+ .deregister_for_notification = deregister_for_notification,
+ .read_remote_rssi = read_remote_rssi,
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+ .scan_filter_param_setup = scan_filter_param_setup,
+ .scan_filter_add_remove = scan_filter_add_remove,
+ .scan_filter_clear = scan_filter_clear,
+ .scan_filter_enable = scan_filter_enable,
+#endif
+ .get_device_type = get_device_type,
+ .set_adv_data = set_adv_data,
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+ .configure_mtu = configure_mtu,
+ .conn_parameter_update = conn_parameter_update,
+ .set_scan_parameters = set_scan_parameters,
+ .multi_adv_enable = multi_adv_enable,
+ .multi_adv_update = multi_adv_update,
+ .multi_adv_set_inst_data = multi_adv_set_inst_data,
+ .multi_adv_disable = multi_adv_disable,
+ .batchscan_cfg_storage = batchscan_cfg_storage,
+ .batchscan_enb_batch_scan = batchscan_enb_batch_scan,
+ .batchscan_dis_batch_scan = batchscan_dis_batch_scan,
+ .batchscan_read_reports = batchscan_read_reports,
+#endif
+ .test_command = test_command,
+};
+
+static btgatt_server_interface_t server_iface = {
+ .register_server = register_server,
+ .unregister_server = unregister_server,
+ .connect = server_connect,
+ .disconnect = server_disconnect,
+ .add_service = add_service,
+ .add_included_service = add_included_service,
+ .add_characteristic = add_characteristic,
+ .add_descriptor = add_descriptor,
+ .start_service = start_service,
+ .stop_service = stop_service,
+ .delete_service = delete_service,
+ .send_indication = send_indication,
+ .send_response = send_response,
+};
+
+static btgatt_interface_t iface = {
+ .size = sizeof(iface),
+ .init = init,
+ .cleanup = cleanup,
+ .client = &client_iface,
+ .server = &server_iface,
+};
+
+btgatt_interface_t *bt_get_gatt_interface(void)
+{
+ return &iface;
+}
diff --git a/repo/android/hal-handsfree-client.c b/repo/android/hal-handsfree-client.c
new file mode 100644
index 0000000..93b5746
--- /dev/null
+++ b/repo/android/hal-handsfree-client.c
@@ -0,0 +1,653 @@
+/*
+ * Copyright (C) 2014 Intel Corporation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include <stdbool.h>
+#include <stddef.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include <cutils/properties.h>
+
+#include "hal-log.h"
+#include "hal.h"
+#include "hal-msg.h"
+#include "ipc-common.h"
+#include "hal-ipc.h"
+
+static const bthf_client_callbacks_t *cbs = NULL;
+
+static bool interface_ready(void)
+{
+ return cbs != NULL;
+}
+
+static void handle_conn_state(void *buf, uint16_t len, int fd)
+{
+ struct hal_ev_hf_client_conn_state *ev = buf;
+
+ if (cbs->connection_state_cb)
+ cbs->connection_state_cb(ev->state, ev->peer_feat,
+ ev->chld_feat,
+ (bt_bdaddr_t *) ev->bdaddr);
+}
+
+static void handle_audio_state(void *buf, uint16_t len, int fd)
+{
+ struct hal_ev_hf_client_audio_state *ev = buf;
+
+ if (cbs->audio_state_cb)
+ cbs->audio_state_cb(ev->state, (bt_bdaddr_t *) (ev->bdaddr));
+}
+
+static void handle_vr_state(void *buf, uint16_t len, int fd)
+{
+ struct hal_ev_hf_client_vr_state *ev = buf;
+
+ if (cbs->vr_cmd_cb)
+ cbs->vr_cmd_cb(ev->state);
+}
+
+static void handle_network_state(void *buf, uint16_t len, int fd)
+{
+ struct hal_ev_hf_client_net_state *ev = buf;
+
+ if (cbs->network_state_cb)
+ cbs->network_state_cb(ev->state);
+}
+
+static void handle_network_roaming(void *buf, uint16_t len, int fd)
+{
+ struct hal_ev_hf_client_net_roaming_type *ev = buf;
+
+ if (cbs->network_roaming_cb)
+ cbs->network_roaming_cb(ev->state);
+}
+
+static void handle_network_signal(void *buf, uint16_t len, int fd)
+{
+ struct hal_ev_hf_client_net_signal_strength *ev = buf;
+
+ if (cbs->network_signal_cb)
+ cbs->network_signal_cb(ev->signal_strength);
+}
+
+static void handle_battery_level(void *buf, uint16_t len, int fd)
+{
+ struct hal_ev_hf_client_battery_level *ev = buf;
+
+ if (cbs->battery_level_cb)
+ cbs->battery_level_cb(ev->battery_level);
+}
+
+static void handle_operator_name(void *buf, uint16_t len, int fd)
+{
+ struct hal_ev_hf_client_operator_name *ev = buf;
+ uint16_t name_len = ev->name_len;
+ char *name = NULL;
+
+ if (len != sizeof(*ev) + name_len ||
+ (name_len != 0 && ev->name[name_len - 1] != '\0')) {
+ error("invalid operator name, aborting");
+ exit(EXIT_FAILURE);
+ }
+
+ if (name_len)
+ name = (char *) ev->name;
+
+ if (cbs->current_operator_cb)
+ cbs->current_operator_cb(name);
+}
+
+static void handle_call(void *buf, uint16_t len, int fd)
+{
+ struct hal_ev_hf_client_call_indicator *ev = buf;
+
+ if (cbs->call_cb)
+ cbs->call_cb(ev->call);
+}
+
+static void handle_call_setup(void *buf, uint16_t len, int fd)
+{
+ struct hal_ev_hf_client_call_setup_indicator *ev = buf;
+
+ if (cbs->callsetup_cb)
+ cbs->callsetup_cb(ev->call_setup);
+}
+
+static void handle_call_held(void *buf, uint16_t len, int fd)
+{
+ struct hal_ev_hf_client_call_held_indicator *ev = buf;
+
+ if (cbs->callheld_cb)
+ cbs->callheld_cb(ev->call_held);
+}
+
+static void handle_response_and_hold(void *buf, uint16_t len, int fd)
+{
+ struct hal_ev_hf_client_response_and_hold_status *ev = buf;
+
+ if (cbs->resp_and_hold_cb)
+ cbs->resp_and_hold_cb(ev->status);
+}
+
+static void handle_clip(void *buf, uint16_t len, int fd)
+{
+ struct hal_ev_hf_client_calling_line_ident *ev = buf;
+ uint16_t num_len = ev->number_len;
+ char *number = NULL;
+
+ if (len != sizeof(*ev) + num_len ||
+ (num_len != 0 && ev->number[num_len - 1] != '\0')) {
+ error("invalid clip, aborting");
+ exit(EXIT_FAILURE);
+ }
+
+ if (num_len)
+ number = (char *) ev->number;
+
+ if (cbs->clip_cb)
+ cbs->clip_cb(number);
+}
+
+static void handle_call_waiting(void *buf, uint16_t len, int fd)
+{
+ struct hal_ev_hf_client_call_waiting *ev = buf;
+ uint16_t num_len = ev->number_len;
+ char *number = NULL;
+
+ if (len != sizeof(*ev) + num_len ||
+ (num_len != 0 && ev->number[num_len - 1] != '\0')) {
+ error("invalid call waiting, aborting");
+ exit(EXIT_FAILURE);
+ }
+
+ if (num_len)
+ number = (char *) ev->number;
+
+ if (cbs->call_waiting_cb)
+ cbs->call_waiting_cb(number);
+}
+
+static void handle_current_calls(void *buf, uint16_t len, int fd)
+{
+ struct hal_ev_hf_client_current_call *ev = buf;
+ uint16_t num_len = ev->number_len;
+ char *number = NULL;
+
+ if (len != sizeof(*ev) + num_len ||
+ (num_len != 0 && ev->number[num_len - 1] != '\0')) {
+ error("invalid current calls, aborting");
+ exit(EXIT_FAILURE);
+ }
+
+ if (num_len)
+ number = (char *) ev->number;
+
+ if (cbs->current_calls_cb)
+ cbs->current_calls_cb(ev->index, ev->direction, ev->call_state,
+ ev->multiparty, number);
+}
+
+static void handle_volume_change(void *buf, uint16_t len, int fd)
+{
+ struct hal_ev_hf_client_volume_changed *ev = buf;
+
+ if (cbs->volume_change_cb)
+ cbs->volume_change_cb(ev->type, ev->volume);
+}
+
+static void handle_command_cmp(void *buf, uint16_t len, int fd)
+{
+ struct hal_ev_hf_client_command_complete *ev = buf;
+
+ if (cbs->cmd_complete_cb)
+ cbs->cmd_complete_cb(ev->type, ev->cme);
+}
+
+static void handle_subscriber_info(void *buf, uint16_t len, int fd)
+{
+ const struct hal_ev_hf_client_subscriber_service_info *ev = buf;
+ uint16_t name_len = ev->name_len;
+ char *name = NULL;
+
+ if (len != sizeof(*ev) + name_len ||
+ (name_len != 0 && ev->name[name_len - 1] != '\0')) {
+ error("invalid sunscriber info, aborting");
+ exit(EXIT_FAILURE);
+ }
+
+ if (name_len)
+ name = (char *) ev->name;
+
+ if (cbs->subscriber_info_cb)
+ cbs->subscriber_info_cb(name, ev->type);
+}
+
+static void handle_in_band_ringtone(void *buf, uint16_t len, int fd)
+{
+ struct hal_ev_hf_client_inband_settings *ev = buf;
+
+ if (cbs->in_band_ring_tone_cb)
+ cbs->in_band_ring_tone_cb(ev->state);
+}
+
+static void handle_last_voice_tag_number(void *buf, uint16_t len, int fd)
+{
+ const struct hal_ev_hf_client_last_void_call_tag_num *ev = buf;
+ char *number = NULL;
+ uint16_t num_len = ev->number_len;
+
+ if (len != sizeof(*ev) + num_len ||
+ (num_len != 0 && ev->number[num_len - 1] != '\0')) {
+ error("invalid voice tag, aborting");
+ exit(EXIT_FAILURE);
+ }
+
+ if (num_len)
+ number = (char *) ev->number;
+
+ if (cbs->last_voice_tag_number_callback)
+ cbs->last_voice_tag_number_callback(number);
+}
+
+static void handle_ring_indication(void *buf, uint16_t len, int fd)
+{
+ if (cbs->ring_indication_cb)
+ cbs->ring_indication_cb();
+}
+
+/*
+ * handlers will be called from notification thread context,
+ * index in table equals to 'opcode - HAL_MINIMUM_EVENT'
+ */
+static const struct hal_ipc_handler ev_handlers[] = {
+ /* HAL_EV_HF_CLIENT_CONN_STATE */
+ { handle_conn_state, false,
+ sizeof(struct hal_ev_hf_client_conn_state) },
+ /* HAL_EV_HF_CLIENT_AUDIO_STATE */
+ { handle_audio_state, false,
+ sizeof(struct hal_ev_hf_client_audio_state) },
+ /* HAL_EV_HF_CLIENT_VR_STATE */
+ { handle_vr_state, false, sizeof(struct hal_ev_hf_client_vr_state) },
+ /*HAL_EV_HF_CLIENT_NET_STATE */
+ { handle_network_state, false,
+ sizeof(struct hal_ev_hf_client_net_state)},
+ /*HAL_EV_HF_CLIENT_NET_ROAMING_TYPE */
+ { handle_network_roaming, false,
+ sizeof(struct hal_ev_hf_client_net_roaming_type) },
+ /* HAL_EV_HF_CLIENT_NET_SIGNAL_STRENGTH */
+ { handle_network_signal, false,
+ sizeof(struct hal_ev_hf_client_net_signal_strength) },
+ /* HAL_EV_HF_CLIENT_BATTERY_LEVEL */
+ { handle_battery_level, false,
+ sizeof(struct hal_ev_hf_client_battery_level) },
+ /* HAL_EV_HF_CLIENT_OPERATOR_NAME */
+ { handle_operator_name, true,
+ sizeof(struct hal_ev_hf_client_operator_name) },
+ /* HAL_EV_HF_CLIENT_CALL_INDICATOR */
+ { handle_call, false,
+ sizeof(struct hal_ev_hf_client_call_indicator) },
+ /* HAL_EV_HF_CLIENT_CALL_SETUP_INDICATOR */
+ { handle_call_setup, false,
+ sizeof(struct hal_ev_hf_client_call_setup_indicator) },
+ /* HAL_EV_HF_CLIENT_CALL_HELD_INDICATOR */
+ { handle_call_held, false,
+ sizeof(struct hal_ev_hf_client_call_held_indicator) },
+ /* HAL_EV_HF_CLIENT_RESPONSE_AND_HOLD_STATUS */
+ { handle_response_and_hold, false,
+ sizeof(struct hal_ev_hf_client_response_and_hold_status) },
+ /* HAL_EV_HF_CLIENT_CALLING_LINE_IDENT */
+ { handle_clip, true,
+ sizeof(struct hal_ev_hf_client_calling_line_ident) },
+ /* HAL_EV_HF_CLIENT_CALL_WAITING */
+ { handle_call_waiting, true,
+ sizeof(struct hal_ev_hf_client_call_waiting) },
+ /* HAL_EV_HF_CLIENT_CURRENT_CALL */
+ { handle_current_calls, true,
+ sizeof(struct hal_ev_hf_client_current_call) },
+ /* HAL_EV_CLIENT_VOLUME_CHANGED */
+ { handle_volume_change, false,
+ sizeof(struct hal_ev_hf_client_volume_changed) },
+ /* HAL_EV_CLIENT_COMMAND_COMPLETE */
+ { handle_command_cmp, false,
+ sizeof(struct hal_ev_hf_client_command_complete) },
+ /* HAL_EV_CLIENT_SUBSCRIBER_SERVICE_INFO */
+ { handle_subscriber_info, true,
+ sizeof(struct hal_ev_hf_client_subscriber_service_info) },
+ /* HAL_EV_CLIENT_INBAND_SETTINGS */
+ { handle_in_band_ringtone, false,
+ sizeof(struct hal_ev_hf_client_inband_settings) },
+ /* HAL_EV_CLIENT_LAST_VOICE_CALL_TAG_NUM */
+ { handle_last_voice_tag_number, true,
+ sizeof(struct hal_ev_hf_client_last_void_call_tag_num) },
+ /* HAL_EV_CLIENT_RING_INDICATION */
+ { handle_ring_indication, false, 0 },
+};
+
+static bt_status_t init(bthf_client_callbacks_t *callbacks)
+{
+ struct hal_cmd_register_module cmd;
+ int ret;
+
+ DBG("");
+
+ if (interface_ready())
+ return BT_STATUS_DONE;
+
+ cbs = callbacks;
+
+ hal_ipc_register(HAL_SERVICE_ID_HANDSFREE_CLIENT, ev_handlers,
+ sizeof(ev_handlers)/sizeof(ev_handlers[0]));
+
+ cmd.service_id = HAL_SERVICE_ID_HANDSFREE_CLIENT;
+ cmd.mode = HAL_MODE_DEFAULT;
+ cmd.max_clients = 1;
+
+ ret = hal_ipc_cmd(HAL_SERVICE_ID_CORE, HAL_OP_REGISTER_MODULE,
+ sizeof(cmd), &cmd, NULL, NULL, NULL);
+ if (ret != BT_STATUS_SUCCESS) {
+ cbs = NULL;
+ hal_ipc_unregister(HAL_SERVICE_ID_HANDSFREE_CLIENT);
+ }
+
+ return ret;
+}
+
+static bt_status_t hf_client_connect(bt_bdaddr_t *bd_addr)
+{
+ struct hal_cmd_hf_client_connect cmd;
+
+ DBG("");
+
+ if (!interface_ready())
+ return BT_STATUS_NOT_READY;
+
+ if (!bd_addr)
+ return BT_STATUS_PARM_INVALID;
+
+ memcpy(cmd.bdaddr, bd_addr, sizeof(cmd.bdaddr));
+
+ return hal_ipc_cmd(HAL_SERVICE_ID_HANDSFREE_CLIENT,
+ HAL_OP_HF_CLIENT_CONNECT, sizeof(cmd), &cmd,
+ NULL, NULL, NULL);
+}
+
+static bt_status_t disconnect(bt_bdaddr_t *bd_addr)
+{
+ struct hal_cmd_hf_client_disconnect cmd;
+
+ DBG("");
+
+ if (!interface_ready())
+ return BT_STATUS_NOT_READY;
+
+ if (!bd_addr)
+ return BT_STATUS_PARM_INVALID;
+
+ memcpy(cmd.bdaddr, bd_addr, sizeof(cmd.bdaddr));
+
+ return hal_ipc_cmd(HAL_SERVICE_ID_HANDSFREE_CLIENT,
+ HAL_OP_HF_CLIENT_DISCONNECT, sizeof(cmd), &cmd,
+ NULL, NULL, NULL);
+}
+
+static bt_status_t connect_audio(bt_bdaddr_t *bd_addr)
+{
+ struct hal_cmd_hf_client_connect_audio cmd;
+
+ DBG("");
+
+ if (!interface_ready())
+ return BT_STATUS_NOT_READY;
+
+ if (!bd_addr)
+ return BT_STATUS_PARM_INVALID;
+
+ memcpy(cmd.bdaddr, bd_addr, sizeof(cmd.bdaddr));
+
+ return hal_ipc_cmd(HAL_SERVICE_ID_HANDSFREE_CLIENT,
+ HAL_OP_HF_CLIENT_CONNECT_AUDIO, sizeof(cmd),
+ &cmd, NULL, NULL, NULL);
+}
+
+static bt_status_t disconnect_audio(bt_bdaddr_t *bd_addr)
+{
+ struct hal_cmd_hf_client_disconnect_audio cmd;
+
+ DBG("");
+
+ if (!interface_ready())
+ return BT_STATUS_NOT_READY;
+
+ if (!bd_addr)
+ return BT_STATUS_PARM_INVALID;
+
+ memcpy(cmd.bdaddr, bd_addr, sizeof(cmd.bdaddr));
+
+ return hal_ipc_cmd(HAL_SERVICE_ID_HANDSFREE_CLIENT,
+ HAL_OP_HF_CLIENT_DISCONNECT_AUDIO, sizeof(cmd),
+ &cmd, NULL, NULL, NULL);
+}
+
+static bt_status_t start_voice_recognition(void)
+{
+ DBG("");
+
+ if (!interface_ready())
+ return BT_STATUS_NOT_READY;
+
+ return hal_ipc_cmd(HAL_SERVICE_ID_HANDSFREE_CLIENT,
+ HAL_OP_HF_CLIENT_START_VR, 0, NULL, NULL, NULL,
+ NULL);
+}
+
+static bt_status_t stop_voice_recognition(void)
+{
+ DBG("");
+
+ if (!interface_ready())
+ return BT_STATUS_NOT_READY;
+
+ return hal_ipc_cmd(HAL_SERVICE_ID_HANDSFREE_CLIENT,
+ HAL_OP_HF_CLIENT_STOP_VR, 0, NULL, NULL, NULL,
+ NULL);
+}
+
+static bt_status_t volume_control(bthf_client_volume_type_t type,
+ int volume)
+{
+ struct hal_cmd_hf_client_volume_control cmd;
+
+ DBG("");
+
+ if (!interface_ready())
+ return BT_STATUS_NOT_READY;
+
+ cmd.type = type;
+ cmd.volume = volume;
+
+ return hal_ipc_cmd(HAL_SERVICE_ID_HANDSFREE_CLIENT,
+ HAL_OP_HF_CLIENT_VOLUME_CONTROL, sizeof(cmd),
+ &cmd, NULL, NULL, NULL);
+}
+
+static bt_status_t dial(const char *number)
+{
+ char buf[IPC_MTU];
+ struct hal_cmd_hf_client_dial *cmd = (void *) buf;
+ size_t len;
+
+ DBG("");
+
+ if (!interface_ready())
+ return BT_STATUS_NOT_READY;
+
+ if (number) {
+ cmd->number_len = strlen(number) + 1;
+ memcpy(cmd->number, number, cmd->number_len);
+ } else {
+ cmd->number_len = 0;
+ }
+
+ len = sizeof(*cmd) + cmd->number_len;
+
+ return hal_ipc_cmd(HAL_SERVICE_ID_HANDSFREE_CLIENT,
+ HAL_OP_HF_CLIENT_DIAL, len, cmd, NULL, NULL,
+ NULL);
+}
+
+static bt_status_t dial_memory(int location)
+{
+ struct hal_cmd_hf_client_dial_memory cmd;
+
+ DBG("");
+
+ if (!interface_ready())
+ return BT_STATUS_NOT_READY;
+
+ cmd.location = location;
+
+ return hal_ipc_cmd(HAL_SERVICE_ID_HANDSFREE_CLIENT,
+ HAL_OP_HF_CLIENT_DIAL_MEMORY,
+ sizeof(cmd), &cmd, NULL, NULL, NULL);
+}
+
+static bt_status_t call_action(bthf_client_call_action_t action, int index)
+{
+ struct hal_cmd_hf_client_call_action cmd;
+
+ DBG("");
+
+ if (!interface_ready())
+ return BT_STATUS_NOT_READY;
+
+ cmd.action = action;
+ cmd.index = index;
+
+ return hal_ipc_cmd(HAL_SERVICE_ID_HANDSFREE_CLIENT,
+ HAL_OP_HF_CLIENT_CALL_ACTION, sizeof(cmd), &cmd,
+ NULL, NULL, NULL);
+}
+
+static bt_status_t query_current_calls(void)
+{
+ DBG("");
+
+ if (!interface_ready())
+ return BT_STATUS_NOT_READY;
+
+ return hal_ipc_cmd(HAL_SERVICE_ID_HANDSFREE_CLIENT,
+ HAL_OP_HF_CLIENT_QUERY_CURRENT_CALLS, 0, NULL,
+ NULL, NULL, NULL);
+}
+
+static bt_status_t query_operator_name(void)
+{
+ DBG("");
+
+ if (!interface_ready())
+ return BT_STATUS_NOT_READY;
+
+ return hal_ipc_cmd(HAL_SERVICE_ID_HANDSFREE_CLIENT,
+ HAL_OP_HF_CLIENT_QUERY_OPERATOR_NAME, 0, NULL,
+ NULL, NULL, NULL);
+}
+
+static bt_status_t retrieve_subsr_info(void)
+{
+ DBG("");
+
+ if (!interface_ready())
+ return BT_STATUS_NOT_READY;
+
+ return hal_ipc_cmd(HAL_SERVICE_ID_HANDSFREE_CLIENT,
+ HAL_OP_HF_CLIENT_RETRIEVE_SUBSCR_INFO, 0, NULL,
+ NULL, NULL, NULL);
+}
+
+static bt_status_t send_dtmf(char tone)
+{
+ struct hal_cmd_hf_client_send_dtmf cmd;
+
+ DBG("");
+
+ if (!interface_ready())
+ return BT_STATUS_NOT_READY;
+
+ cmd.tone = tone;
+
+ return hal_ipc_cmd(HAL_SERVICE_ID_HANDSFREE_CLIENT,
+ HAL_OP_HF_CLIENT_SEND_DTMF, sizeof(cmd), &cmd,
+ NULL, NULL, NULL);
+}
+
+static bt_status_t request_last_voice_tag_number(void)
+{
+ DBG("");
+
+ if (!interface_ready())
+ return BT_STATUS_NOT_READY;
+
+ return hal_ipc_cmd(HAL_SERVICE_ID_HANDSFREE_CLIENT,
+ HAL_OP_HF_CLIENT_GET_LAST_VOICE_TAG_NUM,
+ 0, NULL, NULL, NULL, NULL);
+}
+
+static void cleanup(void)
+{
+ struct hal_cmd_unregister_module cmd;
+
+ DBG("");
+
+ if (!interface_ready())
+ return;
+
+ cmd.service_id = HAL_SERVICE_ID_HANDSFREE_CLIENT;
+
+ hal_ipc_cmd(HAL_SERVICE_ID_CORE, HAL_OP_UNREGISTER_MODULE,
+ sizeof(cmd), &cmd, NULL, NULL, NULL);
+
+ hal_ipc_unregister(HAL_SERVICE_ID_HANDSFREE_CLIENT);
+
+ cbs = NULL;
+}
+
+static bthf_client_interface_t iface = {
+ .size = sizeof(iface),
+ .init = init,
+ .connect = hf_client_connect,
+ .disconnect = disconnect,
+ .connect_audio = connect_audio,
+ .disconnect_audio = disconnect_audio,
+ .start_voice_recognition = start_voice_recognition,
+ .stop_voice_recognition = stop_voice_recognition,
+ .volume_control = volume_control,
+ .dial = dial,
+ .dial_memory = dial_memory,
+ .handle_call_action = call_action,
+ .query_current_calls = query_current_calls,
+ .query_current_operator_name = query_operator_name,
+ .retrieve_subscriber_info = retrieve_subsr_info,
+ .send_dtmf = send_dtmf,
+ .request_last_voice_tag_number = request_last_voice_tag_number,
+ .cleanup = cleanup
+};
+
+bthf_client_interface_t *bt_get_hf_client_interface(void)
+{
+ return &iface;
+}
diff --git a/repo/android/hal-handsfree.c b/repo/android/hal-handsfree.c
new file mode 100644
index 0000000..3847901
--- /dev/null
+++ b/repo/android/hal-handsfree.c
@@ -0,0 +1,892 @@
+/*
+ * Copyright (C) 2014 Intel Corporation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include <stdbool.h>
+#include <stddef.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include <cutils/properties.h>
+
+#include "hal-log.h"
+#include "hal.h"
+#include "hal-msg.h"
+#include "ipc-common.h"
+#include "hal-ipc.h"
+#include "hal-utils.h"
+
+static const bthf_callbacks_t *cbs = NULL;
+
+static bool interface_ready(void)
+{
+ return cbs != NULL;
+}
+
+static void handle_conn_state(void *buf, uint16_t len, int fd)
+{
+ struct hal_ev_handsfree_conn_state *ev = buf;
+
+ if (cbs->connection_state_cb)
+ cbs->connection_state_cb(ev->state,
+ (bt_bdaddr_t *) (ev->bdaddr));
+}
+
+static void handle_audio_state(void *buf, uint16_t len, int fd)
+{
+ struct hal_ev_handsfree_audio_state *ev = buf;
+
+ if (cbs->audio_state_cb)
+ cbs->audio_state_cb(ev->state, (bt_bdaddr_t *) (ev->bdaddr));
+}
+
+static void handle_vr_state(void *buf, uint16_t len, int fd)
+{
+ struct hal_ev_handsfree_vr_state *ev = buf;
+
+ if (cbs->vr_cmd_cb)
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+ cbs->vr_cmd_cb(ev->state, (bt_bdaddr_t *) (ev->bdaddr));
+#else
+ cbs->vr_cmd_cb(ev->state);
+#endif
+}
+
+static void handle_answer(void *buf, uint16_t len, int fd)
+{
+ if (cbs->answer_call_cmd_cb) {
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+ struct hal_ev_handsfree_answer *ev = buf;
+
+ cbs->answer_call_cmd_cb((bt_bdaddr_t *) (ev->bdaddr));
+#else
+ cbs->answer_call_cmd_cb();
+#endif
+ }
+}
+
+static void handle_hangup(void *buf, uint16_t len, int fd)
+{
+ if (cbs->hangup_call_cmd_cb) {
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+ struct hal_ev_handsfree_hangup *ev = buf;
+
+ cbs->hangup_call_cmd_cb((bt_bdaddr_t *) (ev->bdaddr));
+#else
+ cbs->hangup_call_cmd_cb();
+#endif
+ }
+}
+
+static void handle_volume(void *buf, uint16_t len, int fd)
+{
+ struct hal_ev_handsfree_volume *ev = buf;
+
+ if (cbs->volume_cmd_cb)
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+ cbs->volume_cmd_cb(ev->type, ev->volume,
+ (bt_bdaddr_t *) (ev->bdaddr));
+#else
+ cbs->volume_cmd_cb(ev->type, ev->volume);
+#endif
+}
+
+static void handle_dial(void *buf, uint16_t len, int fd)
+{
+ struct hal_ev_handsfree_dial *ev = buf;
+ uint16_t num_len = ev->number_len;
+ char *number = NULL;
+
+ if (len != sizeof(*ev) + num_len ||
+ (num_len != 0 && ev->number[num_len - 1] != '\0')) {
+ error("invalid dial event, aborting");
+ exit(EXIT_FAILURE);
+ }
+
+ if (!cbs->dial_call_cmd_cb)
+ return;
+
+ if (ev->number_len)
+ number = (char *) ev->number;
+
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+ cbs->dial_call_cmd_cb(number, (bt_bdaddr_t *) (ev->bdaddr));
+#else
+ cbs->dial_call_cmd_cb(number);
+#endif
+}
+
+static void handle_dtmf(void *buf, uint16_t len, int fd)
+{
+ struct hal_ev_handsfree_dtmf *ev = buf;
+
+ if (cbs->dtmf_cmd_cb)
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+ cbs->dtmf_cmd_cb(ev->tone, (bt_bdaddr_t *) (ev->bdaddr));
+#else
+ cbs->dtmf_cmd_cb(ev->tone);
+#endif
+}
+
+static void handle_nrec(void *buf, uint16_t len, int fd)
+{
+ struct hal_ev_handsfree_nrec *ev = buf;
+
+ if (cbs->nrec_cmd_cb)
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+ cbs->nrec_cmd_cb(ev->nrec, (bt_bdaddr_t *) (ev->bdaddr));
+#else
+ cbs->nrec_cmd_cb(ev->nrec);
+#endif
+}
+
+static void handle_wbs(void *buf, uint16_t len, int fd)
+{
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+ struct hal_ev_handsfree_wbs *ev = buf;
+
+ if (cbs->wbs_cb)
+ cbs->wbs_cb(ev->wbs, (bt_bdaddr_t *) (ev->bdaddr));
+#endif
+}
+
+static void handle_chld(void *buf, uint16_t len, int fd)
+{
+ struct hal_ev_handsfree_chld *ev = buf;
+
+ if (cbs->chld_cmd_cb)
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+ cbs->chld_cmd_cb(ev->chld, (bt_bdaddr_t *) (ev->bdaddr));
+#else
+ cbs->chld_cmd_cb(ev->chld);
+#endif
+}
+
+static void handle_cnum(void *buf, uint16_t len, int fd)
+{
+ if (cbs->cnum_cmd_cb) {
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+ struct hal_ev_handsfree_cnum *ev = buf;
+
+ cbs->cnum_cmd_cb((bt_bdaddr_t *) (ev->bdaddr));
+#else
+ cbs->cnum_cmd_cb(NULL);
+#endif
+ }
+}
+
+static void handle_cind(void *buf, uint16_t len, int fd)
+{
+ if (cbs->cind_cmd_cb) {
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+ struct hal_ev_handsfree_cind *ev = buf;
+
+ cbs->cind_cmd_cb((bt_bdaddr_t *) (ev->bdaddr));
+#else
+ cbs->cind_cmd_cb();
+#endif
+ }
+}
+
+static void handle_cops(void *buf, uint16_t len, int fd)
+{
+ if (cbs->cops_cmd_cb) {
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+ struct hal_ev_handsfree_cops *ev = buf;
+
+ cbs->cops_cmd_cb((bt_bdaddr_t *) (ev->bdaddr));
+#else
+ cbs->cops_cmd_cb();
+#endif
+ }
+}
+
+static void handle_clcc(void *buf, uint16_t len, int fd)
+{
+ if (cbs->clcc_cmd_cb) {
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+ struct hal_ev_handsfree_clcc *ev = buf;
+
+ cbs->clcc_cmd_cb((bt_bdaddr_t *) (ev->bdaddr));
+#else
+ cbs->clcc_cmd_cb();
+#endif
+ }
+}
+
+static void handle_unknown_at(void *buf, uint16_t len, int fd)
+{
+ struct hal_ev_handsfree_unknown_at *ev = buf;
+
+ if (len != sizeof(*ev) + ev->len ||
+ (ev->len != 0 && ev->buf[ev->len - 1] != '\0')) {
+ error("invalid unknown command event, aborting");
+ exit(EXIT_FAILURE);
+ }
+
+ if (cbs->unknown_at_cmd_cb)
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+ cbs->unknown_at_cmd_cb((char *) ev->buf,
+ (bt_bdaddr_t *) (ev->bdaddr));
+#else
+ cbs->unknown_at_cmd_cb((char *) ev->buf);
+#endif
+}
+
+static void handle_hsp_key_press(void *buf, uint16_t len, int fd)
+{
+ if (cbs->key_pressed_cmd_cb) {
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+ struct hal_ev_handsfree_hsp_key_press *ev = buf;
+
+ cbs->key_pressed_cmd_cb((bt_bdaddr_t *) (ev->bdaddr));
+#else
+ cbs->key_pressed_cmd_cb();
+#endif
+ }
+}
+
+/*
+ * handlers will be called from notification thread context,
+ * index in table equals to 'opcode - HAL_MINIMUM_EVENT'
+ */
+static const struct hal_ipc_handler ev_handlers[] = {
+ /* HAL_EV_HANDSFREE_CONN_STATE */
+ { handle_conn_state, false,
+ sizeof(struct hal_ev_handsfree_conn_state) },
+ /* HAL_EV_HANDSFREE_AUDIO_STATE */
+ { handle_audio_state, false,
+ sizeof(struct hal_ev_handsfree_audio_state) },
+ /* HAL_EV_HANDSFREE_VR */
+ { handle_vr_state, false, sizeof(struct hal_ev_handsfree_vr_state) },
+ /* HAL_EV_HANDSFREE_ANSWER */
+ { handle_answer, false, sizeof(struct hal_ev_handsfree_answer) },
+ /* HAL_EV_HANDSFREE_HANGUP */
+ { handle_hangup, false, sizeof(struct hal_ev_handsfree_hangup) },
+ /* HAL_EV_HANDSFREE_VOLUME */
+ { handle_volume, false, sizeof(struct hal_ev_handsfree_volume) },
+ /* HAL_EV_HANDSFREE_DIAL */
+ { handle_dial, true, sizeof(struct hal_ev_handsfree_dial) },
+ /* HAL_EV_HANDSFREE_DTMF */
+ { handle_dtmf, false, sizeof(struct hal_ev_handsfree_dtmf) },
+ /* HAL_EV_HANDSFREE_NREC */
+ { handle_nrec, false, sizeof(struct hal_ev_handsfree_nrec) },
+ /* HAL_EV_HANDSFREE_CHLD */
+ { handle_chld, false, sizeof(struct hal_ev_handsfree_chld) },
+ /* HAL_EV_HANDSFREE_CNUM */
+ { handle_cnum, false, sizeof(struct hal_ev_handsfree_cnum) },
+ /* HAL_EV_HANDSFREE_CIND */
+ { handle_cind, false, sizeof(struct hal_ev_handsfree_cind) },
+ /* HAL_EV_HANDSFREE_COPS */
+ { handle_cops, false, sizeof(struct hal_ev_handsfree_cops) },
+ /* HAL_EV_HANDSFREE_CLCC */
+ { handle_clcc, false, sizeof(struct hal_ev_handsfree_clcc) },
+ /* HAL_EV_HANDSFREE_UNKNOWN_AT */
+ { handle_unknown_at, true, sizeof(struct hal_ev_handsfree_unknown_at) },
+ /* HAL_EV_HANDSFREE_HSP_KEY_PRESS */
+ { handle_hsp_key_press, false,
+ sizeof(struct hal_ev_handsfree_hsp_key_press) },
+ /* HAL_EV_HANDSFREE_WBS */
+ { handle_wbs, false, sizeof(struct hal_ev_handsfree_wbs) },
+};
+
+static uint8_t get_mode(void)
+{
+ char value[PROPERTY_VALUE_MAX];
+
+ if (get_config("handsfree", value, NULL) > 0) {
+ if (!strcasecmp(value, "hfp"))
+ return HAL_MODE_HANDSFREE_HFP;
+
+ if (!strcasecmp(value, "hfp_wbs"))
+ return HAL_MODE_HANDSFREE_HFP_WBS;
+ }
+
+ return HAL_MODE_HANDSFREE_HSP_ONLY;
+}
+
+static bt_status_t init_real(bthf_callbacks_t *callbacks, int max_hf_clients)
+{
+ struct hal_cmd_register_module cmd;
+ int ret;
+
+ DBG("");
+
+ if (interface_ready())
+ return BT_STATUS_DONE;
+
+ cbs = callbacks;
+
+ hal_ipc_register(HAL_SERVICE_ID_HANDSFREE, ev_handlers,
+ sizeof(ev_handlers)/sizeof(ev_handlers[0]));
+
+ cmd.service_id = HAL_SERVICE_ID_HANDSFREE;
+ cmd.mode = get_mode();
+ cmd.max_clients = max_hf_clients;
+
+ ret = hal_ipc_cmd(HAL_SERVICE_ID_CORE, HAL_OP_REGISTER_MODULE,
+ sizeof(cmd), &cmd, NULL, NULL, NULL);
+
+ if (ret != BT_STATUS_SUCCESS) {
+ cbs = NULL;
+ hal_ipc_unregister(HAL_SERVICE_ID_HANDSFREE);
+ }
+
+ return ret;
+}
+
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+static bt_status_t init(bthf_callbacks_t *callbacks, int max_hf_clients)
+{
+ return init_real(callbacks, max_hf_clients);
+}
+#else
+static bt_status_t init(bthf_callbacks_t *callbacks)
+{
+ return init_real(callbacks, 1);
+}
+#endif
+
+static bt_status_t handsfree_connect(bt_bdaddr_t *bd_addr)
+{
+ struct hal_cmd_handsfree_connect cmd;
+
+ DBG("");
+
+ if (!interface_ready())
+ return BT_STATUS_NOT_READY;
+
+ if (!bd_addr)
+ return BT_STATUS_PARM_INVALID;
+
+ memcpy(cmd.bdaddr, bd_addr, sizeof(cmd.bdaddr));
+
+ return hal_ipc_cmd(HAL_SERVICE_ID_HANDSFREE, HAL_OP_HANDSFREE_CONNECT,
+ sizeof(cmd), &cmd, NULL, NULL, NULL);
+}
+
+static bt_status_t disconnect(bt_bdaddr_t *bd_addr)
+{
+ struct hal_cmd_handsfree_disconnect cmd;
+
+ DBG("");
+
+ if (!interface_ready())
+ return BT_STATUS_NOT_READY;
+
+ if (!bd_addr)
+ return BT_STATUS_PARM_INVALID;
+
+ memcpy(cmd.bdaddr, bd_addr, sizeof(cmd.bdaddr));
+
+ return hal_ipc_cmd(HAL_SERVICE_ID_HANDSFREE,
+ HAL_OP_HANDSFREE_DISCONNECT, sizeof(cmd), &cmd,
+ NULL, NULL, NULL);
+}
+
+static bt_status_t connect_audio(bt_bdaddr_t *bd_addr)
+{
+ struct hal_cmd_handsfree_connect_audio cmd;
+
+ DBG("");
+
+ if (!interface_ready())
+ return BT_STATUS_NOT_READY;
+
+ if (!bd_addr)
+ return BT_STATUS_PARM_INVALID;
+
+ memcpy(cmd.bdaddr, bd_addr, sizeof(cmd.bdaddr));
+
+ return hal_ipc_cmd(HAL_SERVICE_ID_HANDSFREE,
+ HAL_OP_HANDSFREE_CONNECT_AUDIO, sizeof(cmd),
+ &cmd, NULL, NULL, NULL);
+}
+
+static bt_status_t disconnect_audio(bt_bdaddr_t *bd_addr)
+{
+ struct hal_cmd_handsfree_disconnect_audio cmd;
+
+ DBG("");
+
+ if (!interface_ready())
+ return BT_STATUS_NOT_READY;
+
+ if (!bd_addr)
+ return BT_STATUS_PARM_INVALID;
+
+ memcpy(cmd.bdaddr, bd_addr, sizeof(cmd.bdaddr));
+
+ return hal_ipc_cmd(HAL_SERVICE_ID_HANDSFREE,
+ HAL_OP_HANDSFREE_DISCONNECT_AUDIO, sizeof(cmd),
+ &cmd, NULL, NULL, NULL);
+}
+
+static bt_status_t start_voice_recognition_real(bt_bdaddr_t *bd_addr)
+{
+ struct hal_cmd_handsfree_start_vr cmd;
+
+ DBG("");
+
+ if (!interface_ready())
+ return BT_STATUS_NOT_READY;
+
+ memset(&cmd, 0, sizeof(cmd));
+
+ if (bd_addr)
+ memcpy(cmd.bdaddr, bd_addr, sizeof(cmd.bdaddr));
+
+ return hal_ipc_cmd(HAL_SERVICE_ID_HANDSFREE, HAL_OP_HANDSFREE_START_VR,
+ sizeof(cmd), &cmd, NULL, NULL, NULL);
+}
+
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+static bt_status_t start_voice_recognition(bt_bdaddr_t *bd_addr)
+{
+ return start_voice_recognition_real(bd_addr);
+}
+#else
+static bt_status_t start_voice_recognition(void)
+{
+ return start_voice_recognition_real(NULL);
+}
+#endif
+
+static bt_status_t stop_voice_recognition_real(bt_bdaddr_t *bd_addr)
+{
+ struct hal_cmd_handsfree_stop_vr cmd;
+
+ DBG("");
+
+ if (!interface_ready())
+ return BT_STATUS_NOT_READY;
+
+ memset(&cmd, 0, sizeof(cmd));
+
+ if (bd_addr)
+ memcpy(cmd.bdaddr, bd_addr, sizeof(cmd.bdaddr));
+
+ return hal_ipc_cmd(HAL_SERVICE_ID_HANDSFREE, HAL_OP_HANDSFREE_STOP_VR,
+ sizeof(cmd), &cmd, NULL, NULL, NULL);
+}
+
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+static bt_status_t stop_voice_recognition(bt_bdaddr_t *bd_addr)
+{
+ return stop_voice_recognition_real(bd_addr);
+}
+#else
+static bt_status_t stop_voice_recognition(void)
+{
+ return stop_voice_recognition_real(NULL);
+}
+#endif
+
+static bt_status_t volume_control_real(bthf_volume_type_t type, int volume,
+ bt_bdaddr_t *bd_addr)
+{
+ struct hal_cmd_handsfree_volume_control cmd;
+
+ DBG("");
+
+ if (!interface_ready())
+ return BT_STATUS_NOT_READY;
+
+ memset(&cmd, 0, sizeof(cmd));
+
+ cmd.type = type;
+ cmd.volume = volume;
+
+ if (bd_addr)
+ memcpy(cmd.bdaddr, bd_addr, sizeof(cmd.bdaddr));
+
+ return hal_ipc_cmd(HAL_SERVICE_ID_HANDSFREE,
+ HAL_OP_HANDSFREE_VOLUME_CONTROL, sizeof(cmd),
+ &cmd, NULL, NULL, NULL);
+}
+
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+static bt_status_t volume_control(bthf_volume_type_t type, int volume,
+ bt_bdaddr_t *bd_addr)
+{
+ return volume_control_real(type, volume, bd_addr);
+}
+#else
+static bt_status_t volume_control(bthf_volume_type_t type, int volume)
+{
+ return volume_control_real(type, volume, NULL);
+}
+#endif
+
+static bt_status_t device_status_notification(bthf_network_state_t state,
+ bthf_service_type_t type,
+ int signal, int battery)
+{
+ struct hal_cmd_handsfree_device_status_notif cmd;
+
+ DBG("");
+
+ if (!interface_ready())
+ return BT_STATUS_NOT_READY;
+
+ cmd.state = state;
+ cmd.type = type;
+ cmd.signal = signal;
+ cmd.battery = battery;
+
+ return hal_ipc_cmd(HAL_SERVICE_ID_HANDSFREE,
+ HAL_OP_HANDSFREE_DEVICE_STATUS_NOTIF,
+ sizeof(cmd), &cmd, NULL, NULL, NULL);
+}
+
+static bt_status_t cops_response_real(const char *cops, bt_bdaddr_t *bd_addr)
+{
+ char buf[IPC_MTU];
+ struct hal_cmd_handsfree_cops_response *cmd = (void *) buf;
+ size_t len;
+
+ DBG("");
+
+ if (!interface_ready())
+ return BT_STATUS_NOT_READY;
+
+ if (!cops)
+ return BT_STATUS_PARM_INVALID;
+
+ memset(cmd, 0, sizeof(*cmd));
+
+ if (bd_addr)
+ memcpy(cmd->bdaddr, bd_addr, sizeof(cmd->bdaddr));
+
+ /* Size of cmd.buf */
+ cmd->len = strlen(cops) + 1;
+ memcpy(cmd->buf, cops, cmd->len);
+
+ len = sizeof(*cmd) + cmd->len;
+
+ return hal_ipc_cmd(HAL_SERVICE_ID_HANDSFREE,
+ HAL_OP_HANDSFREE_COPS_RESPONSE,
+ len, cmd, NULL, NULL, NULL);
+}
+
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+static bt_status_t cops_response(const char *cops, bt_bdaddr_t *bd_addr)
+{
+ return cops_response_real(cops, bd_addr);
+}
+#else
+static bt_status_t cops_response(const char *cops)
+{
+ return cops_response_real(cops, NULL);
+}
+#endif
+
+static bt_status_t cind_response_real(int svc, int num_active, int num_held,
+ bthf_call_state_t state, int signal,
+ int roam, int batt_chg,
+ bt_bdaddr_t *bd_addr)
+{
+ struct hal_cmd_handsfree_cind_response cmd;
+
+ DBG("");
+
+ if (!interface_ready())
+ return BT_STATUS_NOT_READY;
+
+ memset(&cmd, 0, sizeof(cmd));
+
+ if (bd_addr)
+ memcpy(cmd.bdaddr, bd_addr, sizeof(cmd.bdaddr));
+
+ cmd.svc = svc;
+ cmd.num_active = num_active;
+ cmd.num_held = num_held;
+ cmd.state = state;
+ cmd.signal = signal;
+ cmd.roam = roam;
+ cmd.batt_chg = batt_chg;
+
+ return hal_ipc_cmd(HAL_SERVICE_ID_HANDSFREE,
+ HAL_OP_HANDSFREE_CIND_RESPONSE,
+ sizeof(cmd), &cmd, NULL, NULL, NULL);
+}
+
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+static bt_status_t cind_response(int svc, int num_active, int num_held,
+ bthf_call_state_t state, int signal,
+ int roam, int batt_chg,
+ bt_bdaddr_t *bd_addr)
+{
+ return cind_response_real(svc, num_active, num_held, state, signal,
+ roam, batt_chg, bd_addr);
+}
+#else
+static bt_status_t cind_response(int svc, int num_active, int num_held,
+ bthf_call_state_t state, int signal,
+ int roam, int batt_chg)
+{
+ return cind_response_real(svc, num_active, num_held, state, signal,
+ roam, batt_chg, NULL);
+}
+#endif
+
+static bt_status_t formatted_at_response_real(const char *rsp,
+ bt_bdaddr_t *bd_addr)
+{
+ char buf[IPC_MTU];
+ struct hal_cmd_handsfree_formatted_at_response *cmd = (void *) buf;
+ size_t len;
+
+ DBG("");
+
+ if (!interface_ready())
+ return BT_STATUS_NOT_READY;
+
+ if (!rsp)
+ return BT_STATUS_PARM_INVALID;
+
+ memset(cmd, 0, sizeof(*cmd));
+
+ if (bd_addr)
+ memcpy(cmd->bdaddr, bd_addr, sizeof(cmd->bdaddr));
+
+ cmd->len = strlen(rsp) + 1;
+ memcpy(cmd->buf, rsp, cmd->len);
+
+ len = sizeof(*cmd) + cmd->len;
+
+ return hal_ipc_cmd(HAL_SERVICE_ID_HANDSFREE,
+ HAL_OP_HANDSFREE_FORMATTED_AT_RESPONSE,
+ len, cmd, NULL, NULL, NULL);
+}
+
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+static bt_status_t formatted_at_response(const char *rsp, bt_bdaddr_t *bd_addr)
+{
+ return formatted_at_response_real(rsp, bd_addr);
+}
+#else
+static bt_status_t formatted_at_response(const char *rsp)
+{
+ return formatted_at_response_real(rsp, NULL);
+}
+#endif
+
+static bt_status_t at_response_real(bthf_at_response_t response, int error,
+ bt_bdaddr_t *bd_addr)
+{
+ struct hal_cmd_handsfree_at_response cmd;
+
+ DBG("");
+
+ if (!interface_ready())
+ return BT_STATUS_NOT_READY;
+
+ if (bd_addr)
+ memcpy(cmd.bdaddr, bd_addr, sizeof(cmd.bdaddr));
+
+ memset(&cmd, 0, sizeof(cmd));
+
+ cmd.response = response;
+ cmd.error = error;
+
+ return hal_ipc_cmd(HAL_SERVICE_ID_HANDSFREE,
+ HAL_OP_HANDSFREE_AT_RESPONSE,
+ sizeof(cmd), &cmd, NULL, NULL, NULL);
+}
+
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+static bt_status_t at_response(bthf_at_response_t response, int error,
+ bt_bdaddr_t *bd_addr)
+{
+ return at_response_real(response, error, bd_addr);
+}
+#else
+static bt_status_t at_response(bthf_at_response_t response, int error)
+{
+ return at_response_real(response, error, NULL);
+}
+#endif
+
+static bt_status_t clcc_response_real(int index, bthf_call_direction_t dir,
+ bthf_call_state_t state,
+ bthf_call_mode_t mode,
+ bthf_call_mpty_type_t mpty,
+ const char *number,
+ bthf_call_addrtype_t type,
+ bt_bdaddr_t *bd_addr)
+{
+ char buf[IPC_MTU];
+ struct hal_cmd_handsfree_clcc_response *cmd = (void *) buf;
+ size_t len;
+
+ DBG("");
+
+ if (!interface_ready())
+ return BT_STATUS_NOT_READY;
+
+ memset(cmd, 0, sizeof(*cmd));
+
+ if (bd_addr)
+ memcpy(cmd->bdaddr, bd_addr, sizeof(cmd->bdaddr));
+
+ cmd->index = index;
+ cmd->dir = dir;
+ cmd->state = state;
+ cmd->mode = mode;
+ cmd->mpty = mpty;
+ cmd->type = type;
+
+ if (number) {
+ cmd->number_len = strlen(number) + 1;
+ memcpy(cmd->number, number, cmd->number_len);
+ } else {
+ cmd->number_len = 0;
+ }
+
+ len = sizeof(*cmd) + cmd->number_len;
+
+ return hal_ipc_cmd(HAL_SERVICE_ID_HANDSFREE,
+ HAL_OP_HANDSFREE_CLCC_RESPONSE,
+ len, cmd, NULL, NULL, NULL);
+}
+
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+static bt_status_t clcc_response(int index, bthf_call_direction_t dir,
+ bthf_call_state_t state,
+ bthf_call_mode_t mode,
+ bthf_call_mpty_type_t mpty,
+ const char *number,
+ bthf_call_addrtype_t type,
+ bt_bdaddr_t *bd_addr)
+{
+ return clcc_response_real(index, dir, state, mode, mpty, number, type,
+ bd_addr);
+}
+#else
+static bt_status_t clcc_response(int index, bthf_call_direction_t dir,
+ bthf_call_state_t state,
+ bthf_call_mode_t mode,
+ bthf_call_mpty_type_t mpty,
+ const char *number,
+ bthf_call_addrtype_t type)
+{
+ return clcc_response_real(index, dir, state, mode, mpty, number, type,
+ NULL);
+}
+#endif
+
+static bt_status_t phone_state_change(int num_active, int num_held,
+ bthf_call_state_t state,
+ const char *number,
+ bthf_call_addrtype_t type)
+{
+ char buf[IPC_MTU];
+ struct hal_cmd_handsfree_phone_state_change *cmd = (void *) buf;
+ size_t len;
+
+ DBG("");
+
+ if (!interface_ready())
+ return BT_STATUS_NOT_READY;
+
+ cmd->num_active = num_active;
+ cmd->num_held = num_held;
+ cmd->state = state;
+ cmd->type = type;
+
+ if (number) {
+ cmd->number_len = strlen(number) + 1;
+ memcpy(cmd->number, number, cmd->number_len);
+ } else {
+ cmd->number_len = 0;
+ }
+
+ len = sizeof(*cmd) + cmd->number_len;
+
+ return hal_ipc_cmd(HAL_SERVICE_ID_HANDSFREE,
+ HAL_OP_HANDSFREE_PHONE_STATE_CHANGE,
+ len, cmd, NULL, NULL, NULL);
+}
+
+static void cleanup(void)
+{
+ struct hal_cmd_unregister_module cmd;
+
+ DBG("");
+
+ if (!interface_ready())
+ return;
+
+ cmd.service_id = HAL_SERVICE_ID_HANDSFREE;
+
+ hal_ipc_cmd(HAL_SERVICE_ID_CORE, HAL_OP_UNREGISTER_MODULE,
+ sizeof(cmd), &cmd, NULL, NULL, NULL);
+
+ hal_ipc_unregister(HAL_SERVICE_ID_HANDSFREE);
+
+ cbs = NULL;
+}
+
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+static bt_status_t configure_wbs(bt_bdaddr_t *bd_addr, bthf_wbs_config_t config)
+{
+ struct hal_cmd_handsfree_configure_wbs cmd;
+
+ DBG("%u", config);
+
+ if (!interface_ready())
+ return BT_STATUS_NOT_READY;
+
+ if (!bd_addr)
+ return BT_STATUS_PARM_INVALID;
+
+ memcpy(cmd.bdaddr, bd_addr, sizeof(cmd.bdaddr));
+ cmd.config = config;
+
+ return hal_ipc_cmd(HAL_SERVICE_ID_HANDSFREE,
+ HAL_OP_HANDSFREE_CONFIGURE_WBS,
+ sizeof(cmd), &cmd, NULL, NULL, NULL);
+}
+#endif
+
+static bthf_interface_t iface = {
+ .size = sizeof(iface),
+ .init = init,
+ .connect = handsfree_connect,
+ .disconnect = disconnect,
+ .connect_audio = connect_audio,
+ .disconnect_audio = disconnect_audio,
+ .start_voice_recognition = start_voice_recognition,
+ .stop_voice_recognition = stop_voice_recognition,
+ .volume_control = volume_control,
+ .device_status_notification = device_status_notification,
+ .cops_response = cops_response,
+ .cind_response = cind_response,
+ .formatted_at_response = formatted_at_response,
+ .at_response = at_response,
+ .clcc_response = clcc_response,
+ .phone_state_change = phone_state_change,
+ .cleanup = cleanup,
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+ .configure_wbs = configure_wbs,
+#endif
+};
+
+bthf_interface_t *bt_get_handsfree_interface(void)
+{
+ return &iface;
+}
diff --git a/repo/android/hal-health.c b/repo/android/hal-health.c
new file mode 100644
index 0000000..5d5b111
--- /dev/null
+++ b/repo/android/hal-health.c
@@ -0,0 +1,297 @@
+/*
+ * Copyright (C) 2014 Intel Corporation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include <stdbool.h>
+#include <stddef.h>
+#include <string.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <errno.h>
+
+#include "hal-log.h"
+#include "hal.h"
+#include "hal-msg.h"
+#include "ipc-common.h"
+#include "hal-ipc.h"
+
+static const bthl_callbacks_t *cbacks = NULL;
+
+static bool interface_ready(void)
+{
+ return cbacks != NULL;
+}
+
+static void handle_app_registration_state(void *buf, uint16_t len, int fd)
+{
+ struct hal_ev_health_app_reg_state *ev = buf;
+
+ if (cbacks->app_reg_state_cb)
+ cbacks->app_reg_state_cb(ev->id, ev->state);
+}
+
+static void handle_channel_state(void *buf, uint16_t len, int fd)
+{
+ struct hal_ev_health_channel_state *ev = buf;
+ int flags;
+
+ if (fd < 0)
+ goto end;
+
+ flags = fcntl(fd, F_GETFL, 0);
+ if (flags < 0) {
+ error("health: fcntl GETFL error: %s", strerror(errno));
+ return;
+ }
+
+ /* Clean O_NONBLOCK fd flag as Android Java layer expects */
+ if (fcntl(fd, F_SETFL, flags & ~O_NONBLOCK) < 0) {
+ error("health: fcntl SETFL error: %s", strerror(errno));
+ return;
+ }
+
+end:
+ if (cbacks->channel_state_cb)
+ cbacks->channel_state_cb(ev->app_id, (bt_bdaddr_t *) ev->bdaddr,
+ ev->mdep_index, ev->channel_id,
+ ev->channel_state, fd);
+}
+
+/*
+ * handlers will be called from notification thread context,
+ * index in table equals to 'opcode - HAL_MINIMUM_EVENT'
+ */
+static const struct hal_ipc_handler ev_handlers[] = {
+ /* HAL_EV_HEALTH_APP_REG_STATE */
+ { handle_app_registration_state, false,
+ sizeof(struct hal_ev_health_app_reg_state) },
+ /* HAL_EV_HEALTH_CHANNEL_STATE */
+ { handle_channel_state, false,
+ sizeof(struct hal_ev_health_channel_state) },
+};
+
+static bt_status_t register_application(bthl_reg_param_t *reg, int *app_id)
+{
+ uint8_t buf[IPC_MTU];
+ struct hal_cmd_health_reg_app *cmd = (void *) buf;
+ struct hal_rsp_health_reg_app rsp;
+ size_t rsp_len = sizeof(rsp);
+ bt_status_t status;
+ uint16_t off, len;
+ int i;
+
+ DBG("");
+
+ if (!interface_ready())
+ return BT_STATUS_NOT_READY;
+
+ if (!reg || !app_id || !reg->application_name)
+ return BT_STATUS_PARM_INVALID;
+
+ *app_id = -1;
+ memset(buf, 0, IPC_MTU);
+
+ cmd->num_of_mdep = reg->number_of_mdeps;
+
+ off = 0;
+ cmd->app_name_off = off;
+ len = strlen(reg->application_name) + 1;
+ memcpy(cmd->data, reg->application_name, len);
+ off += len;
+
+ cmd->provider_name_off = off;
+ if (reg->provider_name) {
+ len = strlen(reg->provider_name) + 1;
+ memcpy(cmd->data + off, reg->provider_name, len);
+ off += len;
+ }
+
+ cmd->service_name_off = off;
+ if (reg->srv_name) {
+ len = strlen(reg->srv_name) + 1;
+ memcpy(cmd->data + off, reg->srv_name, len);
+ off += len;
+ }
+
+ cmd->service_descr_off = off;
+ if (reg->srv_desp) {
+ len = strlen(reg->srv_desp) + 1;
+ memcpy(cmd->data + off, reg->srv_desp, len);
+ off += len;
+ }
+
+ cmd->len = off;
+ status = hal_ipc_cmd(HAL_SERVICE_ID_HEALTH, HAL_OP_HEALTH_REG_APP,
+ sizeof(*cmd) + cmd->len, buf,
+ &rsp_len, &rsp, NULL);
+ if (status != BT_STATUS_SUCCESS)
+ return status;
+
+ for (i = 0; i < reg->number_of_mdeps; i++) {
+ struct hal_cmd_health_mdep *mdep = (void *) buf;
+
+ memset(buf, 0, IPC_MTU);
+ mdep->app_id = rsp.app_id;
+ mdep->role = reg->mdep_cfg[i].mdep_role;
+ mdep->data_type = reg->mdep_cfg[i].data_type;
+ mdep->channel_type = reg->mdep_cfg[i].channel_type;
+
+ if (reg->mdep_cfg[i].mdep_description) {
+ mdep->descr_len =
+ strlen(reg->mdep_cfg[i].mdep_description) + 1;
+ memcpy(mdep->descr, reg->mdep_cfg[i].mdep_description,
+ mdep->descr_len);
+ }
+
+ status = hal_ipc_cmd(HAL_SERVICE_ID_HEALTH, HAL_OP_HEALTH_MDEP,
+ sizeof(*mdep) + mdep->descr_len,
+ buf, NULL, NULL, NULL);
+
+ if (status != BT_STATUS_SUCCESS)
+ return status;
+ }
+
+ *app_id = rsp.app_id;
+
+ return status;
+}
+
+static bt_status_t unregister_application(int app_id)
+{
+ struct hal_cmd_health_unreg_app cmd;
+
+ DBG("");
+
+ if (!interface_ready())
+ return BT_STATUS_NOT_READY;
+
+ cmd.app_id = app_id;
+
+ return hal_ipc_cmd(HAL_SERVICE_ID_HEALTH, HAL_OP_HEALTH_UNREG_APP,
+ sizeof(cmd), &cmd, NULL, NULL, NULL);
+}
+
+static bt_status_t connect_channel(int app_id, bt_bdaddr_t *bd_addr,
+ int mdep_cfg_index, int *channel_id)
+{
+ struct hal_cmd_health_connect_channel cmd;
+ struct hal_rsp_health_connect_channel rsp;
+ size_t len = sizeof(rsp);
+ bt_status_t status;
+
+ DBG("");
+
+ if (!interface_ready())
+ return BT_STATUS_NOT_READY;
+
+ if (!bd_addr || !channel_id)
+ return BT_STATUS_PARM_INVALID;
+
+ *channel_id = -1;
+ cmd.app_id = app_id;
+ cmd.mdep_index = mdep_cfg_index;
+ memcpy(cmd.bdaddr, bd_addr, sizeof(cmd.bdaddr));
+
+ status = hal_ipc_cmd(HAL_SERVICE_ID_HEALTH,
+ HAL_OP_HEALTH_CONNECT_CHANNEL,
+ sizeof(cmd), &cmd, &len, &rsp, NULL);
+
+ if (status == BT_STATUS_SUCCESS)
+ *channel_id = rsp.channel_id;
+
+ return status;
+}
+
+static bt_status_t destroy_channel(int channel_id)
+{
+ struct hal_cmd_health_destroy_channel cmd;
+
+ DBG("");
+
+ if (!interface_ready())
+ return BT_STATUS_NOT_READY;
+
+ cmd.channel_id = channel_id;
+
+ return hal_ipc_cmd(HAL_SERVICE_ID_HEALTH, HAL_OP_HEALTH_DESTROY_CHANNEL,
+ sizeof(cmd), &cmd, NULL, NULL, NULL);
+}
+
+static bt_status_t init(bthl_callbacks_t *callbacks)
+{
+ struct hal_cmd_register_module cmd;
+ int ret;
+
+ DBG("");
+
+ if (interface_ready())
+ return BT_STATUS_DONE;
+
+ /* store reference to user callbacks */
+ cbacks = callbacks;
+
+ hal_ipc_register(HAL_SERVICE_ID_HEALTH, ev_handlers,
+ sizeof(ev_handlers)/sizeof(ev_handlers[0]));
+
+ cmd.service_id = HAL_SERVICE_ID_HEALTH;
+ cmd.mode = HAL_MODE_DEFAULT;
+ cmd.max_clients = 1;
+
+ ret = hal_ipc_cmd(HAL_SERVICE_ID_CORE, HAL_OP_REGISTER_MODULE,
+ sizeof(cmd), &cmd, NULL, NULL, NULL);
+
+ if (ret != BT_STATUS_SUCCESS) {
+ cbacks = NULL;
+ hal_ipc_unregister(HAL_SERVICE_ID_HEALTH);
+ }
+
+ return ret;
+}
+
+static void cleanup(void)
+{
+ struct hal_cmd_unregister_module cmd;
+
+ DBG("");
+
+ if (!interface_ready())
+ return;
+
+ cmd.service_id = HAL_SERVICE_ID_HEALTH;
+
+ hal_ipc_cmd(HAL_SERVICE_ID_CORE, HAL_OP_UNREGISTER_MODULE,
+ sizeof(cmd), &cmd, NULL, NULL, NULL);
+
+ hal_ipc_unregister(HAL_SERVICE_ID_HEALTH);
+
+ cbacks = NULL;
+}
+
+static bthl_interface_t health_if = {
+ .size = sizeof(health_if),
+ .init = init,
+ .register_application = register_application,
+ .unregister_application = unregister_application,
+ .connect_channel = connect_channel,
+ .destroy_channel = destroy_channel,
+ .cleanup = cleanup
+};
+
+bthl_interface_t *bt_get_health_interface(void)
+{
+ return &health_if;
+}
diff --git a/repo/android/hal-hidhost.c b/repo/android/hal-hidhost.c
new file mode 100644
index 0000000..1a60326
--- /dev/null
+++ b/repo/android/hal-hidhost.c
@@ -0,0 +1,404 @@
+/*
+ * Copyright (C) 2013 Intel Corporation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include <stdbool.h>
+#include <stddef.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include "hal-log.h"
+#include "hal.h"
+#include "hal-msg.h"
+#include "ipc-common.h"
+#include "hal-ipc.h"
+
+static const bthh_callbacks_t *cbacks;
+
+static bool interface_ready(void)
+{
+ return cbacks != NULL;
+}
+
+static void handle_conn_state(void *buf, uint16_t len, int fd)
+{
+ struct hal_ev_hidhost_conn_state *ev = buf;
+
+ if (cbacks->connection_state_cb)
+ cbacks->connection_state_cb((bt_bdaddr_t *) ev->bdaddr,
+ ev->state);
+}
+
+static void handle_info(void *buf, uint16_t len, int fd)
+{
+ struct hal_ev_hidhost_info *ev = buf;
+ bthh_hid_info_t info;
+
+ info.attr_mask = ev->attr;
+ info.sub_class = ev->subclass;
+ info.app_id = ev->app_id;
+ info.vendor_id = ev->vendor;
+ info.product_id = ev->product;
+ info.version = ev->version;
+ info.ctry_code = ev->country;
+ info.dl_len = ev->descr_len;
+ memcpy(info.dsc_list, ev->descr, info.dl_len);
+
+ if (cbacks->hid_info_cb)
+ cbacks->hid_info_cb((bt_bdaddr_t *) ev->bdaddr, info);
+}
+
+static void handle_proto_mode(void *buf, uint16_t len, int fd)
+{
+ struct hal_ev_hidhost_proto_mode *ev = buf;
+
+ if (cbacks->protocol_mode_cb)
+ cbacks->protocol_mode_cb((bt_bdaddr_t *) ev->bdaddr,
+ ev->status, ev->mode);
+}
+
+static void handle_idle_time(void *buf, uint16_t len, int fd)
+{
+ struct hal_ev_hidhost_idle_time *ev = buf;
+
+ if (cbacks->idle_time_cb)
+ cbacks->idle_time_cb((bt_bdaddr_t *) ev->bdaddr, ev->status,
+ ev->idle_rate);
+}
+
+static void handle_get_report(void *buf, uint16_t len, int fd)
+{
+ struct hal_ev_hidhost_get_report *ev = buf;
+
+ if (len != sizeof(*ev) + ev->len) {
+ error("invalid get report event, aborting");
+ exit(EXIT_FAILURE);
+ }
+
+ if (cbacks->get_report_cb)
+ cbacks->get_report_cb((bt_bdaddr_t *) ev->bdaddr, ev->status,
+ ev->data, ev->len);
+}
+
+static void handle_virtual_unplug(void *buf, uint16_t len, int fd)
+{
+ struct hal_ev_hidhost_virtual_unplug *ev = buf;
+
+ if (cbacks->virtual_unplug_cb)
+ cbacks->virtual_unplug_cb((bt_bdaddr_t *) ev->bdaddr,
+ ev->status);
+}
+
+static void handle_handshake(void *buf, uint16_t len, int fd)
+{
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+ struct hal_ev_hidhost_handshake *ev = buf;
+
+ if (cbacks->handshake_cb)
+ cbacks->handshake_cb((bt_bdaddr_t *) ev->bdaddr, ev->status);
+#endif
+}
+
+/*
+ * handlers will be called from notification thread context,
+ * index in table equals to 'opcode - HAL_MINIMUM_EVENT'
+ */
+static const struct hal_ipc_handler ev_handlers[] = {
+ /* HAL_EV_HIDHOST_CONN_STATE */
+ { handle_conn_state, false, sizeof(struct hal_ev_hidhost_conn_state) },
+ /* HAL_EV_HIDHOST_INFO */
+ { handle_info, false, sizeof(struct hal_ev_hidhost_info) },
+ /* HAL_EV_HIDHOST_PROTO_MODE */
+ { handle_proto_mode, false, sizeof(struct hal_ev_hidhost_proto_mode) },
+ /* HAL_EV_HIDHOST_IDLE_TIME */
+ { handle_idle_time, false, sizeof(struct hal_ev_hidhost_idle_time) },
+ /* HAL_EV_HIDHOST_GET_REPORT */
+ { handle_get_report, true, sizeof(struct hal_ev_hidhost_get_report) },
+ /* HAL_EV_HIDHOST_VIRTUAL_UNPLUG */
+ { handle_virtual_unplug, false,
+ sizeof(struct hal_ev_hidhost_virtual_unplug) },
+ { handle_handshake, false, sizeof(struct hal_ev_hidhost_handshake) },
+};
+
+static bt_status_t hidhost_connect(bt_bdaddr_t *bd_addr)
+{
+ struct hal_cmd_hidhost_connect cmd;
+
+ DBG("");
+
+ if (!interface_ready())
+ return BT_STATUS_NOT_READY;
+
+ if (!bd_addr)
+ return BT_STATUS_PARM_INVALID;
+
+ memcpy(cmd.bdaddr, bd_addr, sizeof(cmd.bdaddr));
+
+ return hal_ipc_cmd(HAL_SERVICE_ID_HIDHOST, HAL_OP_HIDHOST_CONNECT,
+ sizeof(cmd), &cmd, NULL, NULL, NULL);
+}
+
+static bt_status_t disconnect(bt_bdaddr_t *bd_addr)
+{
+ struct hal_cmd_hidhost_disconnect cmd;
+
+ DBG("");
+
+ if (!interface_ready())
+ return BT_STATUS_NOT_READY;
+
+ if (!bd_addr)
+ return BT_STATUS_PARM_INVALID;
+
+ memcpy(cmd.bdaddr, bd_addr, sizeof(cmd.bdaddr));
+
+ return hal_ipc_cmd(HAL_SERVICE_ID_HIDHOST, HAL_OP_HIDHOST_DISCONNECT,
+ sizeof(cmd), &cmd, NULL, NULL, NULL);
+}
+
+static bt_status_t virtual_unplug(bt_bdaddr_t *bd_addr)
+{
+ struct hal_cmd_hidhost_virtual_unplug cmd;
+
+ DBG("");
+
+ if (!interface_ready())
+ return BT_STATUS_NOT_READY;
+
+ if (!bd_addr)
+ return BT_STATUS_PARM_INVALID;
+
+ memcpy(cmd.bdaddr, bd_addr, sizeof(cmd.bdaddr));
+
+ return hal_ipc_cmd(HAL_SERVICE_ID_HIDHOST,
+ HAL_OP_HIDHOST_VIRTUAL_UNPLUG,
+ sizeof(cmd), &cmd, NULL, NULL, NULL);
+}
+
+static bt_status_t set_info(bt_bdaddr_t *bd_addr, bthh_hid_info_t hid_info)
+{
+ struct hal_cmd_hidhost_set_info cmd;
+
+ DBG("");
+
+ if (!interface_ready())
+ return BT_STATUS_NOT_READY;
+
+ if (!bd_addr)
+ return BT_STATUS_PARM_INVALID;
+
+ memcpy(cmd.bdaddr, bd_addr, sizeof(cmd.bdaddr));
+ cmd.attr = hid_info.attr_mask;
+ cmd.subclass = hid_info.sub_class;
+ cmd.app_id = hid_info.app_id;
+ cmd.vendor = hid_info.vendor_id;
+ cmd.product = hid_info.product_id;
+ cmd.country = hid_info.ctry_code;
+ cmd.descr_len = hid_info.dl_len;
+ memcpy(cmd.descr, hid_info.dsc_list, cmd.descr_len);
+
+ return hal_ipc_cmd(HAL_SERVICE_ID_HIDHOST, HAL_OP_HIDHOST_SET_INFO,
+ sizeof(cmd), &cmd, NULL, NULL, NULL);
+}
+
+static bt_status_t get_protocol(bt_bdaddr_t *bd_addr,
+ bthh_protocol_mode_t protocol_mode)
+{
+ struct hal_cmd_hidhost_get_protocol cmd;
+
+ DBG("");
+
+ if (!interface_ready())
+ return BT_STATUS_NOT_READY;
+
+ if (!bd_addr)
+ return BT_STATUS_PARM_INVALID;
+
+ memcpy(cmd.bdaddr, bd_addr, sizeof(cmd.bdaddr));
+
+ /* type match IPC type */
+ cmd.mode = protocol_mode;
+
+ return hal_ipc_cmd(HAL_SERVICE_ID_HIDHOST,
+ HAL_OP_HIDHOST_GET_PROTOCOL,
+ sizeof(cmd), &cmd, NULL, NULL, NULL);
+}
+
+static bt_status_t set_protocol(bt_bdaddr_t *bd_addr,
+ bthh_protocol_mode_t protocol_mode)
+{
+ struct hal_cmd_hidhost_set_protocol cmd;
+
+ DBG("");
+
+ if (!interface_ready())
+ return BT_STATUS_NOT_READY;
+
+ if (!bd_addr)
+ return BT_STATUS_PARM_INVALID;
+
+ memcpy(cmd.bdaddr, bd_addr, sizeof(cmd.bdaddr));
+
+ /* type match IPC type */
+ cmd.mode = protocol_mode;
+
+ return hal_ipc_cmd(HAL_SERVICE_ID_HIDHOST,
+ HAL_OP_HIDHOST_SET_PROTOCOL,
+ sizeof(cmd), &cmd, NULL, NULL, NULL);
+}
+
+static bt_status_t get_report(bt_bdaddr_t *bd_addr,
+ bthh_report_type_t report_type,
+ uint8_t report_id,
+ int buffer_size)
+{
+ struct hal_cmd_hidhost_get_report cmd;
+
+ DBG("");
+
+ if (!interface_ready())
+ return BT_STATUS_NOT_READY;
+
+ if (!bd_addr)
+ return BT_STATUS_PARM_INVALID;
+
+ memcpy(cmd.bdaddr, bd_addr, sizeof(cmd.bdaddr));
+ cmd.id = report_id;
+ cmd.buf_size = buffer_size;
+
+ /* type match IPC type */
+ cmd.type = report_type;
+
+ return hal_ipc_cmd(HAL_SERVICE_ID_HIDHOST, HAL_OP_HIDHOST_GET_REPORT,
+ sizeof(cmd), &cmd, NULL, NULL, NULL);
+}
+
+static bt_status_t set_report(bt_bdaddr_t *bd_addr,
+ bthh_report_type_t report_type,
+ char *report)
+{
+ uint8_t buf[IPC_MTU];
+ struct hal_cmd_hidhost_set_report *cmd = (void *) buf;
+
+ DBG("");
+
+ if (!interface_ready())
+ return BT_STATUS_NOT_READY;
+
+ if (!bd_addr || !report)
+ return BT_STATUS_PARM_INVALID;
+
+ memcpy(cmd->bdaddr, bd_addr, sizeof(cmd->bdaddr));
+ cmd->len = strlen(report);
+ memcpy(cmd->data, report, cmd->len);
+
+ /* type match IPC type */
+ cmd->type = report_type;
+
+ return hal_ipc_cmd(HAL_SERVICE_ID_HIDHOST, HAL_OP_HIDHOST_SET_REPORT,
+ sizeof(*cmd) + cmd->len, buf, NULL, NULL, NULL);
+}
+
+static bt_status_t send_data(bt_bdaddr_t *bd_addr, char *data)
+{
+ uint8_t buf[IPC_MTU];
+ struct hal_cmd_hidhost_send_data *cmd = (void *) buf;
+
+ DBG("");
+
+ if (!interface_ready())
+ return BT_STATUS_NOT_READY;
+
+ if (!bd_addr || !data)
+ return BT_STATUS_PARM_INVALID;
+
+ memcpy(cmd->bdaddr, bd_addr, sizeof(cmd->bdaddr));
+ cmd->len = strlen(data);
+ memcpy(cmd->data, data, cmd->len);
+
+ return hal_ipc_cmd(HAL_SERVICE_ID_HIDHOST, HAL_OP_HIDHOST_SEND_DATA,
+ sizeof(*cmd) + cmd->len, buf, NULL, NULL, NULL);
+}
+
+static bt_status_t init(bthh_callbacks_t *callbacks)
+{
+ struct hal_cmd_register_module cmd;
+ int ret;
+
+ DBG("");
+
+ if (interface_ready())
+ return BT_STATUS_DONE;
+
+ /* store reference to user callbacks */
+ cbacks = callbacks;
+
+ hal_ipc_register(HAL_SERVICE_ID_HIDHOST, ev_handlers,
+ sizeof(ev_handlers)/sizeof(ev_handlers[0]));
+
+ cmd.service_id = HAL_SERVICE_ID_HIDHOST;
+ cmd.mode = HAL_MODE_DEFAULT;
+ cmd.max_clients = 1;
+
+ ret = hal_ipc_cmd(HAL_SERVICE_ID_CORE, HAL_OP_REGISTER_MODULE,
+ sizeof(cmd), &cmd, NULL, NULL, NULL);
+
+ if (ret != BT_STATUS_SUCCESS) {
+ cbacks = NULL;
+ hal_ipc_unregister(HAL_SERVICE_ID_HIDHOST);
+ }
+
+ return ret;
+}
+
+static void cleanup(void)
+{
+ struct hal_cmd_unregister_module cmd;
+
+ DBG("");
+
+ if (!interface_ready())
+ return;
+
+ cmd.service_id = HAL_SERVICE_ID_HIDHOST;
+
+ hal_ipc_cmd(HAL_SERVICE_ID_CORE, HAL_OP_UNREGISTER_MODULE,
+ sizeof(cmd), &cmd, NULL, NULL, NULL);
+
+ hal_ipc_unregister(HAL_SERVICE_ID_HIDHOST);
+
+ cbacks = NULL;
+}
+
+static bthh_interface_t hidhost_if = {
+ .size = sizeof(hidhost_if),
+ .init = init,
+ .connect = hidhost_connect,
+ .disconnect = disconnect,
+ .virtual_unplug = virtual_unplug,
+ .set_info = set_info,
+ .get_protocol = get_protocol,
+ .set_protocol = set_protocol,
+ .get_report = get_report,
+ .set_report = set_report,
+ .send_data = send_data,
+ .cleanup = cleanup
+};
+
+bthh_interface_t *bt_get_hidhost_interface(void)
+{
+ return &hidhost_if;
+}
diff --git a/repo/android/hal-ipc-api.txt b/repo/android/hal-ipc-api.txt
new file mode 100644
index 0000000..e3b7798
--- /dev/null
+++ b/repo/android/hal-ipc-api.txt
@@ -0,0 +1,2737 @@
+Android HAL protocol for Bluetooth
+==================================
+
+The Android HAL daemon for Bluetooth functionality implements the Unix socket
+server protocol around /run/bluetooth/daemon (tentative location) or Linux
+abstract sockets (tentative name).
+
+The daemon is single threaded and uses a mainloop for scheduling and general
+operation.
+
+The protocol is SOCK_SEQPACKET based and follows a strict PDU specification
+with a generic header and initial registration exchange. The communication
+is driven from the HAL with command/response exchanges. The daemon will use
+notification to signal events. The protocol is single PDU exchanged based,
+meaning every command requires a response. Notification does not require
+any confirmation. Not handling this PDU exchange leads to a disconnection of
+the socket.
+
+Command/response and notification use separate sockets. First connected socket
+is used for command/response, second for notification. All services are
+multi-plexed over same pair of sockets. Separation is done to ease
+implementation of simple HAL library with dedicated thread for handling
+notification.
+
+This strict protocol requirement is done to match C based callbacks and
+callout functions that are running in a thread inside the HAL and might
+block.
+
+ .--Android--. .--Android--.
+ | daemon | | HAL |
+ | | Command | |
+ | | <-------------------------- | |
+ | | | |
+ | | --------------------------> | |
+ | | Response | |
+ | | | |
+ | | | |
+ | | Notification | |
+ | | --------------------------> | |
+ | | | |
+ '-----------' '-----------'
+
+Every packet will follow the basic header to support simple multi-plexing
+over the same socket. It will also support a basic control channel with service
+id 0.
+
+ 0 8 16 24 31
+ +--------------+--------------+--------------+--------------+
+ | Service ID | Opcode | Data Length |
+ +--------------+--------------+-----------------------------+
+ | |
+
+The unique service ID is assigned by this specification for each HAL.
+
+As general rule of thumb, the opcode for command matches the opcode for a
+response. Or the opcode 0x00 for an error is returned.
+
+Notification opcodes start from 0x81.
+
+Opcode 0x80 is reserved and shall not be used.
+
+All command/response opcodes have the least significant bit not set. And all
+notifications have the least significant bit set.
+
+The HAL modules only have the job to map the callback and event functions
+to the protocol. They do not need to do anything else. Below is an example
+of a sample transaction for the Bluetooth Core HAL and enabling of an
+adapter.
+
+ HAL Daemon
+ ----------------------------------------------------
+
+ call enable() --> command 0x01
+ return enable() <-- response 0x01
+
+ call adapter_state_changed() <-- notification 0x81
+ return adapter_state_changed()
+
+When the Android hardware framework calls into the Bluetooth Core HAL
+and executes the enable() callback, the HAL module sends the enable
+command with opcode 0x01 to the daemon. As soon as the daemon responds,
+the callback will return with the appropriate result.
+
+After the daemon switched on the adapter, it will send a notification
+with opcode 0x81 to the HAL module.
+
+The Bluetooth Core HAL and Bluetooth Socket HAL are guaranteed to be
+available from the daemon. All other HAL modules are optional.
+
+When the Bluetooth Core HAL init() function is called, it should open
+the socket and register both "bluetooth" and "socket" service modules. It is
+required to register "socket" service at the same time since the HAL module
+does not have its own init() function.
+
+It is possible to send Configure command before registering any services to
+customize stack. This step is optional.
+
+When new profiles are initiated, the get_profile_interface() callback
+will load the profile and during init() of the profile, it should register the
+specific service.
+
+ Bluetooth main thread Daemon
+ -------------------------------------------------------
+
+ init() --> open command socket
+ --> open notification socket
+ --> register module "bluetooth"
+ --> register module "socket"
+
+ get_profile_interface() --> return profile struct
+ --> continue on Handsfree thread
+
+
+ Handsfree thread Daemon
+ --------------------------------------------------------
+
+ init() --> register module handsfree
+
+
+Error response is common for all services and has fixed structure:
+
+ Opcode 0x00 - Error response
+
+ Response parameters: Status (1 octet)
+
+ Valid status values: 0x01 = Fail
+ 0x02 = Not ready
+ 0x03 = No memory
+ 0x04 = Busy
+ 0x05 = Done (already completed)
+ 0x06 = Unsupported
+ 0x07 = Parameter invalid
+ 0x08 = Unhandled
+ 0x09 = Authentication failure
+ 0x0a = Remote device down
+ 0x0b = Authentication rejected
+
+
+Core Service (ID 0)
+===================
+
+ Opcode 0x00 - Error response
+
+ Opcode 0x01 - Register module command/response
+
+ Command parameters: Service id (1 octet)
+ Mode (1 octet)
+ Max Clients (4 octets)
+ Response parameters: <none>
+
+ In case a command is sent for an undeclared service ID, it will
+ be rejected. Also there will be no notifications for undeclared
+ service ID.
+
+ Valid Mode values: 0x00 = Default Mode
+ 0xXX = as defined by service
+
+ In case of an error, the error response will be returned.
+
+ Opcode 0x02 - Unregister module command/response
+
+ Command parameters: Service id (1 octet)
+ Response parameters: <none>
+
+ In case of an error, the error response will be returned.
+
+ Opcode 0x03 - Configuration
+
+ Command parameters: Num options (1 octet)
+ Option Type # (1 octet)
+ Option Length # (2 octets)
+ Option Value # (variable)
+
+ Response parameters: <none>
+
+ Valid configure option types: 0x00 = Vendor
+ 0x01 = Model
+ 0x02 = Name
+ 0x03 = Serial Number
+ 0x04 = System ID
+ 0x05 = PnP ID
+ 0x06 = Firmware Rev
+ 0x07 = Hardware Rev
+
+ In case of an error, the error response will be returned.
+
+Bluetooth Core HAL (ID 1)
+=========================
+
+Android HAL name: "bluetooth" (BT_HARDWARE_MODULE_ID)
+
+ Service modes: 0x00 = Enable BR/EDR/LE if supported (default)
+ 0x01 = Enable BR/EDR only
+ 0x02 = Enable LE only
+
+Commands and responses:
+
+ Opcode 0x00 - Error response
+
+ Opcode 0x01 - Enable command/response
+
+ Command parameters: <none>
+ Response parameters: <none>
+
+ In case of an error, the error response will be returned.
+
+ Opcode 0x02 - Disable command/response
+
+ Command parameters: <none>
+ Response parameters: <none>
+
+ In case of an error, the error response will be returned.
+
+ Opcode 0x03 - Get Adapter Properties command/response
+
+ Command parameters: <none>
+ Response parameters: <none>
+
+ In case of an error, the error response will be returned.
+
+ Opcode 0x04 - Get Adapter Property command/response
+
+ Command parameters: Property type (1 octet)
+ Response parameters: <none>
+
+ In case of an error, the error response will be returned.
+
+ Opcode 0x05 - Set Adapter Property command/response
+
+ Command parameters: Property type (1 octet)
+ Property length (2 octets)
+ Property value (variable)
+ Response parameters: <none>
+
+ In case of an error, the error response will be returned.
+
+ Opcode 0x06 - Get Remote Device Properties command/response
+
+ Command parameters: Remote address (6 octets)
+ Response parameters: <none>
+
+ In case of an error, the error response will be returned.
+
+ Opcode 0x07 - Get Remote Device Property command/response
+
+ Command parameters: Remote address (6 octets)
+ Property type (1 octet)
+ Response parameters: <none>
+
+ In case of an error, the error response will be returned.
+
+ Opcode 0x08 - Set Remote Device Property command/response
+
+ Command parameters: Remote address (6 octets)
+ Property type (1 octet)
+ Property length (2 octets)
+ Property value (variable)
+ Response parameters: <none>
+
+ In case of an error, the error response will be returned.
+
+ Opcode 0x09 - Get Remote Service Record command/response
+
+ Command parameters: Remote address (6 octets)
+ UUID (16 octets)
+ Response parameters: <none>
+
+ In case of an error, the error response will be returned.
+
+ Opcode 0x0a - Get Remote Services command/response
+
+ Command parameters: Remote address (6 octets)
+ Response parameters: <none>
+
+ In case of an error, the error response will be returned.
+
+ Opcode 0x0b - Start Discovery command/response
+
+ Command parameters: <none>
+ Response parameters: <none>
+
+ In case of an error, the error response will be returned.
+
+ Opcode 0x0c - Cancel Discovery command/response
+
+ Command parameters: <none>
+ Response parameters: <none>
+
+ In case of an error, the error response will be returned.
+
+ Opcode 0x0d - Create Bond command/response
+
+ Command parameters: Remote address (6 octets)
+ Transport (1 octet)
+ Response parameters: <none>
+
+ In case of an error, the error response will be returned.
+
+ Opcode 0x0e - Remove Bond command/response
+
+ Command parameters: Remote address (6 octets)
+ Response parameters: <none>
+
+ In case of an error, the error response will be returned.
+
+ Opcode 0x0f - Cancel Bond command/response
+
+ Command parameters: Remote address (6 octets)
+ Response parameters: <none>
+
+ In case of an error, the error response will be returned.
+
+ Opcode 0x10 - PIN Reply command/response
+
+ Command parameters: Remote address (6 octets)
+ Accept (1 octet)
+ PIN length (1 octet)
+ PIN code (16 octets)
+ Response parameters: <none>
+
+ In case of an error, the error response will be returned.
+
+ Opcode 0x11 - SSP Reply command/response
+
+ Command parameters: Remote address (6 octets)
+ SSP variant (1 octet)
+ Accept (1 octet)
+ Passkey (4 octets)
+ Response parameters: <none>
+
+ Valid SSP variant values: 0x00 = Passkey Confirmation
+ 0x01 = Passkey Entry
+ 0x02 = Consent (for Just Works)
+ 0x03 = Passkey Notification
+
+ In case of an error, the error response will be returned.
+
+ Opcode 0x12 - DUT Mode Configure command/response
+
+ Command parameters: Enable (1 octet)
+ Response parameters: <none>
+
+ In case of an error, the error response will be returned.
+
+ Opcode 0x13 - DUT Mode Send command/response
+
+ Command parameters: Opcode (2 octets)
+ Length (1 octet)
+ Data (variable)
+ Response parameters: <none>
+
+ In case of an error, the error response will be returned.
+
+ Opcode 0x14 - LE Test Mode command/response
+
+ Command parameters: Opcode (2 octets)
+ Length (1 octet)
+ Data (variable)
+ Response parameters: <none>
+
+ In case of an error, the error response will be returned.
+
+Notifications:
+
+ Opcode 0x81 - Adapter State Changed notification
+
+ Notifications parameters: State (1 octet)
+
+ Valid state values: 0x00 = Off
+ 0x01 = On
+
+ Opcode 0x82 - Adapter Properties Changed notification
+
+ Notification parameters: Status (1 octet)
+ Num properties (1 octet)
+ Type # (1 octet)
+ Length # (2 octets)
+ Value # (variable)
+ ...
+
+ Opcode 0x83 - Remote Device Properties notification
+
+ Notification parameters: Status (1 octet)
+ Remote address (6 octets)
+ Num properties (1 octet)
+ Type # (1 octet)
+ Length # (2 octets)
+ Value # (variable)
+ ...
+
+ Opcode 0x84 - Device Found notification
+
+ Notification parameters: Num properties (1 octet)
+ Type # (1 octet)
+ Length # (2 octets)
+ Value # (variable)
+ ...
+
+ Opcode 0x85 - Discovery State Changed notification
+
+ Notifications parameters: State (1 octet)
+
+ Opcode 0x86 - PIN Request notification
+
+ Notification parameters: Remote address (6 octets)
+ Remote name (249 octets)
+ Class of device (4 octets)
+
+ Opcode 0x87 - SSP Request notification
+
+ Notification parameters: Remote address (6 octets)
+ Remote name (249 octets)
+ Class of device (4 octets)
+ Pairing variant (1 octet)
+ Passkey (4 octets)
+
+ Opcode 0x88 - Bond State Changed notification
+
+ Notification parameters: Status (1 octet)
+ Remote address (6 octets)
+ Bond state (1 octet)
+
+ Valid bond state values: 0x00 = None
+ 0x01 = Bonding
+ 0x02 = Bonded
+
+ Opcode 0x89 - ACL State Changed notification
+
+ Notification parameters: Status (1 octet)
+ Remote address (6 octets)
+ ACL state (1 octet)
+
+ Opcode 0x8a - DUT Mode Receive notification
+
+ Notification parameters: Opcode (2 octets)
+ Length (1 octet)
+ Data (variable)
+
+ Opcode 0x8b - LE Test Mode notification
+
+ Notification parameters: Status (1 octet)
+ Num packets (2 octets)
+
+
+Bluetooth Socket HAL (ID 2)
+===========================
+
+Android HAL name:: "socket" (BT_PROFILE_SOCKETS_ID)
+
+Commands and responses:
+
+ Opcode 0x00 - Error response
+
+ Opcode 0x01 - Listen command/response
+
+ Command parameters: Socket type (1 octet)
+ Service name (256 octets)
+ Service UUID (16 octets)
+ Channel (4 octets)
+ Socket flags (1 octet)
+ Response parameters: File descriptor (inline)
+
+ Valid socket types: 0x01 = RFCOMM
+ 0x02 = SCO
+ 0x03 = L2CAP
+
+ Valid socket flags: 0x01 = Encrypt
+ 0x02 = Auth
+
+ In case of an error, the error response will be returned.
+
+ Opcode 0x02 - Connect command/response
+
+ Command parameters: Remote address (6 octets)
+ Socket type (1 octet)
+ Service UUID (16 octets)
+ Channel (4 octets)
+ Socket flags (1 octet)
+ Response parameters: File descriptor (inline)
+
+ Valid socket types: 0x01 = RFCOMM
+ 0x02 = SCO
+ 0x03 = L2CAP
+
+ Valid socket flags: 0x01 = Encrypt
+ 0x02 = Auth
+
+ In case of an error, the error response will be returned.
+
+
+Bluetooth HID Host HAL (ID 3)
+============================
+
+Android HAL name: "hidhost" (BT_PROFILE_HIDHOST_ID)
+
+Commands and responses:
+
+ Opcode 0x00 - Error response
+
+ Opcode 0x01 - Connect command/response
+
+ Command parameters: Remote address (6 octets)
+ Response parameters: <none>
+
+ In case of an error, the error response will be returned.
+
+ Opcode 0x02 - Disconnect command/response
+
+ Command parameters: Remote address (6 octets)
+ Response parameters: <none>
+
+ In case of an error, the error response will be returned.
+
+ Opcode 0x03 - Virtual Unplug command/response
+
+ Command parameters: Remote address (6 octets)
+ Response parameters: <none>
+
+ In case of an error, the error response will be returned.
+
+ Opcode 0x04 - Set Info command/response
+
+ Command parameters: Remote address (6 octets)
+ Attribute mask (2 octets)
+ Subclass (1 octet)
+ Application ID (1 octet)
+ Vendor ID (2 octets)
+ Product ID (2 octets)
+ Version (2 octets)
+ Country code (1 octet)
+ Descriptor length (2 octet)
+ Descriptor value (884 octets)
+ Response parameters: <none>
+
+ In case of an error, the error response will be returned.
+
+ Opcode 0x05 - Get Protocol command/response
+
+ Command parameters: Remote address (6 octets)
+ Protocol mode (1 octet)
+ Response parameters: <none>
+
+ Valid protocol modes: 0x00 = Report
+ 0x01 = Boot
+ 0xff = Unsupported
+
+ In case of an error, the error response will be returned.
+
+ Opcode 0x06 - Set Protocol command/response
+
+ Command parameters: Remote address (6 octets)
+ Protocol mode (1 octet)
+ Response parameters: <none>
+
+ Valid protocol modes: 0x00 = Report
+ 0x01 = Boot
+ 0xff = Unsupported
+
+ In case of an error, the error response will be returned.
+
+ Opcode 0x07 - Get Report command/response
+
+ Command parameters: Remote address (6 octets)
+ Report type (1 octet)
+ Report ID (1 octet)
+ Buffer size (2 octet)
+ Response parameters: <none>
+
+ Valid report types: 0x01 = Input
+ 0x02 = Output
+ 0x03 = Feature
+
+ In case of an error, the error response will be returned.
+
+ Opcode 0x08 - Set Report command/response
+
+ Command parameters: Remote address (6 octets)
+ Report type (1 octet)
+ Report length (2 octets)
+ Report data (Report length)
+
+ Response parameters: <none>
+
+ Valid report types: 0x01 = Input
+ 0x02 = Output
+ 0x03 = Feature
+
+ In case of an error, the error response will be returned.
+
+ Opcode 0x09 - Send Data command/response
+
+ Command parameters: Remote address (6 octets)
+ Data length (2 octets)
+ Data (Data length)
+
+ Response parameters: <none>
+
+ In case of an error, the error response will be returned.
+
+Notifications:
+
+ Status is common for many notifications and has fixed value range:
+
+ Status values: 0x00 = Ok
+ 0x01 = Handshake - Device not ready
+ 0x02 = Handshake - Invalid report ID
+ 0x03 = Handshake - Transaction not SPT
+ 0x04 = Handshake - Invalid parameter
+ 0x05 = Handshake - Generic error
+ 0x06 = General error
+ 0x07 = SDP error
+ 0x08 = Set protocol error
+ 0x09 = Device database full
+ 0x0a = Device type not supported
+ 0x0b = No resources
+ 0x0c = Authentication failed
+ 0x0d = HDL
+
+ Opcode 0x81 - Connection State notification
+
+ Notification parameters: Remote address (6 octets)
+ Connection State (1 octets)
+
+ Valid connection states: 0x00 = Connected
+ 0x01 = Connecting
+ 0x02 = Disconnected
+ 0x03 = Disconnecting
+ 0x04 = Failed - Mouse from host
+ 0x05 = Failed - Keyboard from host
+ 0x06 = Failed - Too many devices
+ 0x07 = Failed - No HID driver
+ 0x08 = Failed - generic
+ 0x09 = Unknown
+
+ Opcode 0x82 - HID Info notification
+
+ Notification parameters: Remote address (6 octets)
+ Attribute mask (2 octets)
+ Subclass (1 octet)
+ Application ID (1 octet)
+ Vendor ID (2 octets)
+ Product ID (2 octets)
+ Version (2 octets)
+ Country code (1 octet)
+ Descriptor length (2 octet)
+ Descriptor value (884 octets)
+
+ Opcode 0x83 - Protocol Mode notification
+
+ Notification parameters: Remote address (6 octets)
+ Status (1 octet)
+ Protocol mode (1 octet)
+
+ Valid protocol modes: 0x00 = Report
+ 0x01 = Boot
+ 0xff = Unsupported
+
+ Opcode 0x84 - Idle Time notification
+
+ Notification parameters: Remote address (6 octets)
+ Status (1 octet)
+ Idle time (2 octets)
+
+ Opcode 0x85 - Get Report notification
+
+ Notification parameters: Remote address (6 octets)
+ Status (1 octet)
+ Report length (2 octets)
+ Report data (variable)
+
+ Opcode 0x86 - Virtual Unplug notification
+
+ Notification parameters: Remote address (6 octets)
+ Status (1 octet)
+
+ Opcode 0x87 - Handshake notification
+
+ Notification parameters: Remote address (6 octets)
+ Status (1 octet)
+
+ Only status values from 0x00 to 0x05 are valid as handshake
+ status.
+
+
+Bluetooth PAN HAL (ID 4)
+========================
+
+Android HAL name: "pan" (BT_PROFILE_PAN_ID)
+
+Commands and responses:
+
+ Opcode 0x00 - Error response
+
+ Opcode 0x01 - Enable command/response
+
+ Command parameters: Local role (1 octet)
+ Response parameters: <none>
+
+ Valid role values: 0x00 = None
+ 0x01 = NAP
+ 0x02 = PANU
+
+ In case of an error, the error response will be returned.
+
+ Opcode 0x02 - Get Local Role command/response
+
+ Command parameters: <none>
+ Response parameters: Local role (1 octet)
+
+ Valid role values: 0x00 = None
+ 0x01 = NAP
+ 0x02 = PANU
+
+ In case of an error, the error response will be returned.
+
+ Opcode 0x03 - Connect command/response
+
+ Command parameters: Remote address (6 octets)
+ Local role (1 octet)
+ Remote role (1 octet)
+ Response parameters: <none>
+
+ Valid role values: 0x01 = NAP
+ 0x02 = PANU
+
+ In case of an error, the error response will be returned.
+
+ Opcode 0x04 - Disconnect command/response
+
+ Command parameters: Remote address (6 octets)
+ Response parameters: <none>
+
+ In case of an error, the error response will be returned.
+
+Notifications:
+
+ Opcode 0x81 - Control State notification
+
+ Notification parameters: Control state (1 octet)
+ Status (1 octet)
+ Local role (1 octet)
+ Interface name (17 octet)
+
+ Valid control states: 0x00 = Enabled
+ 0x01 = Disabled
+
+ Valid role values: 0x00 = None
+ 0x01 = NAP
+ 0x02 = PANU
+
+ Opcode 0x82 - Connection State notification
+
+ Notification parameters: Connection state (1 octet)
+ Status (1 octet)
+ Remote address (6 octets)
+ Local role (1 octet)
+ Remote role (1 octet)
+
+ Valid connection states: 0x00 = Connected
+ 0x01 = Connecting
+ 0x02 = Disconnected
+ 0x03 = Disconnecting
+
+ Valid role values: 0x01 = NAP
+ 0x02 = PANU
+
+
+Bluetooth Handsfree HAL (ID 5)
+==============================
+
+Android HAL name: "handsfree" (BT_PROFILE_HANDSFREE_ID)
+
+ Service modes: 0x00 = Headset Profile only mode (default)
+ 0x01 = Handsfree Profile (narrowband speech)
+ 0x02 = Handsfree Profile (narrowband and wideband speech)
+
+Commands and responses:
+
+ Opcode 0x00 - Error response
+
+ Opcode 0x01 - Connect command/response
+
+ Command parameters: Remote address (6 octets)
+ Response parameters: <none>
+
+ In case of an error, the error response will be returned.
+
+ Opcode 0x02 - Disconnect command/response
+
+ Command parameters: Remote address (6 octets)
+ Response parameters: <none>
+
+ In case of an error, the error response will be returned.
+
+ Opcode 0x03 - Connect Audio command/response
+
+ Command parameters: Remote address (6 octets)
+ Response parameters: <none>
+
+ In case of an error, the error response will be returned.
+
+ Opcode 0x04 - Disconnect Audio command/response
+
+ Command parameters: Remote address (6 octets)
+ Response parameters: <none>
+
+ In case of an error, the error response will be returned.
+
+ Opcode 0x05 - Start Voice Recognition command/response
+
+ Command parameters: Remote address (6 octets)
+ Response parameters: <none>
+
+ In case of an error, the error response will be returned.
+
+ Opcode 0x06 - Stop Voice Recognition command/response
+
+ Command parameters: Remote address (6 octets)
+ Response parameters: <none>
+
+ In case of an error, the error response will be returned.
+
+ Opcode 0x07 - Volume Control command/response
+
+ Command parameters: Volume type (1 octet)
+ Volume (1 octet)
+ Remote address (6 octets)
+ Response parameters: <none>
+
+ Valid volume types: 0x00 = Speaker
+ 0x01 = Microphone
+
+ In case of an error, the error response will be returned.
+
+ Opcode 0x08 - Device Status Notification command/response
+
+ Command parameters: Network state (1 octet)
+ Service type (1 octet)
+ Signal strength (1 octet)
+ Battery level (1 octet)
+ Response parameters: <none>
+
+ Valid network states: 0x00 = Not available
+ 0x01 = Available
+
+ Valid service types: 0x00 = Home network
+ 0x01 = Roaming network
+
+ In case of an error, the error response will be returned.
+
+ Opcode 0x09 - COPS Response command/response
+
+ Command parameters: COPS command response (string)
+ Remote address (6 octets)
+ Response parameters: <none>
+
+ In case of an error, the error response will be returned.
+
+ Opcode 0x0a - CIND Response command/response
+
+ Command parameters: Service (1 octet)
+ Number of active calls (1 octet)
+ Number of held calls (1 octet)
+ Call setup state (1 octet)
+ Signal strength (1 octet)
+ Roaming indicator (1 octet)
+ Battery level (1 octet)
+ Remote address (6 octets)
+ Response parameters: <none>
+
+ Valid call setup states: 0x00 = Active
+ 0x01 = Held
+ 0x02 = Dialing
+ 0x03 = Alerting
+ 0x04 = Incoming
+ 0x05 = Waiting
+ 0x06 = Idle
+
+ In case of an error, the error response will be returned.
+
+ Opcode 0x0b - Formatted AT Response command/response
+
+ Command parameters: Pre-formatted AT response (string)
+ Remote address (6 octets)
+ Response parameters: <none>
+
+ In case of an error, the error response will be returned.
+
+ Opcode 0x0c - AT Response command/response
+
+ Command parameters: Response code (1 octet)
+ Error code (1 octet)
+ Remote address (6 octets)
+ Response parameters: <none>
+
+ Valid response codes: 0x00 = ERROR
+ 0x01 = OK
+
+ In case of an error, the error response will be returned.
+
+ Opcode 0x0d - CLCC Response command/response
+
+ Command parameters: Call index (1 octet)
+ Call direction (1 octet)
+ Call state (1 octet)
+ Call mode (1 octet)
+ Call multiparty type (1 octet)
+ Call number type (1 octet)
+ Call number (string)
+ Remote address (6 octets)
+ Response parameters: <none>
+
+ Valid call directions: 0x00 = Outgoing
+ 0x01 = Incoming
+
+ Valid call states: 0x00 = Active
+ 0x01 = Held
+ 0x02 = Dialing
+ 0x03 = Alerting
+ 0x04 = Incoming
+ 0x05 = Waiting
+ 0x06 = Idle
+
+ Valid call modes: 0x00 = Voice
+ 0x01 = Data
+ 0x02 = Fax
+
+ Valid multiparty types: 0x00 = Single call
+ 0x01 = Multiparty call
+
+ Valid number types: 0x81 = Unknown
+ 0x91 = International
+
+ In case of an error, the error response will be returned.
+
+ Opcode 0x0e - Phone Status Change command/response
+
+ Command parameters: Number of active calls (1 octet)
+ Number of held calls (1 octet)
+ Call setup state (1 octet)
+ Call number type (1 octet)
+ Call number (string)
+ Response parameters: <none>
+
+ Valid call setup states: 0x00 = Active
+ 0x01 = Held
+ 0x02 = Dialing
+ 0x03 = Alerting
+ 0x04 = Incoming
+ 0x05 = Waiting
+ 0x06 = Idle
+
+ Valid number types: 0x81 = Unknown
+ 0x91 = International
+
+ In case of an error, the error response will be returned.
+
+ Opcode 0x0f - Configure WBS command/response
+
+ Command parameters: Remote address (6 octets)
+ Config (1 octet)
+ Response parameters: <none>
+
+ Valid config values: 0x00 = None
+ 0x01 = No
+ 0x02 = Yes
+
+ In case of an error, the error response will be returned.
+
+Notifications:
+
+ Opcode 0x81 - Connection State notification
+
+ Notification parameters: Connection state (1 octet)
+ Remote address (6 octets)
+
+ Valid connection states: 0x00 = Disconnected
+ 0x01 = Connecting
+ 0x02 = Connected
+ 0x03 = SLC connected
+ 0x04 = Disconnecting
+
+ Opcode 0x82 - Audio State notification
+
+ Notification parameters: Audio state (1 octet)
+ Remote address (6 octets)
+
+ Valid audio states: 0x00 = Disconnected
+ 0x01 = Connecting
+ 0x02 = Connected
+ 0x03 = Disconnecting
+
+ Opcode 0x83 - Voice Recognition Command notification
+
+ Notification parameters: Voice recognition state (1 octet)
+ Remote address (6 octets)
+
+ Valid voice recognition states: 0x00 = Stopped
+ 0x01 = Started
+
+ Opcode 0x84 - Answer Call Command notification
+
+ Notification parameters: Remote address (6 octets)
+
+ Opcode 0x85 - Hangup Call Command notification
+
+ Notification parameters: Remote address (6 octets)
+
+ Opcode 0x86 - Volume Command notification
+
+ Notification parameters: Volume type (1 octet)
+ Volume (1 octet)
+ Remote address (6 octets)
+
+ Valid volume types: 0x00 = Speaker
+ 0x01 = Microphone
+
+ Opcode 0x87 - Dial Call Command notification
+
+ Notification parameters: Remote address (6 octets)
+ Number (string)
+
+ Opcode 0x88 - DTMF Command notification
+
+ Notification parameters: Tone (1 octet)
+ Remote address (6 octets)
+
+ Opcode 0x89 - NREC Command notification
+
+ Notification parameters: NREC types (1 octet)
+ Remote address (6 octets)
+
+ Valid NREC types: 0x00 = Stop
+ 0x01 = Start
+
+ Opcode 0x8a - CHLD Command notification
+
+ Notification parameters: NREC types (1 octet)
+ Remote address (6 octets)
+
+ Valid CHLD types: 0x00 = Release and hold
+ 0x01 = Release active and accept held
+ 0x02 = Hold active and accept held
+ 0x03 = Add held call to conference
+
+ Opcode 0x8b - CNUM Command notification
+
+ Notification parameters: Remote address (6 octets)
+
+ Opcode 0x8c - CIND Command notification
+
+ Notification parameters: Remote address (6 octets)
+
+ Opcode 0x8d - COPS Command notification
+
+ Notification parameters: Remote address (6 octets)
+
+ Opcode 0x8e - CLCC Command notification
+
+ Notification parameters: Remote address (6 octets)
+
+ Opcode 0x8f - Unknown AT Command notification
+
+ Notification parameters: Remote address (6 octets)
+ AT command (string)
+
+ Opcode 0x90 - Key Pressed Command notification
+
+ Notification parameters: Remote address (6 octets)
+
+ Opcode 0x91 - WBS Command notification
+
+ Notification parameters: WBS types (1 octet)
+ Remote address (6 octets)
+
+ Valid WBS types: 0x00 = None
+ 0x01 = No
+ 0x02 = Yes
+
+Bluetooth Advanced Audio HAL (ID 6)
+Bluetooth Advanced Audio Sink HAL (ID 13)
+=========================================
+
+Android HAL name: "a2dp" (BT_PROFILE_ADVANCED_AUDIO_ID)
+Android HAL name: "a2dp_sink" (BT_PROFILE_ADVANCED_AUDIO__SINK_ID)
+
+Commands and responses:
+
+ Opcode 0x00 - Error response
+
+ Opcode 0x01 - Connect command/response
+
+ Command parameters: Remote address (6 octets)
+ Response parameters: <none>
+
+ In case of an error, the error response will be returned.
+
+ Opcode 0x02 - Disconnect command/response
+
+ Command parameters: Remote address (6 octets)
+ Response parameters: <none>
+
+ In case of an error, the error response will be returned.
+
+Notifications:
+
+ Opcode 0x81 - Connection State notification
+
+ Notification parameters: Connection state (1 octet)
+ Remote address (6 octets)
+
+ Valid connection states: 0x00 = Disconnected
+ 0x01 = Connecting
+ 0x02 = Connected
+ 0x03 = Disconnecting
+
+ Opcode 0x82 - Audio State notification
+
+ Notification parameters: Audio state (1 octet)
+ Remote address (6 octets)
+
+ Valid connection states: 0x00 = Remote suspend
+ 0x01 = Stopped
+ 0x02 = Started
+
+ Opcode 0x83 - Audio Configuration notification
+
+ Notification parameters: Remote address (6 octets)
+ Sample Rate in Hz (4 octets)
+ Channel Count (1 octet)
+
+ Valid channel count: 0x01 = Mono
+ 0x02 = Stereo
+
+Bluetooth Health HAL (ID 7)
+===========================
+
+Android HAL name: "health" (BT_PROFILE_HEALTH_ID)
+
+Commands and responses:
+
+ Opcode 0x00 - Error response
+
+ Opcode 0x01 - Register Application command/response
+
+ Command parameters: Number of MDEP (1 octet)
+ Application name offset (2 octets)
+ Provider name offset (2 octets)
+ Service name offset (2 octets)
+ Service description offset (2 octets)
+ Data length (2 octets)
+ Data (data length)
+ Response parameters: Application ID (2 octets)
+
+ Strings are null terminated.
+ In case of an error, the error response will be returned.
+
+ Opcode 0x02 - Register Application MDEP data command/response
+
+ Command parameters: Application ID (2 octets)
+ MDEP Role (1 octet)
+ Data type (2 octets)
+ Channel type (1 octet)
+ MDEP description length (2 octets)
+ MDEP description (MDEP desciption length)
+ Response parameters: <none>
+
+ In case of an error, the error response will be returned.
+
+ Opcode 0x03 - Unregister Application command/response
+
+ Command parameters: Application ID (2 octets)
+ Response parameters: <none>
+
+ In case of an error, the error response will be returned.
+
+ Opcode 0x04 - Connect Channel command/response
+
+ Command parameters: Application ID (2 octets)
+ Remote address (6 octets)
+ MDEP index (1 octet)
+ Response parameters: Channel ID (2 octets)
+
+ In case of an error, the error response will be returned.
+
+ Opcode 0x05 - Destroy Channel command/response
+
+ Command parameters: Channel ID (2 octets)
+ Response parameters: <none>
+
+ In case of an error, the error response will be returned.
+
+Notifications:
+
+ Opcode 0x81 - Application Registration State notification
+
+ Notification parameters: Application ID (2 octets)
+ Application state (1 octet)
+
+ Valid application states: 0x00 = Registration success
+ 0x01 = Registration failed
+ 0x02 = Deregistration success
+ 0x03 = Deregistration failed
+
+ Opcode 0x82 - Channel State notification
+
+ Notification parameters: Application ID (2 octets)
+ Remote address (6 octets)
+ MDEP index (1 octet)
+ Channel ID (2 octets)
+ Channel state (1 octet)
+ File descriptor (inline)
+
+ Valid channel states: 0x00 = Connecting
+ 0x01 = Connected
+ 0x02 = Disconnecting
+ 0x03 = Disconnected
+ 0x04 = Destroyed
+
+
+Bluetooth Remote Control Target HAL (ID 8)
+===================================
+
+Android HAL name: "avrcp" (BT_PROFILE_AV_RC_ID)
+
+Commands and responses:
+
+ Opcode 0x00 - Error response
+
+ Opcode 0x01 - Get Play Status Response command/response
+
+ Command parameters: Status (1 octet)
+ Duration (4 octets)
+ Position (4 octets)
+
+ In case of an error, the error response will be returned.
+
+ Valid status values: 0x00 = Stopped
+ 0x01 = Playing
+ 0x02 = Paused
+ 0x03 = Fwd seek
+ 0x04 = Rev seek
+ 0xff = Error
+
+ Opcode 0x02 - List Player Attributes Response command/response
+
+ Command parameters: Number of attributes (1 octet)
+ Attribute # (1 octet)
+ ...
+
+ In case of an error, the error response will be returned.
+
+ Valid attributes: 0x01 = Equalizer
+ 0x02 = Repead
+ 0x03 = Shuffle
+ 0x04 = Scan
+
+ Opcode 0x03 - List Player Values Response command/response
+
+ Command parameters: Number of values (1 octet)
+ Value # (1 octet)
+ ...
+
+ In case of an error, the error response will be returned.
+
+ Opcode 0x04 - Get Player Values Response command/response
+
+ Command parameters: Number of attributes (1 octet)
+ Attribute # (1 octet)
+ Value # (1 octet)
+ ...
+
+ In case of an error, the error response will be returned.
+
+ Valid attributes: Same as in List Player Attributes
+
+ Opcode 0x05 - Get Player Attributes Text Response command/response
+
+ Command parameters: Number of attributes (1 octet)
+ Attribute # (1 octet)
+ Attribute # text length (1 octet)
+ Attribute # text (variable)
+ ...
+
+ In case of an error, the error response will be returned.
+
+ Valid attributes: Same as in List Player Attributes
+
+ Opcode 0x06 - Get Player Values Text Response command/response
+
+ Command parameters: Number of values (1 octet)
+ Value # (1 octet)
+ Value # text length (1 octet)
+ Value # text (variable)
+ ...
+
+ In case of an error, the error response will be returned.
+
+ Opcode 0x07 - Get Element Attributes Text Response command/response
+
+ Command parameters: Number of elements (1 octet)
+ Element # (1 octet)
+ Element # text length (1 octet)
+ Element # text (variable)
+ ...
+
+ In case of an error, the error response will be returned.
+
+ Valid elements: 0x01 = Title
+ 0x02 = Artist
+ 0x03 = Album
+ 0x04 = Track Number
+ 0x05 = Number of Tracks
+ 0x06 = Genre
+ 0x06 = Duration
+
+ Opcode 0x08 - Set Player Attributes Value Response command/response
+
+ Command parameters: Status (1 octet)
+
+ In case of an error, the error response will be returned.
+
+ Valid status values: Same as in Get Play Status Response
+
+ Opcode 0x09 - Register Notification Response command/response
+
+ Command parameters: Event (1 octet)
+ Type (1 octet)
+ Data length (1 octet)
+ Data (variable)
+
+ In case of an error, the error response will be returned.
+
+ Valid event values: 0x01 = Status Changed
+ 0x02 = Track Changed
+ 0x03 = Track Reached End
+ 0x04 = Track Reached Start
+ 0x05 = Position Changed
+ 0x08 = Setting Changed
+
+ Valid type values : 0x00 = Interim
+ 0x01 = Changed
+
+ Opcode 0x0a - Set Volume command/response
+
+ Command parameters: Value (1 octet)
+
+ In case of an error, the error response will be returned.
+
+Notifications:
+
+ Opcode 0x81 - Remote Features notification
+
+ Notification parameters: Remote address (6 octets)
+ Features (1 octet)
+
+ Valid features values : 0x00 = None
+ 0x01 = Metadata
+ 0x02 = Absolute Volume
+ 0x03 = Browse
+
+ Opcode 0x82 - Get Play Status notification
+
+ Notification parameters: <none>
+
+ Opcode 0x83 - List Player Attributes notification
+
+ Notification parameters: <none>
+
+ Opcode 0x84 - List Player Values notification
+
+ Notification parameters: Attribute (1 octet)
+
+ Valid attribute values: Same as in List Player Attributes
+
+ Opcode 0x85 - Get Player Values notification
+
+ Notification parameters: Number of attributes (1 octet)
+ Attribute # (1 octet)
+ ...
+
+ Valid attribute values: Same as in List Player Attributes
+
+ Opcode 0x86 - Get Player Attributes Text notification
+
+ Notification parameters: Number of attributes (1 octet)
+ Attribute # (1 octet)
+ ...
+
+ Valid attribute values: Same as in List Player Attributes
+
+ Opcode 0x87 - Get Player Values Text notification
+
+ Notification parameters: Attribute (1 octet)
+ Number of values (1 octet)
+ Value # (1 octet)
+ ...
+
+ Valid attribute values: Same as in List Player Attributes
+
+ Opcode 0x88 - Set Player Values notification
+
+ Notification parameters: Number of attributes (1 octet)
+ Attribute # (1 octet)
+ Value # (1 octet)
+ ...
+
+ Valid attribute values: Same as in List Player Attributes
+
+ Opcode 0x89 - Get Element Attributes notification
+
+ Notification parameters: Number of attributes (1 octet)
+ Attribute # (1 octet)
+ ...
+
+ Valid attribute values: Same as in Get Element Attribute
+
+ Opcode 0x8a - Register Notification notification
+
+ Notification parameters: Event (1 octet)
+ Parameter (4 octets)
+
+ Valid event values: Same as in Register Notification
+
+ Opcode 0x8b - Volume Changed notification
+
+ Notification parameters: Volume (1 octet)
+ Type (1 octet)
+
+ Valid type values: Same as in Register Notification
+
+ Opcode 0x8c - Passthrough Command notification
+
+ Notification parameters: ID (1 octet)
+ State (1 octet)
+
+Bluetooth GATT HAL (ID 9)
+=========================
+
+Android HAL name: "gatt" (BT_PROFILE_GATT_ID)
+
+Structures:
+
+ GATT Service ID: UUID (16 octets)
+ Instance ID (1 octet)
+ Is Primary (1 octet)
+
+ GATT Included Service ID: UUID (16 octets)
+ Instance ID (1 octet)
+ Is Primary (1 octet)
+
+ GATT Characteristic ID: UUID (16 octets)
+ Instance ID (1 octet)
+
+ GATT Descriptor ID: UUID (16 octets)
+ Instance ID (1 octet)
+
+Commands and responses:
+
+ Opcode 0x00 - Error response
+
+ Opcode 0x01 - Client Register command/response
+
+ Command parameters: Service UUID (16 octets)
+ Response parameters: <none>
+
+ In case of an error, the error response will be returned.
+
+ Opcode 0x02 - Client Unregister command/response
+
+ Command parameters: Client Interface (4 octets)
+ Response parameters: <none>
+
+ In case of an error, the error response will be returned.
+
+ Opcode 0x03 - Client Scan command/response
+
+ Command parameters: Client Interface (4 octets)
+ Start (1 octet)
+ Response parameters: <none>
+
+ In case of an error, the error response will be returned.
+
+ Opcode 0x04 - Client Connect Device command/response
+
+ Command parameters: Client Interface (4 octets)
+ Remote address (6 octets)
+ Is Direct (1 octet)
+ Transport (4 octets)
+ Response parameters: <none>
+
+ Valid transport value: 0x00 = Auto
+ 0x01 = BR/EDR
+ 0x02 = LE
+
+ In case of an error, the error response will be returned.
+
+ Opcode 0x05 - Client Disconnect Device command/response
+
+ Command parameters: Client Interface (4 octets)
+ Remote address (6 octets)
+ Connection ID (4 octets)
+ Response parameters: <none>
+
+ In case of an error, the error response will be returned.
+
+ Opcode 0x06 - Client Listen command/response
+
+ Command parameters: Client Interface (4 octets)
+ Start (1 octet)
+ Response parameters: <none>
+
+ In case of an error, the error response will be returned.
+
+ Opcode 0x07 - Client Refresh command/response
+
+ Command parameters: Client Interface (4 octets)
+ Remote address (6 octets)
+ Response parameters: <none>
+
+ In case of an error, the error response will be returned.
+
+ Opcode 0x08 - Client Search Service command/response
+
+ Command parameters: Connection ID (4 octets)
+ Filtered (1 octet)
+ Filter UUID (16 octets)
+ Response parameters: <none>
+
+ Filter UUID shall only be present when Filtered is non-zero.
+
+ In case of an error, the error response will be returned.
+
+ Opcode 0x09 - Client Get Included Service command/response
+
+ Command parameters: Connection ID (4 octets)
+ GATT Service ID (18 octets)
+ Continuation (1 octet)
+ GATT Included Service ID (18 octets)
+ ...
+ Response parameters: <none>
+
+ GATT Included Service ID shall only be present when Continuation
+ is non-zero.
+
+ In case of an error, the error response will be returned.
+
+ Opcode 0x0a - Client Get Characteristic command/response
+
+ Command parameters: Connection ID (4 octets)
+ GATT Service ID (18 octets)
+ Continuation (1 octet)
+ GATT Characteristic ID (17 octets)
+ ...
+ Response parameters: <none>
+
+ GATT Characteristic ID shall only be present when Continuation
+ is non-zero.
+
+ In case of an error, the error response will be returned.
+
+ Opcode 0x0b - Client Get Descriptor command/response
+
+ Command parameters: Connection ID (4 octets)
+ GATT Service ID (18 octets)
+ GATT Characteristic ID (17 octets)
+ Continuation (1 octet)
+ GATT Descriptor ID (17 octets)
+ ...
+ Response parameters: <none>
+
+ GATT Descriptor ID shall only be present when Continuation is
+ non-zero.
+
+ In case of an error, the error response will be returned.
+
+ Opcode 0x0c - Client Read Characteristic command/response
+
+ Command parameters: Connection ID (4 octets)
+ GATT Service ID (18 octets)
+ GATT Characteristic ID (17 octets)
+ Authorization (4 octets)
+ Response parameters: <none>
+
+ In case of an error, the error response will be returned.
+
+ Opcode 0x0d - Client Write Characteristic command/response
+
+ Command parameters: Connection ID (4 octets)
+ GATT Service ID (18 octets)
+ GATT Characteristic ID (17 octets)
+ Write Type (4 octets)
+ Length (4 octets)
+ Authorization Req. (4 octets)
+ Value (variable)
+ Response parameters: <none>
+
+ Valid Write Type: 0x01 = No response
+ 0x02 = Default
+ 0x03 = Prepare
+ 0x04 = Signed
+
+ In case of an error, the error response will be returned.
+
+ Opcode 0x0e - Client Read Descriptor command/response
+
+ Command parameters: Connection ID (4 octets)
+ GATT Service ID (18 octets)
+ GATT Characteristic ID (17 octets)
+ GATT Descriptor ID (17 octets)
+ Authorization Req. (4 octets)
+ Response parameters: <none>
+
+ In case of an error, the error response will be returned.
+
+ Opcode 0x0f - Client Write Descriptor command/response
+
+ Command parameters: Connection ID (4 octets)
+ GATT Service ID (18 octets)
+ GATT Characteristic ID (17 octets)
+ GATT Descriptor ID (17 octets)
+ Write Type (4 octets)
+ Length (4 octets)
+ Authorization Req. (4 octets)
+ Value (variable)
+ Response parameters: <none>
+
+ Valid Write Type: 0x01 = No response
+ 0x02 = Default
+ 0x03 = Prepare
+ 0x04 = Signed
+
+ In case of an error, the error response will be returned.
+
+ Opcode 0x10 - Client Execute Write command/response
+
+ Command parameters: Connection ID (4 octets)
+ Execute (4 octets)
+ Response parameters: <none>
+
+ In case of an error, the error response will be returned.
+
+ Opcode 0x11 - Client Register For Notification command/response
+
+ Command parameters: Client Interface (4 octets)
+ Remote address (6 octets)
+ GATT Service ID (18 octets)
+ GATT Characteristic ID (17 octets)
+ Response parameters: <none>
+
+ In case of an error, the error response will be returned.
+
+ Opcode 0x12 - Client Deregister For Notification command/response
+
+ Command parameters: Client Interface (4 octets)
+ Remote address (6 octets)
+ GATT Service ID (18 octets)
+ GATT Characteristic ID (17 octets)
+ Response parameters: <none>
+
+ In case of an error, the error response will be returned.
+
+ Opcode 0x13 - Client Read Remote RSSI command/response
+
+ Command parameters: Client Interface (4 octets)
+ Remote address (6 octets)
+ Response parameters: <none>
+
+ In case of an error, the error response will be returned.
+
+ Opcode 0x14 - Client Get Device Type command/response
+
+ Command parameters: Remote address (6 octets)
+ Response parameters: Device Type
+
+ Valid Device Type: 0x01 = BREDR
+ 0x02 = BLE
+ 0x03 = DUAL
+
+ In case of an error, the error response will be returned.
+
+ Opcode 0x15 - Client Set Advertising data command/response
+
+ Command parameters: Server Interface (4 octets)
+ Set Scan Resp. (1 octet)
+ Include Name (1 octet)
+ Include TX Power (1 octet)
+ Min. Interval (4 octets)
+ Max. Interval (4 octets)
+ Appearance (4 octets)
+ Manufacturer Len. (2 octets)
+ Manufacturer Data (variable)
+ Response parameters: <none>
+
+ In case of an error, the error response will be returned.
+
+ Opcode 0x16 - Client Test Command command/response
+
+ Command parameters: Command (4 octets)
+ Address (6 octets)
+ UUID (16 octets)
+ U1 (2 octets)
+ U2 (2 octets)
+ U3 (2 octets)
+ U4 (2 octets)
+ U5 (2 octets)
+ Response parameters: <none>
+
+ In case of an error, the error response will be returned.
+
+ Opcode 0x17 - Server Register command/response
+
+ Command parameters: UUID (16 octets)
+ Response parameters: <none>
+
+ In case of an error, the error response will be returned.
+
+ Opcode 0x18 - Server Unregister command/response
+
+ Command parameters: Server (4 octets)
+ Response parameters: <none>
+
+ In case of an error, the error response will be returned.
+
+ Opcode 0x19 - Server Connect Peripheral command/response
+
+ Command parameters: Server (4 octets)
+ Remote address (6 octets)
+ Is Direct (1 octet)
+ Transport (4 octets)
+ Response parameters: <none>
+
+ In case of an error, the error response will be returned.
+
+ Opcode 0x1a - Server Disconnect Peripheral command/response
+
+ Command parameters: Server (4 octets)
+ Remote address (6 octets)
+ Connection ID (1 octet)
+ Response parameters: <none>
+
+ In case of an error, the error response will be returned.
+
+ Opcode 0x1b - Server Add Service command/response
+
+ Command parameters: Server (4 octets)
+ GATT Service ID (18 octets)
+ Number of Handles (4 octet)
+ Response parameters: <none>
+
+ Valid GATT Service ID: UUID (16 octets)
+ Instance ID (1 octet)
+ Is Primary (1 octet)
+
+ In case of an error, the error response will be returned.
+
+ Opcode 0x1c - Server Add Included Service command/response
+
+ Command parameters: Server (4 octets)
+ Service handle (4 octets)
+ Included handle (4 octets)
+ Response parameters: <none>
+
+ In case of an error, the error response will be returned.
+
+ Opcode 0x1d - Server Add Characteristic command/response
+
+ Command parameters: Server (4 octets)
+ Service handle (4 octets)
+ UUID (16 octets)
+ Properties (4 octets)
+ Permissions (4 octets)
+ Response parameters: <none>
+
+ In case of an error, the error response will be returned.
+
+ Opcode 0x1e - Server Add Descriptor command/response
+
+ Command parameters: Server (4 octets)
+ Service handle (4 octets)
+ UUID (16 octets)
+ Permissions (4 octets)
+ Response parameters: <none>
+
+ In case of an error, the error response will be returned.
+
+ Opcode 0x1f - Server Start Service command/response
+
+ Command parameters: Server (4 octets)
+ Service handle (4 octets)
+ Transport (4 octets)
+ Response parameters: <none>
+
+ In case of an error, the error response will be returned.
+
+ Opcode 0x20 - Server Stop Service command/response
+
+ Command parameters: Server (4 octets)
+ Service handle (4 octets)
+ Response parameters: <none>
+
+ In case of an error, the error response will be returned.
+
+ Opcode 0x21 - Server Delete Service command/response
+
+ Command parameters: Server (4 octets)
+ Service handle (4 octets)
+ Response parameters: <none>
+
+ In case of an error, the error response will be returned.
+
+ Opcode 0x22 - Server Send Indication command/response
+
+ Command parameters: Server (4 octets)
+ Attribute handle (4 octets)
+ Connection ID (4 octets)
+ Length (4 octets)
+ Confirmation (4 octets)
+ Value (variable)
+ Response parameters: <none>
+
+ In case of an error, the error response will be returned.
+
+ Opcode 0x23 - Server Send Response command/response
+
+ Command parameters: Connection ID (4 octets)
+ Transaction ID (4 octets)
+ Handle (2 octets)
+ Offset (2 octets)
+ Auth Request (1 octect)
+ Status (4 octets)
+ GATT Response (4 octets)
+ Response parameters: <none>
+
+ Valid GATT Response: GATT Value (607 octets)
+ Handle (2 octets)
+
+ Valid GATT Value: Value (600 octets)
+ Handle (2 octets)
+ Offset (2 octets)
+ Length (2 octets)
+ Authentication Request (1 octet)
+
+ In case of an error, the error response will be returned.
+
+ Opcode 0x24 - Client Scan Filter Params Setup command/response
+
+ Command parameters: Client Interface (4 octets)
+ Action (4 octets)
+ Filter Index (4 octets)
+ Features (4 octets)
+ List Type (4 octets)
+ Filter Type (4 octets)
+ RSSI High Threshold (4 octets)
+ RSSI Low Threshold (4 octets)
+ Delivery Mode (4 octets)
+ Found Timeout (4 octets)
+ Lost Timeout (4 octets)
+ Found Timeout Count (4 octets)
+ Response parameters: <none>
+
+ In case of an error, the error response will be returned.
+
+ Opcode 0x25 - Client Scan Filter Add Remove command/response
+
+ Command parameters: Client Interface (4 octets)
+ Action (4 octets)
+ Filter Type (4 octets)
+ Filter Index (4 octets)
+ Company ID (4 octets)
+ Company ID Mask (4 octets)
+ UUID (16 octets)
+ UUID Mask (16 octets)
+ Address (6 octets)
+ Address Type (1 octet)
+ Data Length (4 octets)
+ Data (variable)
+ Mask Length (4 octets)
+ Mask (variable)
+ Response parameters: <none>
+
+ In case of an error, the error response will be returned.
+
+ Opcode 0x26 - Client Scan Filter Clear command/response
+
+ Command parameters: Client Interface (4 octets)
+ Filter Index (4 octets)
+ Response parameters: <none>
+
+ In case of an error, the error response will be returned.
+
+ Opcode 0x27 - Client Scan Filter Enable command/response
+
+ Command parameters: Client Interface (4 octets)
+ Enable (1 octet)
+ Response parameters: <none>
+
+ In case of an error, the error response will be returned.
+
+ Opcode 0x28 - Client Configure MTU command/response
+
+ Command parameters: Connection ID (4 octets)
+ MTU (4 octets)
+ Response parameters: <none>
+
+ In case of an error, the error response will be returned.
+
+ Opcode 0x29 - Client Connection Parameter Update command/response
+
+ Command parameters: Address (6 octets)
+ Min Interval (4 octets)
+ Max Interval (4 octets)
+ Latency (4 octets)
+ Timeoutl (4 octets)
+ Response parameters: <none>
+
+ In case of an error, the error response will be returned.
+
+ Opcode 0x2a - Client Set Scan Parameters command/response
+
+ Command parameters: Scan Interval (4 octets)
+ Scan Window (4 octets)
+ Response parameters: <none>
+
+ In case of an error, the error response will be returned.
+
+ Opcode 0x2b - Client Setup Multi Advertising command/response
+
+ Command parameters: Client ID (4 octets)
+ Min Interval (4 octets)
+ Max Interval (4 octets)
+ ADV Type (4 octets)
+ Channel Map (4 octets)
+ TX Power (4 octets)
+ Timeout (4 octets)
+ Response parameters: <none>
+
+ In case of an error, the error response will be returned.
+
+ Opcode 0x2c - Client Update Multi Advertising command/response
+
+ Command parameters: Client ID (4 octets)
+ Min Interval (4 octets)
+ Max Interval (4 octets)
+ ADV Type (4 octets)
+ Channel Map (4 octets)
+ TX Power (4 octets)
+ Timeout (4 octets)
+ Response parameters: <none>
+
+ In case of an error, the error response will be returned.
+
+ Opcode 0x2d - Client Setup Multi Advertising Instance command/response
+
+ Command parameters: Client ID (4 octets)
+ Set Scan Response (1 octet)
+ Include Name (1 octet)
+ Include TX Power (1 octet)
+ Appearance (4 octets)
+ Manufacturer Data Length (4 octets)
+ Manufacturer Data (variable)
+ Service Data Length (4 octets)
+ Service Data (variable)
+ Service UUID Length (4 octets)
+ Service UUID (variable)
+ Response parameters: <none>
+
+ In case of an error, the error response will be returned.
+
+ Opcode 0x2e - Client Disable Multi Advertising Instance command/response
+
+ Command parameters: Client ID (4 octets)
+ Response parameters: <none>
+
+ In case of an error, the error response will be returned.
+
+ Opcode 0x2f - Client Configure Batchscan command/response
+
+ Command parameters: Client ID (4 octets)
+ Full Max (4 octets)
+ Trunc Max (4 octets)
+ Notify Threshold (4 octets)
+ Response parameters: <none>
+
+ In case of an error, the error response will be returned.
+
+ Opcode 0x30 - Client Enable Batchscan command/response
+
+ Command parameters: Client ID (4 octets)
+ Scan Mode (4 octets)
+ Scan Interval (4 octets)
+ Scan Window (4 octets)
+ Address Type (4 octets)
+ Discard Rule (4 octets)
+ Response parameters: <none>
+
+ In case of an error, the error response will be returned.
+
+ Opcode 0x31 - Client Disable Batchscan command/response
+
+ Command parameters: Client ID (4 octets)
+ Response parameters: <none>
+
+ In case of an error, the error response will be returned.
+
+ Opcode 0x32 - Client Read Batchscan Resports command/response
+
+ Command parameters: Client ID (4 octets)
+ Scan Mode (4 octets)
+ Response parameters: <none>
+
+ In case of an error, the error response will be returned.
+
+Notifications:
+
+ Opcode 0x81 - Client Register notification
+
+ Notification parameters: Status (4 octets)
+ Client Interface (4 octets)
+ UUID (16 octets)
+
+ Opcode 0x82 - Client Scan Result notification
+
+ Notification parameters: Address (6 octets)
+ RSSI (4 octets)
+ Length (2 octets)
+ Data (variable)
+
+ Opcode 0x83 - Client Connect Device notification
+
+ Notification parameters: Connection ID (4 octets)
+ Status (4 octets)
+ Client Interface (4 octets)
+ Address (6 octets)
+
+ Opcode 0x84 - Client Disconnect Device notification
+
+ Notification parameters: Connection ID (4 octets)
+ Status (4 octets)
+ Client Interface (4 octets)
+ Address (6 octets)
+
+ Opcode 0x85 - Client Search Complete notification
+
+ Notification parameters: Connection ID (4 octets)
+ Status (4 octets)
+
+ Opcode 0x86 - Client Search Result notification
+
+ Notification parameters: Connection ID (4 octets)
+ GATT Service ID (18 octets)
+
+ Opcode 0x87 - Client Get Characteristic notification
+
+ Notification parameters: Connection ID (4 octets)
+ Status (4 octets)
+ GATT Service ID (18 octets)
+ GATT Characteristic ID (17 octets)
+ Char Prop. (4 octets)
+
+ Opcode 0x88 - Client Get Descriptor notification
+
+ Notification parameters: Connection ID (4 octets)
+ Status (4 octets)
+ GATT Service ID (18 octets)
+ GATT Characteristic ID (17 octets)
+ GATT Descriptor ID (17 octets)
+
+ Opcode 0x89 - Client Get Included Service notification
+
+ Notification parameters: Connection ID (4 octets)
+ Status (4 octets)
+ GATT Service ID (18 octets)
+ GATT Included Service ID (18 octets)
+
+ Opcode 0x8a - Client Register For Notification notification
+
+ Notification parameters: Connection ID (4 octets)
+ Registered (4 octets)
+ Status (4 octets)
+ GATT Service ID (18 octets)
+ GATT Characteristic ID (17 octets)
+
+ Opcode 0x8b - Client Notify notification
+
+ Notification parameters: Connection ID (4 octets)
+ Address (6 octets)
+ GATT Service ID (18 octets)
+ GATT Characteristic ID (17 octets)
+ Is Notify (1 octet)
+ Length (2 octets)
+ Value (variable)
+
+ Opcode 0x8c - Client Read Characteristic notification
+
+ Notification parameters: Connection ID (4 octets)
+ Status (4 octets)
+ GATT Read Parameters (variable)
+
+ Valid GATT Read Parameters: GATT Service ID (18 octets)
+ GATT Characteristic ID (17 octets)
+ GATT Descriptor ID (17 octets)
+ Value Type (4 octets)
+ Status (1 octet)
+ Length (2 octets)
+ Value (variable)
+
+ Opcode 0x8d - Client Write Characteristic notification
+
+ Notification parameters: Connection ID (4 octets)
+ Status (4 octets)
+ GATT Write Parameters (53 octets)
+
+ Valid GATT Write Parameters: GATT Service ID (18 octets)
+ GATT Characteristic ID (17 octets)
+ GATT Description ID (17 octets)
+ Status (1 octet)
+
+ Opcode 0x8e - Client Read Descriptor notification
+
+ Notification parameters: Connection ID (4 octets)
+ Status (4 octets)
+ GATT Read Parameters (variable)
+
+ Valid GATT Read Parameters: As described in Read Characteristic
+
+ Opcode 0x8f - Client Write Descriptor notification
+
+ Notification parameters: Connection ID (4 octets)
+ Status (4 octets)
+ GATT Write Parameters (53 octets)
+
+ Valid GATT Write Parameters: As described in Write Characteristic
+
+ Opcode 0x90 - Client Execute Write notification
+
+ Notification parameters: Connection ID (4 octets)
+ Status (4 octets)
+
+ Opcode 0x91 - Client Read Remote RSSI notification
+
+ Notification parameters: Client (4 octets)
+ Address (6 octets)
+ RSSI (4 octets)
+ Status (4 octets)
+
+ Opcode 0x92 - Client Listen notification
+
+ Notification parameters: Status (4 octets)
+ Server Interface (4 octets)
+
+ Opcode 0x93 - Server Register notification
+
+ Notification parameters: Status (4 octets)
+ Server (4 octets)
+ UUID (16 octets)
+
+ Opcode 0x94 - Server Connection notification
+
+ Notification parameters: Connection ID (4 octets)
+ Server (4 octets)
+ Connected (4 octets)
+ Address (6 octets)
+
+ Opcode 0x95 - Server Service Added notification
+
+ Notification parameters: Status (4 octets)
+ Server (4 octets)
+ GATT Service ID (18 octets)
+ Service Handle (4 octets)
+
+ Opcode 0x96 - Server Included Service Added notification
+
+ Notification patemeters: Status (4 octets)
+ Server (4 octets)
+ Service Handle (4 octets)
+ Included Service Handle (4 octets)
+
+ Opcode 0x97 - Server Characteristic Added notification
+
+ Notification parameters: Status (4 octets)
+ Server (4 octets)
+ UUID (16 octets)
+ Service Handle (4 octets)
+ Characteristic Handle (4 octets)
+
+ Opcode 0x98 - Server Descriptor Added notification
+
+ Notification parameters: Status (4 octets)
+ Server (4 octets)
+ UUID (6 octets)
+ Service Handle (4 octets)
+ Descriptor Handle (4 octets)
+
+ Opcode 0x99 - Server Service Started notification
+
+ Notification parameters: Status (4 octets)
+ Server (4 octets)
+ Service Handle (4 octets)
+
+ Opcode 0x9a - Server Service Stopped notification
+
+ Notification parameters: Status (4 octets)
+ Server (4 octets)
+ Service Handle (4 octets)
+
+ Opcode 0x9b - Server Service Deleted notification
+
+ Notification parameters: Status (4 octets)
+ Server (4 octets)
+ Service Handle (4 octets)
+
+ Opcode 0x9c - Server Request Read notification
+
+ Notification parameters: Connection ID (4 octets)
+ Trans ID (4 octets)
+ Address (6 octets)
+ Attribute Handle (4 octets)
+ Offset (4 octets)
+ Is Long (1 octet)
+
+ Opcode 0x9d - Server Request Write notification
+
+ Notification parameters: Connection ID (4 octets)
+ Trans ID (4 octets)
+ Address (6 octets)
+ Attribute Handle (4 octets)
+ Offset (4 octets)
+ Length (4 octets)
+ Need Response (4 octets)
+ Is Prepare (1 octet)
+ Value (variable)
+
+ Opcode 0x9e - Server Request Execute Write notification
+
+ Notification parameters: Connection ID (4 octets)
+ Trans ID (4 octets)
+ Address (6 octets)
+ Execute Write (4 octets)
+
+ Opcode 0x9f - Server Response Confirmation notification
+
+ Notification parameters: Status (4 octets)
+ Handle (4 octets)
+
+ Opcode 0xa0 - Client Configure MTU notification
+
+ Notification parameters: Connection ID (4 octets)
+ Status (4 octets)
+ MTU (4 octets)
+
+ Opcode 0xa1 - Client Filter Configuration notification
+
+ Notification parameters: Action (4 octets)
+ Client ID (4 octets)
+ Status (4 octets)
+ Filter Type (4 octets)
+ Available Space (4 octets)
+
+ Opcode 0xa2 - Client Filter Parameters notification
+
+ Notification parameters: Action (4 octets)
+ Client ID (4 octets)
+ Status (4 octets)
+ Available Space (4 octets)
+
+ Opcode 0xa3 - Client Filter Status notification
+
+ Notification parameters: Enable (4 octets)
+ Client ID (4 octets)
+ Status (4 octets)
+
+ Opcode 0xa4 - Client Multi Advertising Enable notification
+
+ Notification parameters: Client ID (4 octets)
+ Status (4 octets)
+
+ Opcode 0xa5 - Client Multi Advertising Update notification
+
+ Notification parameters: Client ID (4 octets)
+ Status (4 octets)
+
+ Opcode 0xa6 - Client Multi Advertising Data notification
+
+ Notification parameters: Client ID (4 octets)
+ Status (4 octets)
+
+ Opcode 0xa7 - Client Multi Advertising Disable notification
+
+ Notification parameters: Client ID (4 octets)
+ Status (4 octets)
+
+ Opcode 0xa8 - Client Congestion notification
+
+ Notification parameters: Connection ID (4 octets)
+ Congested (1 octet)
+
+ Opcode 0xa9 - Client Configure Batchscan notification
+
+ Notification parameters: Client ID (4 octets)
+ Status (4 octets)
+
+ Opcode 0xaa - Client Enable Batchscan notification
+
+ Notification parameters: Action (4 octets)
+ Client ID (4 octets)
+ Status (4 octets)
+
+ Opcode 0xab - Client Batchscan Reports notification
+
+ Notification parameters: Client ID (4 octets)
+ Status (4 octets)
+ Report Format (4 octets)
+ Num Reports (4 octets)
+ Data Length (4 octets)
+ Data (variable)
+
+ Opcode 0xac - Client Batchscan Threshold notification
+
+ Notification parameters: Client ID (4 octets)
+
+ Opcode 0xad - Client Track ADV notification
+
+ Notification parameters: Client ID (4 octets)
+ Filter Index (4 octets)
+ Address Type (4 octets)
+ Address (6 octets)
+ State (4 octets)
+
+ Opcode 0xae - Server Indication Sent notification
+
+ Notification parameters: Connection ID (4 octets)
+ Status (4 octets)
+
+ Opcode 0xaf - Server Congestion notification
+
+ Notification parameters: Connection ID (4 octets)
+ Congested (1 octet)
+
+ Opcode 0xb0 - Server MTU Changed notification
+
+ Notification parameters: Connection ID (4 octets)
+ MTU (4 octets)
+
+
+Bluetooth Handsfree Client HAL (ID 10)
+======================================
+
+Android HAL name: "hf_client" (BT_PROFILE_HANDSFREE_CLIENT_ID)
+
+Commands and response:
+
+ Opcode 0x00 - Error response
+
+ Opcode 0x01 - Connect command/respose
+
+ Command parameters: Remote address (6 octects)
+ Response parameters: <none>
+
+ In case of an error, the error response will be returned.
+
+ Opcode 0x02 - Disonnect command/response
+
+ Command parameters: Remote address (6 octetcs)
+ Response parameters: <none>
+
+ In case of an error, the error response will be returned.
+
+ Opcode 0x03 - Connect Audio command/response
+
+ Command parameters: Remote address (6 octets)
+ Response parameters: <none>
+
+ In case of an error, the error response will be returned.
+
+ Opcode 0x04 - Disconnect Audio command/response
+
+ Command parameters: Remote address (6 octets)
+ Response parameters: <none>
+
+ In case of an error, the error response will be returned.
+
+ Opcode 0x05 - Start Voice Recognition command/response
+
+ Command parameters: <none>
+ Response parameters: <none>
+
+ In case of an error, the error response will be returned.
+
+ Opcode 0x06 - Stop Voice Recognition command/response
+
+ Command parameters: <none>
+ Response parameters: <none>
+
+ In case of an error, the error response will be returned.
+
+ Opcode 0x07 - Volume Control command/response
+
+ Command parameters: Volume type (1 octet)
+ Volume (1 octet)
+ Response parameters: <none>
+
+ Valid volume types: 0x00 = Speaker
+ 0x01 = Microphone
+
+ In case of an error, the error response will be returned.
+
+ Opcode 0x08 - Dial command/response
+
+ Command parameters: Number (string)
+ Response parameters: <none>
+
+ In case of an error, the error response will be returned.
+
+ Opcode 0x09 - Dial Memory command/response
+
+ Command parameters: Location (4 octet)
+ Response parameters: <none>
+
+ In case of an error, the error response will be returned.
+
+ Opcode 0x10 - Handle Call Action command/response
+
+ Command parameters: Action (1 octet)
+ Call Index (1 octet)
+ Response parameters: <none>
+
+ Valid actions: 0x00 = CHLD_0
+ 0x01 = CHLD_1
+ 0x02 = CHLD_2
+ 0x03 = CHLD_3
+ 0x04 = CHLD_4
+ 0x05 = CHLD_1x
+ 0x06 = CHLD_2x
+ 0x07 = ATA
+ 0x08 = CHUP
+ 0x09 = BTRH_0
+ 0x10 = BTRH_1
+ 0x11 = BTRH_2
+
+ In case of an error, the error response will be returned.
+
+ Opcode 0x11 - Query Current Calls commad/response
+
+ Command parameters: <none>
+ Response parameters: <none>
+
+ In case of an error, the error response will be returned.
+
+ Opcode 0x12 - Query Current Operator Name
+
+ Command parameters: <none>
+ Response parameters: <none>
+
+ In case of an error, the error response will be returned.
+
+ Opcode 0x13 - Retrieve Subscriber Info command/response
+
+ Command parameters: <none>
+ Response parameters: <none>
+
+ In case of an error, the error response will be returned.
+
+ Opcode 0x14 - Send DTMF Tone command/response
+
+ Command parameters: Tone (1 octet)
+ Response parameters: <none>
+
+ In case of an error, the error response will be returned.
+
+ Opcode 0x15 - Request Last Voice Tag Number command/response
+
+ Command parameters: <none>
+ Response parameters: <none>
+
+ In case of an error, the error response will be returned.
+
+Notifications:
+
+ Opcode 0x81 - Connection State Changed notification
+
+ Notification parameters: State (1 octet)
+ Peer Features (4 octets)
+ CHLD Features (4 octets)
+ Address (6 octets)
+
+ Valid State values: 0x00 = Disconnected
+ 0x01 = Connecting
+ 0x02 = Connected
+ 0x03 = SLC Connected
+ 0x04 = Disconnecting
+
+ Peer Features is a bitmask of the supported features. Currently
+ available bits:
+
+ 0 Three way calling
+ 1 Echo cancellation and/or noise reduction
+ 2 Voice recognition
+ 3 In band ring tone
+ 4 Attach a number to a voice tag
+ 5 Ability to reject a call
+ 6 Enhanced call status
+ 7 Enhanced call control
+ 8 Extended Error Result Codes
+ 9 Codec negotiations
+ 10-31 Reserved for future use
+
+ CHLD Features is a bitmask of the supported features. Currently
+ available bits:
+
+ 0 Release waiting call or held calls
+ 1 Release active calls and accept other call
+ 2 Release specified active call only
+ 3 Place all active calls on hold and accept other call
+ 4 Request private mode with secified call
+ 5 Add a held call to the multiparty
+ 6 Connect two calls and leave multiparty
+ 7-31 Reserved for future use
+
+ Note: Peer and CHLD Features are valid only in SCL Connected state
+
+
+ Opcode 0x82 - Audio State Changed notification
+
+ Notification parameters: State (1 octet)
+ Address (6 octets)
+
+ Valid State values: 0x00 = Disconnected
+ 0x01 = Connecting
+ 0x02 = Connected
+ 0x03 = Connected mSBC
+
+ Opcode 0x83 - Voice Recognition State Changed notification
+
+ Notification parameters: State (1 octet)
+
+ Valid State values: 0x00 = VR Stopped
+ 0x01 = VR Started
+
+ Opcode 0x84 - Network State Changed notification
+
+ Notification parameters: State (1 octet)
+
+ Valid State values: 0x00 = Network Not Available
+ 0x01 = Network Available
+
+ Opcode 0x85 - Network Roaming Type Changed notification
+
+ Notification parameters: Type (1 octet)
+
+ Valid Type values: 0x00 = Home
+ 0x01 = Roaming
+
+ Opcode 0x86 - Network Signal Strength notification
+
+ Notification parameters: Signal Strength (1 octet)
+
+ Opcode 0x87 - Battery Level notification
+
+ Notification parameters: Battery Level (1 octet)
+
+ Opcode 0x88 - Current Operator Name notification
+
+ Notification parameters: Name (string)
+
+ Opcode 0x89 - Call Indicatior notification
+
+ Notification parameters: Call (1 octet)
+
+ Valid Call values: 0x00 = No Call In Progress
+ 0x01 = Call In Progress
+
+ Opcode 0x8a - Call Setup Indicator notification
+
+ Notification parameters: Call Setup (1 octet)
+
+ Valid Call Setup values: 0x00 = None
+ 0x01 = Incoming
+ 0x02 = Outgoing
+ 0x03 = Alerting
+
+ Opcode 0x8b - Call Held Indicator notification
+
+ Notification parameters: Call Held (1 octet)
+
+ Valid Call Held values: 0x00 = None
+ 0x01 = Hold and Active
+ 0x02 = Hold
+
+ Opcode 0x8c - Resposne and Hold Status notification
+
+ Notification parameters: Status (1 octet)
+
+ Valid Status values: 0x00 = Held
+ 0x01 = Accept
+ 0x02 = Reject
+
+ Opcode 0x8d - Calling Line Identification notification
+
+ Notification parameters: Number (string)
+
+ Note: This will be called only on incoming call if number is
+ provided.
+
+ Opcode 0x8e - Call Waiting notification
+
+ Notification parameters: Nunmber (string)
+
+ Opcode 0x8f - Current Calls List notification
+
+ Notification parameters: Index (1 octet)
+ Direction (1 octet)
+ Call State (1 octet)
+ Multiparty (1 octet)
+ Number (string)
+
+ Valid Direction values: 0x00 = Outgoing
+ 0x01 = Incoming
+
+ Valid Call Sate values: 0x00 = Active
+ 0x01 = Held
+ 0x02 = Dialing
+ 0x03 = Alerting
+ 0x04 = Incoming
+ 0x05 = Waiting
+ 0x06 = Call held by Response and Hold
+
+ Valid Multiparty values: 0x00 = Single Call
+ 0x01 = Multiparty (conference) Call
+
+ Note: Number might be empty
+
+ Opcode 0x90 - Volume Changed notification
+
+ Notification parameters: Type (1 octet)
+ Volume (1 octet)
+
+ Valid Type values: 0x00 = Speaker
+ 0x01 = Microphone
+
+ Opcode 0x91 - Command Complete Callback notification
+
+ Notification parameters: Type (1 octet)
+ CME (1 octet)
+
+ Valid Type values: 0x00 = OK
+ 0x01 = Error
+ 0x02 = Error no carrier
+ 0x03 = Error busy
+ 0x04 = Error no answer
+ 0x05 = Error delayed
+ 0x06 = Error blacklisted
+ 0x07 = Error CME
+
+ Note: CME parameter is valid only for Error CME type
+
+ Opcode 0x92 - Subscriber Service Info Callback notification
+
+ Notification parameters: Name (string)
+ Type (1 octet)
+
+ Valid Type values: 0x00 = Service unknown
+ 0x01 = Service voice
+ 0x02 = Service fax
+
+ Opcode 0x93 - In Band Ring Settings Callback notification
+
+ Notification parameters: State (1 octet)
+
+ Valid State values: 0x00 = In band ringtone not provided
+ 0x01 = In band ringtone provided
+
+ Opcode 0x94 - Last Voice Call Tag Number Callback notification
+
+ Notification parameters: Number (string)
+
+ Opcode 0x95 - Ring Indication notification
+
+ Notification parameters: <none>
+
+
+Bluetooth Map Client HAL (ID 11)
+=========================
+
+Android HAL name: "map_client" (BT_PROFILE_MAP_CLIENT_ID)
+
+Commands and responses:
+
+ Opcode 0x00 - Error response
+
+ Opcode 0x01 - Get Remote MAS Instances
+
+ Command parameters: Remote address (6 octets)
+ Response parameters: <none>
+
+ In case of an error, the error response will be returned.
+
+Notifications:
+
+ Opcode 0x81 - Remote MAS Instances notification
+
+ Notification parameters: Status (1 octet)
+ Remote address (6 octets)
+ Number of instances (4 octets)
+ Instance ID # (4 octets)
+ Channel # (4 octets)
+ Message types (4 octets)
+ Name # (string)
+
+Bluetooth Remote Control Controller HAL (ID 12)
+===================================
+
+Android HAL name: "avrcp-ctrl" (BT_PROFILE_AV_RC_CTRL_ID)
+
+Commands and responses:
+
+ Opcode 0x00 - Error response
+
+ Opcode 0x01 - Send Pass Through command/response
+
+ Command parameters: Remote Address (6 octets)
+ Key Code (1 octet)
+ Key State (1 octet)
+
+ In case of an error, the error response will be returned.
+
+Notifications:
+
+ Opcode 0x81 - Passthrough Response Notification
+
+ Notification parameters: ID (1 octet)
+ Key State (1 octet)
+
+ Opcode 0x82 - Connection State Notification
+
+ Notification parameters: State (1 octet)
+ Remote Address (6 octets)
diff --git a/repo/android/hal-ipc.c b/repo/android/hal-ipc.c
new file mode 100644
index 0000000..363072c
--- /dev/null
+++ b/repo/android/hal-ipc.c
@@ -0,0 +1,471 @@
+/*
+ * Copyright (C) 2013 Intel Corporation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include <pthread.h>
+#include <errno.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <stdbool.h>
+#include <poll.h>
+#include <unistd.h>
+#include <stdint.h>
+#include <stdlib.h>
+
+#include <cutils/properties.h>
+
+#include "hal.h"
+#include "hal-msg.h"
+#include "hal-log.h"
+#include "ipc-common.h"
+#include "hal-ipc.h"
+
+#define CONNECT_TIMEOUT (10 * 1000)
+
+static int listen_sk = -1;
+static int cmd_sk = -1;
+static int notif_sk = -1;
+
+static pthread_mutex_t cmd_sk_mutex = PTHREAD_MUTEX_INITIALIZER;
+
+static pthread_t notif_th = 0;
+
+struct service_handler {
+ const struct hal_ipc_handler *handler;
+ uint8_t size;
+};
+
+static struct service_handler services[HAL_SERVICE_ID_MAX + 1];
+
+void hal_ipc_register(uint8_t service, const struct hal_ipc_handler *handlers,
+ uint8_t size)
+{
+ services[service].handler = handlers;
+ services[service].size = size;
+}
+
+void hal_ipc_unregister(uint8_t service)
+{
+ services[service].handler = NULL;
+ services[service].size = 0;
+}
+
+static bool handle_msg(void *buf, ssize_t len, int fd)
+{
+ struct ipc_hdr *msg = buf;
+ const struct hal_ipc_handler *handler;
+ uint8_t opcode;
+
+ if (len < (ssize_t) sizeof(*msg)) {
+ error("IPC: message too small (%zd bytes)", len);
+ return false;
+ }
+
+ if (len != (ssize_t) (sizeof(*msg) + msg->len)) {
+ error("IPC: message malformed (%zd bytes)", len);
+ return false;
+ }
+
+ /* if service is valid */
+ if (msg->service_id > HAL_SERVICE_ID_MAX) {
+ error("IPC: unknown service (0x%x)", msg->service_id);
+ return false;
+ }
+
+ /* if service is registered */
+ if (!services[msg->service_id].handler) {
+ error("IPC: unregistered service (0x%x)", msg->service_id);
+ return false;
+ }
+
+ /* if opcode fit valid range */
+ if (msg->opcode < HAL_MINIMUM_EVENT) {
+ error("IPC: invalid opcode for service 0x%x (0x%x)",
+ msg->service_id, msg->opcode);
+ return false;
+ }
+
+ /*
+ * opcode is used as table offset and must be adjusted as events start
+ * with HAL_MINIMUM_EVENT offset
+ */
+ opcode = msg->opcode - HAL_MINIMUM_EVENT;
+
+ /* if opcode is valid */
+ if (opcode >= services[msg->service_id].size) {
+ error("IPC: invalid opcode for service 0x%x (0x%x)",
+ msg->service_id, msg->opcode);
+ return false;
+ }
+
+ handler = &services[msg->service_id].handler[opcode];
+
+ /* if payload size is valid */
+ if ((handler->var_len && handler->data_len > msg->len) ||
+ (!handler->var_len && handler->data_len != msg->len)) {
+ error("IPC: message size invalid for service 0x%x opcode 0x%x "
+ "(%u bytes)",
+ msg->service_id, msg->opcode, msg->len);
+ return false;
+ }
+
+ handler->handler(msg->payload, msg->len, fd);
+
+ return true;
+}
+
+static void *notification_handler(void *data)
+{
+ struct msghdr msg;
+ struct iovec iv;
+ struct cmsghdr *cmsg;
+ char cmsgbuf[CMSG_SPACE(sizeof(int))];
+ char buf[IPC_MTU];
+ ssize_t ret;
+ int fd;
+
+ bt_thread_associate();
+
+ while (true) {
+ memset(&msg, 0, sizeof(msg));
+ memset(buf, 0, sizeof(buf));
+ memset(cmsgbuf, 0, sizeof(cmsgbuf));
+
+ iv.iov_base = buf;
+ iv.iov_len = sizeof(buf);
+
+ msg.msg_iov = &iv;
+ msg.msg_iovlen = 1;
+
+ msg.msg_control = cmsgbuf;
+ msg.msg_controllen = sizeof(cmsgbuf);
+
+ ret = recvmsg(notif_sk, &msg, 0);
+ if (ret < 0) {
+ error("Receiving notifications failed: %s",
+ strerror(errno));
+ goto failed;
+ }
+
+ /* socket was shutdown */
+ if (ret == 0) {
+ pthread_mutex_lock(&cmd_sk_mutex);
+ if (cmd_sk == -1) {
+ pthread_mutex_unlock(&cmd_sk_mutex);
+ break;
+ }
+ pthread_mutex_unlock(&cmd_sk_mutex);
+
+ error("Notification socket closed");
+ goto failed;
+ }
+
+ fd = -1;
+
+ /* Receive auxiliary data in msg */
+ for (cmsg = CMSG_FIRSTHDR(&msg); cmsg;
+ cmsg = CMSG_NXTHDR(&msg, cmsg)) {
+ if (cmsg->cmsg_level == SOL_SOCKET
+ && cmsg->cmsg_type == SCM_RIGHTS) {
+ memcpy(&fd, CMSG_DATA(cmsg), sizeof(int));
+ break;
+ }
+ }
+
+ if (!handle_msg(buf, ret, fd))
+ goto failed;
+ }
+
+ close(notif_sk);
+ notif_sk = -1;
+
+ bt_thread_disassociate();
+
+ DBG("exit");
+
+ return NULL;
+
+failed:
+ exit(EXIT_FAILURE);
+}
+
+static int accept_connection(int sk)
+{
+ int err;
+ struct pollfd pfd;
+ int new_sk;
+
+ memset(&pfd, 0 , sizeof(pfd));
+ pfd.fd = sk;
+ pfd.events = POLLIN;
+
+ err = poll(&pfd, 1, CONNECT_TIMEOUT);
+ if (err < 0) {
+ err = errno;
+ error("Failed to poll: %d (%s)", err, strerror(err));
+ return -1;
+ }
+
+ if (err == 0) {
+ error("bluetoothd connect timeout");
+ return -1;
+ }
+
+ new_sk = accept(sk, NULL, NULL);
+ if (new_sk < 0) {
+ err = errno;
+ error("Failed to accept socket: %d (%s)", err, strerror(err));
+ return -1;
+ }
+
+ return new_sk;
+}
+
+bool hal_ipc_accept(void)
+{
+ int err;
+
+ cmd_sk = accept_connection(listen_sk);
+ if (cmd_sk < 0)
+ return false;
+
+ notif_sk = accept_connection(listen_sk);
+ if (notif_sk < 0) {
+ close(cmd_sk);
+ cmd_sk = -1;
+ return false;
+ }
+
+ err = pthread_create(¬if_th, NULL, notification_handler, NULL);
+ if (err) {
+ notif_th = 0;
+ error("Failed to start notification thread: %d (%s)", err,
+ strerror(err));
+ close(cmd_sk);
+ cmd_sk = -1;
+ close(notif_sk);
+ notif_sk = -1;
+ return false;
+ }
+
+ info("IPC connected");
+
+ return true;
+}
+
+bool hal_ipc_init(const char *path, size_t size)
+{
+ struct sockaddr_un addr;
+ int sk;
+ int err;
+
+ sk = socket(AF_LOCAL, SOCK_SEQPACKET, 0);
+ if (sk < 0) {
+ err = errno;
+ error("Failed to create socket: %d (%s)", err,
+ strerror(err));
+ return false;
+ }
+
+ memset(&addr, 0, sizeof(addr));
+ addr.sun_family = AF_UNIX;
+
+ memcpy(addr.sun_path, path, size);
+
+ if (bind(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+ err = errno;
+ error("Failed to bind socket: %d (%s)", err, strerror(err));
+ close(sk);
+ return false;
+ }
+
+ if (listen(sk, 2) < 0) {
+ err = errno;
+ error("Failed to listen on socket: %d (%s)", err,
+ strerror(err));
+ close(sk);
+ return false;
+ }
+
+ listen_sk = sk;
+
+ return true;
+}
+
+void hal_ipc_cleanup(void)
+{
+ close(listen_sk);
+ listen_sk = -1;
+
+ pthread_mutex_lock(&cmd_sk_mutex);
+ if (cmd_sk >= 0) {
+ close(cmd_sk);
+ cmd_sk = -1;
+ }
+ pthread_mutex_unlock(&cmd_sk_mutex);
+
+ if (notif_sk < 0)
+ return;
+
+ shutdown(notif_sk, SHUT_RD);
+
+ pthread_join(notif_th, NULL);
+ notif_th = 0;
+}
+
+int hal_ipc_cmd(uint8_t service_id, uint8_t opcode, uint16_t len, void *param,
+ size_t *rsp_len, void *rsp, int *fd)
+{
+ ssize_t ret;
+ struct msghdr msg;
+ struct iovec iv[2];
+ struct ipc_hdr cmd;
+ char cmsgbuf[CMSG_SPACE(sizeof(int))];
+ struct ipc_status s;
+ size_t s_len = sizeof(s);
+
+ if (cmd_sk < 0) {
+ error("Invalid cmd socket passed to hal_ipc_cmd");
+ goto failed;
+ }
+
+ if (!rsp || !rsp_len) {
+ memset(&s, 0, s_len);
+ rsp_len = &s_len;
+ rsp = &s;
+ }
+
+ memset(&msg, 0, sizeof(msg));
+ memset(&cmd, 0, sizeof(cmd));
+
+ cmd.service_id = service_id;
+ cmd.opcode = opcode;
+ cmd.len = len;
+
+ iv[0].iov_base = &cmd;
+ iv[0].iov_len = sizeof(cmd);
+
+ iv[1].iov_base = param;
+ iv[1].iov_len = len;
+
+ msg.msg_iov = iv;
+ msg.msg_iovlen = 2;
+
+ pthread_mutex_lock(&cmd_sk_mutex);
+
+ ret = sendmsg(cmd_sk, &msg, 0);
+ if (ret < 0) {
+ error("Sending command failed:%s", strerror(errno));
+ pthread_mutex_unlock(&cmd_sk_mutex);
+ goto failed;
+ }
+
+ /* socket was shutdown */
+ if (ret == 0) {
+ error("Command socket closed");
+ pthread_mutex_unlock(&cmd_sk_mutex);
+ goto failed;
+ }
+
+ memset(&msg, 0, sizeof(msg));
+ memset(&cmd, 0, sizeof(cmd));
+
+ iv[0].iov_base = &cmd;
+ iv[0].iov_len = sizeof(cmd);
+
+ iv[1].iov_base = rsp;
+ iv[1].iov_len = *rsp_len;
+
+ msg.msg_iov = iv;
+ msg.msg_iovlen = 2;
+
+ if (fd) {
+ memset(cmsgbuf, 0, sizeof(cmsgbuf));
+ msg.msg_control = cmsgbuf;
+ msg.msg_controllen = sizeof(cmsgbuf);
+ }
+
+ ret = recvmsg(cmd_sk, &msg, 0);
+
+ pthread_mutex_unlock(&cmd_sk_mutex);
+
+ if (ret < 0) {
+ error("Receiving command response failed: %s", strerror(errno));
+ goto failed;
+ }
+
+
+ if (ret < (ssize_t) sizeof(cmd)) {
+ error("Too small response received(%zd bytes)", ret);
+ goto failed;
+ }
+
+ if (cmd.service_id != service_id) {
+ error("Invalid service id (0x%x vs 0x%x)",
+ cmd.service_id, service_id);
+ goto failed;
+ }
+
+ if (ret != (ssize_t) (sizeof(cmd) + cmd.len)) {
+ error("Malformed response received(%zd bytes)", ret);
+ goto failed;
+ }
+
+ if (cmd.opcode != opcode && cmd.opcode != HAL_OP_STATUS) {
+ error("Invalid opcode received (0x%x vs 0x%x)",
+ cmd.opcode, opcode);
+ goto failed;
+ }
+
+ if (cmd.opcode == HAL_OP_STATUS) {
+ struct ipc_status *s = rsp;
+
+ if (sizeof(*s) != cmd.len) {
+ error("Invalid status length");
+ goto failed;
+ }
+
+ if (s->code == HAL_STATUS_SUCCESS) {
+ error("Invalid success status response");
+ goto failed;
+ }
+
+ return s->code;
+ }
+
+ /* Receive auxiliary data in msg */
+ if (fd) {
+ struct cmsghdr *cmsg;
+
+ *fd = -1;
+
+ for (cmsg = CMSG_FIRSTHDR(&msg); cmsg;
+ cmsg = CMSG_NXTHDR(&msg, cmsg)) {
+ if (cmsg->cmsg_level == SOL_SOCKET
+ && cmsg->cmsg_type == SCM_RIGHTS) {
+ memcpy(fd, CMSG_DATA(cmsg), sizeof(int));
+ break;
+ }
+ }
+ }
+
+ *rsp_len = cmd.len;
+
+ return BT_STATUS_SUCCESS;
+
+failed:
+ exit(EXIT_FAILURE);
+}
diff --git a/repo/android/hal-ipc.h b/repo/android/hal-ipc.h
new file mode 100644
index 0000000..08ed7cc
--- /dev/null
+++ b/repo/android/hal-ipc.h
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2013 Intel Corporation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+struct hal_ipc_handler {
+ void (*handler) (void *buf, uint16_t len, int fd);
+ bool var_len;
+ size_t data_len;
+};
+
+bool hal_ipc_init(const char *path, size_t size);
+bool hal_ipc_accept(void);
+void hal_ipc_cleanup(void);
+
+int hal_ipc_cmd(uint8_t service_id, uint8_t opcode, uint16_t len, void *param,
+ size_t *rsp_len, void *rsp, int *fd);
+
+void hal_ipc_register(uint8_t service, const struct hal_ipc_handler *handlers,
+ uint8_t size);
+void hal_ipc_unregister(uint8_t service);
diff --git a/repo/android/hal-log.h b/repo/android/hal-log.h
new file mode 100644
index 0000000..63ff61b
--- /dev/null
+++ b/repo/android/hal-log.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2013 Intel Corporation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#define LOG_TAG "BlueZ"
+
+#ifdef __BIONIC__
+#include <cutils/log.h>
+#else
+#include <stdio.h>
+#define LOG_INFO " I"
+#define LOG_WARN " W"
+#define LOG_ERROR " E"
+#define LOG_DEBUG " D"
+#define ALOG(pri, tag, fmt, arg...) fprintf(stderr, tag pri": " fmt"\n", ##arg)
+#endif
+
+#define info(fmt, arg...) ALOG(LOG_INFO, LOG_TAG, fmt, ##arg)
+#define warn(fmt, arg...) ALOG(LOG_WARN, LOG_TAG, fmt, ##arg)
+#define error(fmt, arg...) ALOG(LOG_ERROR, LOG_TAG, fmt, ##arg)
+#define DBG(fmt, arg...) ALOG(LOG_DEBUG, LOG_TAG, "%s:%s() "fmt, __FILE__, \
+ __func__, ##arg)
diff --git a/repo/android/hal-map-client.c b/repo/android/hal-map-client.c
new file mode 100644
index 0000000..adf04fc
--- /dev/null
+++ b/repo/android/hal-map-client.c
@@ -0,0 +1,160 @@
+/*
+ * Copyright (C) 2014 Intel Corporation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include <stdlib.h>
+#include <stdbool.h>
+#include <string.h>
+
+#include "hal-log.h"
+#include "hal.h"
+#include "hal-msg.h"
+#include "hal-ipc.h"
+
+static const btmce_callbacks_t *cbs = NULL;
+
+static bool interface_ready(void)
+{
+ return cbs != NULL;
+}
+
+/* Event Handlers */
+
+static void remote_mas_instances_to_hal(btmce_mas_instance_t *send_instance,
+ struct hal_map_client_mas_instance *instance,
+ int num_instances, uint16_t len)
+{
+ void *buf = instance;
+ char *name;
+ int i;
+
+ DBG("");
+
+ for (i = 0; i < num_instances; i++) {
+ name = (char *) instance->name;
+ if (sizeof(*instance) + instance->name_len > len ||
+ (instance->name_len != 0 &&
+ name[instance->name_len - 1] != '\0')) {
+ error("invalid remote mas instance %d, aborting", i);
+ exit(EXIT_FAILURE);
+ }
+
+ send_instance[i].id = instance->id;
+ send_instance[i].msg_types = instance->msg_types;
+ send_instance[i].scn = instance->scn;
+ send_instance[i].p_name = name;
+
+ len -= sizeof(*instance) + instance->name_len;
+ buf += sizeof(*instance) + instance->name_len;
+ instance = buf;
+ }
+
+ if (!len)
+ return;
+
+ error("invalid remote mas instances (%u bytes left), aborting", len);
+ exit(EXIT_FAILURE);
+}
+
+static void handle_remote_mas_instances(void *buf, uint16_t len, int fd)
+{
+ struct hal_ev_map_client_remote_mas_instances *ev = buf;
+ btmce_mas_instance_t instances[ev->num_instances];
+
+ DBG("");
+
+ len -= sizeof(*ev);
+ remote_mas_instances_to_hal(instances, ev->instances, ev->num_instances,
+ len);
+
+ if (cbs->remote_mas_instances_cb)
+ cbs->remote_mas_instances_cb(ev->status,
+ (bt_bdaddr_t *) ev->bdaddr,
+ ev->num_instances, instances);
+}
+
+/*
+ * handlers will be called from notification thread context,
+ * index in table equals to 'opcode - HAL_MINIMUM_EVENT'
+ */
+static const struct hal_ipc_handler ev_handlers[] = {
+ /* HAL_EV_MCE_REMOTE_MAS_INSTANCES */
+ { handle_remote_mas_instances, true,
+ sizeof(struct hal_ev_map_client_remote_mas_instances) }
+};
+
+/* API */
+
+static bt_status_t get_remote_mas_instances(bt_bdaddr_t *bd_addr)
+{
+ struct hal_cmd_map_client_get_instances cmd;
+
+ if (!interface_ready())
+ return BT_STATUS_NOT_READY;
+
+ memcpy(cmd.bdaddr, bd_addr, sizeof(*bd_addr));
+
+ return hal_ipc_cmd(HAL_SERVICE_ID_MAP_CLIENT,
+ HAL_OP_MAP_CLIENT_GET_INSTANCES, sizeof(cmd),
+ &cmd, NULL, NULL, NULL);
+}
+
+static bt_status_t init(btmce_callbacks_t *callbacks)
+{
+ struct hal_cmd_register_module cmd;
+ int ret;
+
+ DBG("");
+
+ /*
+ * Interface ready check was removed because there is no cleanup
+ * function to unregister and clear callbacks. MAP client testers may
+ * restart bluetooth, unregister this profile and try to reuse it.
+ * This situation make service unregistered but callbacks are still
+ * set - interface is ready. On android devices there is no need to
+ * re-init MAP client profile while bluetooth is loaded.
+ */
+
+ cbs = callbacks;
+
+ hal_ipc_register(HAL_SERVICE_ID_MAP_CLIENT, ev_handlers,
+ sizeof(ev_handlers)/sizeof(ev_handlers[0]));
+
+ cmd.service_id = HAL_SERVICE_ID_MAP_CLIENT;
+ cmd.mode = HAL_MODE_DEFAULT;
+ cmd.max_clients = 1;
+
+ ret = hal_ipc_cmd(HAL_SERVICE_ID_CORE, HAL_OP_REGISTER_MODULE,
+ sizeof(cmd), &cmd, 0, NULL, NULL);
+
+ if (ret != BT_STATUS_SUCCESS) {
+ cbs = NULL;
+ hal_ipc_unregister(HAL_SERVICE_ID_MAP_CLIENT);
+ }
+
+ return ret;
+}
+
+static btmce_interface_t iface = {
+ .size = sizeof(iface),
+ .init = init,
+ .get_remote_mas_instances = get_remote_mas_instances
+};
+
+btmce_interface_t *bt_get_map_client_interface(void)
+{
+ return &iface;
+}
diff --git a/repo/android/hal-msg.h b/repo/android/hal-msg.h
new file mode 100644
index 0000000..ea79fa7
--- /dev/null
+++ b/repo/android/hal-msg.h
@@ -0,0 +1,2335 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2013 Intel Corporation. All rights reserved.
+ *
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+static const char BLUEZ_HAL_SK_PATH[] = "\0bluez_hal_socket";
+
+#define HAL_MINIMUM_EVENT 0x81
+
+#define HAL_SERVICE_ID_CORE 0
+#define HAL_SERVICE_ID_BLUETOOTH 1
+#define HAL_SERVICE_ID_SOCKET 2
+#define HAL_SERVICE_ID_HIDHOST 3
+#define HAL_SERVICE_ID_PAN 4
+#define HAL_SERVICE_ID_HANDSFREE 5
+#define HAL_SERVICE_ID_A2DP 6
+#define HAL_SERVICE_ID_HEALTH 7
+#define HAL_SERVICE_ID_AVRCP 8
+#define HAL_SERVICE_ID_GATT 9
+#define HAL_SERVICE_ID_HANDSFREE_CLIENT 10
+#define HAL_SERVICE_ID_MAP_CLIENT 11
+#define HAL_SERVICE_ID_AVRCP_CTRL 12
+#define HAL_SERVICE_ID_A2DP_SINK 13
+
+#define HAL_SERVICE_ID_MAX HAL_SERVICE_ID_A2DP_SINK
+
+/* Core Service */
+
+#define HAL_STATUS_SUCCESS IPC_STATUS_SUCCESS
+#define HAL_STATUS_FAILED 0x01
+#define HAL_STATUS_NOT_READY 0x02
+#define HAL_STATUS_NOMEM 0x03
+#define HAL_STATUS_BUSY 0x04
+#define HAL_STATUS_DONE 0x05
+#define HAL_STATUS_UNSUPPORTED 0x06
+#define HAL_STATUS_INVALID 0x07
+#define HAL_STATUS_UNHANDLED 0x08
+#define HAL_STATUS_AUTH_FAILURE 0x09
+#define HAL_STATUS_REMOTE_DEVICE_DOWN 0x0a
+
+#define HAL_OP_STATUS IPC_OP_STATUS
+
+#define HAL_MODE_DEFAULT 0x00
+#define HAL_MODE_BREDR 0x01
+#define HAL_MODE_LE 0x02
+
+#define HAL_OP_REGISTER_MODULE 0x01
+struct hal_cmd_register_module {
+ uint8_t service_id;
+ uint8_t mode;
+ int32_t max_clients;
+} __attribute__((packed));
+
+#define HAL_OP_UNREGISTER_MODULE 0x02
+struct hal_cmd_unregister_module {
+ uint8_t service_id;
+} __attribute__((packed));
+
+#define HAL_CONFIG_VENDOR 0x00
+#define HAL_CONFIG_MODEL 0x01
+#define HAL_CONFIG_NAME 0x02
+#define HAL_CONFIG_SERIAL_NUMBER 0x03
+#define HAL_CONFIG_SYSTEM_ID 0x04
+#define HAL_CONFIG_PNP_ID 0x05
+#define HAL_CONFIG_FW_REV 0x06
+#define HAL_CONFIG_HW_REV 0x07
+
+struct hal_config_prop {
+ uint8_t type;
+ uint16_t len;
+ uint8_t val[0];
+} __attribute__((packed));
+
+#define HAL_OP_CONFIGURATION 0x03
+struct hal_cmd_configuration {
+ uint8_t num;
+ struct hal_config_prop props[0];
+} __attribute__((packed));
+
+/* Bluetooth Core HAL API */
+
+#define HAL_OP_ENABLE 0x01
+
+#define HAL_OP_DISABLE 0x02
+
+#define HAL_OP_GET_ADAPTER_PROPS 0x03
+
+#define HAL_OP_GET_ADAPTER_PROP 0x04
+struct hal_cmd_get_adapter_prop {
+ uint8_t type;
+} __attribute__((packed));
+
+#define HAL_MAX_NAME_LENGTH 249
+
+#define HAL_PROP_ADAPTER_NAME 0x01
+#define HAL_PROP_ADAPTER_ADDR 0x02
+#define HAL_PROP_ADAPTER_UUIDS 0x03
+#define HAL_PROP_ADAPTER_CLASS 0x04
+#define HAL_PROP_ADAPTER_TYPE 0x05
+#define HAL_PROP_ADAPTER_SERVICE_REC 0x06
+#define HAL_PROP_ADAPTER_SCAN_MODE 0x07
+#define HAL_PROP_ADAPTER_BONDED_DEVICES 0x08
+#define HAL_PROP_ADAPTER_DISC_TIMEOUT 0x09
+
+#define HAL_PROP_DEVICE_NAME 0x01
+#define HAL_PROP_DEVICE_ADDR 0x02
+#define HAL_PROP_DEVICE_UUIDS 0x03
+#define HAL_PROP_DEVICE_CLASS 0x04
+#define HAL_PROP_DEVICE_TYPE 0x05
+#define HAL_PROP_DEVICE_SERVICE_REC 0x06
+struct hal_prop_device_service_rec {
+ uint8_t uuid[16];
+ uint16_t channel;
+ uint8_t name_len;
+ uint8_t name[];
+} __attribute__((packed));
+
+#define HAL_PROP_DEVICE_FRIENDLY_NAME 0x0a
+#define HAL_PROP_DEVICE_RSSI 0x0b
+#define HAL_PROP_DEVICE_VERSION_INFO 0x0c
+struct hal_prop_device_info {
+ uint8_t version;
+ uint16_t sub_version;
+ uint16_t manufacturer;
+} __attribute__((packed));
+
+#define HAL_PROP_ADAPTER_LOCAL_LE_FEAT 0x0d
+#define HAL_PROP_DEVICE_TIMESTAMP 0xFF
+
+#define HAL_ADAPTER_SCAN_MODE_NONE 0x00
+#define HAL_ADAPTER_SCAN_MODE_CONN 0x01
+#define HAL_ADAPTER_SCAN_MODE_CONN_DISC 0x02
+
+#define HAL_TYPE_BREDR 0x01
+#define HAL_TYPE_LE 0x02
+#define HAL_TYPE_DUAL 0x03
+
+#define HAL_OP_SET_ADAPTER_PROP 0x05
+struct hal_cmd_set_adapter_prop {
+ uint8_t type;
+ uint16_t len;
+ uint8_t val[0];
+} __attribute__((packed));
+
+#define HAL_OP_GET_REMOTE_DEVICE_PROPS 0x06
+struct hal_cmd_get_remote_device_props {
+ uint8_t bdaddr[6];
+} __attribute__((packed));
+
+#define HAL_OP_GET_REMOTE_DEVICE_PROP 0x07
+struct hal_cmd_get_remote_device_prop {
+ uint8_t bdaddr[6];
+ uint8_t type;
+} __attribute__((packed));
+
+#define HAL_OP_SET_REMOTE_DEVICE_PROP 0x08
+struct hal_cmd_set_remote_device_prop {
+ uint8_t bdaddr[6];
+ uint8_t type;
+ uint16_t len;
+ uint8_t val[0];
+} __attribute__((packed));
+
+#define HAL_OP_GET_REMOTE_SERVICE_REC 0x09
+struct hal_cmd_get_remote_service_rec {
+ uint8_t bdaddr[6];
+ uint8_t uuid[16];
+} __attribute__((packed));
+
+#define HAL_OP_GET_REMOTE_SERVICES 0x0a
+struct hal_cmd_get_remote_services {
+ uint8_t bdaddr[6];
+} __attribute__((packed));
+
+#define HAL_OP_START_DISCOVERY 0x0b
+
+#define HAL_OP_CANCEL_DISCOVERY 0x0c
+
+#define BT_TRANSPORT_UNKNOWN 0x00
+#define BT_TRANSPORT_BR_EDR 0x01
+#define BT_TRANSPORT_LE 0x02
+
+#define HAL_OP_CREATE_BOND 0x0d
+struct hal_cmd_create_bond {
+ uint8_t bdaddr[6];
+ uint8_t transport;
+} __attribute__((packed));
+
+#define HAL_OP_REMOVE_BOND 0x0e
+struct hal_cmd_remove_bond {
+ uint8_t bdaddr[6];
+} __attribute__((packed));
+
+#define HAL_OP_CANCEL_BOND 0x0f
+struct hal_cmd_cancel_bond {
+ uint8_t bdaddr[6];
+} __attribute__((packed));
+
+#define HAL_OP_PIN_REPLY 0x10
+struct hal_cmd_pin_reply {
+ uint8_t bdaddr[6];
+ uint8_t accept;
+ uint8_t pin_len;
+ uint8_t pin_code[16];
+} __attribute__((packed));
+
+#define HAL_SSP_VARIANT_CONFIRM 0x00
+#define HAL_SSP_VARIANT_ENTRY 0x01
+#define HAL_SSP_VARIANT_CONSENT 0x02
+#define HAL_SSP_VARIANT_NOTIF 0x03
+
+#define HAL_OP_SSP_REPLY 0x11
+struct hal_cmd_ssp_reply {
+ uint8_t bdaddr[6];
+ uint8_t ssp_variant;
+ uint8_t accept;
+ uint32_t passkey;
+} __attribute__((packed));
+
+#define HAL_OP_DUT_MODE_CONF 0x12
+struct hal_cmd_dut_mode_conf {
+ uint8_t enable;
+} __attribute__((packed));
+
+#define HAL_OP_DUT_MODE_SEND 0x13
+struct hal_cmd_dut_mode_send {
+ uint16_t opcode;
+ uint8_t len;
+ uint8_t data[0];
+} __attribute__((packed));
+
+#define HAL_OP_LE_TEST_MODE 0x14
+struct hal_cmd_le_test_mode {
+ uint16_t opcode;
+ uint8_t len;
+ uint8_t data[0];
+} __attribute__((packed));
+
+#define HAL_OP_GET_CONNECTION_STATE 0x15
+struct hal_cmd_get_connection_state {
+ uint8_t bdaddr[6];
+} __attribute__((packed));
+
+struct hal_rsp_get_connection_state {
+ int32_t connection_state;
+} __attribute__((packed));
+
+#define HAL_OP_READ_ENERGY_INFO 0x16
+
+/* Bluetooth Socket HAL api */
+
+#define HAL_MODE_SOCKET_DEFAULT HAL_MODE_DEFAULT
+#define HAL_MODE_SOCKET_DYNAMIC_MAP 0x01
+
+#define HAL_SOCK_RFCOMM 0x01
+#define HAL_SOCK_SCO 0x02
+#define HAL_SOCK_L2CAP 0x03
+
+#define HAL_SOCK_FLAG_ENCRYPT 0x01
+#define HAL_SOCK_FLAG_AUTH 0x02
+
+#define HAL_OP_SOCKET_LISTEN 0x01
+struct hal_cmd_socket_listen {
+ uint8_t type;
+ uint8_t name[256];
+ uint8_t uuid[16];
+ int32_t channel;
+ uint8_t flags;
+} __attribute__((packed));
+
+#define HAL_OP_SOCKET_CONNECT 0x02
+struct hal_cmd_socket_connect {
+ uint8_t bdaddr[6];
+ uint8_t type;
+ uint8_t uuid[16];
+ int32_t channel;
+ uint8_t flags;
+} __attribute__((packed));
+
+/* Bluetooth HID Host HAL API */
+
+#define HAL_OP_HIDHOST_CONNECT 0x01
+struct hal_cmd_hidhost_connect {
+ uint8_t bdaddr[6];
+} __attribute__((packed));
+
+#define HAL_OP_HIDHOST_DISCONNECT 0x02
+struct hal_cmd_hidhost_disconnect {
+ uint8_t bdaddr[6];
+} __attribute__((packed));
+
+#define HAL_OP_HIDHOST_VIRTUAL_UNPLUG 0x03
+struct hal_cmd_hidhost_virtual_unplug {
+ uint8_t bdaddr[6];
+} __attribute__((packed));
+
+#define HAL_OP_HIDHOST_SET_INFO 0x04
+struct hal_cmd_hidhost_set_info {
+ uint8_t bdaddr[6];
+ uint8_t attr;
+ uint8_t subclass;
+ uint8_t app_id;
+ uint16_t vendor;
+ uint16_t product;
+ uint16_t country;
+ uint16_t descr_len;
+ uint8_t descr[0];
+} __attribute__((packed));
+
+#define HAL_HIDHOST_REPORT_PROTOCOL 0x00
+#define HAL_HIDHOST_BOOT_PROTOCOL 0x01
+#define HAL_HIDHOST_UNSUPPORTED_PROTOCOL 0xff
+
+#define HAL_OP_HIDHOST_GET_PROTOCOL 0x05
+struct hal_cmd_hidhost_get_protocol {
+ uint8_t bdaddr[6];
+ uint8_t mode;
+} __attribute__((packed));
+
+#define HAL_OP_HIDHOST_SET_PROTOCOL 0x06
+struct hal_cmd_hidhost_set_protocol {
+ uint8_t bdaddr[6];
+ uint8_t mode;
+} __attribute__((packed));
+
+#define HAL_HIDHOST_INPUT_REPORT 0x01
+#define HAL_HIDHOST_OUTPUT_REPORT 0x02
+#define HAL_HIDHOST_FEATURE_REPORT 0x03
+
+#define HAL_OP_HIDHOST_GET_REPORT 0x07
+struct hal_cmd_hidhost_get_report {
+ uint8_t bdaddr[6];
+ uint8_t type;
+ uint8_t id;
+ uint16_t buf_size;
+} __attribute__((packed));
+
+#define HAL_OP_HIDHOST_SET_REPORT 0x08
+struct hal_cmd_hidhost_set_report {
+ uint8_t bdaddr[6];
+ uint8_t type;
+ uint16_t len;
+ uint8_t data[0];
+} __attribute__((packed));
+
+#define HAL_OP_HIDHOST_SEND_DATA 0x09
+struct hal_cmd_hidhost_send_data {
+ uint8_t bdaddr[6];
+ uint16_t len;
+ uint8_t data[0];
+} __attribute__((packed));
+
+/* a2dp source and sink HAL API */
+
+#define HAL_OP_A2DP_CONNECT 0x01
+struct hal_cmd_a2dp_connect {
+ uint8_t bdaddr[6];
+} __attribute__((packed));
+
+#define HAL_OP_A2DP_DISCONNECT 0x02
+struct hal_cmd_a2dp_disconnect {
+ uint8_t bdaddr[6];
+} __attribute__((packed));
+
+/* PAN HAL API */
+
+/* PAN Roles */
+#define HAL_PAN_ROLE_NONE 0x00
+#define HAL_PAN_ROLE_NAP 0x01
+#define HAL_PAN_ROLE_PANU 0x02
+
+/* PAN Control states */
+#define HAL_PAN_CTRL_ENABLED 0x00
+#define HAL_PAN_CTRL_DISABLED 0x01
+
+/* PAN Connection states */
+#define HAL_PAN_STATE_CONNECTED 0x00
+#define HAL_PAN_STATE_CONNECTING 0x01
+#define HAL_PAN_STATE_DISCONNECTED 0x02
+#define HAL_PAN_STATE_DISCONNECTING 0x03
+
+/* PAN status values */
+#define HAL_PAN_STATUS_FAIL 0x01
+#define HAL_PAN_STATUS_NOT_READY 0x02
+#define HAL_PAN_STATUS_NO_MEMORY 0x03
+#define HAL_PAN_STATUS_BUSY 0x04
+#define HAL_PAN_STATUS_DONE 0x05
+#define HAL_PAN_STATUS_UNSUPORTED 0x06
+#define HAL_PAN_STATUS_INVAL 0x07
+#define HAL_PAN_STATUS_UNHANDLED 0x08
+#define HAL_PAN_STATUS_AUTH_FAILED 0x09
+#define HAL_PAN_STATUS_DEVICE_DOWN 0x0A
+
+#define HAL_OP_PAN_ENABLE 0x01
+struct hal_cmd_pan_enable {
+ uint8_t local_role;
+} __attribute__((packed));
+
+#define HAL_OP_PAN_GET_ROLE 0x02
+struct hal_rsp_pan_get_role {
+ uint8_t local_role;
+} __attribute__((packed));
+
+#define HAL_OP_PAN_CONNECT 0x03
+struct hal_cmd_pan_connect {
+ uint8_t bdaddr[6];
+ uint8_t local_role;
+ uint8_t remote_role;
+} __attribute__((packed));
+
+#define HAL_OP_PAN_DISCONNECT 0x04
+struct hal_cmd_pan_disconnect {
+ uint8_t bdaddr[6];
+} __attribute__((packed));
+
+#define HAL_HEALTH_MDEP_ROLE_SOURCE 0x00
+#define HAL_HEALTH_MDEP_ROLE_SINK 0x01
+
+#define HAL_HEALTH_CHANNEL_TYPE_RELIABLE 0x00
+#define HAL_HEALTH_CHANNEL_TYPE_STREAMING 0x01
+#define HAL_HEALTH_CHANNEL_TYPE_ANY 0x02
+
+#define HAL_OP_HEALTH_REG_APP 0x01
+struct hal_cmd_health_reg_app {
+ uint8_t num_of_mdep;
+ uint16_t app_name_off;
+ uint16_t provider_name_off;
+ uint16_t service_name_off;
+ uint16_t service_descr_off;
+ uint16_t len;
+ uint8_t data[0];
+} __attribute__((packed));
+
+struct hal_rsp_health_reg_app {
+ uint16_t app_id;
+} __attribute__((packed));
+
+#define HAL_OP_HEALTH_MDEP 0x02
+struct hal_cmd_health_mdep {
+ uint16_t app_id;
+ uint8_t role;
+ uint16_t data_type;
+ uint8_t channel_type;
+ uint16_t descr_len;
+ uint8_t descr[0];
+} __attribute__((packed));
+
+#define HAL_OP_HEALTH_UNREG_APP 0x03
+struct hal_cmd_health_unreg_app {
+ uint16_t app_id;
+} __attribute__((packed));
+
+#define HAL_OP_HEALTH_CONNECT_CHANNEL 0x04
+struct hal_cmd_health_connect_channel {
+ uint16_t app_id;
+ uint8_t bdaddr[6];
+ uint8_t mdep_index;
+} __attribute__((packed));
+
+struct hal_rsp_health_connect_channel {
+ uint16_t channel_id;
+} __attribute__((packed));
+
+#define HAL_OP_HEALTH_DESTROY_CHANNEL 0x05
+struct hal_cmd_health_destroy_channel {
+ uint16_t channel_id;
+} __attribute__((packed));
+
+/* Handsfree HAL API */
+
+#define HAL_MODE_HANDSFREE_HSP_ONLY HAL_MODE_DEFAULT
+#define HAL_MODE_HANDSFREE_HFP 0x01
+#define HAL_MODE_HANDSFREE_HFP_WBS 0x02
+
+#define HAL_OP_HANDSFREE_CONNECT 0x01
+struct hal_cmd_handsfree_connect {
+ uint8_t bdaddr[6];
+} __attribute__((packed));
+
+#define HAL_OP_HANDSFREE_DISCONNECT 0x02
+struct hal_cmd_handsfree_disconnect {
+ uint8_t bdaddr[6];
+} __attribute__((packed));
+
+#define HAL_OP_HANDSFREE_CONNECT_AUDIO 0x03
+struct hal_cmd_handsfree_connect_audio {
+ uint8_t bdaddr[6];
+} __attribute__((packed));
+
+#define HAL_OP_HANDSFREE_DISCONNECT_AUDIO 0x04
+struct hal_cmd_handsfree_disconnect_audio {
+ uint8_t bdaddr[6];
+} __attribute__((packed));
+
+#define HAL_OP_HANDSFREE_START_VR 0x05
+struct hal_cmd_handsfree_start_vr {
+ uint8_t bdaddr[6];
+} __attribute__((packed));
+
+#define HAL_OP_HANDSFREE_STOP_VR 0x06
+struct hal_cmd_handsfree_stop_vr {
+ uint8_t bdaddr[6];
+} __attribute__((packed));
+
+#define HAL_HANDSFREE_VOLUME_TYPE_SPEAKER 0x00
+#define HAL_HANDSFREE_VOLUME_TYPE_MIC 0x01
+
+#define HAL_OP_HANDSFREE_VOLUME_CONTROL 0x07
+struct hal_cmd_handsfree_volume_control {
+ uint8_t type;
+ uint8_t volume;
+ uint8_t bdaddr[6];
+} __attribute__((packed));
+
+#define HAL_HANDSFREE_NETWORK_STATE_NOT_AVAILABLE 0x00
+#define HAL_HANDSFREE_NETWORK_STATE_AVAILABLE 0x01
+
+#define HAL_HANDSFREE_SERVICE_TYPE_HOME 0x00
+#define HAL_HANDSFREE_SERVICE_TYPE_ROAMING 0x01
+
+#define HAL_OP_HANDSFREE_DEVICE_STATUS_NOTIF 0x08
+struct hal_cmd_handsfree_device_status_notif {
+ uint8_t state;
+ uint8_t type;
+ uint8_t signal;
+ uint8_t battery;
+} __attribute__((packed));
+
+#define HAL_OP_HANDSFREE_COPS_RESPONSE 0x09
+struct hal_cmd_handsfree_cops_response {
+ uint16_t len;
+ uint8_t bdaddr[6];
+ uint8_t buf[0];
+} __attribute__((packed));
+
+#define HAL_HANDSFREE_CALL_STATE_ACTIVE 0x00
+#define HAL_HANDSFREE_CALL_STATE_HELD 0x01
+#define HAL_HANDSFREE_CALL_STATE_DIALING 0x02
+#define HAL_HANDSFREE_CALL_STATE_ALERTING 0x03
+#define HAL_HANDSFREE_CALL_STATE_INCOMING 0x04
+#define HAL_HANDSFREE_CALL_STATE_WAITING 0x05
+#define HAL_HANDSFREE_CALL_STATE_IDLE 0x06
+
+#define HAL_OP_HANDSFREE_CIND_RESPONSE 0x0A
+struct hal_cmd_handsfree_cind_response {
+ uint8_t svc;
+ uint8_t num_active;
+ uint8_t num_held;
+ uint8_t state;
+ uint8_t signal;
+ uint8_t roam;
+ uint8_t batt_chg;
+ uint8_t bdaddr[6];
+} __attribute__((packed));
+
+#define HAL_OP_HANDSFREE_FORMATTED_AT_RESPONSE 0x0B
+struct hal_cmd_handsfree_formatted_at_response {
+ uint16_t len;
+ uint8_t bdaddr[6];
+ uint8_t buf[0];
+} __attribute__((packed));
+
+#define HAL_HANDSFREE_AT_RESPONSE_ERROR 0x00
+#define HAL_HANDSFREE_AT_RESPONSE_OK 0x01
+
+#define HAL_OP_HANDSFREE_AT_RESPONSE 0x0C
+struct hal_cmd_handsfree_at_response {
+ uint8_t response;
+ uint8_t error;
+ uint8_t bdaddr[6];
+} __attribute__((packed));
+
+#define HAL_HANDSFREE_CALL_DIRECTION_OUTGOING 0x00
+#define HAL_HANDSFREE_CALL_DIRECTION_INCOMING 0x01
+
+#define HAL_HANDSFREE_CALL_TYPE_VOICE 0x00
+#define HAL_HANDSFREE_CALL_TYPE_DATA 0x01
+#define HAL_HANDSFREE_CALL_TYPE_FAX 0x02
+
+#define HAL_HANDSFREE_CALL_MPTY_TYPE_SINGLE 0x00
+#define HAL_HANDSFREE_CALL_MPTY_TYPE_MULTI 0x01
+
+#define HAL_HANDSFREE_CALL_ADDRTYPE_UNKNOWN 0x81
+#define HAL_HANDSFREE_CALL_ADDRTYPE_INTERNATIONAL 0x91
+
+#define HAL_OP_HANDSFREE_CLCC_RESPONSE 0x0D
+struct hal_cmd_handsfree_clcc_response {
+ uint8_t index;
+ uint8_t dir;
+ uint8_t state;
+ uint8_t mode;
+ uint8_t mpty;
+ uint8_t type;
+ uint8_t bdaddr[6];
+ uint16_t number_len;
+ uint8_t number[0];
+} __attribute__((packed));
+
+#define HAL_OP_HANDSFREE_PHONE_STATE_CHANGE 0x0E
+struct hal_cmd_handsfree_phone_state_change {
+ uint8_t num_active;
+ uint8_t num_held;
+ uint8_t state;
+ uint8_t type;
+ uint16_t number_len;
+ uint8_t number[0];
+} __attribute__((packed));
+
+#define HAL_HANDSFREE_WBS_NONE 0x00
+#define HAL_HANDSFREE_WBS_NO 0x01
+#define HAL_HANDSFREE_WBS_YES 0x02
+
+#define HAL_OP_HANDSFREE_CONFIGURE_WBS 0x0F
+struct hal_cmd_handsfree_configure_wbs {
+ uint8_t bdaddr[6];
+ uint8_t config;
+} __attribute__((packed));
+
+/* AVRCP TARGET HAL API */
+
+#define HAL_AVRCP_PLAY_STATUS_STOPPED 0x00
+#define HAL_AVRCP_PLAY_STATUS_PLAYING 0x01
+#define HAL_AVRCP_PLAY_STATUS_PAUSED 0x02
+#define HAL_AVRCP_PLAY_STATUS_FWD_SEEK 0x03
+#define HAL_AVRCP_PLAY_STATUS_REV_SEEK 0x04
+#define HAL_AVRCP_PLAY_STATUS_ERROR 0xff
+
+#define HAL_OP_AVRCP_GET_PLAY_STATUS 0x01
+struct hal_cmd_avrcp_get_play_status {
+ uint8_t status;
+ uint32_t duration;
+ uint32_t position;
+} __attribute__((packed));
+
+#define HAL_AVRCP_PLAYER_ATTR_EQUALIZER 0x01
+#define HAL_AVRCP_PLAYER_ATTR_REPEAT 0x02
+#define HAL_AVRCP_PLAYER_ATTR_SHUFFLE 0x03
+#define HAL_AVRCP_PLAYER_ATTR_SCAN 0x04
+
+#define HAL_OP_AVRCP_LIST_PLAYER_ATTRS 0x02
+struct hal_cmd_avrcp_list_player_attrs {
+ uint8_t number;
+ uint8_t attrs[0];
+} __attribute__((packed));
+
+#define HAL_OP_AVRCP_LIST_PLAYER_VALUES 0x03
+struct hal_cmd_avrcp_list_player_values {
+ uint8_t number;
+ uint8_t values[0];
+} __attribute__((packed));
+
+struct hal_avrcp_player_attr_value {
+ uint8_t attr;
+ uint8_t value;
+} __attribute__((packed));
+
+#define HAL_OP_AVRCP_GET_PLAYER_ATTRS 0x04
+struct hal_cmd_avrcp_get_player_attrs {
+ uint8_t number;
+ struct hal_avrcp_player_attr_value attrs[0];
+} __attribute__((packed));
+
+struct hal_avrcp_player_setting_text {
+ uint8_t id;
+ uint8_t len;
+ uint8_t text[0];
+} __attribute__((packed));
+
+#define HAL_OP_AVRCP_GET_PLAYER_ATTRS_TEXT 0x05
+struct hal_cmd_avrcp_get_player_attrs_text {
+ uint8_t number;
+ struct hal_avrcp_player_setting_text attrs[0];
+} __attribute__((packed));
+
+#define HAL_OP_AVRCP_GET_PLAYER_VALUES_TEXT 0x06
+struct hal_cmd_avrcp_get_player_values_text {
+ uint8_t number;
+ struct hal_avrcp_player_setting_text values[0];
+} __attribute__((packed));
+
+#define HAL_AVRCP_MEDIA_ATTR_TITLE 0x01
+#define HAL_AVRCP_MEDIA_ATTR_ARTIST 0x02
+#define HAL_AVRCP_MEDIA_ATTR_ALBUM 0x03
+#define HAL_AVRCP_MEDIA_ATTR_TRACK_NUM 0x04
+#define HAL_AVRCP_MEDIA_ATTR_NUM_TRACKS 0x05
+#define HAL_AVRCP_MEDIA_ATTR_GENRE 0x06
+#define HAL_AVRCP_MEDIA_ATTR_DURATION 0x07
+
+#define HAL_OP_AVRCP_GET_ELEMENT_ATTRS_TEXT 0x07
+struct hal_cmd_avrcp_get_element_attrs_text {
+ uint8_t number;
+ struct hal_avrcp_player_setting_text values[0];
+} __attribute__((packed));
+
+#define HAL_OP_AVRCP_SET_PLAYER_ATTRS_VALUE 0x08
+struct hal_cmd_avrcp_set_player_attrs_value {
+ uint8_t status;
+} __attribute__((packed));
+
+#define HAL_AVRCP_EVENT_STATUS_CHANGED 0x01
+#define HAL_AVRCP_EVENT_TRACK_CHANGED 0x02
+#define HAL_AVRCP_EVENT_TRACK_REACHED_END 0x03
+#define HAL_AVRCP_EVENT_TRACK_REACHED_START 0x04
+#define HAL_AVRCP_EVENT_POSITION_CHANGED 0x05
+#define HAL_AVRCP_EVENT_SETTING_CHANGED 0x08
+
+#define HAL_AVRCP_EVENT_TYPE_INTERIM 0x00
+#define HAL_AVRCP_EVENT_TYPE_CHANGED 0x01
+
+#define HAL_OP_AVRCP_REGISTER_NOTIFICATION 0x09
+struct hal_cmd_avrcp_register_notification {
+ uint8_t event;
+ uint8_t type;
+ uint8_t len;
+ uint8_t data[0];
+} __attribute__((packed));
+
+#define HAL_OP_AVRCP_SET_VOLUME 0x0a
+struct hal_cmd_avrcp_set_volume {
+ uint8_t value;
+} __attribute__((packed));
+
+/* AVRCP CTRL HAL API */
+
+#define HAL_OP_AVRCP_CTRL_SEND_PASSTHROUGH 0x01
+struct hal_cmd_avrcp_ctrl_send_passthrough {
+ uint8_t bdaddr[6];
+ uint8_t key_code;
+ uint8_t key_state;
+} __attribute__((packed));
+
+/* GATT HAL API */
+
+#define HAL_OP_GATT_CLIENT_REGISTER 0x01
+struct hal_cmd_gatt_client_register {
+ uint8_t uuid[16];
+} __attribute__((packed));
+
+#define HAL_OP_GATT_CLIENT_UNREGISTER 0x02
+struct hal_cmd_gatt_client_unregister {
+ int32_t client_if;
+} __attribute__((packed));
+
+#define HAL_OP_GATT_CLIENT_SCAN 0x03
+struct hal_cmd_gatt_client_scan {
+ int32_t client_if;
+ uint8_t start;
+} __attribute__((packed));
+
+#define HAL_OP_GATT_CLIENT_CONNECT 0x04
+struct hal_cmd_gatt_client_connect {
+ int32_t client_if;
+ uint8_t bdaddr[6];
+ uint8_t is_direct;
+ int32_t transport;
+} __attribute__((packed));
+
+#define HAL_OP_GATT_CLIENT_DISCONNECT 0x05
+struct hal_cmd_gatt_client_disconnect {
+ int32_t client_if;
+ uint8_t bdaddr[6];
+ int32_t conn_id;
+} __attribute__((packed));
+
+#define HAL_OP_GATT_CLIENT_LISTEN 0x06
+struct hal_cmd_gatt_client_listen {
+ int32_t client_if;
+ uint8_t start;
+} __attribute__((packed));
+
+#define HAL_OP_GATT_CLIENT_REFRESH 0x07
+struct hal_cmd_gatt_client_refresh {
+ int32_t client_if;
+ uint8_t bdaddr[6];
+} __attribute__((packed));
+
+#define HAL_OP_GATT_CLIENT_SEARCH_SERVICE 0x08
+struct hal_cmd_gatt_client_search_service {
+ int32_t conn_id;
+ uint8_t filtered;
+ uint8_t filter_uuid[0];
+} __attribute__((packed));
+
+#define HAL_OP_GATT_CLIENT_GET_INCLUDED_SERVICE 0x09
+struct hal_gatt_srvc_id {
+ uint8_t uuid[16];
+ uint8_t inst_id;
+ uint8_t is_primary;
+} __attribute__((packed));
+
+struct hal_cmd_gatt_client_get_included_service {
+ int32_t conn_id;
+ struct hal_gatt_srvc_id srvc_id;
+ uint8_t continuation;
+ struct hal_gatt_srvc_id incl_srvc_id[0];
+} __attribute__((packed));
+
+#define HAL_OP_GATT_CLIENT_GET_CHARACTERISTIC 0x0a
+struct hal_gatt_gatt_id {
+ uint8_t uuid[16];
+ uint8_t inst_id;
+} __attribute__((packed));
+
+struct hal_cmd_gatt_client_get_characteristic {
+ int32_t conn_id;
+ struct hal_gatt_srvc_id srvc_id;
+ uint8_t continuation;
+ struct hal_gatt_gatt_id char_id[0];
+} __attribute__((packed));
+
+#define HAL_OP_GATT_CLIENT_GET_DESCRIPTOR 0x0b
+struct hal_cmd_gatt_client_get_descriptor {
+ int32_t conn_id;
+ struct hal_gatt_srvc_id srvc_id;
+ struct hal_gatt_gatt_id char_id;
+ uint8_t continuation;
+ struct hal_gatt_gatt_id descr_id[0];
+} __attribute__((packed));
+
+#define HAL_OP_GATT_CLIENT_READ_CHARACTERISTIC 0x0c
+struct hal_cmd_gatt_client_read_characteristic {
+ int32_t conn_id;
+ struct hal_gatt_srvc_id srvc_id;
+ struct hal_gatt_gatt_id char_id;
+ int32_t auth_req;
+} __attribute__((packed));
+
+#define GATT_WRITE_TYPE_NO_RESPONSE 0x01
+#define GATT_WRITE_TYPE_DEFAULT 0x02
+#define GATT_WRITE_TYPE_PREPARE 0x03
+#define GATT_WRITE_TYPE_SIGNED 0x04
+
+#define HAL_OP_GATT_CLIENT_WRITE_CHARACTERISTIC 0x0d
+struct hal_cmd_gatt_client_write_characteristic {
+ int32_t conn_id;
+ struct hal_gatt_srvc_id srvc_id;
+ struct hal_gatt_gatt_id char_id;
+ int32_t write_type;
+ int32_t len;
+ int32_t auth_req;
+ uint8_t value[0];
+} __attribute__((packed));
+
+#define HAL_OP_GATT_CLIENT_READ_DESCRIPTOR 0x0e
+struct hal_cmd_gatt_client_read_descriptor {
+ int32_t conn_id;
+ struct hal_gatt_srvc_id srvc_id;
+ struct hal_gatt_gatt_id char_id;
+ struct hal_gatt_gatt_id descr_id;
+ int32_t auth_req;
+} __attribute__((packed));
+
+#define HAL_OP_GATT_CLIENT_WRITE_DESCRIPTOR 0x0f
+struct hal_cmd_gatt_client_write_descriptor {
+ int32_t conn_id;
+ struct hal_gatt_srvc_id srvc_id;
+ struct hal_gatt_gatt_id char_id;
+ struct hal_gatt_gatt_id descr_id;
+ int32_t write_type;
+ int32_t len;
+ int32_t auth_req;
+ uint8_t value[0];
+} __attribute__((packed));
+
+#define HAL_OP_GATT_CLIENT_EXECUTE_WRITE 0x10
+struct hal_cmd_gatt_client_execute_write {
+ int32_t conn_id;
+ int32_t execute;
+} __attribute__((packed));
+
+#define HAL_OP_GATT_CLIENT_REGISTER_FOR_NOTIFICATION 0x11
+struct hal_cmd_gatt_client_register_for_notification {
+ int32_t client_if;
+ uint8_t bdaddr[6];
+ struct hal_gatt_srvc_id srvc_id;
+ struct hal_gatt_gatt_id char_id;
+} __attribute__((packed));
+
+#define HAL_OP_GATT_CLIENT_DEREGISTER_FOR_NOTIFICATION 0x12
+struct hal_cmd_gatt_client_deregister_for_notification {
+ int32_t client_if;
+ uint8_t bdaddr[6];
+ struct hal_gatt_srvc_id srvc_id;
+ struct hal_gatt_gatt_id char_id;
+} __attribute__((packed));
+
+#define HAL_OP_GATT_CLIENT_READ_REMOTE_RSSI 0x13
+struct hal_cmd_gatt_client_read_remote_rssi {
+ int32_t client_if;
+ uint8_t bdaddr[6];
+} __attribute__((packed));
+
+#define HAL_OP_GATT_CLIENT_GET_DEVICE_TYPE 0x14
+struct hal_cmd_gatt_client_get_device_type {
+ uint8_t bdaddr[6];
+} __attribute__((packed));
+
+struct hal_rsp_gatt_client_get_device_type {
+ uint8_t type;
+} __attribute__((packed));
+
+#define HAL_OP_GATT_CLIENT_SET_ADV_DATA 0x015
+struct hal_cmd_gatt_client_set_adv_data {
+ int32_t server_if;
+ uint8_t set_scan_rsp;
+ uint8_t include_name;
+ uint8_t include_txpower;
+ int32_t min_interval;
+ int32_t max_interval;
+ int32_t appearance;
+ uint16_t manufacturer_len;
+ uint16_t service_data_len;
+ uint16_t service_uuid_len;
+ uint8_t data[0];
+} __attribute__((packed));
+
+#define GATT_CLIENT_TEST_CMD_ENABLE 0x01
+#define GATT_CLIENT_TEST_CMD_CONNECT 0x02
+#define GATT_CLIENT_TEST_CMD_DISCONNECT 0x03
+#define GATT_CLIENT_TEST_CMD_DISCOVER 0x04
+#define GATT_CLIENT_TEST_CMD_READ 0xe0
+#define GATT_CLIENT_TEST_CMD_WRITE 0xe1
+#define GATT_CLIENT_TEST_CMD_INCREASE_SECURITY 0xe2
+#define GATT_CLIENT_TEST_CMD_PAIRING_CONFIG 0xf0
+
+#define HAL_OP_GATT_CLIENT_TEST_COMMAND 0x16
+struct hal_cmd_gatt_client_test_command {
+ int32_t command;
+ uint8_t bda1[6];
+ uint8_t uuid1[16];
+ uint16_t u1;
+ uint16_t u2;
+ uint16_t u3;
+ uint16_t u4;
+ uint16_t u5;
+} __attribute__((packed));
+
+#define HAL_OP_GATT_SERVER_REGISTER 0x17
+struct hal_cmd_gatt_server_register {
+ uint8_t uuid[16];
+} __attribute__((packed));
+
+#define HAL_OP_GATT_SERVER_UNREGISTER 0x18
+struct hal_cmd_gatt_server_unregister {
+ int32_t server_if;
+} __attribute__((packed));
+
+#define HAL_OP_GATT_SERVER_CONNECT 0x19
+struct hal_cmd_gatt_server_connect {
+ int32_t server_if;
+ uint8_t bdaddr[6];
+ uint8_t is_direct;
+ int32_t transport;
+} __attribute__((packed));
+
+#define HAL_OP_GATT_SERVER_DISCONNECT 0x1a
+struct hal_cmd_gatt_server_disconnect {
+ int32_t server_if;
+ uint8_t bdaddr[6];
+ int32_t conn_id;
+} __attribute__((packed));
+
+#define HAL_OP_GATT_SERVER_ADD_SERVICE 0x1b
+struct hal_cmd_gatt_server_add_service {
+ int32_t server_if;
+ struct hal_gatt_srvc_id srvc_id;
+ int32_t num_handles;
+} __attribute__((packed));
+
+#define HAL_OP_GATT_SERVER_ADD_INC_SERVICE 0x1c
+struct hal_cmd_gatt_server_add_inc_service {
+ int32_t server_if;
+ int32_t service_handle;
+ int32_t included_handle;
+} __attribute__((packed));
+
+#define HAL_OP_GATT_SERVER_ADD_CHARACTERISTIC 0x1d
+struct hal_cmd_gatt_server_add_characteristic {
+ int32_t server_if;
+ int32_t service_handle;
+ uint8_t uuid[16];
+ int32_t properties;
+ int32_t permissions;
+} __attribute__((packed));
+
+#define HAL_OP_GATT_SERVER_ADD_DESCRIPTOR 0x1e
+struct hal_cmd_gatt_server_add_descriptor {
+ int32_t server_if;
+ int32_t service_handle;
+ uint8_t uuid[16];
+ int32_t permissions;
+} __attribute__((packed));
+
+#define GATT_SERVER_TRANSPORT_LE_BIT 0x01
+#define GATT_SERVER_TRANSPORT_BREDR_BIT 0x02
+
+#define HAL_OP_GATT_SERVER_START_SERVICE 0x1f
+struct hal_cmd_gatt_server_start_service {
+ int32_t server_if;
+ int32_t service_handle;
+ int32_t transport;
+} __attribute__((packed));
+
+#define HAL_OP_GATT_SERVER_STOP_SERVICE 0x20
+struct hal_cmd_gatt_server_stop_service {
+ int32_t server_if;
+ int32_t service_handle;
+} __attribute__((packed));
+
+#define HAL_OP_GATT_SERVER_DELETE_SERVICE 0x21
+struct hal_cmd_gatt_server_delete_service {
+ int32_t server_if;
+ int32_t service_handle;
+} __attribute__((packed));
+
+#define HAL_OP_GATT_SERVER_SEND_INDICATION 0x22
+struct hal_cmd_gatt_server_send_indication {
+ int32_t server_if;
+ int32_t attribute_handle;
+ int32_t conn_id;
+ int32_t len;
+ int32_t confirm;
+ uint8_t value[0];
+} __attribute__((packed));
+
+#define HAL_OP_GATT_SERVER_SEND_RESPONSE 0x23
+struct hal_cmd_gatt_server_send_response {
+ int32_t conn_id;
+ int32_t trans_id;
+ uint16_t handle;
+ uint16_t offset;
+ uint8_t auth_req;
+ int32_t status;
+ uint16_t len;
+ uint8_t data[0];
+} __attribute__((packed));
+
+#define HAL_OP_GATT_CLIENT_SCAN_FILTER_SETUP 0x024
+struct hal_cmd_gatt_client_scan_filter_setup {
+ int32_t client_if;
+ int32_t action;
+ int32_t filter_index;
+ int32_t features;
+ int32_t list_type;
+ int32_t filter_type;
+ int32_t rssi_hi;
+ int32_t rssi_lo;
+ int32_t delivery_mode;
+ int32_t found_timeout;
+ int32_t lost_timeout;
+ int32_t found_timeout_cnt;
+} __attribute__((packed));
+
+#define HAL_OP_GATT_CLIENT_SCAN_FILTER_ADD_REMOVE 0x025
+struct hal_cmd_gatt_client_scan_filter_add_remove {
+ int32_t client_if;
+ int32_t action;
+ int32_t filter_type;
+ int32_t filter_index;
+ int32_t company_id;
+ int32_t company_id_mask;
+ uint8_t uuid[16];
+ uint8_t uuid_mask[16];
+ uint8_t address[6];
+ uint8_t address_type;
+ int32_t data_len;
+ int32_t mask_len;
+ uint8_t data_mask[0]; /* common buffer for data and mask */
+} __attribute__((packed));
+
+#define HAL_OP_GATT_CLIENT_SCAN_FILTER_CLEAR 0x26
+struct hal_cmd_gatt_client_scan_filter_clear {
+ int32_t client_if;
+ int32_t index;
+} __attribute__((packed));
+
+#define HAL_OP_GATT_CLIENT_SCAN_FILTER_ENABLE 0x27
+struct hal_cmd_gatt_client_scan_filter_enable {
+ int32_t client_if;
+ uint8_t enable;
+} __attribute__((packed));
+
+#define HAL_OP_GATT_CLIENT_CONFIGURE_MTU 0x28
+struct hal_cmd_gatt_client_configure_mtu {
+ int32_t conn_id;
+ int32_t mtu;
+} __attribute__((packed));
+
+#define HAL_OP_GATT_CLIENT_CONN_PARAM_UPDATE 0x29
+struct hal_cmd_gatt_client_conn_param_update {
+ uint8_t address[6];
+ int32_t min_interval;
+ int32_t max_interval;
+ int32_t latency;
+ int32_t timeout;
+} __attribute__((packed));
+
+#define HAL_OP_GATT_CLIENT_SET_SCAN_PARAM 0x2a
+struct hal_cmd_gatt_client_set_scan_param {
+ int32_t interval;
+ int32_t window;
+} __attribute__((packed));
+
+#define HAL_OP_GATT_CLIENT_SETUP_MULTI_ADV 0x2b
+struct hal_cmd_gatt_client_setup_multi_adv {
+ int32_t client_if;
+ int32_t min_interval;
+ int32_t max_interval;
+ int32_t type;
+ int32_t channel_map;
+ int32_t tx_power;
+ int32_t timeout;
+} __attribute__((packed));
+
+#define HAL_OP_GATT_CLIENT_UPDATE_MULTI_ADV 0x2c
+struct hal_cmd_gatt_client_update_multi_adv {
+ int32_t client_if;
+ int32_t min_interval;
+ int32_t max_interval;
+ int32_t type;
+ int32_t channel_map;
+ int32_t tx_power;
+ int32_t timeout;
+} __attribute__((packed));
+
+#define HAL_OP_GATT_CLIENT_SETUP_MULTI_ADV_INST 0x2d
+struct hal_cmd_gatt_client_setup_multi_adv_inst {
+ int32_t client_if;
+ uint8_t set_scan_rsp;
+ uint8_t include_name;
+ uint8_t include_tx_power;
+ int32_t appearance;
+ int32_t manufacturer_data_len;
+ int32_t service_data_len;
+ int32_t service_uuid_len;
+ uint8_t data_service_uuid[0];
+} __attribute__((packed));
+
+#define HAL_OP_GATT_CLIENT_DISABLE_MULTI_ADV_INST 0x2e
+struct hal_cmd_gatt_client_disable_multi_adv_inst {
+ int32_t client_if;
+} __attribute__((packed));
+
+#define HAL_OP_GATT_CLIENT_CONFIGURE_BATCHSCAN 0x2f
+struct hal_cmd_gatt_client_configure_batchscan {
+ int32_t client_if;
+ int32_t full_max;
+ int32_t trunc_max;
+ int32_t notify_threshold;
+} __attribute__((packed));
+
+#define HAL_OP_GATT_CLIENT_ENABLE_BATCHSCAN 0x30
+struct hal_cmd_gatt_client_enable_batchscan {
+ int32_t client_if;
+ int32_t mode;
+ int32_t interval;
+ int32_t window;
+ int32_t address_type;
+ int32_t discard_rule;
+} __attribute__((packed));
+
+#define HAL_OP_GATT_CLIENT_DISABLE_BATCHSCAN 0x31
+struct hal_cmd_gatt_client_disable_batchscan {
+ int32_t client_if;
+} __attribute__((packed));
+
+#define HAL_OP_GATT_CLIENT_READ_BATCHSCAN_REPORTS 0x32
+struct hal_cmd_gatt_client_read_batchscan_reports {
+ int32_t client_if;
+ int32_t scan_mode;
+} __attribute__((packed));
+
+/* Handsfree client HAL API */
+
+#define HAL_OP_HF_CLIENT_CONNECT 0x01
+struct hal_cmd_hf_client_connect {
+ uint8_t bdaddr[6];
+} __attribute__((packed));
+
+#define HAL_OP_HF_CLIENT_DISCONNECT 0x02
+struct hal_cmd_hf_client_disconnect {
+ uint8_t bdaddr[6];
+} __attribute__((packed));
+
+#define HAL_OP_HF_CLIENT_CONNECT_AUDIO 0x03
+struct hal_cmd_hf_client_connect_audio {
+ uint8_t bdaddr[6];
+} __attribute__((packed));
+
+#define HAL_OP_HF_CLIENT_DISCONNECT_AUDIO 0x04
+struct hal_cmd_hf_client_disconnect_audio {
+ uint8_t bdaddr[6];
+} __attribute__((packed));
+
+#define HAL_OP_HF_CLIENT_START_VR 0x05
+#define HAL_OP_HF_CLIENT_STOP_VR 0x06
+
+#define HF_CLIENT_VOLUME_TYPE_SPEAKER 0x00
+#define HF_CLIENT_VOLUME_TYPE_MIC 0x01
+
+#define HAL_OP_HF_CLIENT_VOLUME_CONTROL 0x07
+struct hal_cmd_hf_client_volume_control {
+ uint8_t type;
+ uint8_t volume;
+} __attribute__((packed));
+
+#define HAL_OP_HF_CLIENT_DIAL 0x08
+struct hal_cmd_hf_client_dial {
+ uint16_t number_len;
+ uint8_t number[0];
+} __attribute__((packed));
+
+#define HAL_OP_HF_CLIENT_DIAL_MEMORY 0x09
+struct hal_cmd_hf_client_dial_memory {
+ int32_t location;
+} __attribute__((packed));
+
+#define HAL_HF_CLIENT_ACTION_CHLD_0 0x00
+#define HAL_HF_CLIENT_ACTION_CHLD_1 0x01
+#define HAL_HF_CLIENT_ACTION_CHLD_2 0x02
+#define HAL_HF_CLIENT_ACTION_CHLD_3 0x03
+#define HAL_HF_CLIENT_ACTION_CHLD_4 0x04
+#define HAL_HF_CLIENT_ACTION_CHLD_1x 0x05
+#define HAL_HF_CLIENT_ACTION_CHLD_2x 0x06
+#define HAL_HF_CLIENT_ACTION_ATA 0x07
+#define HAL_HF_CLIENT_ACTION_CHUP 0x08
+#define HAL_HF_CLIENT_ACTION_BRTH_0 0x09
+#define HAL_HF_CLIENT_ACTION_BRTH_1 0x0a
+#define HAL_HF_CLIENT_ACTION_BRTH_2 0x0b
+
+#define HAL_OP_HF_CLIENT_CALL_ACTION 0x0a
+struct hal_cmd_hf_client_call_action {
+ uint8_t action;
+ int32_t index;
+} __attribute__((packed));
+
+#define HAL_OP_HF_CLIENT_QUERY_CURRENT_CALLS 0x0b
+#define HAL_OP_HF_CLIENT_QUERY_OPERATOR_NAME 0x0c
+#define HAL_OP_HF_CLIENT_RETRIEVE_SUBSCR_INFO 0x0d
+
+#define HAL_OP_HF_CLIENT_SEND_DTMF 0x0e
+struct hal_cmd_hf_client_send_dtmf {
+ uint8_t tone;
+} __attribute__((packed));
+
+#define HAL_OP_HF_CLIENT_GET_LAST_VOICE_TAG_NUM 0x0f
+
+/* MAP CLIENT HAL API */
+
+#define HAL_OP_MAP_CLIENT_GET_INSTANCES 0x01
+struct hal_cmd_map_client_get_instances {
+ uint8_t bdaddr[6];
+} __attribute__((packed));
+
+/* Notifications and confirmations */
+
+#define HAL_POWER_OFF 0x00
+#define HAL_POWER_ON 0x01
+
+#define HAL_EV_ADAPTER_STATE_CHANGED 0x81
+struct hal_ev_adapter_state_changed {
+ uint8_t state;
+} __attribute__((packed));
+
+#define HAL_EV_ADAPTER_PROPS_CHANGED 0x82
+struct hal_property {
+ uint8_t type;
+ uint16_t len;
+ uint8_t val[0];
+} __attribute__((packed));
+struct hal_ev_adapter_props_changed {
+ uint8_t status;
+ uint8_t num_props;
+ struct hal_property props[0];
+} __attribute__((packed));
+
+#define HAL_EV_REMOTE_DEVICE_PROPS 0x83
+struct hal_ev_remote_device_props {
+ uint8_t status;
+ uint8_t bdaddr[6];
+ uint8_t num_props;
+ struct hal_property props[0];
+} __attribute__((packed));
+
+#define HAL_EV_DEVICE_FOUND 0x84
+struct hal_ev_device_found {
+ uint8_t num_props;
+ struct hal_property props[0];
+} __attribute__((packed));
+
+#define HAL_DISCOVERY_STATE_STOPPED 0x00
+#define HAL_DISCOVERY_STATE_STARTED 0x01
+
+#define HAL_EV_DISCOVERY_STATE_CHANGED 0x85
+struct hal_ev_discovery_state_changed {
+ uint8_t state;
+} __attribute__((packed));
+
+#define HAL_EV_PIN_REQUEST 0x86
+struct hal_ev_pin_request {
+ uint8_t bdaddr[6];
+ uint8_t name[249];
+ uint32_t class_of_dev;
+} __attribute__((packed));
+
+#define HAL_EV_SSP_REQUEST 0x87
+struct hal_ev_ssp_request {
+ uint8_t bdaddr[6];
+ uint8_t name[249];
+ uint32_t class_of_dev;
+ uint8_t pairing_variant;
+ uint32_t passkey;
+} __attribute__((packed));
+
+#define HAL_BOND_STATE_NONE 0
+#define HAL_BOND_STATE_BONDING 1
+#define HAL_BOND_STATE_BONDED 2
+
+#define HAL_EV_BOND_STATE_CHANGED 0x88
+struct hal_ev_bond_state_changed {
+ uint8_t status;
+ uint8_t bdaddr[6];
+ uint8_t state;
+} __attribute__((packed));
+
+#define HAL_ACL_STATE_CONNECTED 0x00
+#define HAL_ACL_STATE_DISCONNECTED 0x01
+
+#define HAL_EV_ACL_STATE_CHANGED 0x89
+struct hal_ev_acl_state_changed {
+ uint8_t status;
+ uint8_t bdaddr[6];
+ uint8_t state;
+} __attribute__((packed));
+
+#define HAL_EV_DUT_MODE_RECEIVE 0x8a
+struct hal_ev_dut_mode_receive {
+ uint16_t opcode;
+ uint8_t len;
+ uint8_t data[0];
+} __attribute__((packed));
+
+#define HAL_EV_LE_TEST_MODE 0x8b
+struct hal_ev_le_test_mode {
+ uint8_t status;
+ uint16_t num_packets;
+} __attribute__((packed));
+
+#define HAL_EV_ENERGY_INFO 0x8c
+struct hal_ev_energy_info {
+ uint8_t status;
+ uint8_t ctrl_state;
+ uint64_t tx_time;
+ uint64_t rx_time;
+ uint64_t idle_time;
+ uint64_t energy_used;
+} __attribute__((packed));
+
+#define HAL_HIDHOST_STATE_CONNECTED 0x00
+#define HAL_HIDHOST_STATE_CONNECTING 0x01
+#define HAL_HIDHOST_STATE_DISCONNECTED 0x02
+#define HAL_HIDHOST_STATE_DISCONNECTING 0x03
+#define HAL_HIDHOST_STATE_NO_HID 0x07
+#define HAL_HIDHOST_STATE_FAILED 0x08
+#define HAL_HIDHOST_STATE_UNKNOWN 0x09
+
+#define HAL_EV_HIDHOST_CONN_STATE 0x81
+struct hal_ev_hidhost_conn_state {
+ uint8_t bdaddr[6];
+ uint8_t state;
+} __attribute__((packed));
+
+#define HAL_HIDHOST_STATUS_OK 0x00
+
+#define HAL_HIDHOST_HS_NOT_READY 0x01
+#define HAL_HIDHOST_HS_INVALID_RAPORT_ID 0x02
+#define HAL_HIDHOST_HS_TRANS_NOT_SUPPORTED 0x03
+#define HAL_HIDHOST_HS_INVALID_PARAM 0x04
+#define HAL_HIDHOST_HS_ERROR 0x05
+
+#define HAL_HIDHOST_GENERAL_ERROR 0x06
+#define HAL_HIDHOST_SDP_ERROR 0x07
+#define HAL_HIDHOST_PROTOCOL_ERROR 0x08
+#define HAL_HIDHOST_DB_ERROR 0x09
+#define HAL_HIDHOST_TOD_UNSUPPORTED_ERROR 0x0a
+#define HAL_HIDHOST_NO_RESOURCES_ERROR 0x0b
+#define HAL_HIDHOST_AUTH_FAILED_ERROR 0x0c
+#define HAL_HIDHOST_HDL_ERROR 0x0d
+
+#define HAL_EV_HIDHOST_INFO 0x82
+struct hal_ev_hidhost_info {
+ uint8_t bdaddr[6];
+ uint8_t attr;
+ uint8_t subclass;
+ uint8_t app_id;
+ uint16_t vendor;
+ uint16_t product;
+ uint16_t version;
+ uint8_t country;
+ uint16_t descr_len;
+ uint8_t descr[884];
+} __attribute__((packed));
+
+#define HAL_EV_HIDHOST_PROTO_MODE 0x83
+struct hal_ev_hidhost_proto_mode {
+ uint8_t bdaddr[6];
+ uint8_t status;
+ uint8_t mode;
+} __attribute__((packed));
+
+#define HAL_EV_HIDHOST_IDLE_TIME 0x84
+struct hal_ev_hidhost_idle_time {
+ uint8_t bdaddr[6];
+ uint8_t status;
+ uint32_t idle_rate;
+} __attribute__((packed));
+
+#define HAL_EV_HIDHOST_GET_REPORT 0x85
+struct hal_ev_hidhost_get_report {
+ uint8_t bdaddr[6];
+ uint8_t status;
+ uint16_t len;
+ uint8_t data[0];
+} __attribute__((packed));
+
+#define HAL_EV_HIDHOST_VIRTUAL_UNPLUG 0x86
+struct hal_ev_hidhost_virtual_unplug {
+ uint8_t bdaddr[6];
+ uint8_t status;
+} __attribute__((packed));
+
+#define HAL_EV_HIDHOST_HANDSHAKE 0x87
+struct hal_ev_hidhost_handshake {
+ uint8_t bdaddr[6];
+ uint8_t status;
+} __attribute__((packed));
+
+#define HAL_EV_PAN_CTRL_STATE 0x81
+struct hal_ev_pan_ctrl_state {
+ uint8_t state;
+ uint8_t status;
+ uint8_t local_role;
+ uint8_t name[17];
+} __attribute__((packed));
+
+#define HAL_EV_PAN_CONN_STATE 0x82
+struct hal_ev_pan_conn_state {
+ uint8_t state;
+ uint8_t status;
+ uint8_t bdaddr[6];
+ uint8_t local_role;
+ uint8_t remote_role;
+} __attribute__((packed));
+
+#define HAL_HEALTH_APP_REG_SUCCESS 0x00
+#define HAL_HEALTH_APP_REG_FAILED 0x01
+#define HAL_HEALTH_APP_DEREG_SUCCESS 0x02
+#define HAL_HEALTH_APP_DEREG_FAILED 0x03
+
+#define HAL_HEALTH_CHANNEL_CONNECTING 0x00
+#define HAL_HEALTH_CHANNEL_CONNECTED 0x01
+#define HAL_HEALTH_CHANNEL_DISCONNECTING 0x02
+#define HAL_HEALTH_CHANNEL_DISCONNECTED 0x03
+#define HAL_HEALTH_CHANNEL_DESTROYED 0x04
+
+#define HAL_EV_HEALTH_APP_REG_STATE 0x81
+struct hal_ev_health_app_reg_state {
+ uint16_t id;
+ uint8_t state;
+} __attribute__((packed));
+
+#define HAL_EV_HEALTH_CHANNEL_STATE 0x82
+struct hal_ev_health_channel_state {
+ uint16_t app_id;
+ uint8_t bdaddr[6];
+ uint8_t mdep_index;
+ uint16_t channel_id;
+ uint8_t channel_state;
+} __attribute__((packed));
+
+#define HAL_A2DP_STATE_DISCONNECTED 0x00
+#define HAL_A2DP_STATE_CONNECTING 0x01
+#define HAL_A2DP_STATE_CONNECTED 0x02
+#define HAL_A2DP_STATE_DISCONNECTING 0x03
+
+#define HAL_EV_A2DP_CONN_STATE 0x81
+struct hal_ev_a2dp_conn_state {
+ uint8_t state;
+ uint8_t bdaddr[6];
+} __attribute__((packed));
+
+#define HAL_AUDIO_SUSPEND 0x00
+#define HAL_AUDIO_STOPPED 0x01
+#define HAL_AUDIO_STARTED 0x02
+
+#define HAL_EV_A2DP_AUDIO_STATE 0x82
+struct hal_ev_a2dp_audio_state {
+ uint8_t state;
+ uint8_t bdaddr[6];
+} __attribute__((packed));
+
+#define HAL_EV_A2DP_AUDIO_CONFIG 0x83
+struct hal_ev_a2dp_audio_config {
+ uint8_t bdaddr[6];
+ uint32_t sample_rate;
+ uint8_t channel_count;
+} __attribute__((packed));
+
+#define HAL_EV_HANDSFREE_CONN_STATE_DISCONNECTED 0x00
+#define HAL_EV_HANDSFREE_CONN_STATE_CONNECTING 0x01
+#define HAL_EV_HANDSFREE_CONN_STATE_CONNECTED 0x02
+#define HAL_EV_HANDSFREE_CONN_STATE_SLC_CONNECTED 0x03
+#define HAL_EV_HANDSFREE_CONN_STATE_DISCONNECTING 0x04
+
+#define HAL_EV_HANDSFREE_CONN_STATE 0x81
+struct hal_ev_handsfree_conn_state {
+ uint8_t state;
+ uint8_t bdaddr[6];
+} __attribute__((packed));
+
+#define HAL_EV_HANDSFREE_AUDIO_STATE_DISCONNECTED 0x00
+#define HAL_EV_HANDSFREE_AUDIO_STATE_CONNECTING 0x01
+#define HAL_EV_HANDSFREE_AUDIO_STATE_CONNECTED 0x02
+#define HAL_EV_HANDSFREE_AUDIO_STATE_DISCONNECTING 0x03
+
+#define HAL_EV_HANDSFREE_AUDIO_STATE 0x82
+struct hal_ev_handsfree_audio_state {
+ uint8_t state;
+ uint8_t bdaddr[6];
+} __attribute__((packed));
+
+#define HAL_HANDSFREE_VR_STOPPED 0x00
+#define HAL_HANDSFREE_VR_STARTED 0x01
+
+#define HAL_EV_HANDSFREE_VR 0x83
+struct hal_ev_handsfree_vr_state {
+ uint8_t state;
+ uint8_t bdaddr[6];
+} __attribute__((packed));
+
+#define HAL_EV_HANDSFREE_ANSWER 0x84
+struct hal_ev_handsfree_answer {
+ uint8_t bdaddr[6];
+} __attribute__((packed));
+
+#define HAL_EV_HANDSFREE_HANGUP 0x85
+struct hal_ev_handsfree_hangup {
+ uint8_t bdaddr[6];
+} __attribute__((packed));
+
+#define HAL_EV_HANDSFREE_VOLUME 0x86
+struct hal_ev_handsfree_volume {
+ uint8_t type;
+ uint8_t volume;
+ uint8_t bdaddr[6];
+} __attribute__((packed));
+
+#define HAL_EV_HANDSFREE_DIAL 0x87
+struct hal_ev_handsfree_dial {
+ uint8_t bdaddr[6];
+ uint16_t number_len;
+ uint8_t number[0];
+} __attribute__((packed));
+
+#define HAL_EV_HANDSFREE_DTMF 0x88
+struct hal_ev_handsfree_dtmf {
+ uint8_t tone;
+ uint8_t bdaddr[6];
+} __attribute__((packed));
+
+#define HAL_HANDSFREE_NREC_STOP 0x00
+#define HAL_HANDSFREE_NREC_START 0x01
+
+#define HAL_EV_HANDSFREE_NREC 0x89
+struct hal_ev_handsfree_nrec {
+ uint8_t nrec;
+ uint8_t bdaddr[6];
+} __attribute__((packed));
+
+#define HAL_HANDSFREE_CHLD_TYPE_RELEASEHELD 0x00
+#define HAL_HANDSFREE_CHLD_TYPE_RELEASEACTIVE_ACCEPTHELD 0x01
+#define HAL_HANDSFREE_CHLD_TYPE_HOLDACTIVE_ACCEPTHELD 0x02
+#define HAL_HANDSFREE_CHLD_TYPE_ADDHELDTOCONF 0x03
+
+#define HAL_EV_HANDSFREE_CHLD 0x8A
+struct hal_ev_handsfree_chld {
+ uint8_t chld;
+ uint8_t bdaddr[6];
+} __attribute__((packed));
+
+#define HAL_EV_HANDSFREE_CNUM 0x8B
+struct hal_ev_handsfree_cnum {
+ uint8_t bdaddr[6];
+} __attribute__((packed));
+
+#define HAL_EV_HANDSFREE_CIND 0x8C
+struct hal_ev_handsfree_cind {
+ uint8_t bdaddr[6];
+} __attribute__((packed));
+
+#define HAL_EV_HANDSFREE_COPS 0x8D
+struct hal_ev_handsfree_cops {
+ uint8_t bdaddr[6];
+} __attribute__((packed));
+
+#define HAL_EV_HANDSFREE_CLCC 0x8E
+struct hal_ev_handsfree_clcc {
+ uint8_t bdaddr[6];
+} __attribute__((packed));
+
+#define HAL_EV_HANDSFREE_UNKNOWN_AT 0x8F
+struct hal_ev_handsfree_unknown_at {
+ uint8_t bdaddr[6];
+ uint16_t len;
+ uint8_t buf[0];
+} __attribute__((packed));
+
+#define HAL_EV_HANDSFREE_HSP_KEY_PRESS 0x90
+struct hal_ev_handsfree_hsp_key_press {
+ uint8_t bdaddr[6];
+} __attribute__((packed));
+
+#define HAL_EV_HANDSFREE_WBS 0x91
+struct hal_ev_handsfree_wbs {
+ uint8_t wbs;
+ uint8_t bdaddr[6];
+} __attribute__((packed));
+
+#define HAL_AVRCP_FEATURE_NONE 0x00
+#define HAL_AVRCP_FEATURE_METADATA 0x01
+#define HAL_AVRCP_FEATURE_ABSOLUTE_VOLUME 0x02
+#define HAL_AVRCP_FEATURE_BROWSE 0x04
+
+#define HAL_EV_AVRCP_REMOTE_FEATURES 0x81
+struct hal_ev_avrcp_remote_features {
+ uint8_t bdaddr[6];
+ uint8_t features;
+} __attribute__((packed));
+
+#define HAL_EV_AVRCP_GET_PLAY_STATUS 0x82
+#define HAL_EV_AVRCP_LIST_PLAYER_ATTRS 0x83
+
+#define HAL_EV_AVRCP_LIST_PLAYER_VALUES 0x84
+struct hal_ev_avrcp_list_player_values {
+ uint8_t attr;
+} __attribute__((packed));
+
+#define HAL_EV_AVRCP_GET_PLAYER_VALUES 0x85
+struct hal_ev_avrcp_get_player_values {
+ uint8_t number;
+ uint8_t attrs[0];
+} __attribute__((packed));
+
+#define HAL_EV_AVRCP_GET_PLAYER_ATTRS_TEXT 0x86
+struct hal_ev_avrcp_get_player_attrs_text {
+ uint8_t number;
+ uint8_t attrs[0];
+} __attribute__((packed));
+
+#define HAL_EV_AVRCP_GET_PLAYER_VALUES_TEXT 0x87
+struct hal_ev_avrcp_get_player_values_text {
+ uint8_t attr;
+ uint8_t number;
+ uint8_t values[0];
+} __attribute__((packed));
+
+#define HAL_EV_AVRCP_SET_PLAYER_VALUES 0x88
+struct hal_ev_avrcp_set_player_values {
+ uint8_t number;
+ struct hal_avrcp_player_attr_value attrs[0];
+} __attribute__((packed));
+
+#define HAL_EV_AVRCP_GET_ELEMENT_ATTRS 0x89
+struct hal_ev_avrcp_get_element_attrs {
+ uint8_t number;
+ uint8_t attrs[0];
+} __attribute__((packed));
+
+#define HAL_EV_AVRCP_REGISTER_NOTIFICATION 0x8a
+struct hal_ev_avrcp_register_notification {
+ uint8_t event;
+ uint32_t param;
+} __attribute__((packed));
+
+#define HAL_EV_AVRCP_VOLUME_CHANGED 0x8b
+struct hal_ev_avrcp_volume_changed {
+ uint8_t volume;
+ uint8_t type;
+} __attribute__((packed));
+
+#define HAL_EV_AVRCP_PASSTHROUGH_CMD 0x8c
+struct hal_ev_avrcp_passthrough_cmd {
+ uint8_t id;
+ uint8_t state;
+} __attribute__((packed));
+
+#define HAL_EV_AVRCP_CTRL_CONN_STATE 0x81
+struct hal_ev_avrcp_ctrl_conn_state {
+ uint8_t state;
+ uint8_t bdaddr[6];
+} __attribute__((packed));
+
+#define HAL_EV_AVRCP_CTRL_PASSTHROUGH_RSP 0x82
+struct hal_ev_avrcp_ctrl_passthrough_rsp {
+ uint8_t id;
+ uint8_t key_state;
+} __attribute__((packed));
+
+#define HAL_EV_GATT_CLIENT_REGISTER_CLIENT 0x81
+struct hal_ev_gatt_client_register_client {
+ int32_t status;
+ int32_t client_if;
+ uint8_t app_uuid[16];
+} __attribute__((packed));
+
+#define HAL_EV_GATT_CLIENT_SCAN_RESULT 0x82
+struct hal_ev_gatt_client_scan_result {
+ uint8_t bda[6];
+ int32_t rssi;
+ uint16_t len;
+ uint8_t adv_data[0];
+} __attribute__((packed));
+
+#define HAL_EV_GATT_CLIENT_CONNECT 0x83
+struct hal_ev_gatt_client_connect {
+ int32_t conn_id;
+ int32_t status;
+ int32_t client_if;
+ uint8_t bda[6];
+} __attribute__((packed));
+
+#define HAL_EV_GATT_CLIENT_DISCONNECT 0x84
+struct hal_ev_gatt_client_disconnect {
+ int32_t conn_id;
+ int32_t status;
+ int32_t client_if;
+ uint8_t bda[6];
+} __attribute__((packed));
+
+#define HAL_EV_GATT_CLIENT_SEARCH_COMPLETE 0x85
+struct hal_ev_gatt_client_search_complete {
+ int32_t conn_id;
+ int32_t status;
+} __attribute__((packed));
+
+#define HAL_EV_GATT_CLIENT_SEARCH_RESULT 0x86
+struct hal_ev_gatt_client_search_result {
+ int32_t conn_id;
+ struct hal_gatt_srvc_id srvc_id;
+} __attribute__((packed));
+
+#define HAL_EV_GATT_CLIENT_GET_CHARACTERISTIC 0x87
+struct hal_ev_gatt_client_get_characteristic {
+ int32_t conn_id;
+ int32_t status;
+ struct hal_gatt_srvc_id srvc_id;
+ struct hal_gatt_gatt_id char_id;
+ int32_t char_prop;
+} __attribute__((packed));
+
+#define HAL_EV_GATT_CLIENT_GET_DESCRIPTOR 0x88
+struct hal_ev_gatt_client_get_descriptor {
+ int32_t conn_id;
+ int32_t status;
+ struct hal_gatt_srvc_id srvc_id;
+ struct hal_gatt_gatt_id char_id;
+ struct hal_gatt_gatt_id descr_id;
+} __attribute__((packed));
+
+#define HAL_EV_GATT_CLIENT_GET_INC_SERVICE 0X89
+struct hal_ev_gatt_client_get_inc_service {
+ int32_t conn_id;
+ int32_t status;
+ struct hal_gatt_srvc_id srvc_id;
+ struct hal_gatt_srvc_id incl_srvc_id;
+} __attribute__((packed));
+
+#define HAL_EV_GATT_CLIENT_REGISTER_FOR_NOTIF 0x8a
+struct hal_ev_gatt_client_reg_for_notif {
+ int32_t conn_id;
+ int32_t registered;
+ int32_t status;
+ struct hal_gatt_srvc_id srvc_id;
+ struct hal_gatt_gatt_id char_id;
+} __attribute__((packed));
+
+#define HAL_EV_GATT_CLIENT_NOTIFY 0x8b
+struct hal_ev_gatt_client_notify {
+ int32_t conn_id;
+ uint8_t bda[6];
+ struct hal_gatt_srvc_id srvc_id;
+ struct hal_gatt_gatt_id char_id;
+ uint8_t is_notify;
+ uint16_t len;
+ uint8_t value[0];
+} __attribute__((packed));
+
+#define HAL_EV_GATT_CLIENT_READ_CHARACTERISTIC 0x8c
+struct hal_gatt_read_params {
+ struct hal_gatt_srvc_id srvc_id;
+ struct hal_gatt_gatt_id char_id;
+ struct hal_gatt_gatt_id descr_id;
+ uint8_t status;
+ uint16_t value_type;
+ uint16_t len;
+ uint8_t value[0];
+} __attribute__((packed));
+
+struct hal_ev_gatt_client_read_characteristic {
+ int32_t conn_id;
+ int32_t status;
+ struct hal_gatt_read_params data;
+} __attribute__((packed));
+
+#define HAL_EV_GATT_CLIENT_WRITE_CHARACTERISTIC 0x8d
+struct hal_gatt_write_params {
+ struct hal_gatt_srvc_id srvc_id;
+ struct hal_gatt_gatt_id char_id;
+ struct hal_gatt_gatt_id descr_id;
+ uint8_t status;
+} __attribute__((packed));
+
+struct hal_ev_gatt_client_write_characteristic {
+ int32_t conn_id;
+ int32_t status;
+ struct hal_gatt_write_params data;
+} __attribute__((packed));
+
+#define HAL_EV_GATT_CLIENT_READ_DESCRIPTOR 0x8e
+struct hal_ev_gatt_client_read_descriptor {
+ int32_t conn_id;
+ int32_t status;
+ struct hal_gatt_read_params data;
+} __attribute__((packed));
+
+#define HAL_EV_GATT_CLIENT_WRITE_DESCRIPTOR 0x8f
+struct hal_ev_gatt_client_write_descriptor {
+ int32_t conn_id;
+ int32_t status;
+ struct hal_gatt_write_params data;
+} __attribute__((packed));
+
+#define HAL_EV_GATT_CLIENT_EXEC_WRITE 0x90
+struct hal_ev_gatt_client_exec_write {
+ int32_t conn_id;
+ int32_t status;
+} __attribute__((packed));
+
+#define HAL_EV_GATT_CLIENT_READ_REMOTE_RSSI 0x91
+struct hal_ev_gatt_client_read_remote_rssi {
+ int32_t client_if;
+ uint8_t address[6];
+ int32_t rssi;
+ int32_t status;
+} __attribute__((packed));
+
+#define HAL_EV_GATT_CLIENT_LISTEN 0x92
+struct hal_ev_gatt_client_listen {
+ int32_t status;
+ int32_t server_if;
+} __attribute__((packed));
+
+#define HAL_EV_GATT_SERVER_REGISTER 0x93
+struct hal_ev_gatt_server_register {
+ int32_t status;
+ int32_t server_if;
+ uint8_t uuid[16];
+} __attribute__((packed));
+
+#define HAL_EV_GATT_SERVER_CONNECTION 0x94
+struct hal_ev_gatt_server_connection {
+ int32_t conn_id;
+ int32_t server_if;
+ int32_t connected;
+ uint8_t bdaddr[6];
+} __attribute__((packed));
+
+#define HAL_EV_GATT_SERVER_SERVICE_ADDED 0x95
+struct hal_ev_gatt_server_service_added {
+ int32_t status;
+ int32_t server_if;
+ struct hal_gatt_srvc_id srvc_id;
+ int32_t srvc_handle;
+} __attribute__((packed));
+
+#define HAL_EV_GATT_SERVER_INC_SRVC_ADDED 0x96
+struct hal_ev_gatt_server_inc_srvc_added {
+ int32_t status;
+ int32_t server_if;
+ int32_t srvc_handle;
+ int32_t incl_srvc_handle;
+} __attribute__((packed));
+
+#define HAL_EV_GATT_SERVER_CHAR_ADDED 0x97
+struct hal_ev_gatt_server_characteristic_added {
+ int32_t status;
+ int32_t server_if;
+ uint8_t uuid[16];
+ int32_t srvc_handle;
+ int32_t char_handle;
+} __attribute__((packed));
+
+#define HAL_EV_GATT_SERVER_DESCRIPTOR_ADDED 0x98
+struct hal_ev_gatt_server_descriptor_added {
+ int32_t status;
+ int32_t server_if;
+ uint8_t uuid[16];
+ int32_t srvc_handle;
+ int32_t descr_handle;
+} __attribute__((packed));
+
+#define HAL_EV_GATT_SERVER_SERVICE_STARTED 0x99
+struct hal_ev_gatt_server_service_started {
+ int32_t status;
+ int32_t server_if;
+ int32_t srvc_handle;
+} __attribute__((packed));
+
+#define HAL_EV_GATT_SERVER_SERVICE_STOPPED 0x9a
+struct hal_ev_gatt_server_service_stopped {
+ int32_t status;
+ int32_t server_if;
+ int32_t srvc_handle;
+} __attribute__((packed));
+
+#define HAL_EV_GATT_SERVER_SERVICE_DELETED 0x9b
+struct hal_ev_gatt_server_service_deleted {
+ int32_t status;
+ int32_t server_if;
+ int32_t srvc_handle;
+} __attribute__((packed));
+
+#define HAL_EV_GATT_SERVER_REQUEST_READ 0x9c
+struct hal_ev_gatt_server_request_read {
+ int32_t conn_id;
+ int32_t trans_id;
+ uint8_t bdaddr[6];
+ int32_t attr_handle;
+ int32_t offset;
+ uint8_t is_long;
+} __attribute__((packed));
+
+#define HAL_EV_GATT_SERVER_REQUEST_WRITE 0x9d
+struct hal_ev_gatt_server_request_write {
+ int32_t conn_id;
+ int32_t trans_id;
+ uint8_t bdaddr[6];
+ int32_t attr_handle;
+ int32_t offset;
+ int32_t length;
+ uint8_t need_rsp;
+ uint8_t is_prep;
+ uint8_t value[0];
+} __attribute__((packed));
+
+#define HAL_EV_GATT_SERVER_REQUEST_EXEC_WRITE 0x9e
+struct hal_ev_gatt_server_request_exec_write {
+ int32_t conn_id;
+ int32_t trans_id;
+ uint8_t bdaddr[6];
+ int32_t exec_write;
+} __attribute__((packed));
+
+#define HAL_EV_GATT_SERVER_RSP_CONFIRMATION 0x9f
+struct hal_ev_gatt_server_rsp_confirmation {
+ int32_t status;
+ int32_t handle;
+} __attribute__((packed));
+
+#define HAL_EV_GATT_CLIENT_CONFIGURE_MTU 0xa0
+struct hal_ev_gatt_client_configure_mtu {
+ int32_t conn_id;
+ int32_t status;
+ int32_t mtu;
+} __attribute__((packed));
+
+#define HAL_EV_GATT_CLIENT_FILTER_CONFIG 0xa1
+struct hal_ev_gatt_client_filter_config {
+ int32_t action;
+ int32_t client_if;
+ int32_t status;
+ int32_t type;
+ int32_t space;
+} __attribute__((packed));
+
+#define HAL_EV_GATT_CLIENT_FILTER_PARAMS 0xa2
+struct hal_ev_gatt_client_filter_params {
+ int32_t action;
+ int32_t client_if;
+ int32_t status;
+ int32_t space;
+} __attribute__((packed));
+
+#define HAL_EV_GATT_CLIENT_FILTER_STATUS 0xa3
+struct hal_ev_gatt_client_filter_status {
+ int32_t enable;
+ int32_t client_if;
+ int32_t status;
+} __attribute__((packed));
+
+#define HAL_EV_GATT_CLIENT_MULTI_ADV_ENABLE 0xa4
+struct hal_ev_gatt_client_multi_adv_enable {
+ int32_t client_if;
+ int32_t status;
+} __attribute__((packed));
+
+
+#define HAL_EV_GATT_CLIENT_MULTI_ADV_UPDATE 0xa5
+struct hal_ev_gatt_client_multi_adv_update {
+ int32_t client_if;
+ int32_t status;
+} __attribute__((packed));
+
+
+#define HAL_EV_GATT_CLIENT_MULTI_ADV_DATA 0xa6
+struct hal_ev_gatt_client_multi_adv_data {
+ int32_t client_if;
+ int32_t status;
+} __attribute__((packed));
+
+
+#define HAL_EV_GATT_CLIENT_MULTI_ADV_DISABLE 0xa7
+struct hal_ev_gatt_client_multi_adv_disable {
+ int32_t client_if;
+ int32_t status;
+} __attribute__((packed));
+
+#define HAL_EV_GATT_CLIENT_CONGESTION 0xa8
+struct hal_ev_gatt_client_congestion {
+ int32_t conn_id;
+ uint8_t congested;
+} __attribute__((packed));
+
+#define HAL_EV_GATT_CLIENT_CONFIG_BATCHSCAN 0xa9
+struct hal_ev_gatt_client_config_batchscan {
+ int32_t client_if;
+ int32_t status;
+} __attribute__((packed));
+
+#define HAL_EV_GATT_CLIENT_ENABLE_BATCHSCAN 0xaa
+struct hal_ev_gatt_client_enable_batchscan {
+ int32_t action;
+ int32_t client_if;
+ int32_t status;
+} __attribute__((packed));
+
+#define HAL_EV_GATT_CLIENT_BATCHSCAN_REPORTS 0xab
+struct hal_ev_gatt_client_batchscan_reports {
+ int32_t client_if;
+ int32_t status;
+ int32_t format;
+ int32_t num;
+ int32_t data_len;
+ uint8_t data[0];
+} __attribute__((packed));
+
+#define HAL_EV_GATT_CLIENT_BATCHSCAN_THRESHOLD 0xac
+struct hal_ev_gatt_client_batchscan_threshold {
+ int32_t client_if;
+} __attribute__((packed));
+
+#define HAL_EV_GATT_CLIENT_TRACK_ADV 0xad
+struct hal_ev_gatt_client_track_adv {
+ int32_t client_if;
+ int32_t filetr_index;
+ int32_t address_type;
+ uint8_t address[6];
+ int32_t state;
+} __attribute__((packed));
+
+#define HAL_EV_GATT_SERVER_INDICATION_SENT 0xae
+struct hal_ev_gatt_server_indication_sent {
+ int32_t conn_id;
+ int32_t status;
+} __attribute__((packed));
+
+#define HAL_EV_GATT_SERVER_CONGESTION 0xaf
+struct hal_ev_gatt_server_congestion {
+ int32_t conn_id;
+ uint8_t congested;
+} __attribute__((packed));
+
+#define HAL_EV_GATT_SERVER_MTU_CHANGED 0xb0
+struct hal_ev_gatt_server_mtu_changed {
+ int32_t conn_id;
+ int32_t mtu;
+} __attribute__((packed));
+
+#define HAL_GATT_PERMISSION_READ 0x0001
+#define HAL_GATT_PERMISSION_READ_ENCRYPTED 0x0002
+#define HAL_GATT_PERMISSION_READ_ENCRYPTED_MITM 0x0004
+#define HAL_GATT_PERMISSION_WRITE 0x0010
+#define HAL_GATT_PERMISSION_WRITE_ENCRYPTED 0x0020
+#define HAL_GATT_PERMISSION_WRITE_ENCRYPTED_MITM 0x0040
+#define HAL_GATT_PERMISSION_WRITE_SIGNED 0x0080
+#define HAL_GATT_PERMISSION_WRITE_SIGNED_MITM 0x0100
+
+#define HAL_GATT_AUTHENTICATION_NONE 0
+#define HAL_GATT_AUTHENTICATION_NO_MITM 1
+#define HAL_GATT_AUTHENTICATION_MITM 2
+
+#define HAL_HF_CLIENT_CONN_STATE_DISCONNECTED 0x00
+#define HAL_HF_CLIENT_CONN_STATE_CONNECTING 0x01
+#define HAL_HF_CLIENT_CONN_STATE_CONNECTED 0x02
+#define HAL_HF_CLIENT_CONN_STATE_SLC_CONNECTED 0x03
+#define HAL_HF_CLIENT_CONN_STATE_DISCONNECTING 0x04
+
+#define HAL_HF_CLIENT_PEER_FEAT_3WAY 0x00000001
+#define HAL_HF_CLIENT_PEER_FEAT_ECNR 0x00000002
+#define HAL_HF_CLIENT_PEER_FEAT_VREC 0x00000004
+#define HAL_HF_CLIENT_PEER_FEAT_INBAND 0x00000008
+#define HAL_HF_CLIENT_PEER_FEAT_VTAG 0x00000010
+#define HAL_HF_CLIENT_PEER_FEAT_REJECT 0x00000020
+#define HAL_HF_CLIENT_PEER_FEAT_ECS 0x00000040
+#define HAL_HF_CLIENT_PEER_FEAT_ECC 0x00000080
+#define HAL_HF_CLIENT_PEER_FEAT_EXTERR 0x00000100
+#define HAL_HF_CLIENT_PEER_FEAT_CODEC 0x00000200
+
+#define HAL_HF_CLIENT_CHLD_FEAT_REL 0x00000001
+#define HAL_HF_CLIENT_CHLD_FEAT_REL_ACC 0x00000002
+#define HAL_HF_CLIENT_CHLD_FEAT_REL_X 0x00000004
+#define HAL_HF_CLIENT_CHLD_FEAT_HOLD_ACC 0x00000008
+#define HAL_HF_CLIENT_CHLD_FEAT_PRIV_X 0x00000010
+#define HAL_HF_CLIENT_CHLD_FEAT_MERGE 0x00000020
+#define HAL_HF_CLIENT_CHLD_FEAT_MERGE_DETACH 0x00000040
+
+#define HAL_EV_HF_CLIENT_CONN_STATE 0x81
+struct hal_ev_hf_client_conn_state {
+ uint8_t state;
+ uint32_t peer_feat;
+ uint32_t chld_feat;
+ uint8_t bdaddr[6];
+} __attribute__((packed));
+
+#define HAL_HF_CLIENT_AUDIO_STATE_DISCONNECTED 0x00
+#define HAL_HF_CLIENT_AUDIO_STATE_CONNECTING 0x01
+#define HAL_HF_CLIENT_AUDIO_STATE_CONNECTED 0x02
+#define HAL_HF_CLIENT_AUDIO_STATE_CONNECTED_MSBC 0x03
+
+#define HAL_EV_HF_CLIENT_AUDIO_STATE 0x82
+struct hal_ev_hf_client_audio_state {
+ uint8_t state;
+ uint8_t bdaddr[6];
+} __attribute__((packed));
+
+#define HAL_HF_CLIENT_VR_STOPPED 0x00
+#define HAL_HF_CLIENT_VR_STARTED 0x01
+
+#define HAL_EV_HF_CLIENT_VR_STATE 0x83
+struct hal_ev_hf_client_vr_state {
+ uint8_t state;
+} __attribute__((packed));
+
+#define HAL_HF_CLIENT_NET_NOT_AVAILABLE 0x00
+#define HAL_HF_CLIENT_NET_AVAILABLE 0x01
+
+#define HAL_EV_HF_CLIENT_NET_STATE 0x84
+struct hal_ev_hf_client_net_state {
+ uint8_t state;
+} __attribute__((packed));
+
+#define HAL_HF_CLIENT_NET_ROAMING_TYPE_HOME 0x00
+#define HAL_HF_CLIENT_NET_ROAMING_TYPE_ROAMING 0x01
+
+#define HAL_EV_HF_CLIENT_NET_ROAMING_TYPE 0x85
+struct hal_ev_hf_client_net_roaming_type {
+ uint8_t state;
+} __attribute__((packed));
+
+#define HAL_EV_HF_CLIENT_NET_SIGNAL_STRENGTH 0x86
+struct hal_ev_hf_client_net_signal_strength {
+ uint8_t signal_strength;
+} __attribute__((packed));
+
+#define HAL_EV_HF_CLIENT_BATTERY_LEVEL 0x87
+struct hal_ev_hf_client_battery_level {
+ uint8_t battery_level;
+} __attribute__((packed));
+
+#define HAL_EV_HF_CLIENT_OPERATOR_NAME 0x88
+struct hal_ev_hf_client_operator_name {
+ uint16_t name_len;
+ uint8_t name[0];
+} __attribute__((packed));
+
+#define HAL_HF_CLIENT_CALL_IND_NO_CALL_IN_PROGERSS 0x00
+#define HAL_HF_CLIENT_CALL_IND_CALL_IN_PROGERSS 0x01
+
+#define HAL_EV_HF_CLIENT_CALL_INDICATOR 0x89
+struct hal_ev_hf_client_call_indicator {
+ uint8_t call;
+} __attribute__((packed));
+
+#define HAL_HF_CLIENT_CALL_SETUP_NONE 0x00
+#define HAL_HF_CLIENT_CALL_SETUP_INCOMING 0x01
+#define HAL_HF_CLIENT_CALL_SETUP_OUTGOING 0x02
+#define HAL_HF_CLIENT_CALL_SETUP_ALERTING 0x03
+
+#define HAL_EV_HF_CLIENT_CALL_SETUP_INDICATOR 0x8a
+struct hal_ev_hf_client_call_setup_indicator {
+ uint8_t call_setup;
+} __attribute__((packed));
+
+#define HAL_HF_CLIENT_CALL_HELD_IND_NONE 0x00
+#define HAL_HF_CLIENT_CALL_HELD_IND_HOLD_AND_ACTIVE 0x01
+#define HAL_HF_CLIENT_CALL_SETUP_IND_HOLD 0x02
+
+#define HAL_EV_HF_CLIENT_CALL_HELD_INDICATOR 0x8b
+struct hal_ev_hf_client_call_held_indicator {
+ uint8_t call_held;
+} __attribute__((packed));
+
+#define HAL_HF_CLIENT_RESP_AND_HOLD_STATUS_HELD 0x00
+#define HAL_HF_CLIENT_RESP_AND_HOLD_STATUS_ACCEPT 0x01
+#define HAL_HF_CLIENT_RESP_AND_HOLD_STATUS_REJECT 0x02
+
+#define HAL_EV_HF_CLIENT_RESPONSE_AND_HOLD_STATUS 0x8c
+struct hal_ev_hf_client_response_and_hold_status {
+ uint8_t status;
+} __attribute__((packed));
+
+#define HAL_EV_HF_CLIENT_CALLING_LINE_IDENT 0x8d
+struct hal_ev_hf_client_calling_line_ident {
+ uint16_t number_len;
+ uint8_t number[0];
+} __attribute__((packed));
+
+#define HAL_EV_HF_CLIENT_CALL_WAITING 0x8e
+struct hal_ev_hf_client_call_waiting {
+ uint16_t number_len;
+ uint8_t number[0];
+} __attribute__((packed));
+
+#define HAL_HF_CLIENT_DIRECTION_OUTGOING 0x00
+#define HAL_HF_CLIENT_DIRECTION_INCOMING 0x01
+
+#define HAL_HF_CLIENT_CALL_STATE_ACTIVE 0x00
+#define HAL_HF_CLIENT_CALL_STATE_HELD 0x01
+#define HAL_HF_CLIENT_CALL_STATE_DIALING 0x02
+#define HAL_HF_CLIENT_CALL_STATE_ALERTING 0x03
+#define HAL_HF_CLIENT_CALL_STATE_INCOMING 0x04
+#define HAL_HF_CLIENT_CALL_STATE_WAITING 0x05
+#define HAL_HF_CLIENT_CALL_STATE_HELD_BY_RESP_AND_HOLD 0x06
+
+#define HAL_EV_HF_CLIENT_CURRENT_CALL 0x8f
+struct hal_ev_hf_client_current_call {
+ uint8_t index;
+ uint8_t direction;
+ uint8_t call_state;
+ uint8_t multiparty;
+ uint16_t number_len;
+ uint8_t number[0];
+} __attribute__((packed));
+
+#define HAL_EV_CLIENT_VOLUME_CHANGED 0x90
+struct hal_ev_hf_client_volume_changed {
+ uint8_t type;
+ uint8_t volume;
+} __attribute__((packed));
+
+#define HAL_HF_CLIENT_CMD_COMP_OK 0x00
+#define HAL_HF_CLIENT_CMD_COMP_ERR 0x01
+#define HAL_HF_CLIENT_CMD_COMP_ERR_NO_CARRIER 0x02
+#define HAL_HF_CLIENT_CMD_COMP_ERR_BUSY 0x03
+#define HAL_HF_CLIENT_CMD_COMP_ERR_NO_ANSWER 0x04
+#define HAL_HF_CLIENT_CMD_COMP_ERR_DELAYED 0x05
+#define HAL_HF_CLIENT_CMD_COMP_ERR_BACKLISTED 0x06
+#define HAL_HF_CLIENT_CMD_COMP_ERR_CME 0x07
+
+#define HAL_EV_CLIENT_COMMAND_COMPLETE 0x91
+struct hal_ev_hf_client_command_complete {
+ uint8_t type;
+ uint8_t cme;
+} __attribute__((packed));
+
+#define HAL_HF_CLIENT_SUBSCR_TYPE_UNKNOWN 0x00
+#define HAL_HF_CLIENT_SUBSCR_TYPE_VOICE 0x01
+#define HAL_HF_CLIENT_SUBSCR_TYPE_FAX 0x02
+
+#define HAL_EV_CLIENT_SUBSCRIBER_SERVICE_INFO 0x92
+struct hal_ev_hf_client_subscriber_service_info {
+ uint8_t type;
+ uint16_t name_len;
+ uint8_t name[0];
+} __attribute__((packed));
+
+#define HAL_HF_CLIENT_INBAND_RINGTONE_NOT_PROVIDED 0x00
+#define HAL_HF_CLIENT_INBAND_RINGTONE_PROVIDED 0x01
+
+#define HAL_EV_CLIENT_INBAND_SETTINGS 0x93
+struct hal_ev_hf_client_inband_settings {
+ uint8_t state;
+} __attribute__((packed));
+
+#define HAL_EV_CLIENT_LAST_VOICE_CALL_TAG_NUM 0x94
+struct hal_ev_hf_client_last_void_call_tag_num {
+ uint16_t number_len;
+ uint8_t number[0];
+} __attribute__((packed));
+
+#define HAL_EV_CLIENT_RING_INDICATION 0x95
+
+#define HAL_EV_MAP_CLIENT_REMOTE_MAS_INSTANCES 0x81
+struct hal_map_client_mas_instance {
+ int32_t id;
+ int32_t scn;
+ int32_t msg_types;
+ int32_t name_len;
+ uint8_t name[0];
+} __attribute__((packed));
+
+struct hal_ev_map_client_remote_mas_instances {
+ int8_t status;
+ uint8_t bdaddr[6];
+ int32_t num_instances;
+ struct hal_map_client_mas_instance instances[0];
+} __attribute__((packed));
diff --git a/repo/android/hal-pan.c b/repo/android/hal-pan.c
new file mode 100644
index 0000000..61d44a9
--- /dev/null
+++ b/repo/android/hal-pan.c
@@ -0,0 +1,210 @@
+/*
+ * Copyright (C) 2013 Intel Corporation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include <stdbool.h>
+#include <stddef.h>
+#include <string.h>
+
+#include "hal-utils.h"
+#include "hal-log.h"
+#include "hal.h"
+#include "hal-msg.h"
+#include "hal-ipc.h"
+
+static const btpan_callbacks_t *cbs = NULL;
+
+static bool interface_ready(void)
+{
+ return cbs != NULL;
+}
+
+static void handle_conn_state(void *buf, uint16_t len, int fd)
+{
+ struct hal_ev_pan_conn_state *ev = buf;
+
+ if (cbs->connection_state_cb)
+ cbs->connection_state_cb(ev->state, ev->status,
+ (bt_bdaddr_t *) ev->bdaddr,
+ ev->local_role, ev->remote_role);
+}
+
+static void handle_ctrl_state(void *buf, uint16_t len, int fd)
+{
+ struct hal_ev_pan_ctrl_state *ev = buf;
+
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+ if (cbs->control_state_cb)
+ cbs->control_state_cb(ev->state, ev->local_role, ev->status,
+ (char *)ev->name);
+#else
+ /*
+ * Callback declared in bt_pan.h is 'typedef void
+ * (*btpan_control_state_callback)(btpan_control_state_t state,
+ * bt_status_t error, int local_role, const char* ifname);
+ * But PanService.Java defined it wrong way.
+ * private void onControlStateChanged(int local_role, int state,
+ * int error, String ifname).
+ * First and third parameters are misplaced, so sending data according
+ * to PanService.Java.
+ */
+ if (cbs->control_state_cb)
+ cbs->control_state_cb(ev->local_role, ev->state, ev->status,
+ (char *)ev->name);
+#endif
+}
+
+/*
+ * handlers will be called from notification thread context,
+ * index in table equals to 'opcode - HAL_MINIMUM_EVENT'
+ */
+static const struct hal_ipc_handler ev_handlers[] = {
+ /* HAL_EV_PAN_CTRL_STATE */
+ { handle_ctrl_state, false, sizeof(struct hal_ev_pan_ctrl_state) },
+ /* HAL_EV_PAN_CONN_STATE */
+ { handle_conn_state, false, sizeof(struct hal_ev_pan_conn_state) },
+};
+
+static bt_status_t pan_enable(int local_role)
+{
+ struct hal_cmd_pan_enable cmd;
+
+ DBG("");
+
+ if (!interface_ready())
+ return BT_STATUS_NOT_READY;
+
+ cmd.local_role = local_role;
+
+ return hal_ipc_cmd(HAL_SERVICE_ID_PAN, HAL_OP_PAN_ENABLE,
+ sizeof(cmd), &cmd, NULL, NULL, NULL);
+}
+
+static int pan_get_local_role(void)
+{
+ struct hal_rsp_pan_get_role rsp;
+ size_t len = sizeof(rsp);
+ bt_status_t status;
+
+ DBG("");
+
+ if (!interface_ready())
+ return BTPAN_ROLE_NONE;
+
+ status = hal_ipc_cmd(HAL_SERVICE_ID_PAN, HAL_OP_PAN_GET_ROLE, 0, NULL,
+ &len, &rsp, NULL);
+ if (status != BT_STATUS_SUCCESS)
+ return BTPAN_ROLE_NONE;
+
+ return rsp.local_role;
+}
+
+static bt_status_t pan_connect(const bt_bdaddr_t *bd_addr, int local_role,
+ int remote_role)
+{
+ struct hal_cmd_pan_connect cmd;
+
+ DBG("");
+
+ if (!interface_ready())
+ return BT_STATUS_NOT_READY;
+
+ memcpy(cmd.bdaddr, bd_addr, sizeof(cmd.bdaddr));
+ cmd.local_role = local_role;
+ cmd.remote_role = remote_role;
+
+ return hal_ipc_cmd(HAL_SERVICE_ID_PAN, HAL_OP_PAN_CONNECT,
+ sizeof(cmd), &cmd, NULL, NULL, NULL);
+}
+
+static bt_status_t pan_disconnect(const bt_bdaddr_t *bd_addr)
+{
+ struct hal_cmd_pan_disconnect cmd;
+
+ DBG("");
+
+ if (!interface_ready())
+ return BT_STATUS_NOT_READY;
+
+ memcpy(cmd.bdaddr, bd_addr, sizeof(cmd.bdaddr));
+
+ return hal_ipc_cmd(HAL_SERVICE_ID_PAN, HAL_OP_PAN_DISCONNECT,
+ sizeof(cmd), &cmd, NULL, NULL, NULL);
+}
+
+static bt_status_t pan_init(const btpan_callbacks_t *callbacks)
+{
+ struct hal_cmd_register_module cmd;
+ int ret;
+
+ DBG("");
+
+ if (interface_ready())
+ return BT_STATUS_DONE;
+
+ cbs = callbacks;
+
+ hal_ipc_register(HAL_SERVICE_ID_PAN, ev_handlers,
+ sizeof(ev_handlers)/sizeof(ev_handlers[0]));
+
+ cmd.service_id = HAL_SERVICE_ID_PAN;
+ cmd.mode = HAL_MODE_DEFAULT;
+ cmd.max_clients = 1;
+
+ ret = hal_ipc_cmd(HAL_SERVICE_ID_CORE, HAL_OP_REGISTER_MODULE,
+ sizeof(cmd), &cmd, NULL, NULL, NULL);
+
+ if (ret != BT_STATUS_SUCCESS) {
+ cbs = NULL;
+ hal_ipc_unregister(HAL_SERVICE_ID_PAN);
+ }
+
+ return ret;
+}
+
+static void pan_cleanup(void)
+{
+ struct hal_cmd_unregister_module cmd;
+
+ DBG("");
+
+ if (!interface_ready())
+ return;
+
+ cmd.service_id = HAL_SERVICE_ID_PAN;
+
+ hal_ipc_cmd(HAL_SERVICE_ID_CORE, HAL_OP_UNREGISTER_MODULE,
+ sizeof(cmd), &cmd, NULL, NULL, NULL);
+
+ hal_ipc_unregister(HAL_SERVICE_ID_PAN);
+
+ cbs = NULL;
+}
+
+static btpan_interface_t pan_if = {
+ .size = sizeof(pan_if),
+ .init = pan_init,
+ .enable = pan_enable,
+ .get_local_role = pan_get_local_role,
+ .connect = pan_connect,
+ .disconnect = pan_disconnect,
+ .cleanup = pan_cleanup
+};
+
+btpan_interface_t *bt_get_pan_interface(void)
+{
+ return &pan_if;
+}
diff --git a/repo/android/hal-sco.c b/repo/android/hal-sco.c
new file mode 100644
index 0000000..2c95866
--- /dev/null
+++ b/repo/android/hal-sco.c
@@ -0,0 +1,1530 @@
+/*
+ * Copyright (C) 2013 Intel Corporation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include <errno.h>
+#include <pthread.h>
+#include <poll.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <unistd.h>
+
+#include <hardware/audio.h>
+#include <hardware/hardware.h>
+#include <audio_utils/resampler.h>
+
+#include "hal-utils.h"
+#include "sco-msg.h"
+#include "ipc-common.h"
+#include "hal-log.h"
+#include "hal.h"
+
+#define AUDIO_STREAM_DEFAULT_RATE 44100
+#define AUDIO_STREAM_SCO_RATE 8000
+#define AUDIO_STREAM_DEFAULT_FORMAT AUDIO_FORMAT_PCM_16_BIT
+
+#define OUT_BUFFER_SIZE 2560
+#define OUT_STREAM_FRAMES 2560
+#define IN_STREAM_FRAMES 5292
+
+#define SOCKET_POLL_TIMEOUT_MS 500
+
+static int listen_sk = -1;
+static int ipc_sk = -1;
+
+static int sco_fd = -1;
+static uint16_t sco_mtu = 0;
+static pthread_mutex_t sco_mutex = PTHREAD_MUTEX_INITIALIZER;
+
+static pthread_t ipc_th = 0;
+static pthread_mutex_t sk_mutex = PTHREAD_MUTEX_INITIALIZER;
+
+static struct sco_stream_in *sco_stream_in = NULL;
+static struct sco_stream_out *sco_stream_out = NULL;
+
+struct sco_audio_config {
+ uint32_t rate;
+ uint32_t channels;
+ uint32_t frame_num;
+ audio_format_t format;
+};
+
+struct sco_stream_out {
+ struct audio_stream_out stream;
+
+ struct sco_audio_config cfg;
+
+ uint8_t *downmix_buf;
+ uint8_t *cache;
+ size_t cache_len;
+
+ size_t samples;
+ struct timespec start;
+
+ struct resampler_itfe *resampler;
+ int16_t *resample_buf;
+ uint32_t resample_frame_num;
+
+ bt_bdaddr_t bd_addr;
+};
+
+static void sco_close_socket(void)
+{
+ DBG("sco fd %d", sco_fd);
+
+ if (sco_fd < 0)
+ return;
+
+ shutdown(sco_fd, SHUT_RDWR);
+ close(sco_fd);
+ sco_fd = -1;
+}
+
+struct sco_stream_in {
+ struct audio_stream_in stream;
+
+ struct sco_audio_config cfg;
+
+ struct resampler_itfe *resampler;
+ int16_t *resample_buf;
+ uint32_t resample_frame_num;
+
+ bt_bdaddr_t bd_addr;
+};
+
+struct sco_dev {
+ struct audio_hw_device dev;
+ struct sco_stream_out *out;
+ struct sco_stream_in *in;
+};
+
+/*
+ * return the minimum frame numbers from resampling between BT stack's rate
+ * and audio flinger's. For output stream, 'output' shall be true, otherwise
+ * false for input streams at audio flinger side.
+ */
+static size_t get_resample_frame_num(uint32_t sco_rate, uint32_t rate,
+ size_t frame_num, bool output)
+{
+ size_t resample_frames_num = frame_num * sco_rate / rate + output;
+
+ DBG("resampler: sco_rate %d frame_num %zd rate %d resample frames %zd",
+ sco_rate, frame_num, rate, resample_frames_num);
+
+ return resample_frames_num;
+}
+
+/* SCO IPC functions */
+
+static int sco_ipc_cmd(uint8_t service_id, uint8_t opcode, uint16_t len,
+ void *param, size_t *rsp_len, void *rsp, int *fd)
+{
+ ssize_t ret;
+ struct msghdr msg;
+ struct iovec iv[2];
+ struct ipc_hdr cmd;
+ char cmsgbuf[CMSG_SPACE(sizeof(int))];
+ struct ipc_status s;
+ size_t s_len = sizeof(s);
+
+ pthread_mutex_lock(&sk_mutex);
+
+ if (ipc_sk < 0) {
+ error("sco: Invalid cmd socket passed to sco_ipc_cmd");
+ goto failed;
+ }
+
+ if (!rsp || !rsp_len) {
+ memset(&s, 0, s_len);
+ rsp_len = &s_len;
+ rsp = &s;
+ }
+
+ memset(&msg, 0, sizeof(msg));
+ memset(&cmd, 0, sizeof(cmd));
+
+ cmd.service_id = service_id;
+ cmd.opcode = opcode;
+ cmd.len = len;
+
+ iv[0].iov_base = &cmd;
+ iv[0].iov_len = sizeof(cmd);
+
+ iv[1].iov_base = param;
+ iv[1].iov_len = len;
+
+ msg.msg_iov = iv;
+ msg.msg_iovlen = 2;
+
+ ret = sendmsg(ipc_sk, &msg, 0);
+ if (ret < 0) {
+ error("sco: Sending command failed:%s", strerror(errno));
+ goto failed;
+ }
+
+ /* socket was shutdown */
+ if (ret == 0) {
+ error("sco: Command socket closed");
+ goto failed;
+ }
+
+ memset(&msg, 0, sizeof(msg));
+ memset(&cmd, 0, sizeof(cmd));
+
+ iv[0].iov_base = &cmd;
+ iv[0].iov_len = sizeof(cmd);
+
+ iv[1].iov_base = rsp;
+ iv[1].iov_len = *rsp_len;
+
+ msg.msg_iov = iv;
+ msg.msg_iovlen = 2;
+
+ if (fd) {
+ memset(cmsgbuf, 0, sizeof(cmsgbuf));
+ msg.msg_control = cmsgbuf;
+ msg.msg_controllen = sizeof(cmsgbuf);
+ }
+
+ ret = recvmsg(ipc_sk, &msg, 0);
+ if (ret < 0) {
+ error("sco: Receiving command response failed:%s",
+ strerror(errno));
+ goto failed;
+ }
+
+ if (ret < (ssize_t) sizeof(cmd)) {
+ error("sco: Too small response received(%zd bytes)", ret);
+ goto failed;
+ }
+
+ if (cmd.service_id != service_id) {
+ error("sco: Invalid service id (%u vs %u)", cmd.service_id,
+ service_id);
+ goto failed;
+ }
+
+ if (ret != (ssize_t) (sizeof(cmd) + cmd.len)) {
+ error("sco: Malformed response received(%zd bytes)", ret);
+ goto failed;
+ }
+
+ if (cmd.opcode != opcode && cmd.opcode != SCO_OP_STATUS) {
+ error("sco: Invalid opcode received (%u vs %u)",
+ cmd.opcode, opcode);
+ goto failed;
+ }
+
+ if (cmd.opcode == SCO_OP_STATUS) {
+ struct ipc_status *s = rsp;
+
+ if (sizeof(*s) != cmd.len) {
+ error("sco: Invalid status length");
+ goto failed;
+ }
+
+ if (s->code == SCO_STATUS_SUCCESS) {
+ error("sco: Invalid success status response");
+ goto failed;
+ }
+
+ pthread_mutex_unlock(&sk_mutex);
+
+ return s->code;
+ }
+
+ pthread_mutex_unlock(&sk_mutex);
+
+ /* Receive auxiliary data in msg */
+ if (fd) {
+ struct cmsghdr *cmsg;
+
+ *fd = -1;
+
+ for (cmsg = CMSG_FIRSTHDR(&msg); cmsg;
+ cmsg = CMSG_NXTHDR(&msg, cmsg)) {
+ if (cmsg->cmsg_level == SOL_SOCKET
+ && cmsg->cmsg_type == SCM_RIGHTS) {
+ memcpy(fd, CMSG_DATA(cmsg), sizeof(int));
+ break;
+ }
+ }
+
+ if (*fd < 0)
+ goto failed;
+ }
+
+ *rsp_len = cmd.len;
+
+ return SCO_STATUS_SUCCESS;
+
+failed:
+ /* Some serious issue happen on IPC - recover */
+ shutdown(ipc_sk, SHUT_RDWR);
+ pthread_mutex_unlock(&sk_mutex);
+
+ return SCO_STATUS_FAILED;
+}
+
+static int ipc_get_sco_fd(bt_bdaddr_t *bd_addr)
+{
+ int ret = SCO_STATUS_SUCCESS;
+
+ pthread_mutex_lock(&sco_mutex);
+
+ if (sco_fd < 0) {
+ struct sco_cmd_get_fd cmd;
+ struct sco_rsp_get_fd rsp;
+ size_t rsp_len = sizeof(rsp);
+
+ DBG("Getting SCO fd");
+
+ memcpy(cmd.bdaddr, bd_addr, sizeof(cmd.bdaddr));
+
+ ret = sco_ipc_cmd(SCO_SERVICE_ID, SCO_OP_GET_FD, sizeof(cmd),
+ &cmd, &rsp_len, &rsp, &sco_fd);
+
+ /* Sometimes mtu returned is wrong */
+ sco_mtu = /* rsp.mtu */ 48;
+ }
+
+ pthread_mutex_unlock(&sco_mutex);
+
+ return ret;
+}
+
+/* Audio stream functions */
+
+static void downmix_to_mono(struct sco_stream_out *out, const uint8_t *buffer,
+ size_t frame_num)
+{
+ const int16_t *input = (const void *) buffer;
+ int16_t *output = (void *) out->downmix_buf;
+ size_t i;
+
+ for (i = 0; i < frame_num; i++) {
+ int16_t l = get_le16(&input[i * 2]);
+ int16_t r = get_le16(&input[i * 2 + 1]);
+
+ put_le16((l + r) / 2, &output[i]);
+ }
+}
+
+static uint64_t timespec_diff_us(struct timespec *a, struct timespec *b)
+{
+ struct timespec res;
+
+ res.tv_sec = a->tv_sec - b->tv_sec;
+ res.tv_nsec = a->tv_nsec - b->tv_nsec;
+
+ if (res.tv_nsec < 0) {
+ res.tv_sec--;
+ res.tv_nsec += 1000000000ll; /* 1sec */
+ }
+
+ return res.tv_sec * 1000000ll + res.tv_nsec / 1000ll;
+}
+
+static bool write_data(struct sco_stream_out *out, const uint8_t *buffer,
+ size_t bytes)
+{
+ struct pollfd pfd;
+ size_t len, written = 0;
+ int ret;
+ uint8_t *p;
+ uint64_t audio_sent_us, audio_passed_us;
+
+ pfd.fd = sco_fd;
+ pfd.events = POLLOUT | POLLHUP | POLLNVAL;
+
+ while (bytes > written) {
+ struct timespec now;
+
+ /* poll for sending */
+ if (poll(&pfd, 1, SOCKET_POLL_TIMEOUT_MS) == 0) {
+ DBG("timeout fd %d", sco_fd);
+ return false;
+ }
+
+ if (pfd.revents & (POLLHUP | POLLNVAL)) {
+ error("error fd %d, events 0x%x", sco_fd, pfd.revents);
+ return false;
+ }
+
+ len = bytes - written > sco_mtu ? sco_mtu : bytes - written;
+
+ clock_gettime(CLOCK_REALTIME, &now);
+ /* Mark start of the stream */
+ if (!out->samples)
+ memcpy(&out->start, &now, sizeof(out->start));
+
+ audio_sent_us = out->samples * 1000000ll / AUDIO_STREAM_SCO_RATE;
+ audio_passed_us = timespec_diff_us(&now, &out->start);
+ if ((int) (audio_sent_us - audio_passed_us) > 1500) {
+ struct timespec timeout = {0,
+ (audio_sent_us -
+ audio_passed_us) * 1000};
+ DBG("Sleeping for %d ms",
+ (int) (audio_sent_us - audio_passed_us));
+ nanosleep(&timeout, NULL);
+ } else if ((int)(audio_passed_us - audio_sent_us) > 50000) {
+ DBG("\n\nResync\n\n");
+ out->samples = 0;
+ memcpy(&out->start, &now, sizeof(out->start));
+ }
+
+ if (out->cache_len) {
+ DBG("First packet cache_len %zd", out->cache_len);
+ memcpy(out->cache + out->cache_len, buffer,
+ sco_mtu - out->cache_len);
+ p = out->cache;
+ } else {
+ if (bytes - written >= sco_mtu)
+ p = (void *) buffer + written;
+ else {
+ memcpy(out->cache, buffer + written,
+ bytes - written);
+ out->cache_len = bytes - written;
+ DBG("Last packet, cache %zd bytes",
+ bytes - written);
+ written += bytes - written;
+ continue;
+ }
+ }
+
+ ret = write(sco_fd, p, len);
+ if (ret > 0) {
+ if (out->cache_len) {
+ written = sco_mtu - out->cache_len;
+ out->cache_len = 0;
+ } else
+ written += ret;
+
+ out->samples += ret / 2;
+
+ DBG("written %d samples %zd total %zd bytes",
+ ret, out->samples, written);
+ continue;
+ }
+
+ if (errno == EAGAIN) {
+ ret = errno;
+ warn("write failed (%d)", ret);
+ continue;
+ }
+
+ if (errno != EINTR) {
+ ret = errno;
+ error("write failed (%d) fd %d bytes %zd", ret, sco_fd,
+ bytes);
+ return false;
+ }
+ }
+
+ DBG("written %zd bytes", bytes);
+
+ return true;
+}
+
+static ssize_t out_write(struct audio_stream_out *stream, const void *buffer,
+ size_t bytes)
+{
+ struct sco_stream_out *out = (struct sco_stream_out *) stream;
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+ size_t frame_num = bytes / audio_stream_out_frame_size(stream);
+#else
+ size_t frame_num = bytes / audio_stream_frame_size(&out->stream.common);
+#endif
+ size_t output_frame_num = frame_num;
+ void *send_buf = out->downmix_buf;
+ size_t total;
+
+ DBG("write to fd %d bytes %zu", sco_fd, bytes);
+
+ if (ipc_get_sco_fd(&out->bd_addr) != SCO_STATUS_SUCCESS)
+ return -1;
+
+ if (!out->downmix_buf) {
+ error("sco: downmix buffer not initialized");
+ return -1;
+ }
+
+ downmix_to_mono(out, buffer, frame_num);
+
+ if (out->resampler) {
+ int ret;
+
+ /* limit resampler's output within what resample buf can hold */
+ output_frame_num = out->resample_frame_num;
+
+ ret = out->resampler->resample_from_input(out->resampler,
+ send_buf,
+ &frame_num,
+ out->resample_buf,
+ &output_frame_num);
+ if (ret) {
+ error("Failed to resample frames: %zd input %zd (%s)",
+ frame_num, output_frame_num, strerror(ret));
+ return -1;
+ }
+
+ send_buf = out->resample_buf;
+
+ DBG("Resampled: frame_num %zd, output_frame_num %zd",
+ frame_num, output_frame_num);
+ }
+
+ total = output_frame_num * sizeof(int16_t) * 1;
+
+ DBG("total %zd", total);
+
+ if (!write_data(out, send_buf, total))
+ return -1;
+
+ return bytes;
+}
+
+static uint32_t out_get_sample_rate(const struct audio_stream *stream)
+{
+ struct sco_stream_out *out = (struct sco_stream_out *) stream;
+
+ DBG("rate %u", out->cfg.rate);
+
+ return out->cfg.rate;
+}
+
+static int out_set_sample_rate(struct audio_stream *stream, uint32_t rate)
+{
+ DBG("rate %u", rate);
+
+ return 0;
+}
+
+static size_t out_get_buffer_size(const struct audio_stream *stream)
+{
+ struct sco_stream_out *out = (struct sco_stream_out *) stream;
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+ size_t size = audio_stream_out_frame_size(&out->stream) *
+ out->cfg.frame_num;
+#else
+ size_t size = audio_stream_frame_size(&out->stream.common) *
+ out->cfg.frame_num;
+#endif
+
+ /* buffer size without resampling */
+ if (out->cfg.rate == AUDIO_STREAM_SCO_RATE)
+ size = 576 * 2;
+
+ DBG("buf size %zd", size);
+
+ return size;
+}
+
+static uint32_t out_get_channels(const struct audio_stream *stream)
+{
+ struct sco_stream_out *out = (struct sco_stream_out *) stream;
+
+ DBG("channels num: %u", popcount(out->cfg.channels));
+
+ return out->cfg.channels;
+}
+
+static audio_format_t out_get_format(const struct audio_stream *stream)
+{
+ struct sco_stream_out *out = (struct sco_stream_out *) stream;
+
+ DBG("format: %u", out->cfg.format);
+
+ return out->cfg.format;
+}
+
+static int out_set_format(struct audio_stream *stream, audio_format_t format)
+{
+ DBG("");
+
+ return -ENOSYS;
+}
+
+static int out_standby(struct audio_stream *stream)
+{
+ DBG("");
+
+ return 0;
+}
+
+static int out_dump(const struct audio_stream *stream, int fd)
+{
+ DBG("");
+
+ return -ENOSYS;
+}
+
+static int out_set_parameters(struct audio_stream *stream, const char *kvpairs)
+{
+ DBG("%s", kvpairs);
+
+ return 0;
+}
+
+static char *out_get_parameters(const struct audio_stream *stream,
+ const char *keys)
+{
+ DBG("");
+
+ return strdup("");
+}
+
+static uint32_t out_get_latency(const struct audio_stream_out *stream)
+{
+ DBG("");
+
+ return 0;
+}
+
+static int out_set_volume(struct audio_stream_out *stream, float left,
+ float right)
+{
+ DBG("");
+
+ return -ENOSYS;
+}
+
+static int out_get_render_position(const struct audio_stream_out *stream,
+ uint32_t *dsp_frames)
+{
+ DBG("");
+
+ return -ENOSYS;
+}
+
+static int out_add_audio_effect(const struct audio_stream *stream,
+ effect_handle_t effect)
+{
+ DBG("");
+
+ return -ENOSYS;
+}
+
+static int out_remove_audio_effect(const struct audio_stream *stream,
+ effect_handle_t effect)
+{
+ DBG("");
+
+ return -ENOSYS;
+}
+
+static int sco_open_output_stream_real(struct audio_hw_device *dev,
+ audio_io_handle_t handle,
+ audio_devices_t devices,
+ audio_output_flags_t flags,
+ struct audio_config *config,
+ struct audio_stream_out **stream_out,
+ const char *address)
+{
+ struct sco_dev *adev = (struct sco_dev *) dev;
+ struct sco_stream_out *out;
+ int chan_num, ret;
+ size_t resample_size;
+
+ DBG("config %p device flags 0x%02x", config, devices);
+
+ if (sco_stream_out) {
+ DBG("stream_out already open");
+ return -EIO;
+ }
+
+ out = calloc(1, sizeof(struct sco_stream_out));
+ if (!out)
+ return -ENOMEM;
+
+ DBG("stream %p sco fd %d mtu %u", out, sco_fd, sco_mtu);
+
+ out->stream.common.get_sample_rate = out_get_sample_rate;
+ out->stream.common.set_sample_rate = out_set_sample_rate;
+ out->stream.common.get_buffer_size = out_get_buffer_size;
+ out->stream.common.get_channels = out_get_channels;
+ out->stream.common.get_format = out_get_format;
+ out->stream.common.set_format = out_set_format;
+ out->stream.common.standby = out_standby;
+ out->stream.common.dump = out_dump;
+ out->stream.common.set_parameters = out_set_parameters;
+ out->stream.common.get_parameters = out_get_parameters;
+ out->stream.common.add_audio_effect = out_add_audio_effect;
+ out->stream.common.remove_audio_effect = out_remove_audio_effect;
+ out->stream.get_latency = out_get_latency;
+ out->stream.set_volume = out_set_volume;
+ out->stream.write = out_write;
+ out->stream.get_render_position = out_get_render_position;
+
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+ if (address) {
+ DBG("address %s", address);
+
+ str2bt_bdaddr_t(address, &out->bd_addr);
+ }
+#endif
+
+ if (ipc_get_sco_fd(&out->bd_addr) != SCO_STATUS_SUCCESS)
+ DBG("SCO is not connected yet; get fd on write()");
+
+ if (config) {
+ DBG("config: rate %u chan mask %x format %d offload %p",
+ config->sample_rate, config->channel_mask,
+ config->format, &config->offload_info);
+
+ out->cfg.format = config->format;
+ out->cfg.channels = config->channel_mask;
+ out->cfg.rate = config->sample_rate;
+ } else {
+ out->cfg.format = AUDIO_STREAM_DEFAULT_FORMAT;
+ out->cfg.channels = AUDIO_CHANNEL_OUT_STEREO;
+ out->cfg.rate = AUDIO_STREAM_DEFAULT_RATE;
+ }
+
+ out->cfg.frame_num = OUT_STREAM_FRAMES;
+
+ out->downmix_buf = malloc(out_get_buffer_size(&out->stream.common));
+ if (!out->downmix_buf) {
+ free(out);
+ return -ENOMEM;
+ }
+
+ out->cache = malloc(sco_mtu);
+ if (!out->cache) {
+ free(out->downmix_buf);
+ free(out);
+ return -ENOMEM;
+ }
+
+ if (out->cfg.rate == AUDIO_STREAM_SCO_RATE)
+ goto skip_resampler;
+
+ /* Channel numbers for resampler */
+ chan_num = 1;
+
+ ret = create_resampler(out->cfg.rate, AUDIO_STREAM_SCO_RATE, chan_num,
+ RESAMPLER_QUALITY_DEFAULT, NULL,
+ &out->resampler);
+ if (ret) {
+ error("Failed to create resampler (%s)", strerror(-ret));
+ goto failed;
+ }
+
+ out->resample_frame_num = get_resample_frame_num(AUDIO_STREAM_SCO_RATE,
+ out->cfg.rate,
+ out->cfg.frame_num, 1);
+
+ if (!out->resample_frame_num) {
+ error("frame num is too small to resample, discard it");
+ goto failed;
+ }
+
+ resample_size = sizeof(int16_t) * chan_num * out->resample_frame_num;
+
+ out->resample_buf = malloc(resample_size);
+ if (!out->resample_buf) {
+ error("failed to allocate resample buffer for %u frames",
+ out->resample_frame_num);
+ goto failed;
+ }
+
+ DBG("Resampler: input %d output %d chan %d frames %u size %zd",
+ out->cfg.rate, AUDIO_STREAM_SCO_RATE, chan_num,
+ out->resample_frame_num, resample_size);
+skip_resampler:
+ *stream_out = &out->stream;
+ adev->out = out;
+ sco_stream_out = out;
+
+ return 0;
+failed:
+ if (out->resampler)
+ release_resampler(out->resampler);
+
+ free(out->cache);
+ free(out->downmix_buf);
+ free(out);
+ *stream_out = NULL;
+ adev->out = NULL;
+ sco_stream_out = NULL;
+
+ return ret;
+}
+
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+static int sco_open_output_stream(struct audio_hw_device *dev,
+ audio_io_handle_t handle,
+ audio_devices_t devices,
+ audio_output_flags_t flags,
+ struct audio_config *config,
+ struct audio_stream_out **stream_out,
+ const char *address)
+{
+ return sco_open_output_stream_real(dev, handle, devices, flags,
+ config, stream_out, address);
+}
+#else
+static int sco_open_output_stream(struct audio_hw_device *dev,
+ audio_io_handle_t handle,
+ audio_devices_t devices,
+ audio_output_flags_t flags,
+ struct audio_config *config,
+ struct audio_stream_out **stream_out)
+{
+ return sco_open_output_stream_real(dev, handle, devices, flags,
+ config, stream_out, NULL);
+}
+#endif
+
+static void sco_close_output_stream(struct audio_hw_device *dev,
+ struct audio_stream_out *stream_out)
+{
+ struct sco_dev *sco_dev = (struct sco_dev *) dev;
+ struct sco_stream_out *out = (struct sco_stream_out *) stream_out;
+
+ DBG("dev %p stream %p fd %d", dev, out, sco_fd);
+
+ if (out->resampler) {
+ release_resampler(out->resampler);
+ free(out->resample_buf);
+ }
+
+ free(out->cache);
+ free(out->downmix_buf);
+ free(out);
+ sco_dev->out = NULL;
+
+ pthread_mutex_lock(&sco_mutex);
+
+ sco_stream_out = NULL;
+
+ if (!sco_stream_in)
+ sco_close_socket();
+
+ pthread_mutex_unlock(&sco_mutex);
+}
+
+static int sco_set_parameters(struct audio_hw_device *dev,
+ const char *kvpairs)
+{
+ DBG("%s", kvpairs);
+
+ return 0;
+}
+
+static char *sco_get_parameters(const struct audio_hw_device *dev,
+ const char *keys)
+{
+ DBG("");
+
+ return strdup("");
+}
+
+static int sco_init_check(const struct audio_hw_device *dev)
+{
+ DBG("");
+
+ return 0;
+}
+
+static int sco_set_voice_volume(struct audio_hw_device *dev, float volume)
+{
+ DBG("%f", volume);
+
+ return 0;
+}
+
+static int sco_set_master_volume(struct audio_hw_device *dev, float volume)
+{
+ DBG("%f", volume);
+
+ return 0;
+}
+
+static int sco_set_mode(struct audio_hw_device *dev, int mode)
+{
+ DBG("");
+
+ return -ENOSYS;
+}
+
+static int sco_set_mic_mute(struct audio_hw_device *dev, bool state)
+{
+ DBG("");
+
+ return -ENOSYS;
+}
+
+static int sco_get_mic_mute(const struct audio_hw_device *dev, bool *state)
+{
+ DBG("");
+
+ return -ENOSYS;
+}
+
+static size_t sco_get_input_buffer_size(const struct audio_hw_device *dev,
+ const struct audio_config *config)
+{
+ DBG("");
+
+ return -ENOSYS;
+}
+
+static uint32_t in_get_sample_rate(const struct audio_stream *stream)
+{
+ struct sco_stream_in *in = (struct sco_stream_in *) stream;
+
+ DBG("rate %u", in->cfg.rate);
+
+ return in->cfg.rate;
+}
+
+static int in_set_sample_rate(struct audio_stream *stream, uint32_t rate)
+{
+ DBG("rate %u", rate);
+
+ return 0;
+}
+
+static size_t in_get_buffer_size(const struct audio_stream *stream)
+{
+ struct sco_stream_in *in = (struct sco_stream_in *) stream;
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+ size_t size = audio_stream_in_frame_size(&in->stream) *
+ in->cfg.frame_num;
+#else
+ size_t size = audio_stream_frame_size(&in->stream.common) *
+ in->cfg.frame_num;
+#endif
+
+ /* buffer size without resampling */
+ if (in->cfg.rate == AUDIO_STREAM_SCO_RATE)
+ size = 576;
+
+ DBG("buf size %zd", size);
+
+ return size;
+}
+
+static uint32_t in_get_channels(const struct audio_stream *stream)
+{
+ struct sco_stream_in *in = (struct sco_stream_in *) stream;
+
+ DBG("channels num: %u", popcount(in->cfg.channels));
+
+ return in->cfg.channels;
+}
+
+static audio_format_t in_get_format(const struct audio_stream *stream)
+{
+ struct sco_stream_in *in = (struct sco_stream_in *) stream;
+
+ DBG("format: %u", in->cfg.format);
+
+ return in->cfg.format;
+}
+
+static int in_set_format(struct audio_stream *stream, audio_format_t format)
+{
+ DBG("");
+
+ return -ENOSYS;
+}
+
+static int in_standby(struct audio_stream *stream)
+{
+ DBG("");
+
+ return 0;
+}
+
+static int in_dump(const struct audio_stream *stream, int fd)
+{
+ DBG("");
+
+ return -ENOSYS;
+}
+
+static int in_set_parameters(struct audio_stream *stream, const char *kvpairs)
+{
+ DBG("%s", kvpairs);
+
+ return 0;
+}
+
+static char *in_get_parameters(const struct audio_stream *stream,
+ const char *keys)
+{
+ DBG("");
+
+ return strdup("");
+}
+
+static int in_add_audio_effect(const struct audio_stream *stream,
+ effect_handle_t effect)
+{
+ DBG("");
+
+ return -ENOSYS;
+}
+
+static int in_remove_audio_effect(const struct audio_stream *stream,
+ effect_handle_t effect)
+{
+ DBG("");
+
+ return -ENOSYS;
+}
+
+static int in_set_gain(struct audio_stream_in *stream, float gain)
+{
+ DBG("");
+
+ return -ENOSYS;
+}
+
+static bool read_data(struct sco_stream_in *in, char *buffer, size_t bytes)
+{
+ struct pollfd pfd;
+ size_t len, read_bytes = 0;
+
+ pfd.fd = sco_fd;
+ pfd.events = POLLIN | POLLHUP | POLLNVAL;
+
+ while (bytes > read_bytes) {
+ int ret;
+
+ /* poll for reading */
+ if (poll(&pfd, 1, SOCKET_POLL_TIMEOUT_MS) == 0) {
+ DBG("timeout fd %d", sco_fd);
+ return false;
+ }
+
+ if (pfd.revents & (POLLHUP | POLLNVAL)) {
+ error("error fd %d, events 0x%x", sco_fd, pfd.revents);
+ return false;
+ }
+
+ len = bytes - read_bytes > sco_mtu ? sco_mtu :
+ bytes - read_bytes;
+
+ ret = read(sco_fd, buffer + read_bytes, len);
+ if (ret > 0) {
+ read_bytes += ret;
+ DBG("read %d total %zd", ret, read_bytes);
+ continue;
+ }
+
+ if (errno == EAGAIN) {
+ ret = errno;
+ warn("read failed (%d)", ret);
+ continue;
+ }
+
+ if (errno != EINTR) {
+ ret = errno;
+ error("read failed (%d) fd %d bytes %zd", ret, sco_fd,
+ bytes);
+ return false;
+ }
+ }
+
+ DBG("read %zd bytes", read_bytes);
+
+ return true;
+}
+
+static ssize_t in_read(struct audio_stream_in *stream, void *buffer,
+ size_t bytes)
+{
+ struct sco_stream_in *in = (struct sco_stream_in *) stream;
+ size_t frame_size, frame_num, input_frame_num;
+ void *read_buf = buffer;
+ size_t total = bytes;
+ int ret;
+
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+ frame_size = audio_stream_in_frame_size(&in->stream);
+#else
+ frame_size = audio_stream_frame_size(&stream->common);
+#endif
+
+ if (!frame_size)
+ return -1;
+
+ frame_num = bytes / frame_size;
+ input_frame_num = frame_num;
+
+ DBG("Read from fd %d bytes %zu", sco_fd, bytes);
+
+ if (ipc_get_sco_fd(&in->bd_addr) != SCO_STATUS_SUCCESS)
+ return -1;
+
+ if (!in->resampler && in->cfg.rate != AUDIO_STREAM_SCO_RATE) {
+ error("Cannot find resampler");
+ return -1;
+ }
+
+ if (in->resampler) {
+ input_frame_num = get_resample_frame_num(AUDIO_STREAM_SCO_RATE,
+ in->cfg.rate,
+ frame_num, 0);
+ if (input_frame_num > in->resample_frame_num) {
+ DBG("resize input frames from %zd to %d",
+ input_frame_num, in->resample_frame_num);
+ input_frame_num = in->resample_frame_num;
+ }
+
+ read_buf = in->resample_buf;
+
+ total = input_frame_num * sizeof(int16_t) * 1;
+ }
+
+ if(!read_data(in, read_buf, total))
+ return -1;
+
+ if (in->resampler) {
+ ret = in->resampler->resample_from_input(in->resampler,
+ in->resample_buf,
+ &input_frame_num,
+ (int16_t *) buffer,
+ &frame_num);
+ if (ret) {
+ error("Failed to resample frames: %zd input %zd (%s)",
+ frame_num, input_frame_num,
+ strerror(ret));
+ return -1;
+ }
+
+ DBG("resampler: remain %zd output %zd frames", input_frame_num,
+ frame_num);
+ }
+
+ return bytes;
+}
+
+static uint32_t in_get_input_frames_lost(struct audio_stream_in *stream)
+{
+ DBG("");
+
+ return -ENOSYS;
+}
+
+static int sco_open_input_stream_real(struct audio_hw_device *dev,
+ audio_io_handle_t handle,
+ audio_devices_t devices,
+ struct audio_config *config,
+ struct audio_stream_in **stream_in,
+ audio_input_flags_t flags,
+ const char *address,
+ audio_source_t source)
+{
+ struct sco_dev *sco_dev = (struct sco_dev *) dev;
+ struct sco_stream_in *in;
+ int chan_num, ret;
+ size_t resample_size;
+
+ DBG("config %p device flags 0x%02x", config, devices);
+
+ if (sco_stream_in) {
+ DBG("stream_in already open");
+ ret = -EIO;
+ goto failed2;
+ }
+
+ in = calloc(1, sizeof(struct sco_stream_in));
+ if (!in)
+ return -ENOMEM;
+
+ DBG("stream %p sco fd %d mtu %u", in, sco_fd, sco_mtu);
+
+ in->stream.common.get_sample_rate = in_get_sample_rate;
+ in->stream.common.set_sample_rate = in_set_sample_rate;
+ in->stream.common.get_buffer_size = in_get_buffer_size;
+ in->stream.common.get_channels = in_get_channels;
+ in->stream.common.get_format = in_get_format;
+ in->stream.common.set_format = in_set_format;
+ in->stream.common.standby = in_standby;
+ in->stream.common.dump = in_dump;
+ in->stream.common.set_parameters = in_set_parameters;
+ in->stream.common.get_parameters = in_get_parameters;
+ in->stream.common.add_audio_effect = in_add_audio_effect;
+ in->stream.common.remove_audio_effect = in_remove_audio_effect;
+ in->stream.set_gain = in_set_gain;
+ in->stream.read = in_read;
+ in->stream.get_input_frames_lost = in_get_input_frames_lost;
+
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+ if (address) {
+ DBG("address %s", address);
+
+ str2bt_bdaddr_t(address, &in->bd_addr);
+ }
+#endif
+
+ if (config) {
+ DBG("config: rate %u chan mask %x format %d offload %p",
+ config->sample_rate, config->channel_mask,
+ config->format, &config->offload_info);
+
+ in->cfg.format = config->format;
+ in->cfg.channels = config->channel_mask;
+ in->cfg.rate = config->sample_rate;
+ } else {
+ in->cfg.format = AUDIO_STREAM_DEFAULT_FORMAT;
+ in->cfg.channels = AUDIO_CHANNEL_OUT_MONO;
+ in->cfg.rate = AUDIO_STREAM_DEFAULT_RATE;
+ }
+
+ in->cfg.frame_num = IN_STREAM_FRAMES;
+
+ if (in->cfg.rate == AUDIO_STREAM_SCO_RATE)
+ goto skip_resampler;
+
+ /* Channel numbers for resampler */
+ chan_num = 1;
+
+ ret = create_resampler(AUDIO_STREAM_SCO_RATE, in->cfg.rate, chan_num,
+ RESAMPLER_QUALITY_DEFAULT, NULL,
+ &in->resampler);
+ if (ret) {
+ error("Failed to create resampler (%s)", strerror(-ret));
+ goto failed;
+ }
+
+ in->resample_frame_num = get_resample_frame_num(AUDIO_STREAM_SCO_RATE,
+ in->cfg.rate,
+ in->cfg.frame_num, 0);
+
+ resample_size = sizeof(int16_t) * chan_num * in->resample_frame_num;
+
+ in->resample_buf = malloc(resample_size);
+ if (!in->resample_buf) {
+ error("failed to allocate resample buffer for %d frames",
+ in->resample_frame_num);
+ goto failed;
+ }
+
+ DBG("Resampler: input %d output %d chan %d frames %u size %zd",
+ AUDIO_STREAM_SCO_RATE, in->cfg.rate, chan_num,
+ in->resample_frame_num, resample_size);
+skip_resampler:
+ *stream_in = &in->stream;
+ sco_dev->in = in;
+ sco_stream_in = in;
+
+ return 0;
+failed:
+ if (in->resampler)
+ release_resampler(in->resampler);
+ free(in);
+failed2:
+ *stream_in = NULL;
+ sco_dev->in = NULL;
+ sco_stream_in = NULL;
+
+ return ret;
+}
+
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+static int sco_open_input_stream(struct audio_hw_device *dev,
+ audio_io_handle_t handle,
+ audio_devices_t devices,
+ struct audio_config *config,
+ struct audio_stream_in **stream_in,
+ audio_input_flags_t flags,
+ const char *address,
+ audio_source_t source)
+{
+ return sco_open_input_stream_real(dev, handle, devices, config,
+ stream_in, flags, address,
+ source);
+}
+#else
+static int sco_open_input_stream(struct audio_hw_device *dev,
+ audio_io_handle_t handle,
+ audio_devices_t devices,
+ struct audio_config *config,
+ struct audio_stream_in **stream_in)
+{
+ return sco_open_input_stream_real(dev, handle, devices, config,
+ stream_in, 0, NULL, 0);
+}
+#endif
+
+static void sco_close_input_stream(struct audio_hw_device *dev,
+ struct audio_stream_in *stream_in)
+{
+ struct sco_dev *sco_dev = (struct sco_dev *) dev;
+ struct sco_stream_in *in = (struct sco_stream_in *) stream_in;
+
+ DBG("dev %p stream %p fd %d", dev, in, sco_fd);
+
+ if (in->resampler) {
+ release_resampler(in->resampler);
+ free(in->resample_buf);
+ }
+
+ free(in);
+ sco_dev->in = NULL;
+
+ pthread_mutex_lock(&sco_mutex);
+
+ sco_stream_in = NULL;
+
+ if (!sco_stream_out)
+ sco_close_socket();
+
+ pthread_mutex_unlock(&sco_mutex);
+}
+
+static int sco_dump(const audio_hw_device_t *device, int fd)
+{
+ DBG("");
+
+ return 0;
+}
+
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+static int set_master_mute(struct audio_hw_device *dev, bool mute)
+{
+ DBG("");
+ return -ENOSYS;
+}
+
+static int get_master_mute(struct audio_hw_device *dev, bool *mute)
+{
+ DBG("");
+ return -ENOSYS;
+}
+
+static int create_audio_patch(struct audio_hw_device *dev,
+ unsigned int num_sources,
+ const struct audio_port_config *sources,
+ unsigned int num_sinks,
+ const struct audio_port_config *sinks,
+ audio_patch_handle_t *handle)
+{
+ DBG("");
+ return -ENOSYS;
+}
+
+static int release_audio_patch(struct audio_hw_device *dev,
+ audio_patch_handle_t handle)
+{
+ DBG("");
+ return -ENOSYS;
+}
+
+static int get_audio_port(struct audio_hw_device *dev, struct audio_port *port)
+{
+ DBG("");
+ return -ENOSYS;
+}
+
+static int set_audio_port_config(struct audio_hw_device *dev,
+ const struct audio_port_config *config)
+{
+ DBG("");
+ return -ENOSYS;
+}
+#endif
+
+static int sco_close(hw_device_t *device)
+{
+ DBG("");
+
+ free(device);
+
+ return 0;
+}
+
+static void *ipc_handler(void *data)
+{
+ bool done = false;
+ struct pollfd pfd;
+ int sk;
+
+ DBG("");
+
+ while (!done) {
+ DBG("Waiting for connection ...");
+
+ sk = accept(listen_sk, NULL, NULL);
+ if (sk < 0) {
+ int err = errno;
+
+ if (err == EINTR)
+ continue;
+
+ if (err != ECONNABORTED && err != EINVAL)
+ error("sco: Failed to accept socket: %d (%s)",
+ err, strerror(err));
+
+ break;
+ }
+
+ pthread_mutex_lock(&sk_mutex);
+ ipc_sk = sk;
+ pthread_mutex_unlock(&sk_mutex);
+
+ DBG("SCO IPC: Connected");
+
+ memset(&pfd, 0, sizeof(pfd));
+ pfd.fd = ipc_sk;
+ pfd.events = POLLHUP | POLLERR | POLLNVAL;
+
+ /* Check if socket is still alive. Empty while loop.*/
+ while (poll(&pfd, 1, -1) < 0 && errno == EINTR);
+
+ info("SCO HAL: Socket closed");
+
+ pthread_mutex_lock(&sk_mutex);
+ close(ipc_sk);
+ ipc_sk = -1;
+ pthread_mutex_unlock(&sk_mutex);
+ }
+
+ info("Closing SCO IPC thread");
+ return NULL;
+}
+
+static int sco_ipc_init(void)
+{
+ struct sockaddr_un addr;
+ int err;
+ int sk;
+
+ DBG("");
+
+ sk = socket(PF_LOCAL, SOCK_SEQPACKET, 0);
+ if (sk < 0) {
+ err = -errno;
+ error("sco: Failed to create socket: %d (%s)", -err,
+ strerror(-err));
+ return err;
+ }
+
+ memset(&addr, 0, sizeof(addr));
+ addr.sun_family = AF_UNIX;
+
+ memcpy(addr.sun_path, BLUEZ_SCO_SK_PATH, sizeof(BLUEZ_SCO_SK_PATH));
+
+ if (bind(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+ err = -errno;
+ error("sco: Failed to bind socket: %d (%s)", -err,
+ strerror(-err));
+ goto failed;
+ }
+
+ if (listen(sk, 1) < 0) {
+ err = -errno;
+ error("sco: Failed to listen on the socket: %d (%s)", -err,
+ strerror(-err));
+ goto failed;
+ }
+
+ listen_sk = sk;
+
+ err = pthread_create(&ipc_th, NULL, ipc_handler, NULL);
+ if (err) {
+ err = -err;
+ ipc_th = 0;
+ error("sco: Failed to start IPC thread: %d (%s)",
+ -err, strerror(-err));
+ goto failed;
+ }
+
+ return 0;
+
+failed:
+ close(sk);
+ return err;
+}
+
+static int sco_open(const hw_module_t *module, const char *name,
+ hw_device_t **device)
+{
+ struct sco_dev *dev;
+ int err;
+
+ DBG("");
+
+ if (strcmp(name, AUDIO_HARDWARE_INTERFACE)) {
+ error("SCO: interface %s not matching [%s]", name,
+ AUDIO_HARDWARE_INTERFACE);
+ return -EINVAL;
+ }
+
+ err = sco_ipc_init();
+ if (err < 0)
+ return err;
+
+ dev = calloc(1, sizeof(struct sco_dev));
+ if (!dev)
+ return -ENOMEM;
+
+ dev->dev.common.tag = HARDWARE_DEVICE_TAG;
+ dev->dev.common.version = AUDIO_DEVICE_API_VERSION_CURRENT;
+ dev->dev.common.module = (struct hw_module_t *) module;
+ dev->dev.common.close = sco_close;
+
+ dev->dev.init_check = sco_init_check;
+ dev->dev.set_voice_volume = sco_set_voice_volume;
+ dev->dev.set_master_volume = sco_set_master_volume;
+ dev->dev.set_mode = sco_set_mode;
+ dev->dev.set_mic_mute = sco_set_mic_mute;
+ dev->dev.get_mic_mute = sco_get_mic_mute;
+ dev->dev.set_parameters = sco_set_parameters;
+ dev->dev.get_parameters = sco_get_parameters;
+ dev->dev.get_input_buffer_size = sco_get_input_buffer_size;
+ dev->dev.open_output_stream = sco_open_output_stream;
+ dev->dev.close_output_stream = sco_close_output_stream;
+ dev->dev.open_input_stream = sco_open_input_stream;
+ dev->dev.close_input_stream = sco_close_input_stream;
+ dev->dev.dump = sco_dump;
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+ dev->dev.set_master_mute = set_master_mute;
+ dev->dev.get_master_mute = get_master_mute;
+ dev->dev.create_audio_patch = create_audio_patch;
+ dev->dev.release_audio_patch = release_audio_patch;
+ dev->dev.get_audio_port = get_audio_port;
+ dev->dev.set_audio_port_config = set_audio_port_config;
+#endif
+
+ *device = &dev->dev.common;
+
+ return 0;
+}
+
+static struct hw_module_methods_t hal_module_methods = {
+ .open = sco_open,
+};
+
+struct audio_module HAL_MODULE_INFO_SYM = {
+ .common = {
+ .tag = HARDWARE_MODULE_TAG,
+ .version_major = 1,
+ .version_minor = 0,
+ .id = AUDIO_HARDWARE_MODULE_ID,
+ .name = "SCO Audio HW HAL",
+ .author = "Intel Corporation",
+ .methods = &hal_module_methods,
+ },
+};
diff --git a/repo/android/hal-socket.c b/repo/android/hal-socket.c
new file mode 100644
index 0000000..cfd50d1
--- /dev/null
+++ b/repo/android/hal-socket.c
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2013 Intel Corporation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "hal-ipc.h"
+#include "hal-log.h"
+#include "hal-msg.h"
+#include "hal-utils.h"
+#include "hal.h"
+
+static bt_status_t socket_listen(btsock_type_t type, const char *service_name,
+ const uint8_t *uuid, int chan,
+ int *sock, int flags)
+{
+ struct hal_cmd_socket_listen cmd;
+
+ if (!sock)
+ return BT_STATUS_PARM_INVALID;
+
+ DBG("uuid %s chan %d sock %p type %d service_name %s flags 0x%02x",
+ btuuid2str(uuid), chan, sock, type, service_name, flags);
+
+ memset(&cmd, 0, sizeof(cmd));
+
+ /* type match IPC type */
+ cmd.type = type;
+ cmd.flags = flags;
+ cmd.channel = chan;
+
+ if (uuid)
+ memcpy(cmd.uuid, uuid, sizeof(cmd.uuid));
+
+ if (service_name)
+ memcpy(cmd.name, service_name, strlen(service_name));
+
+ return hal_ipc_cmd(HAL_SERVICE_ID_SOCKET, HAL_OP_SOCKET_LISTEN,
+ sizeof(cmd), &cmd, NULL, NULL, sock);
+}
+
+static bt_status_t socket_connect(const bt_bdaddr_t *bdaddr, btsock_type_t type,
+ const uint8_t *uuid, int chan,
+ int *sock, int flags)
+{
+ struct hal_cmd_socket_connect cmd;
+
+ if (!sock)
+ return BT_STATUS_PARM_INVALID;
+
+ DBG("bdaddr %s uuid %s chan %d sock %p type %d flags 0x%02x",
+ bdaddr2str(bdaddr), btuuid2str(uuid), chan, sock, type, flags);
+
+ memset(&cmd, 0, sizeof(cmd));
+
+ /* type match IPC type */
+ cmd.type = type;
+ cmd.flags = flags;
+ cmd.channel = chan;
+
+ if (uuid)
+ memcpy(cmd.uuid, uuid, sizeof(cmd.uuid));
+
+ if (bdaddr)
+ memcpy(cmd.bdaddr, bdaddr, sizeof(cmd.bdaddr));
+
+ return hal_ipc_cmd(HAL_SERVICE_ID_SOCKET, HAL_OP_SOCKET_CONNECT,
+ sizeof(cmd), &cmd, NULL, NULL, sock);
+}
+
+static btsock_interface_t socket_if = {
+ sizeof(socket_if),
+ socket_listen,
+ socket_connect
+};
+
+btsock_interface_t *bt_get_socket_interface(void)
+{
+ return &socket_if;
+}
diff --git a/repo/android/hal-utils.c b/repo/android/hal-utils.c
new file mode 100644
index 0000000..e45f6e4
--- /dev/null
+++ b/repo/android/hal-utils.c
@@ -0,0 +1,418 @@
+/*
+ * Copyright (C) 2013 Intel Corporation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <stdint.h>
+#include <stdbool.h>
+
+#include <cutils/properties.h>
+
+#include "hal.h"
+#include "hal-utils.h"
+
+/*
+ * converts uuid to string
+ * buf should be at least 39 bytes
+ *
+ * returns string representation of uuid
+ */
+const char *bt_uuid_t2str(const uint8_t *uuid, char *buf)
+{
+ int shift = 0;
+ unsigned int i;
+ int is_bt;
+
+ if (!uuid)
+ return strcpy(buf, "NULL");
+
+ is_bt = !memcmp(&uuid[4], &BT_BASE_UUID[4], HAL_UUID_LEN - 4);
+
+ for (i = 0; i < HAL_UUID_LEN; i++) {
+ if (i == 4 && is_bt)
+ break;
+
+ if (i == 4 || i == 6 || i == 8 || i == 10) {
+ buf[i * 2 + shift] = '-';
+ shift++;
+ }
+ sprintf(buf + i * 2 + shift, "%02x", uuid[i]);
+ }
+
+ return buf;
+}
+
+const char *btuuid2str(const uint8_t *uuid)
+{
+ static char buf[MAX_UUID_STR_LEN];
+
+ return bt_uuid_t2str(uuid, buf);
+}
+
+INTMAP(bt_status_t, -1, "(unknown)")
+ DELEMENT(BT_STATUS_SUCCESS),
+ DELEMENT(BT_STATUS_FAIL),
+ DELEMENT(BT_STATUS_NOT_READY),
+ DELEMENT(BT_STATUS_NOMEM),
+ DELEMENT(BT_STATUS_BUSY),
+ DELEMENT(BT_STATUS_DONE),
+ DELEMENT(BT_STATUS_UNSUPPORTED),
+ DELEMENT(BT_STATUS_PARM_INVALID),
+ DELEMENT(BT_STATUS_UNHANDLED),
+ DELEMENT(BT_STATUS_AUTH_FAILURE),
+ DELEMENT(BT_STATUS_RMT_DEV_DOWN),
+ENDMAP
+
+INTMAP(bt_state_t, -1, "(unknown)")
+ DELEMENT(BT_STATE_OFF),
+ DELEMENT(BT_STATE_ON),
+ENDMAP
+
+INTMAP(bt_device_type_t, -1, "(unknown)")
+ DELEMENT(BT_DEVICE_DEVTYPE_BREDR),
+ DELEMENT(BT_DEVICE_DEVTYPE_BLE),
+ DELEMENT(BT_DEVICE_DEVTYPE_DUAL),
+ENDMAP
+
+INTMAP(bt_scan_mode_t, -1, "(unknown)")
+ DELEMENT(BT_SCAN_MODE_NONE),
+ DELEMENT(BT_SCAN_MODE_CONNECTABLE),
+ DELEMENT(BT_SCAN_MODE_CONNECTABLE_DISCOVERABLE),
+ENDMAP
+
+INTMAP(bt_discovery_state_t, -1, "(unknown)")
+ DELEMENT(BT_DISCOVERY_STOPPED),
+ DELEMENT(BT_DISCOVERY_STARTED),
+ENDMAP
+
+INTMAP(bt_acl_state_t, -1, "(unknown)")
+ DELEMENT(BT_ACL_STATE_CONNECTED),
+ DELEMENT(BT_ACL_STATE_DISCONNECTED),
+ENDMAP
+
+INTMAP(bt_bond_state_t, -1, "(unknown)")
+ DELEMENT(BT_BOND_STATE_NONE),
+ DELEMENT(BT_BOND_STATE_BONDING),
+ DELEMENT(BT_BOND_STATE_BONDED),
+ENDMAP
+
+INTMAP(bt_ssp_variant_t, -1, "(unknown)")
+ DELEMENT(BT_SSP_VARIANT_PASSKEY_CONFIRMATION),
+ DELEMENT(BT_SSP_VARIANT_PASSKEY_ENTRY),
+ DELEMENT(BT_SSP_VARIANT_CONSENT),
+ DELEMENT(BT_SSP_VARIANT_PASSKEY_NOTIFICATION),
+ENDMAP
+
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+INTMAP(bt_property_type_t, -1, "(unknown)")
+ DELEMENT(BT_PROPERTY_BDNAME),
+ DELEMENT(BT_PROPERTY_BDADDR),
+ DELEMENT(BT_PROPERTY_UUIDS),
+ DELEMENT(BT_PROPERTY_CLASS_OF_DEVICE),
+ DELEMENT(BT_PROPERTY_TYPE_OF_DEVICE),
+ DELEMENT(BT_PROPERTY_SERVICE_RECORD),
+ DELEMENT(BT_PROPERTY_ADAPTER_SCAN_MODE),
+ DELEMENT(BT_PROPERTY_ADAPTER_BONDED_DEVICES),
+ DELEMENT(BT_PROPERTY_ADAPTER_DISCOVERY_TIMEOUT),
+ DELEMENT(BT_PROPERTY_REMOTE_FRIENDLY_NAME),
+ DELEMENT(BT_PROPERTY_REMOTE_RSSI),
+ DELEMENT(BT_PROPERTY_REMOTE_VERSION_INFO),
+ DELEMENT(BT_PROPERTY_LOCAL_LE_FEATURES),
+ DELEMENT(BT_PROPERTY_REMOTE_DEVICE_TIMESTAMP),
+ENDMAP
+#else
+INTMAP(bt_property_type_t, -1, "(unknown)")
+ DELEMENT(BT_PROPERTY_BDNAME),
+ DELEMENT(BT_PROPERTY_BDADDR),
+ DELEMENT(BT_PROPERTY_UUIDS),
+ DELEMENT(BT_PROPERTY_CLASS_OF_DEVICE),
+ DELEMENT(BT_PROPERTY_TYPE_OF_DEVICE),
+ DELEMENT(BT_PROPERTY_SERVICE_RECORD),
+ DELEMENT(BT_PROPERTY_ADAPTER_SCAN_MODE),
+ DELEMENT(BT_PROPERTY_ADAPTER_BONDED_DEVICES),
+ DELEMENT(BT_PROPERTY_ADAPTER_DISCOVERY_TIMEOUT),
+ DELEMENT(BT_PROPERTY_REMOTE_FRIENDLY_NAME),
+ DELEMENT(BT_PROPERTY_REMOTE_RSSI),
+ DELEMENT(BT_PROPERTY_REMOTE_VERSION_INFO),
+ DELEMENT(BT_PROPERTY_REMOTE_DEVICE_TIMESTAMP),
+ENDMAP
+#endif
+
+INTMAP(bt_cb_thread_evt, -1, "(unknown)")
+ DELEMENT(ASSOCIATE_JVM),
+ DELEMENT(DISASSOCIATE_JVM),
+ENDMAP
+
+/* Find first index of given value in table m */
+int int2str_findint(int v, const struct int2str m[])
+{
+ int i;
+
+ for (i = 0; m[i].str; ++i) {
+ if (m[i].val == v)
+ return i;
+ }
+ return -1;
+}
+
+/* Find first index of given string in table m */
+int int2str_findstr(const char *str, const struct int2str m[])
+{
+ int i;
+
+ for (i = 0; m[i].str; ++i) {
+ if (strcmp(m[i].str, str) == 0)
+ return i;
+ }
+ return -1;
+}
+
+/*
+ * convert bd_addr to string
+ * buf must be at least 18 char long
+ *
+ * returns buf
+ */
+const char *bt_bdaddr_t2str(const bt_bdaddr_t *bd_addr, char *buf)
+{
+ const uint8_t *p;
+
+ if (!bd_addr)
+ return strcpy(buf, "NULL");
+
+ p = bd_addr->address;
+
+ snprintf(buf, MAX_ADDR_STR_LEN, "%02x:%02x:%02x:%02x:%02x:%02x",
+ p[0], p[1], p[2], p[3], p[4], p[5]);
+
+ return buf;
+}
+
+/* converts string to bt_bdaddr_t */
+void str2bt_bdaddr_t(const char *str, bt_bdaddr_t *bd_addr)
+{
+ uint8_t *p = bd_addr->address;
+
+ sscanf(str, "%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx",
+ &p[0], &p[1], &p[2], &p[3], &p[4], &p[5]);
+}
+
+/* converts string to uuid */
+void str2bt_uuid_t(const char *str, bt_uuid_t *uuid)
+{
+ int i = 0;
+
+ memcpy(uuid, BT_BASE_UUID, sizeof(bt_uuid_t));
+
+ while (*str && i < (int) sizeof(bt_uuid_t)) {
+ while (*str == '-')
+ str++;
+
+ if (sscanf(str, "%02hhx", &uuid->uu[i]) != 1)
+ break;
+
+ i++;
+ str += 2;
+ }
+}
+
+const char *enum_defines(void *v, int i)
+{
+ const struct int2str *m = v;
+
+ return m[i].str != NULL ? m[i].str : NULL;
+}
+
+const char *enum_strings(void *v, int i)
+{
+ const char **m = v;
+
+ return m[i] != NULL ? m[i] : NULL;
+}
+
+const char *enum_one_string(void *v, int i)
+{
+ const char *m = v;
+
+ return (i == 0) && (m[0] != 0) ? m : NULL;
+}
+
+const char *bdaddr2str(const bt_bdaddr_t *bd_addr)
+{
+ static char buf[MAX_ADDR_STR_LEN];
+
+ return bt_bdaddr_t2str(bd_addr, buf);
+}
+
+static void bonded_devices2string(char *str, void *prop, int prop_len)
+{
+ int count = prop_len / sizeof(bt_bdaddr_t);
+ bt_bdaddr_t *addr = prop;
+
+ strcat(str, "{");
+
+ while (count--) {
+ strcat(str, bdaddr2str(addr));
+ if (count)
+ strcat(str, ", ");
+ addr++;
+ }
+
+ strcat(str, "}");
+}
+
+static void uuids2string(char *str, void *prop, int prop_len)
+{
+ int count = prop_len / sizeof(bt_uuid_t);
+ bt_uuid_t *uuid = prop;
+
+ strcat(str, "{");
+
+ while (count--) {
+ strcat(str, btuuid2str(uuid->uu));
+ if (count)
+ strcat(str, ", ");
+ uuid++;
+ }
+
+ strcat(str, "}");
+}
+
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+static void local_le_feat2string(char *str, const bt_local_le_features_t *f)
+{
+ uint16_t scan_num;
+
+ str += sprintf(str, "{\n");
+
+ str += sprintf(str, "Privacy supported: %s,\n",
+ f->local_privacy_enabled ? "TRUE" : "FALSE");
+
+ str += sprintf(str, "Num of advertising instances: %u,\n",
+ f->max_adv_instance);
+
+ str += sprintf(str, "PRA offloading support: %s,\n",
+ f->rpa_offload_supported ? "TRUE" : "FALSE");
+
+ str += sprintf(str, "Num of offloaded IRKs: %u,\n",
+ f->max_irk_list_size);
+
+ str += sprintf(str, "Num of offloaded scan filters: %u,\n",
+ f->max_adv_filter_supported);
+
+ scan_num = (f->scan_result_storage_size_hibyte << 8) +
+ f->scan_result_storage_size_lobyte;
+
+ str += sprintf(str, "Num of offloaded scan results: %u,\n", scan_num);
+
+ str += sprintf(str, "Activity & energy report support: %s\n",
+ f->activity_energy_info_supported ? "TRUE" : "FALSE");
+
+ sprintf(str, "}");
+}
+#endif
+
+const char *btproperty2str(const bt_property_t *property)
+{
+ bt_service_record_t *rec;
+ static char buf[4096];
+ char *p;
+
+ p = buf + sprintf(buf, "type=%s len=%d val=",
+ bt_property_type_t2str(property->type),
+ property->len);
+
+ switch (property->type) {
+ case BT_PROPERTY_BDNAME:
+ case BT_PROPERTY_REMOTE_FRIENDLY_NAME:
+ snprintf(p, property->len + 1, "%s",
+ ((bt_bdname_t *) property->val)->name);
+ break;
+ case BT_PROPERTY_BDADDR:
+ sprintf(p, "%s", bdaddr2str((bt_bdaddr_t *) property->val));
+ break;
+ case BT_PROPERTY_CLASS_OF_DEVICE:
+ sprintf(p, "%06x", *((int *) property->val));
+ break;
+ case BT_PROPERTY_TYPE_OF_DEVICE:
+ sprintf(p, "%s", bt_device_type_t2str(
+ *((bt_device_type_t *) property->val)));
+ break;
+ case BT_PROPERTY_REMOTE_RSSI:
+ sprintf(p, "%d", *((char *) property->val));
+ break;
+ case BT_PROPERTY_ADAPTER_SCAN_MODE:
+ sprintf(p, "%s",
+ bt_scan_mode_t2str(*((bt_scan_mode_t *) property->val)));
+ break;
+ case BT_PROPERTY_ADAPTER_DISCOVERY_TIMEOUT:
+ sprintf(p, "%d", *((int *) property->val));
+ break;
+ case BT_PROPERTY_ADAPTER_BONDED_DEVICES:
+ bonded_devices2string(p, property->val, property->len);
+ break;
+ case BT_PROPERTY_UUIDS:
+ uuids2string(p, property->val, property->len);
+ break;
+ case BT_PROPERTY_SERVICE_RECORD:
+ rec = property->val;
+ sprintf(p, "{%s, %d, %s}", btuuid2str(rec->uuid.uu),
+ rec->channel, rec->name);
+ break;
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+ case BT_PROPERTY_LOCAL_LE_FEATURES:
+ local_le_feat2string(p, property->val);
+ break;
+#endif
+ case BT_PROPERTY_REMOTE_VERSION_INFO:
+ case BT_PROPERTY_REMOTE_DEVICE_TIMESTAMP:
+ default:
+ sprintf(p, "%p", property->val);
+ break;
+ }
+
+ return buf;
+}
+
+#define PROP_PREFIX "persist.sys.bluetooth."
+#define PROP_PREFIX_RO "ro.bluetooth."
+
+int get_config(const char *config_key, char *value, const char *fallback)
+{
+ char key[PROPERTY_KEY_MAX];
+ int ret;
+
+ if (strlen(config_key) + sizeof(PROP_PREFIX) > sizeof(key))
+ return 0;
+
+ snprintf(key, sizeof(key), PROP_PREFIX"%s", config_key);
+
+ ret = property_get(key, value, "");
+ if (ret > 0)
+ return ret;
+
+ snprintf(key, sizeof(key), PROP_PREFIX_RO"%s", config_key);
+
+ ret = property_get(key, value, "");
+ if (ret > 0)
+ return ret;
+
+ if (!fallback)
+ return 0;
+
+ return property_get(fallback, value, "");
+}
diff --git a/repo/android/hal-utils.h b/repo/android/hal-utils.h
new file mode 100644
index 0000000..9c59948
--- /dev/null
+++ b/repo/android/hal-utils.h
@@ -0,0 +1,152 @@
+/*
+ * Copyright (C) 2013 Intel Corporation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include <endian.h>
+
+#include <hardware/bluetooth.h>
+
+#define MAX_UUID_STR_LEN 37
+#define HAL_UUID_LEN 16
+#define MAX_ADDR_STR_LEN 18
+
+static const char BT_BASE_UUID[] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00,
+ 0x80, 0x00, 0x00, 0x80, 0x5f, 0x9b, 0x34, 0xfb
+};
+
+const char *bt_uuid_t2str(const uint8_t *uuid, char *buf);
+const char *btuuid2str(const uint8_t *uuid);
+const char *bt_bdaddr_t2str(const bt_bdaddr_t *bd_addr, char *buf);
+void str2bt_bdaddr_t(const char *str, bt_bdaddr_t *bd_addr);
+void str2bt_uuid_t(const char *str, bt_uuid_t *uuid);
+const char *btproperty2str(const bt_property_t *property);
+const char *bdaddr2str(const bt_bdaddr_t *bd_addr);
+
+int get_config(const char *config_key, char *value, const char *fallback);
+
+/*
+ * Begin mapping section
+ *
+ * There are some mappings between integer values (enums) and strings
+ * to be presented to user. To make it easier to convert between those two
+ * set of macros is given. It is specially useful when we want to have
+ * strings that match constants from header files like:
+ * BT_STATUS_SUCCESS (0) and corresponding "BT_STATUS_SUCCESS"
+ * Example of usage:
+ *
+ * INTMAP(int, -1, "invalid")
+ * DELEMENT(BT_STATUS_SUCCESS)
+ * DELEMENT(BT_STATUS_FAIL)
+ * MELEMENT(123, "Some strange value")
+ * ENDMAP
+ *
+ * Just by doing this we have mapping table plus two functions:
+ * int str2int(const char *str);
+ * const char *int2str(int v);
+ *
+ * second argument to INTMAP specifies value to be returned from
+ * str2int function when there is not mapping for such number
+ * third argument specifies default value to be returned from int2str
+ *
+ * If same mapping is to be used in several source files put
+ * INTMAP in c file and DECINTMAP in h file.
+ *
+ * For mappings that are to be used in single file only
+ * use SINTMAP which will create the same but everything will be marked
+ * as static.
+ */
+
+struct int2str {
+ int val; /* int value */
+ const char *str; /* corresponding string */
+};
+
+int int2str_findint(int v, const struct int2str m[]);
+int int2str_findstr(const char *str, const struct int2str m[]);
+const char *enum_defines(void *v, int i);
+const char *enum_strings(void *v, int i);
+const char *enum_one_string(void *v, int i);
+
+#define TYPE_ENUM(type) ((void *) &__##type##2str[0])
+#define DECINTMAP(type) \
+extern struct int2str __##type##2str[]; \
+const char *type##2##str(type v); \
+type str##2##type(const char *str); \
+
+#define INTMAP(type, deft, defs) \
+const char *type##2##str(type v) \
+{ \
+ int i = int2str_findint((int) v, __##type##2str); \
+ return (i < 0) ? defs : __##type##2str[i].str; \
+} \
+type str##2##type(const char *str) \
+{ \
+ int i = int2str_findstr(str, __##type##2str); \
+ return (i < 0) ? (type) deft : (type) (__##type##2str[i].val); \
+} \
+struct int2str __##type##2str[] = {
+
+#define SINTMAP(type, deft, defs) \
+static struct int2str __##type##2str[]; \
+static inline const char *type##2##str(type v) \
+{ \
+ int i = int2str_findint((int) v, __##type##2str); \
+ return (i < 0) ? defs : __##type##2str[i].str; \
+} \
+static inline type str##2##type(const char *str) \
+{ \
+ int i = int2str_findstr(str, __##type##2str); \
+ return (i < 0) ? (type) deft : (type) (__##type##2str[i].val); \
+} \
+static struct int2str __##type##2str[] = {
+
+#define ENDMAP {0, NULL} };
+
+/* use this to generate string from header file constant */
+#define MELEMENT(v, s) {v, s}
+/* use this to have arbitrary mapping from int to string */
+#define DELEMENT(s) {s, #s}
+/* End of mapping section */
+
+DECINTMAP(bt_status_t);
+DECINTMAP(bt_state_t);
+DECINTMAP(bt_device_type_t);
+DECINTMAP(bt_scan_mode_t);
+DECINTMAP(bt_discovery_state_t);
+DECINTMAP(bt_acl_state_t);
+DECINTMAP(bt_bond_state_t);
+DECINTMAP(bt_ssp_variant_t);
+DECINTMAP(bt_property_type_t);
+DECINTMAP(bt_cb_thread_evt);
+
+static inline uint16_t get_le16(const void *src)
+{
+ const struct __attribute__((packed)) {
+ uint16_t le16;
+ } *p = src;
+
+ return le16toh(p->le16);
+}
+
+static inline void put_le16(uint16_t val, void *dst)
+{
+ struct __attribute__((packed)) {
+ uint16_t le16;
+ } *p = dst;
+
+ p->le16 = htole16(val);
+}
diff --git a/repo/android/hal.h b/repo/android/hal.h
new file mode 100644
index 0000000..709c197
--- /dev/null
+++ b/repo/android/hal.h
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2013 Intel Corporation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include <hardware/bluetooth.h>
+#include <hardware/bt_sock.h>
+#include <hardware/bt_hh.h>
+#include <hardware/bt_pan.h>
+#include <hardware/bt_av.h>
+#include <hardware/bt_rc.h>
+#include <hardware/bt_hf.h>
+#include <hardware/bt_gatt.h>
+#include <hardware/bt_gatt_client.h>
+#include <hardware/bt_gatt_server.h>
+#include <hardware/bt_hl.h>
+
+#define PLATFORM_VER(a, b, c) ((a << 16) | ( b << 8) | (c))
+
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+#include <hardware/bt_hf_client.h>
+#include <hardware/bt_mce.h>
+#endif
+
+btsock_interface_t *bt_get_socket_interface(void);
+bthh_interface_t *bt_get_hidhost_interface(void);
+btpan_interface_t *bt_get_pan_interface(void);
+btav_interface_t *bt_get_a2dp_interface(void);
+btrc_interface_t *bt_get_avrcp_interface(void);
+bthf_interface_t *bt_get_handsfree_interface(void);
+btgatt_interface_t *bt_get_gatt_interface(void);
+bthl_interface_t *bt_get_health_interface(void);
+
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+btrc_ctrl_interface_t *bt_get_avrcp_ctrl_interface(void);
+bthf_client_interface_t *bt_get_hf_client_interface(void);
+btmce_interface_t *bt_get_map_client_interface(void);
+btav_interface_t *bt_get_a2dp_sink_interface(void);
+#endif
+
+void bt_thread_associate(void);
+void bt_thread_disassociate(void);
diff --git a/repo/android/handsfree-client.c b/repo/android/handsfree-client.c
new file mode 100644
index 0000000..65659b8
--- /dev/null
+++ b/repo/android/handsfree-client.c
@@ -0,0 +1,2204 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2014 Intel Corporation. All rights reserved.
+ *
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdlib.h>
+#include <stdbool.h>
+#include <errno.h>
+#include <unistd.h>
+#include <glib.h>
+
+#include "lib/bluetooth.h"
+#include "lib/sdp.h"
+#include "lib/sdp_lib.h"
+#include "src/sdp-client.h"
+#include "src/shared/hfp.h"
+#include "src/shared/queue.h"
+#include "src/shared/util.h"
+#include "btio/btio.h"
+#include "ipc.h"
+#include "ipc-common.h"
+#include "src/log.h"
+#include "utils.h"
+
+#include "bluetooth.h"
+#include "hal-msg.h"
+#include "handsfree-client.h"
+#include "sco.h"
+
+#define HFP_HF_CHANNEL 7
+
+#define HFP_HF_FEAT_ECNR 0x00000001
+#define HFP_HF_FEAT_3WAY 0x00000002
+#define HFP_HF_FEAT_CLI 0x00000004
+#define HFP_HF_FEAT_VR 0x00000008
+#define HFP_HF_FEAT_RVC 0x00000010
+#define HFP_HF_FEAT_ECS 0x00000020
+#define HFP_HF_FEAT_ECC 0x00000040
+#define HFP_HF_FEAT_CODEC 0x00000080
+#define HFP_HF_FEAT_HF_IND 0x00000100
+#define HFP_HF_FEAT_ESCO_S4_T2 0x00000200
+
+#define HFP_AG_FEAT_3WAY 0x00000001
+#define HFP_AG_FEAT_ECNR 0x00000002
+#define HFP_AG_FEAT_VR 0x00000004
+#define HFP_AG_FEAT_INBAND 0x00000008
+#define HFP_AG_FEAT_VTAG 0x00000010
+#define HFP_AG_FEAT_REJ_CALL 0x00000020
+#define HFP_AG_FEAT_ECS 0x00000040
+#define HFP_AG_FEAT_ECC 0x00000080
+#define HFP_AG_FEAT_EXT_ERR 0x00000100
+#define HFP_AG_FEAT_CODEC 0x00000200
+
+#define HFP_HF_FEATURES (HFP_HF_FEAT_ECNR | HFP_HF_FEAT_3WAY |\
+ HFP_HF_FEAT_CLI | HFP_HF_FEAT_VR |\
+ HFP_HF_FEAT_RVC | HFP_HF_FEAT_ECS |\
+ HFP_HF_FEAT_ECC)
+
+#define CVSD_OFFSET 0
+#define MSBC_OFFSET 1
+#define CODECS_COUNT (MSBC_OFFSET + 1)
+
+#define CODEC_ID_CVSD 0x01
+#define CODEC_ID_MSBC 0x02
+
+#define MAX_NUMBER_LEN 33
+#define MAX_OPERATOR_NAME_LEN 17
+
+enum hfp_indicator {
+ HFP_INDICATOR_SERVICE = 0,
+ HFP_INDICATOR_CALL,
+ HFP_INDICATOR_CALLSETUP,
+ HFP_INDICATOR_CALLHELD,
+ HFP_INDICATOR_SIGNAL,
+ HFP_INDICATOR_ROAM,
+ HFP_INDICATOR_BATTCHG,
+ HFP_INDICATOR_LAST
+};
+
+typedef void (*ciev_func_t)(uint8_t val);
+
+struct indicator {
+ uint8_t index;
+ uint32_t min;
+ uint32_t max;
+ uint32_t val;
+ ciev_func_t cb;
+};
+
+struct hfp_codec {
+ uint8_t type;
+ bool local_supported;
+ bool remote_supported;
+};
+
+struct device {
+ bdaddr_t bdaddr;
+ struct hfp_hf *hf;
+ uint8_t state;
+ uint8_t audio_state;
+
+ uint8_t negotiated_codec;
+ uint32_t features;
+ struct hfp_codec codecs[2];
+
+ struct indicator ag_ind[HFP_INDICATOR_LAST];
+
+ uint32_t chld_features;
+};
+
+static const struct hfp_codec codecs_defaults[] = {
+ { CODEC_ID_CVSD, true, false},
+ { CODEC_ID_MSBC, false, false},
+};
+
+static bdaddr_t adapter_addr;
+
+static struct ipc *hal_ipc = NULL;
+
+static uint32_t hfp_hf_features = 0;
+static uint32_t hfp_hf_record_id = 0;
+static struct queue *devices = NULL;
+static GIOChannel *hfp_hf_server = NULL;
+
+static struct bt_sco *sco = NULL;
+
+static struct device *find_default_device(void)
+{
+ return queue_peek_head(devices);
+}
+
+static bool match_by_bdaddr(const void *data, const void *user_data)
+{
+ const bdaddr_t *addr1 = data;
+ const bdaddr_t *addr2 = user_data;
+
+ return !bacmp(addr1, addr2);
+}
+
+static struct device *find_device(const bdaddr_t *addr)
+{
+ return queue_find(devices, match_by_bdaddr, addr);
+}
+
+static void init_codecs(struct device *dev)
+{
+ memcpy(&dev->codecs, codecs_defaults, sizeof(dev->codecs));
+
+ if (hfp_hf_features & HFP_HF_FEAT_CODEC)
+ dev->codecs[MSBC_OFFSET].local_supported = true;
+}
+
+static struct device *device_create(const bdaddr_t *bdaddr)
+{
+ struct device *dev;
+
+ dev = new0(struct device, 1);
+
+ bacpy(&dev->bdaddr, bdaddr);
+ dev->state = HAL_HF_CLIENT_CONN_STATE_DISCONNECTED;
+ dev->audio_state = HAL_HF_CLIENT_AUDIO_STATE_DISCONNECTED;
+
+ init_codecs(dev);
+
+ queue_push_tail(devices, dev);
+
+ return dev;
+}
+
+static struct device *get_device(const bdaddr_t *addr)
+{
+ struct device *dev;
+
+ dev = find_device(addr);
+ if (dev)
+ return dev;
+
+ /* We do support only one device as for now */
+ if (queue_isempty(devices))
+ return device_create(addr);
+
+ return NULL;
+}
+
+static void device_set_state(struct device *dev, uint8_t state)
+{
+ struct hal_ev_hf_client_conn_state ev;
+ char address[18];
+
+ if (dev->state == state)
+ return;
+
+ memset(&ev, 0, sizeof(ev));
+
+ dev->state = state;
+
+ ba2str(&dev->bdaddr, address);
+ DBG("device %s state %u", address, state);
+
+ bdaddr2android(&dev->bdaddr, ev.bdaddr);
+ ev.state = state;
+
+ ev.chld_feat = dev->chld_features;
+ ev.peer_feat = dev->features;
+
+ ipc_send_notif(hal_ipc, HAL_SERVICE_ID_HANDSFREE_CLIENT,
+ HAL_EV_HF_CLIENT_CONN_STATE, sizeof(ev), &ev);
+}
+
+static void device_destroy(struct device *dev)
+{
+ device_set_state(dev, HAL_HF_CLIENT_CONN_STATE_DISCONNECTED);
+ queue_remove(devices, dev);
+
+ if (dev->hf)
+ hfp_hf_unref(dev->hf);
+
+ free(dev);
+}
+
+static void handle_disconnect(const void *buf, uint16_t len)
+{
+ const struct hal_cmd_hf_client_disconnect *cmd = buf;
+ struct device *dev;
+ uint32_t status;
+ bdaddr_t bdaddr;
+ char addr[18];
+
+ DBG("");
+
+ android2bdaddr(&cmd->bdaddr, &bdaddr);
+
+ ba2str(&bdaddr, addr);
+ DBG("Disconnect %s", addr);
+
+ dev = get_device(&bdaddr);
+ if (!dev) {
+ status = HAL_STATUS_FAILED;
+ goto done;
+ }
+
+ if (dev->state == HAL_HF_CLIENT_CONN_STATE_DISCONNECTED) {
+ status = HAL_STATUS_FAILED;
+ goto done;
+ }
+
+ if (dev->state == HAL_HF_CLIENT_CONN_STATE_DISCONNECTING) {
+ status = HAL_STATUS_SUCCESS;
+ goto done;
+ }
+
+ if (dev->state == HAL_HF_CLIENT_CONN_STATE_CONNECTING) {
+ device_destroy(dev);
+ status = HAL_STATUS_SUCCESS;
+ goto done;
+ }
+
+ status = hfp_hf_disconnect(dev->hf) ? HAL_STATUS_SUCCESS :
+ HAL_STATUS_FAILED;
+
+ if (status)
+ device_set_state(dev, HAL_HF_CLIENT_CONN_STATE_DISCONNECTING);
+
+done:
+
+ ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_HANDSFREE_CLIENT,
+ HAL_OP_HF_CLIENT_DISCONNECT, status);
+}
+
+static void set_audio_state(struct device *dev, uint8_t state)
+{
+ struct hal_ev_hf_client_audio_state ev;
+ char address[18];
+
+ if (dev->audio_state == state)
+ return;
+
+ dev->audio_state = state;
+
+ ba2str(&dev->bdaddr, address);
+ DBG("device %s audio state %u", address, state);
+
+ bdaddr2android(&dev->bdaddr, ev.bdaddr);
+ ev.state = state;
+
+ ipc_send_notif(hal_ipc, HAL_SERVICE_ID_HANDSFREE_CLIENT,
+ HAL_EV_HF_CLIENT_AUDIO_STATE, sizeof(ev), &ev);
+}
+
+static void bcc_cb(enum hfp_result result, enum hfp_error cme_err,
+ void *user_data)
+{
+ struct device *dev = user_data;
+
+ if (result != HFP_RESULT_OK)
+ set_audio_state(dev, HAL_HF_CLIENT_AUDIO_STATE_DISCONNECTED);
+}
+
+static bool codec_negotiation_supported(struct device *dev)
+{
+ return (dev->features & HFP_AG_FEAT_CODEC) &&
+ (hfp_hf_features & HFP_HF_FEAT_CODEC);
+}
+
+static bool connect_sco(struct device *dev)
+{
+ if (codec_negotiation_supported(dev))
+ return hfp_hf_send_command(dev->hf, bcc_cb, dev,
+ "AT+BCC");
+
+ return bt_sco_connect(sco, &dev->bdaddr, BT_VOICE_CVSD_16BIT);
+}
+
+static void handle_connect_audio(const void *buf, uint16_t len)
+{
+ const struct hal_cmd_hf_client_connect_audio *cmd = (void *) buf;
+ struct device *dev;
+ uint8_t status;
+ bdaddr_t bdaddr;
+
+ DBG("");
+
+ android2bdaddr(&cmd->bdaddr, &bdaddr);
+
+ dev = find_device(&bdaddr);
+ if (!dev || dev->state != HAL_HF_CLIENT_CONN_STATE_SLC_CONNECTED ||
+ dev->audio_state != HAL_HF_CLIENT_AUDIO_STATE_DISCONNECTED) {
+ error("hf-client: Cannot create SCO, check SLC or audio state");
+ status = HAL_STATUS_FAILED;
+ goto done;
+ }
+
+ if (connect_sco(dev)) {
+ status = HAL_STATUS_SUCCESS;
+ set_audio_state(dev, HAL_HF_CLIENT_AUDIO_STATE_CONNECTING);
+ } else {
+ status = HAL_STATUS_FAILED;
+ }
+
+done:
+ ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_HANDSFREE_CLIENT,
+ HAL_OP_HF_CLIENT_CONNECT_AUDIO, status);
+}
+
+static void handle_disconnect_audio(const void *buf, uint16_t len)
+{
+ const struct hal_cmd_hf_client_disconnect_audio *cmd = (void *) buf;
+ struct device *dev;
+ uint8_t status;
+ bdaddr_t bdaddr;
+
+ DBG("");
+
+ android2bdaddr(&cmd->bdaddr, &bdaddr);
+
+ dev = find_device(&bdaddr);
+ if (!dev ||
+ dev->audio_state == HAL_HF_CLIENT_AUDIO_STATE_DISCONNECTED) {
+ error("hf-client: Device not found or audio not connected");
+ status = HAL_STATUS_FAILED;
+ goto done;
+ }
+
+ bt_sco_disconnect(sco);
+ status = HAL_STATUS_SUCCESS;
+
+done:
+ ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_HANDSFREE_CLIENT,
+ HAL_OP_HF_CLIENT_DISCONNECT_AUDIO, status);
+}
+
+static void cmd_complete_cb(enum hfp_result result, enum hfp_error cme_err,
+ void *user_data)
+{
+ struct hal_ev_hf_client_command_complete ev;
+
+ DBG("");
+ memset(&ev, 0, sizeof(ev));
+
+ switch (result) {
+ case HFP_RESULT_OK:
+ ev.type = HAL_HF_CLIENT_CMD_COMP_OK;
+ break;
+ case HFP_RESULT_NO_CARRIER:
+ ev.type = HAL_HF_CLIENT_CMD_COMP_ERR_NO_CARRIER;
+ break;
+ case HFP_RESULT_ERROR:
+ ev.type = HAL_HF_CLIENT_CMD_COMP_ERR;
+ break;
+ case HFP_RESULT_BUSY:
+ ev.type = HAL_HF_CLIENT_CMD_COMP_ERR_BUSY;
+ break;
+ case HFP_RESULT_NO_ANSWER:
+ ev.type = HAL_HF_CLIENT_CMD_COMP_ERR_NO_ANSWER;
+ break;
+ case HFP_RESULT_DELAYED:
+ ev.type = HAL_HF_CLIENT_CMD_COMP_ERR_DELAYED;
+ break;
+ case HFP_RESULT_BLACKLISTED:
+ ev.type = HAL_HF_CLIENT_CMD_COMP_ERR_BACKLISTED;
+ break;
+ case HFP_RESULT_CME_ERROR:
+ ev.type = HAL_HF_CLIENT_CMD_COMP_ERR_CME;
+ ev.cme = cme_err;
+ break;
+ case HFP_RESULT_CONNECT:
+ case HFP_RESULT_RING:
+ case HFP_RESULT_NO_DIALTONE:
+ default:
+ error("hf-client: Unknown error code %d", result);
+ ev.type = HAL_HF_CLIENT_CMD_COMP_ERR;
+ break;
+ }
+
+ ipc_send_notif(hal_ipc, HAL_SERVICE_ID_HANDSFREE_CLIENT,
+ HAL_EV_CLIENT_COMMAND_COMPLETE, sizeof(ev), &ev);
+}
+
+static void handle_start_vr(const void *buf, uint16_t len)
+{
+ struct device *dev;
+ uint8_t status;
+
+ DBG("");
+
+ dev = find_default_device();
+ if (!dev) {
+ status = HAL_STATUS_FAILED;
+ goto done;
+ }
+
+ if (hfp_hf_send_command(dev->hf, cmd_complete_cb, NULL, "AT+BVRA=1"))
+ status = HAL_STATUS_SUCCESS;
+ else
+ status = HAL_STATUS_FAILED;
+
+done:
+ ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_HANDSFREE_CLIENT,
+ HAL_OP_HF_CLIENT_START_VR, status);
+}
+
+static void handle_stop_vr(const void *buf, uint16_t len)
+{
+ struct device *dev;
+ uint8_t status;
+
+ DBG("");
+
+ dev = find_default_device();
+ if (!dev) {
+ status = HAL_STATUS_FAILED;
+ goto done;
+ }
+
+ if (hfp_hf_send_command(dev->hf, cmd_complete_cb, NULL, "AT+BVRA=0"))
+ status = HAL_STATUS_SUCCESS;
+ else
+ status = HAL_STATUS_FAILED;
+
+done:
+ ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_HANDSFREE_CLIENT,
+ HAL_OP_HF_CLIENT_STOP_VR, status);
+}
+
+static void handle_volume_control(const void *buf, uint16_t len)
+{
+ const struct hal_cmd_hf_client_volume_control *cmd = buf;
+ struct device *dev;
+ uint8_t status;
+ uint8_t vol;
+ bool ret;
+
+ DBG("");
+
+ dev = find_default_device();
+ if (!dev) {
+ status = HAL_STATUS_FAILED;
+ goto done;
+ }
+
+ /*
+ * Volume is in the range 0-15. Make sure we send correct value
+ * to remote device
+ */
+ vol = cmd->volume > 15 ? 15 : cmd->volume;
+
+ switch (cmd->type) {
+ case HF_CLIENT_VOLUME_TYPE_SPEAKER:
+ ret = hfp_hf_send_command(dev->hf, cmd_complete_cb, NULL,
+ "AT+VGS=%u", vol);
+ break;
+ case HF_CLIENT_VOLUME_TYPE_MIC:
+ ret = hfp_hf_send_command(dev->hf, cmd_complete_cb, NULL,
+ "AT+VGM=%u", vol);
+ break;
+ default:
+ ret = false;
+ break;
+ }
+
+ status = ret ? HAL_STATUS_SUCCESS : HAL_STATUS_FAILED;
+
+done:
+ ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_HANDSFREE_CLIENT,
+ HAL_OP_HF_CLIENT_VOLUME_CONTROL,
+ status);
+}
+
+static void handle_dial(const void *buf, uint16_t len)
+{
+ const struct hal_cmd_hf_client_dial *cmd = buf;
+ struct device *dev;
+ uint8_t status;
+ bool ret;
+
+ DBG("");
+
+ if (len != sizeof(*cmd) + cmd->number_len)
+ goto failed;
+
+ dev = find_default_device();
+ if (!dev) {
+ status = HAL_STATUS_FAILED;
+ goto done;
+ }
+
+ if (cmd->number_len > 0) {
+ if (cmd->number[cmd->number_len - 1] != '\0')
+ goto failed;
+
+ DBG("Dialing %s", cmd->number);
+
+ ret = hfp_hf_send_command(dev->hf, cmd_complete_cb, NULL,
+ "ATD%s;", cmd->number);
+ } else {
+ DBG("Redialing");
+
+ ret = hfp_hf_send_command(dev->hf, cmd_complete_cb, NULL,
+ "AT+BLDN");
+ }
+
+ status = ret ? HAL_STATUS_SUCCESS : HAL_STATUS_FAILED;
+
+done:
+ ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_HANDSFREE_CLIENT,
+ HAL_OP_HF_CLIENT_DIAL, status);
+
+ return;
+
+failed:
+ error("Malformed number data, size (%u bytes), terminating", len);
+ raise(SIGTERM);
+}
+
+static void handle_dial_memory(const void *buf, uint16_t len)
+{
+ const struct hal_cmd_hf_client_dial_memory *cmd = buf;
+ struct device *dev;
+ uint8_t status;
+
+ DBG("");
+
+ dev = find_default_device();
+ if (!dev) {
+ status = HAL_STATUS_FAILED;
+ goto done;
+ }
+
+ /* For some reason location in BT HAL is int. Therefore that check */
+ if (cmd->location < 0) {
+ status = HAL_STATUS_FAILED;
+ goto done;
+ }
+
+ if (hfp_hf_send_command(dev->hf, cmd_complete_cb, NULL , "ATD>%d;",
+ cmd->location))
+ status = HAL_STATUS_SUCCESS;
+ else
+ status = HAL_STATUS_FAILED;
+
+done:
+ ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_HANDSFREE_CLIENT,
+ HAL_OP_HF_CLIENT_DIAL_MEMORY, status);
+}
+
+static void handle_call_action(const void *buf, uint16_t len)
+{
+ const struct hal_cmd_hf_client_call_action *cmd = buf;
+ struct device *dev;
+ uint8_t status;
+ bool ret;
+
+ DBG("");
+
+ dev = find_default_device();
+ if (!dev) {
+ status = HAL_STATUS_FAILED;
+ goto done;
+ }
+
+ switch (cmd->action) {
+ case HAL_HF_CLIENT_ACTION_CHLD_0:
+ ret = hfp_hf_send_command(dev->hf, cmd_complete_cb, NULL,
+ "AT+CHLD=0");
+ break;
+ case HAL_HF_CLIENT_ACTION_CHLD_1:
+ ret = hfp_hf_send_command(dev->hf, cmd_complete_cb, NULL,
+ "AT+CHLD=1");
+ break;
+ case HAL_HF_CLIENT_ACTION_CHLD_2:
+ ret = hfp_hf_send_command(dev->hf, cmd_complete_cb,
+ NULL, "AT+CHLD=2");
+ break;
+ case HAL_HF_CLIENT_ACTION_CHLD_3:
+ ret = hfp_hf_send_command(dev->hf, cmd_complete_cb, NULL,
+ "AT+CHLD=3");
+ break;
+ case HAL_HF_CLIENT_ACTION_CHLD_4:
+ ret = hfp_hf_send_command(dev->hf, cmd_complete_cb, NULL,
+ "AT+CHLD=4");
+ break;
+ case HAL_HF_CLIENT_ACTION_CHLD_1x:
+ /* Index is int in BT HAL. Let's be paranoid here */
+ if (cmd->index <= 0)
+ ret = false;
+ else
+ ret = hfp_hf_send_command(dev->hf, cmd_complete_cb,
+ NULL, "AT+CHLD=1%d", cmd->index);
+ break;
+ case HAL_HF_CLIENT_ACTION_CHLD_2x:
+ /* Index is int in BT HAL. Let's be paranoid here */
+ if (cmd->index <= 0)
+ ret = false;
+ else
+ ret = hfp_hf_send_command(dev->hf, cmd_complete_cb,
+ NULL, "AT+CHLD=2%d", cmd->index);
+ break;
+ case HAL_HF_CLIENT_ACTION_ATA:
+ ret = hfp_hf_send_command(dev->hf, cmd_complete_cb, NULL,
+ "ATA");
+ break;
+ case HAL_HF_CLIENT_ACTION_CHUP:
+ ret = hfp_hf_send_command(dev->hf, cmd_complete_cb, NULL,
+ "AT+CHUP");
+ break;
+ case HAL_HF_CLIENT_ACTION_BRTH_0:
+ ret = hfp_hf_send_command(dev->hf, cmd_complete_cb, NULL,
+ "AT+BTRH=0");
+ break;
+ case HAL_HF_CLIENT_ACTION_BRTH_1:
+ ret = hfp_hf_send_command(dev->hf, cmd_complete_cb, NULL,
+ "AT+BTRH=1");
+ break;
+ case HAL_HF_CLIENT_ACTION_BRTH_2:
+ ret = hfp_hf_send_command(dev->hf, cmd_complete_cb, NULL,
+ "AT+BTRH=2");
+ break;
+ default:
+ error("hf-client: Unknown action %d", cmd->action);
+ ret = false;
+ break;
+ }
+
+ status = ret ? HAL_STATUS_SUCCESS : HAL_STATUS_FAILED;
+
+done:
+ ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_HANDSFREE_CLIENT,
+ HAL_OP_HF_CLIENT_CALL_ACTION, status);
+}
+
+static void handle_query_current_calls(const void *buf, uint16_t len)
+{
+ struct device *dev;
+ uint8_t status;
+
+ DBG("");
+
+ dev = find_default_device();
+ if (!dev) {
+ status = HAL_STATUS_FAILED;
+ goto done;
+ }
+
+ if (hfp_hf_send_command(dev->hf, cmd_complete_cb, NULL, "AT+CLCC"))
+ status = HAL_STATUS_SUCCESS;
+ else
+ status = HAL_STATUS_FAILED;
+
+done:
+ ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_HANDSFREE_CLIENT,
+ HAL_OP_HF_CLIENT_QUERY_CURRENT_CALLS,
+ status);
+}
+
+static void handle_query_operator_name(const void *buf, uint16_t len)
+{
+ struct device *dev;
+ uint8_t status;
+
+ DBG("");
+
+ dev = find_default_device();
+ if (!dev) {
+ status = HAL_STATUS_FAILED;
+ goto done;
+ }
+
+ if (hfp_hf_send_command(dev->hf, cmd_complete_cb, NULL, "AT+COPS?"))
+ status = HAL_STATUS_SUCCESS;
+ else
+ status = HAL_STATUS_FAILED;
+
+done:
+ ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_HANDSFREE_CLIENT,
+ HAL_OP_HF_CLIENT_QUERY_OPERATOR_NAME,
+ status);
+}
+
+static void handle_retrieve_subscr_info(const void *buf, uint16_t len)
+{
+ struct device *dev;
+ uint8_t status;
+
+ DBG("");
+
+ dev = find_default_device();
+ if (!dev) {
+ status = HAL_STATUS_FAILED;
+ goto done;
+ }
+
+ if (hfp_hf_send_command(dev->hf, cmd_complete_cb, NULL, "AT+CNUM"))
+ status = HAL_STATUS_SUCCESS;
+ else
+ status = HAL_STATUS_FAILED;
+
+done:
+ ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_HANDSFREE_CLIENT,
+ HAL_OP_HF_CLIENT_RETRIEVE_SUBSCR_INFO,
+ status);
+}
+
+static void handle_send_dtmf(const void *buf, uint16_t len)
+{
+ const struct hal_cmd_hf_client_send_dtmf *cmd = buf;
+ struct device *dev;
+ uint8_t status;
+
+ dev = find_default_device();
+ if (!dev) {
+ status = HAL_STATUS_FAILED;
+ goto done;
+ }
+
+ if (hfp_hf_send_command(dev->hf, cmd_complete_cb, NULL, "AT+VTS=%c",
+ (char) cmd->tone))
+ status = HAL_STATUS_SUCCESS;
+ else
+ status = HAL_STATUS_FAILED;
+
+done:
+ ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_HANDSFREE_CLIENT,
+ HAL_OP_HF_CLIENT_SEND_DTMF, status);
+}
+
+static void handle_get_last_vc_tag_num(const void *buf, uint16_t len)
+{
+ struct device *dev;
+ uint8_t status;
+
+ dev = find_default_device();
+ if (!dev) {
+ status = HAL_STATUS_FAILED;
+ goto done;
+ }
+
+ if (hfp_hf_send_command(dev->hf, cmd_complete_cb, NULL, "AT+BINP=1"))
+ status = HAL_STATUS_SUCCESS;
+ else
+ status = HAL_STATUS_FAILED;
+
+done:
+ ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_HANDSFREE_CLIENT,
+ HAL_OP_HF_CLIENT_GET_LAST_VOICE_TAG_NUM, status);
+}
+
+static void disconnect_watch(void *user_data)
+{
+ DBG("");
+
+ device_destroy(user_data);
+}
+
+static void slc_error(struct device *dev)
+{
+ error("hf-client: Could not create SLC - dropping connection");
+ hfp_hf_disconnect(dev->hf);
+}
+
+static void set_chld_feat(struct device *dev, char *feat)
+{
+ DBG(" %s", feat);
+
+ if (strcmp(feat, "0") == 0)
+ dev->chld_features |= HAL_HF_CLIENT_CHLD_FEAT_REL;
+ else if (strcmp(feat, "1") == 0)
+ dev->chld_features |= HAL_HF_CLIENT_CHLD_FEAT_REL_ACC;
+ else if (strcmp(feat, "1x") == 0)
+ dev->chld_features |= HAL_HF_CLIENT_CHLD_FEAT_REL_X;
+ else if (strcmp(feat, "2") == 0)
+ dev->chld_features |= HAL_HF_CLIENT_CHLD_FEAT_HOLD_ACC;
+ else if (strcmp(feat, "2x") == 0)
+ dev->chld_features |= HAL_HF_CLIENT_CHLD_FEAT_PRIV_X;
+ else if (strcmp(feat, "3") == 0)
+ dev->chld_features |= HAL_HF_CLIENT_CHLD_FEAT_MERGE;
+ else if (strcmp(feat, "4") == 0)
+ dev->chld_features |= HAL_HF_CLIENT_CHLD_FEAT_MERGE_DETACH;
+}
+
+static void get_local_codecs_string(struct device *dev, char *buf,
+ uint8_t len)
+{
+ int i;
+ uint8_t offset;
+
+ memset(buf, 0, len);
+ offset = 0;
+
+ for (i = 0; i < CODECS_COUNT; i++) {
+ char c[8];
+ int l;
+
+ if (!dev->codecs[i].local_supported)
+ continue;
+
+ memset(c, 0, sizeof(c));
+
+ l = sprintf(c, "%d,", dev->codecs[i].type);
+
+ if (l > (len - offset - 1)) {
+ error("hf-client: Codecs cannot fit into buffer");
+ return;
+ }
+
+ strcat(&buf[offset], c);
+ offset += l;
+ }
+}
+
+static void bvra_cb(struct hfp_context *context, void *user_data)
+{
+ struct hal_ev_hf_client_vr_state ev;
+ unsigned int val;
+
+ if (!hfp_context_get_number(context, &val) || val > 1)
+ return;
+
+ ev.state = val ? HAL_HF_CLIENT_VR_STARTED : HAL_HF_CLIENT_VR_STOPPED;
+
+ ipc_send_notif(hal_ipc, HAL_SERVICE_ID_HANDSFREE_CLIENT,
+ HAL_EV_HF_CLIENT_VR_STATE, sizeof(ev), &ev);
+}
+
+static void vgm_cb(struct hfp_context *context, void *user_data)
+{
+ struct hal_ev_hf_client_volume_changed ev;
+ unsigned int val;
+
+ if (!hfp_context_get_number(context, &val) || val > 15)
+ return;
+
+ ev.type = HF_CLIENT_VOLUME_TYPE_MIC;
+ ev.volume = val;
+
+ ipc_send_notif(hal_ipc, HAL_SERVICE_ID_HANDSFREE_CLIENT,
+ HAL_EV_HF_CLIENT_VR_STATE, sizeof(ev), &ev);
+}
+
+static void vgs_cb(struct hfp_context *context, void *user_data)
+{
+ struct hal_ev_hf_client_volume_changed ev;
+ unsigned int val;
+
+ if (!hfp_context_get_number(context, &val) || val > 15)
+ return;
+
+ ev.type = HF_CLIENT_VOLUME_TYPE_SPEAKER;
+ ev.volume = val;
+
+ ipc_send_notif(hal_ipc, HAL_SERVICE_ID_HANDSFREE_CLIENT,
+ HAL_EV_CLIENT_VOLUME_CHANGED, sizeof(ev), &ev);
+}
+
+static void brth_cb(struct hfp_context *context, void *user_data)
+{
+ struct hal_ev_hf_client_response_and_hold_status ev;
+ unsigned int val;
+
+ DBG("");
+
+ if (!hfp_context_get_number(context, &val) ||
+ val > HAL_HF_CLIENT_RESP_AND_HOLD_STATUS_REJECT) {
+ error("hf-client: incorrect BTRH response ");
+ return;
+ }
+
+ ev.status = val;
+
+ ipc_send_notif(hal_ipc, HAL_SERVICE_ID_HANDSFREE_CLIENT,
+ HAL_EV_HF_CLIENT_RESPONSE_AND_HOLD_STATUS,
+ sizeof(ev), &ev);
+}
+
+static void clcc_cb(struct hfp_context *context, void *user_data)
+{
+ uint8_t buf[IPC_MTU];
+ struct hal_ev_hf_client_current_call *ev = (void *) buf;
+ unsigned int val;
+
+ DBG("");
+
+ memset(buf, 0, sizeof(buf));
+
+ if (!hfp_context_get_number(context, &val)) {
+ error("hf-client: Could not get index");
+ return;
+ }
+
+ ev->index = val;
+
+ if (!hfp_context_get_number(context, &val) ||
+ val > HAL_HF_CLIENT_DIRECTION_INCOMING) {
+ error("hf-client: Could not get direction");
+ return;
+ }
+
+ ev->direction = val;
+
+ if (!hfp_context_get_number(context, &val) ||
+ val > HAL_HF_CLIENT_CALL_STATE_HELD_BY_RESP_AND_HOLD) {
+ error("hf-client: Could not get callstate");
+ return;
+ }
+
+ ev->call_state = val;
+
+ /* Next field is MODE but Android is not interested in this. Skip it */
+ if (!hfp_context_get_number(context, &val)) {
+ error("hf-client: Could not get mode");
+ return;
+ }
+
+ if (!hfp_context_get_number(context, &val) || val > 1) {
+ error("hf-client: Could not get multiparty");
+ return;
+ }
+
+ ev->multiparty = val;
+
+ if (hfp_context_get_string(context, (char *) &ev->number[0],
+ MAX_NUMBER_LEN))
+ ev->number_len = strlen((char *) ev->number) + 1;
+
+ ipc_send_notif(hal_ipc, HAL_SERVICE_ID_HANDSFREE_CLIENT,
+ HAL_EV_HF_CLIENT_CURRENT_CALL,
+ sizeof(*ev) + ev->number_len, ev);
+}
+
+static void ciev_cb(struct hfp_context *context, void *user_data)
+{
+ struct device *dev = user_data;
+ unsigned int index, val;
+ int i;
+
+ DBG("");
+
+ if (!hfp_context_get_number(context, &index))
+ return;
+
+ if (!hfp_context_get_number(context, &val))
+ return;
+
+ for (i = 0; i < HFP_INDICATOR_LAST; i++) {
+ if (dev->ag_ind[i].index != index)
+ continue;
+
+ if (dev->ag_ind[i].cb) {
+ dev->ag_ind[i].val = val;
+ dev->ag_ind[i].cb(val);
+ return;
+ }
+ }
+}
+
+static void cnum_cb(struct hfp_context *context, void *user_data)
+{
+ uint8_t buf[IPC_MTU];
+ struct hal_ev_hf_client_subscriber_service_info *ev = (void *) buf;
+ unsigned int service;
+
+ DBG("");
+
+ /* Alpha field is empty string, just skip it */
+ hfp_context_skip_field(context);
+
+ if (!hfp_context_get_string(context, (char *) &ev->name[0],
+ MAX_NUMBER_LEN)) {
+ error("hf-client: Could not get number");
+ return;
+ }
+
+ ev->name_len = strlen((char *) &ev->name[0]) + 1;
+
+ /* Type is not used in Android */
+ hfp_context_skip_field(context);
+
+ /* Speed field is empty string, just skip it */
+ hfp_context_skip_field(context);
+
+ if (!hfp_context_get_number(context, &service))
+ return;
+
+ switch (service) {
+ case 4:
+ ev->type = HAL_HF_CLIENT_SUBSCR_TYPE_VOICE;
+ break;
+ case 5:
+ ev->type = HAL_HF_CLIENT_SUBSCR_TYPE_FAX;
+ break;
+ default:
+ ev->type = HAL_HF_CLIENT_SUBSCR_TYPE_UNKNOWN;
+ break;
+ }
+
+ ipc_send_notif(hal_ipc, HAL_SERVICE_ID_HANDSFREE_CLIENT,
+ HAL_EV_CLIENT_SUBSCRIBER_SERVICE_INFO,
+ sizeof(*ev) + ev->name_len, ev);
+}
+
+static void cops_cb(struct hfp_context *context, void *user_data)
+{
+ uint8_t buf[IPC_MTU];
+ struct hal_ev_hf_client_operator_name *ev = (void *) buf;
+ unsigned int format;
+
+ DBG("");
+
+ /* Not interested in mode */
+ hfp_context_skip_field(context);
+
+ if (!hfp_context_get_number(context, &format))
+ return;
+
+ if (format != 0)
+ info("hf-client: Not correct string format in +COSP");
+
+ if (!hfp_context_get_string(context, (char *) &ev->name[0],
+ MAX_OPERATOR_NAME_LEN)) {
+ error("hf-client: incorrect COPS response");
+ return;
+ }
+
+ ev->name_len = strlen((char *) &ev->name[0]) + 1;
+
+ ipc_send_notif(hal_ipc, HAL_SERVICE_ID_HANDSFREE_CLIENT,
+ HAL_EV_HF_CLIENT_OPERATOR_NAME,
+ sizeof(*ev) + ev->name_len, ev);
+}
+
+static void binp_cb(struct hfp_context *context, void *user_data)
+{
+ uint8_t buf[IPC_MTU];
+ struct hal_ev_hf_client_last_void_call_tag_num *ev = (void *) buf;
+ char number[33];
+
+ DBG("");
+
+ if (!hfp_context_get_string(context, number, sizeof(number))) {
+ error("hf-client: incorrect COPS response");
+ return;
+ }
+
+ ev->number_len = strlen(number) + 1;
+ memcpy(ev->number, number, ev->number_len);
+
+ ipc_send_notif(hal_ipc, HAL_SERVICE_ID_HANDSFREE_CLIENT,
+ HAL_EV_CLIENT_LAST_VOICE_CALL_TAG_NUM,
+ sizeof(*ev) + ev->number_len, ev);
+}
+
+static bool is_codec_supported_localy(struct device *dev, uint8_t codec)
+{
+ int i;
+
+ for (i = 0; i < CODECS_COUNT; i++) {
+ if (dev->codecs[i].type != codec)
+ continue;
+
+ return dev->codecs[i].local_supported;
+ }
+
+ return false;
+}
+
+static void bcs_resp(enum hfp_result result, enum hfp_error cme_err,
+ void *user_data)
+{
+ if (result != HFP_RESULT_OK)
+ error("hf-client: Error on AT+BCS (err=%u)", result);
+}
+
+static void bcs_cb(struct hfp_context *context, void *user_data)
+{
+ struct device *dev = user_data;
+ unsigned int codec;
+ char codecs_string[8];
+
+ DBG("");
+
+ if (!hfp_context_get_number(context, &codec))
+ goto failed;
+
+ if (!is_codec_supported_localy(dev, codec))
+ goto failed;
+
+ dev->negotiated_codec = codec;
+
+ hfp_hf_send_command(dev->hf, bcs_resp, dev, "AT+BCS=%u", codec);
+
+ return;
+
+failed:
+ error("hf-client: Could not get codec");
+
+ get_local_codecs_string(dev, codecs_string, sizeof(codecs_string));
+
+ hfp_hf_send_command(dev->hf, bcs_resp, dev, "AT+BCS=%s", codecs_string);
+}
+
+static void slc_completed(struct device *dev)
+{
+ int i;
+ struct indicator *ag_ind;
+
+ DBG("");
+
+ ag_ind = dev->ag_ind;
+
+ device_set_state(dev, HAL_HF_CLIENT_CONN_STATE_SLC_CONNECTED);
+
+ /* Notify Android with indicators */
+ for (i = 0; i < HFP_INDICATOR_LAST; i++) {
+ if (!ag_ind[i].cb)
+ continue;
+
+ ag_ind[i].cb(ag_ind[i].val);
+ }
+
+ /* TODO: register unsolicited results handlers */
+
+ hfp_hf_register(dev->hf, bvra_cb, "+BRVA", dev, NULL);
+ hfp_hf_register(dev->hf, vgm_cb, "+VGM", dev, NULL);
+ hfp_hf_register(dev->hf, vgs_cb, "+VGS", dev, NULL);
+ hfp_hf_register(dev->hf, brth_cb, "+BTRH", dev, NULL);
+ hfp_hf_register(dev->hf, clcc_cb, "+CLCC", dev, NULL);
+ hfp_hf_register(dev->hf, ciev_cb, "+CIEV", dev, NULL);
+ hfp_hf_register(dev->hf, cops_cb, "+COPS", dev, NULL);
+ hfp_hf_register(dev->hf, cnum_cb, "+CNUM", dev, NULL);
+ hfp_hf_register(dev->hf, binp_cb, "+BINP", dev, NULL);
+ hfp_hf_register(dev->hf, bcs_cb, "+BCS", dev, NULL);
+
+ if (!hfp_hf_send_command(dev->hf, cmd_complete_cb, NULL, "AT+COPS=3,0"))
+ info("hf-client: Could not send AT+COPS=3,0");
+}
+
+static void slc_chld_cb(struct hfp_context *context, void *user_data)
+{
+ struct device *dev = user_data;
+ char feat[3];
+
+ if (!hfp_context_open_container(context))
+ goto failed;
+
+ while (hfp_context_get_unquoted_string(context, feat, sizeof(feat)))
+ set_chld_feat(dev, feat);
+
+ if (!hfp_context_close_container(context))
+ goto failed;
+
+ return;
+
+failed:
+ error("hf-client: Error on CHLD response");
+ slc_error(dev);
+}
+
+static void slc_chld_resp(enum hfp_result result, enum hfp_error cme_err,
+ void *user_data)
+{
+ struct device *dev = user_data;
+
+ DBG("");
+
+ hfp_hf_unregister(dev->hf, "+CHLD");
+
+ if (result != HFP_RESULT_OK) {
+ error("hf-client: CHLD error: %d", result);
+ slc_error(dev);
+ return;
+ }
+
+ slc_completed(dev);
+}
+
+static void slc_cmer_resp(enum hfp_result result, enum hfp_error cme_err,
+ void *user_data)
+{
+ struct device *dev = user_data;
+
+ DBG("");
+
+ if (result != HFP_RESULT_OK) {
+ error("hf-client: CMER error: %d", result);
+ goto failed;
+ }
+
+ /* Continue with SLC creation */
+ if (!(dev->features & HFP_AG_FEAT_3WAY)) {
+ slc_completed(dev);
+ return;
+ }
+
+ if (!hfp_hf_register(dev->hf, slc_chld_cb, "+CHLD", dev, NULL)) {
+ error("hf-client: Could not register +CHLD");
+ goto failed;
+ }
+
+ if (!hfp_hf_send_command(dev->hf, slc_chld_resp, dev, "AT+CHLD=?")) {
+ error("hf-client: Could not send AT+CHLD");
+ goto failed;
+ }
+
+ return;
+
+failed:
+ slc_error(dev);
+}
+
+static void set_indicator_value(uint8_t index, unsigned int val,
+ struct indicator *ag_ind)
+{
+ int i;
+
+ for (i = 0; i < HFP_INDICATOR_LAST; i++) {
+ if (index != ag_ind[i].index)
+ continue;
+
+ ag_ind[i].val = val;
+ ag_ind[i].cb(val);
+ return;
+ }
+}
+
+static void slc_cind_status_cb(struct hfp_context *context,
+ void *user_data)
+{
+ struct device *dev = user_data;
+ uint8_t index = 1;
+
+ DBG("");
+
+ while (hfp_context_has_next(context)) {
+ uint32_t val;
+
+ if (!hfp_context_get_number(context, &val)) {
+ error("hf-client: Error on CIND status response");
+ return;
+ }
+
+ set_indicator_value(index++, val, dev->ag_ind);
+ }
+}
+
+static void slc_cind_status_resp(enum hfp_result result,
+ enum hfp_error cme_err,
+ void *user_data)
+{
+ struct device *dev = user_data;
+
+ DBG("");
+
+ hfp_hf_unregister(dev->hf, "+CIND");
+
+ if (result != HFP_RESULT_OK) {
+ error("hf-client: CIND error: %d", result);
+ goto failed;
+ }
+
+ /* Continue with SLC creation */
+ if (!hfp_hf_send_command(dev->hf, slc_cmer_resp, dev,
+ "AT+CMER=3,0,0,1")) {
+ error("hf-client: Counld not send AT+CMER");
+ goto failed;
+ }
+
+ return;
+
+failed:
+ slc_error(dev);
+}
+
+static void slc_cind_resp(enum hfp_result result, enum hfp_error cme_err,
+ void *user_data)
+{
+ struct device *dev = user_data;
+
+ DBG("");
+
+ hfp_hf_unregister(dev->hf, "+CIND");
+
+ if (result != HFP_RESULT_OK) {
+ error("hf-client: CIND error: %d", result);
+ goto failed;
+ }
+
+ /* Continue with SLC creation */
+ if (!hfp_hf_register(dev->hf, slc_cind_status_cb, "+CIND", dev,
+ NULL)) {
+ error("hf-client: Counld not register +CIND");
+ goto failed;
+ }
+
+ if (!hfp_hf_send_command(dev->hf, slc_cind_status_resp, dev,
+ "AT+CIND?")) {
+ error("hf-client: Counld not send AT+CIND?");
+ goto failed;
+ }
+
+ return;
+
+failed:
+ slc_error(dev);
+}
+
+static void ciev_service_cb(uint8_t val)
+{
+ struct hal_ev_hf_client_net_state ev;
+
+ DBG("");
+
+ if (val > HAL_HF_CLIENT_NET_ROAMING_TYPE_ROAMING) {
+ error("hf-client: Incorrect state %u:", val);
+ return;
+ }
+
+ ev.state = val;
+
+ ipc_send_notif(hal_ipc, HAL_SERVICE_ID_HANDSFREE_CLIENT,
+ HAL_EV_HF_CLIENT_NET_STATE, sizeof(ev), &ev);
+}
+
+static void ciev_call_cb(uint8_t val)
+{
+ struct hal_ev_hf_client_call_indicator ev;
+
+ DBG("");
+
+ if (val > HAL_HF_CLIENT_CALL_IND_CALL_IN_PROGERSS) {
+ error("hf-client: Incorrect call state %u:", val);
+ return;
+ }
+
+ ev.call = val;
+
+ ipc_send_notif(hal_ipc, HAL_SERVICE_ID_HANDSFREE_CLIENT,
+ HAL_EV_HF_CLIENT_CALL_INDICATOR, sizeof(ev), &ev);
+}
+
+static void ciev_callsetup_cb(uint8_t val)
+{
+ struct hal_ev_hf_client_call_setup_indicator ev;
+
+ DBG("");
+
+ if (val > HAL_HF_CLIENT_CALL_SETUP_ALERTING) {
+ error("hf-client: Incorrect call setup state %u:", val);
+ return;
+ }
+
+ ev.call_setup = val;
+
+ ipc_send_notif(hal_ipc, HAL_SERVICE_ID_HANDSFREE_CLIENT,
+ HAL_EV_HF_CLIENT_CALL_SETUP_INDICATOR,
+ sizeof(ev), &ev);
+}
+
+static void ciev_callheld_cb(uint8_t val)
+{
+ struct hal_ev_hf_client_call_held_indicator ev;
+
+ DBG("");
+
+ if (val > HAL_HF_CLIENT_CALL_SETUP_IND_HOLD) {
+ error("hf-client: Incorrect call held state %u:", val);
+ return;
+ }
+
+ ev.call_held = val;
+
+ ipc_send_notif(hal_ipc, HAL_SERVICE_ID_HANDSFREE_CLIENT,
+ HAL_EV_HF_CLIENT_CALL_HELD_INDICATOR,
+ sizeof(ev), &ev);
+}
+
+static void ciev_signal_cb(uint8_t val)
+{
+ struct hal_ev_hf_client_net_signal_strength ev;
+
+ DBG("");
+
+ if (val > 5) {
+ error("hf-client: Incorrect signal value %u:", val);
+ return;
+ }
+
+ ev.signal_strength = val;
+
+ ipc_send_notif(hal_ipc, HAL_SERVICE_ID_HANDSFREE_CLIENT,
+ HAL_EV_HF_CLIENT_NET_SIGNAL_STRENGTH,
+ sizeof(ev), &ev);
+}
+
+static void ciev_roam_cb(uint8_t val)
+{
+ struct hal_ev_hf_client_net_roaming_type ev;
+
+ DBG("");
+
+ if (val > HAL_HF_CLIENT_NET_ROAMING_TYPE_ROAMING) {
+ error("hf-client: Incorrect roaming state %u:", val);
+ return;
+ }
+
+ ev.state = val;
+
+ ipc_send_notif(hal_ipc, HAL_SERVICE_ID_HANDSFREE_CLIENT,
+ HAL_EV_HF_CLIENT_NET_ROAMING_TYPE,
+ sizeof(ev), &ev);
+}
+
+static void ciev_battchg_cb(uint8_t val)
+{
+ struct hal_ev_hf_client_battery_level ev;
+
+ DBG("");
+
+ if (val > 5) {
+ error("hf-client: Incorrect battery charge value %u:", val);
+ return;
+ }
+
+ ev.battery_level = val;
+
+ ipc_send_notif(hal_ipc, HAL_SERVICE_ID_HANDSFREE_CLIENT,
+ HAL_EV_HF_CLIENT_BATTERY_LEVEL, sizeof(ev), &ev);
+}
+
+static void set_indicator_parameters(uint8_t index, const char *indicator,
+ unsigned int min,
+ unsigned int max,
+ struct indicator *ag_ind)
+{
+ DBG("%s, %i", indicator, index);
+
+ /* TODO: Verify min/max values ? */
+
+ if (strcmp("service", indicator) == 0) {
+ ag_ind[HFP_INDICATOR_SERVICE].index = index;
+ ag_ind[HFP_INDICATOR_SERVICE].min = min;
+ ag_ind[HFP_INDICATOR_SERVICE].max = max;
+ ag_ind[HFP_INDICATOR_SERVICE].cb = ciev_service_cb;
+ return;
+ }
+
+ if (strcmp("call", indicator) == 0) {
+ ag_ind[HFP_INDICATOR_CALL].index = index;
+ ag_ind[HFP_INDICATOR_CALL].min = min;
+ ag_ind[HFP_INDICATOR_CALL].max = max;
+ ag_ind[HFP_INDICATOR_CALL].cb = ciev_call_cb;
+ return;
+ }
+
+ if (strcmp("callsetup", indicator) == 0) {
+ ag_ind[HFP_INDICATOR_CALLSETUP].index = index;
+ ag_ind[HFP_INDICATOR_CALLSETUP].min = min;
+ ag_ind[HFP_INDICATOR_CALLSETUP].max = max;
+ ag_ind[HFP_INDICATOR_CALLSETUP].cb = ciev_callsetup_cb;
+ return;
+ }
+
+ if (strcmp("callheld", indicator) == 0) {
+ ag_ind[HFP_INDICATOR_CALLHELD].index = index;
+ ag_ind[HFP_INDICATOR_CALLHELD].min = min;
+ ag_ind[HFP_INDICATOR_CALLHELD].max = max;
+ ag_ind[HFP_INDICATOR_CALLHELD].cb = ciev_callheld_cb;
+ return;
+ }
+
+ if (strcmp("signal", indicator) == 0) {
+ ag_ind[HFP_INDICATOR_SIGNAL].index = index;
+ ag_ind[HFP_INDICATOR_SIGNAL].min = min;
+ ag_ind[HFP_INDICATOR_SIGNAL].max = max;
+ ag_ind[HFP_INDICATOR_SIGNAL].cb = ciev_signal_cb;
+ return;
+ }
+
+ if (strcmp("roam", indicator) == 0) {
+ ag_ind[HFP_INDICATOR_ROAM].index = index;
+ ag_ind[HFP_INDICATOR_ROAM].min = min;
+ ag_ind[HFP_INDICATOR_ROAM].max = max;
+ ag_ind[HFP_INDICATOR_ROAM].cb = ciev_roam_cb;
+ return;
+ }
+
+ if (strcmp("battchg", indicator) == 0) {
+ ag_ind[HFP_INDICATOR_BATTCHG].index = index;
+ ag_ind[HFP_INDICATOR_BATTCHG].min = min;
+ ag_ind[HFP_INDICATOR_BATTCHG].max = max;
+ ag_ind[HFP_INDICATOR_BATTCHG].cb = ciev_battchg_cb;
+ return;
+ }
+
+ error("hf-client: Unknown indicator: %s", indicator);
+}
+
+static void slc_cind_cb(struct hfp_context *context, void *user_data)
+{
+ struct device *dev = user_data;
+ int index = 1;
+
+ DBG("");
+
+ while (hfp_context_has_next(context)) {
+ char name[255];
+ unsigned int min, max;
+
+ /* e.g ("callsetup",(0-3)) */
+ if (!hfp_context_open_container(context))
+ break;
+
+ if (!hfp_context_get_string(context, name, sizeof(name))) {
+ error("hf-client: Could not get string");
+ goto failed;
+ }
+
+ if (!hfp_context_open_container(context)) {
+ error("hf-client: Could not open container");
+ goto failed;
+ }
+
+ if (!hfp_context_get_range(context, &min, &max)) {
+ if (!hfp_context_get_number(context, &min)) {
+ error("hf-client: Could not get number");
+ goto failed;
+ }
+
+ if (!hfp_context_get_number(context, &max)) {
+ error("hf-client: Could not get number");
+ goto failed;
+ }
+ }
+
+ if (!hfp_context_close_container(context)) {
+ error("hf-client: Could not close container");
+ goto failed;
+ }
+
+ if (!hfp_context_close_container(context)) {
+ error("hf-client: Could not close container");
+ goto failed;
+ }
+
+ set_indicator_parameters(index, name, min, max, dev->ag_ind);
+ index++;
+ }
+
+ return;
+
+failed:
+ error("hf-client: Error on CIND response");
+ slc_error(dev);
+}
+
+static void slc_bac_resp(enum hfp_result result, enum hfp_error cme_err,
+ void *user_data)
+{
+ struct device *dev = user_data;
+
+ DBG("");
+
+ if (result != HFP_RESULT_OK)
+ goto failed;
+
+ /* Continue with SLC creation */
+ if (!hfp_hf_register(dev->hf, slc_cind_cb, "+CIND", dev, NULL)) {
+ error("hf-client: Could not register for +CIND");
+ goto failed;
+ }
+
+ if (!hfp_hf_send_command(dev->hf, slc_cind_resp, dev, "AT+CIND=?"))
+ goto failed;
+
+ return;
+
+failed:
+ error("hf-client: Error on BAC response");
+ slc_error(dev);
+}
+
+static bool send_supported_codecs(struct device *dev)
+{
+ char codecs_string[8];
+ char bac[16];
+
+ memset(bac, 0, sizeof(bac));
+
+ strcpy(bac, "AT+BAC=");
+
+ get_local_codecs_string(dev, codecs_string, sizeof(codecs_string));
+ strcat(bac, codecs_string);
+
+ return hfp_hf_send_command(dev->hf, slc_bac_resp, dev, bac);
+}
+
+static void slc_brsf_cb(struct hfp_context *context, void *user_data)
+{
+ unsigned int feat;
+ struct device *dev = user_data;
+
+ DBG("");
+
+ if (hfp_context_get_number(context, &feat))
+ dev->features = feat;
+}
+
+static void slc_brsf_resp(enum hfp_result result, enum hfp_error cme_err,
+ void *user_data)
+{
+ struct device *dev = user_data;
+
+ hfp_hf_unregister(dev->hf, "+BRSF");
+
+ if (result != HFP_RESULT_OK) {
+ error("hf-client: BRSF error: %d", result);
+ goto failed;
+ }
+
+ /* Continue with SLC creation */
+ if (codec_negotiation_supported(dev)) {
+ if (send_supported_codecs(dev))
+ return;
+
+ error("hf-client: Could not send BAC command");
+ goto failed;
+ }
+
+ /* No WBS on remote side. Continue with indicators */
+ if (!hfp_hf_register(dev->hf, slc_cind_cb, "+CIND", dev, NULL)) {
+ error("hf-client: Could not register for +CIND");
+ goto failed;
+ }
+
+ if (!hfp_hf_send_command(dev->hf, slc_cind_resp, dev, "AT+CIND=?")) {
+ error("hf-client: Could not send AT+CIND command");
+ goto failed;
+ }
+
+ return;
+
+failed:
+ slc_error(dev);
+}
+
+static bool create_slc(struct device *dev)
+{
+ DBG("");
+
+ if (!hfp_hf_register(dev->hf, slc_brsf_cb, "+BRSF", dev, NULL))
+ return false;
+
+ return hfp_hf_send_command(dev->hf, slc_brsf_resp, dev, "AT+BRSF=%u",
+ hfp_hf_features);
+}
+
+static void connect_cb(GIOChannel *chan, GError *err, gpointer user_data)
+{
+ struct device *dev = user_data;
+
+ DBG("");
+
+ if (err) {
+ error("hf-client: connect failed (%s)", err->message);
+ goto failed;
+ }
+
+ dev->hf = hfp_hf_new(g_io_channel_unix_get_fd(chan));
+ if (!dev->hf) {
+ error("hf-client: Could not create hfp io");
+ goto failed;
+ }
+
+ g_io_channel_set_close_on_unref(chan, FALSE);
+
+ hfp_hf_set_close_on_unref(dev->hf, true);
+ hfp_hf_set_disconnect_handler(dev->hf, disconnect_watch, dev, NULL);
+
+ if (!create_slc(dev)) {
+ error("hf-client: Could not start SLC creation");
+ hfp_hf_disconnect(dev->hf);
+ goto failed;
+ }
+
+ device_set_state(dev, HAL_HF_CLIENT_CONN_STATE_CONNECTED);
+
+ return;
+
+failed:
+ g_io_channel_shutdown(chan, TRUE, NULL);
+ device_destroy(dev);
+}
+
+static void sdp_hfp_search_cb(sdp_list_t *recs, int err, gpointer data)
+{
+ sdp_list_t *protos, *classes;
+ struct device *dev = data;
+ GError *gerr = NULL;
+ GIOChannel *io;
+ uuid_t uuid;
+ int channel;
+
+ DBG("");
+
+ if (err < 0) {
+ error("hf-client: unable to get SDP record: %s",
+ strerror(-err));
+ goto failed;
+ }
+
+ if (!recs || !recs->data) {
+ info("hf-client: no HFP SDP records found");
+ goto failed;
+ }
+
+ if (sdp_get_service_classes(recs->data, &classes) < 0 || !classes) {
+ error("hf-client: unable to get service classes from record");
+ goto failed;
+ }
+
+ /* TODO read remote version? */
+
+ memcpy(&uuid, classes->data, sizeof(uuid));
+ sdp_list_free(classes, free);
+
+ if (!sdp_uuid128_to_uuid(&uuid) || uuid.type != SDP_UUID16 ||
+ uuid.value.uuid16 != HANDSFREE_AGW_SVCLASS_ID) {
+ error("hf-client: invalid service record or not HFP");
+ goto failed;
+ }
+
+ if (sdp_get_access_protos(recs->data, &protos) < 0) {
+ error("hf-client: unable to get access protocols from record");
+ sdp_list_free(classes, free);
+ goto failed;
+ }
+
+ channel = sdp_get_proto_port(protos, RFCOMM_UUID);
+ sdp_list_foreach(protos, (sdp_list_func_t) sdp_list_free, NULL);
+ sdp_list_free(protos, NULL);
+ if (channel <= 0) {
+ error("hf-client: unable to get RFCOMM channel from record");
+ goto failed;
+ }
+
+ io = bt_io_connect(connect_cb, dev, NULL, &gerr,
+ BT_IO_OPT_SOURCE_BDADDR, &adapter_addr,
+ BT_IO_OPT_DEST_BDADDR, &dev->bdaddr,
+ BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_MEDIUM,
+ BT_IO_OPT_CHANNEL, channel,
+ BT_IO_OPT_INVALID);
+ if (!io) {
+ error("hf-client: unable to connect: %s", gerr->message);
+ g_error_free(gerr);
+ goto failed;
+ }
+
+ g_io_channel_unref(io);
+ return;
+
+failed:
+ device_destroy(dev);
+}
+
+static int sdp_search_hfp(struct device *dev)
+{
+ uuid_t uuid;
+
+ sdp_uuid16_create(&uuid, HANDSFREE_AGW_SVCLASS_ID);
+
+ return bt_search_service(&adapter_addr, &dev->bdaddr, &uuid,
+ sdp_hfp_search_cb, dev, NULL, 0);
+}
+
+static void handle_connect(const void *buf, uint16_t len)
+{
+ struct device *dev;
+ const struct hal_cmd_hf_client_connect *cmd = buf;
+ uint32_t status;
+ bdaddr_t bdaddr;
+ char addr[18];
+
+ DBG("");
+
+ android2bdaddr(&cmd->bdaddr, &bdaddr);
+
+ ba2str(&bdaddr, addr);
+ DBG("connecting to %s", addr);
+
+ dev = get_device(&bdaddr);
+ if (!dev) {
+ status = HAL_STATUS_FAILED;
+ goto done;
+ }
+
+ if (dev->state != HAL_HF_CLIENT_CONN_STATE_DISCONNECTED) {
+ status = HAL_STATUS_FAILED;
+ goto done;
+ }
+
+ if (sdp_search_hfp(dev) < 0) {
+ status = HAL_STATUS_FAILED;
+ device_destroy(dev);
+ goto done;
+ }
+
+ device_set_state(dev, HAL_HF_CLIENT_CONN_STATE_CONNECTING);
+
+ status = HAL_STATUS_SUCCESS;
+
+done:
+ ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_HANDSFREE_CLIENT,
+ HAL_OP_HF_CLIENT_CONNECT, status);
+}
+
+static void confirm_cb(GIOChannel *chan, gpointer data)
+{
+ struct device *dev;
+ char address[18];
+ bdaddr_t bdaddr;
+ GError *err = NULL;
+
+ bt_io_get(chan, &err,
+ BT_IO_OPT_DEST, address,
+ BT_IO_OPT_DEST_BDADDR, &bdaddr,
+ BT_IO_OPT_INVALID);
+ if (err) {
+ error("hf-client: confirm failed (%s)", err->message);
+ g_error_free(err);
+ goto drop;
+ }
+
+ DBG("Incoming connection from %s", address);
+
+ dev = get_device(&bdaddr);
+ if (!dev) {
+ error("hf-client: There is other AG connected");
+ goto drop;
+ }
+
+ if (dev->state != HAL_HF_CLIENT_CONN_STATE_DISCONNECTED) {
+ /* TODO: Handle colision */
+ error("hf-client: Connections is up or ongoing ?");
+ goto drop;
+ }
+
+ device_set_state(dev, HAL_HF_CLIENT_CONN_STATE_CONNECTING);
+
+ if (!bt_io_accept(chan, connect_cb, dev, NULL, NULL)) {
+ error("hf-client: failed to accept connection");
+ device_destroy(dev);
+ goto drop;
+ }
+
+ return;
+
+drop:
+ g_io_channel_shutdown(chan, TRUE, NULL);
+}
+
+static const struct ipc_handler cmd_handlers[] = {
+ /* HAL_OP_HF_CLIENT_CONNECT */
+ { handle_connect, false,
+ sizeof(struct hal_cmd_hf_client_connect) },
+ /* HAL_OP_HF_CLIENT_DISCONNECT */
+ { handle_disconnect, false,
+ sizeof(struct hal_cmd_hf_client_disconnect) },
+ /* HAL_OP_HF_CLIENT_CONNECT_AUDIO */
+ { handle_connect_audio, false,
+ sizeof(struct hal_cmd_hf_client_connect_audio) },
+ /* HAL_OP_HF_CLIENT_DISCONNECT_AUDIO */
+ { handle_disconnect_audio, false,
+ sizeof(struct hal_cmd_hf_client_disconnect_audio) },
+ /* define HAL_OP_HF_CLIENT_START_VR */
+ { handle_start_vr, false, 0 },
+ /* define HAL_OP_HF_CLIENT_STOP_VR */
+ { handle_stop_vr, false, 0 },
+ /* HAL_OP_HF_CLIENT_VOLUME_CONTROL */
+ { handle_volume_control, false,
+ sizeof(struct hal_cmd_hf_client_volume_control) },
+ /* HAL_OP_HF_CLIENT_DIAL */
+ { handle_dial, true, sizeof(struct hal_cmd_hf_client_dial) },
+ /* HAL_OP_HF_CLIENT_DIAL_MEMORY */
+ { handle_dial_memory, false,
+ sizeof(struct hal_cmd_hf_client_dial_memory) },
+ /* HAL_OP_HF_CLIENT_CALL_ACTION */
+ { handle_call_action, false,
+ sizeof(struct hal_cmd_hf_client_call_action) },
+ /* HAL_OP_HF_CLIENT_QUERY_CURRENT_CALLS */
+ { handle_query_current_calls, false, 0 },
+ /* HAL_OP_HF_CLIENT_QUERY_OPERATOR_NAME */
+ { handle_query_operator_name, false, 0 },
+ /* HAL_OP_HF_CLIENT_RETRIEVE_SUBSCR_INFO */
+ { handle_retrieve_subscr_info, false, 0 },
+ /* HAL_OP_HF_CLIENT_SEND_DTMF */
+ { handle_send_dtmf, false,
+ sizeof(struct hal_cmd_hf_client_send_dtmf) },
+ /* HAL_OP_HF_CLIENT_GET_LAST_VOICE_TAG_NUM */
+ { handle_get_last_vc_tag_num, false, 0 },
+};
+
+static sdp_record_t *hfp_hf_record(void)
+{
+ sdp_list_t *svclass_id, *pfseq, *apseq, *root;
+ uuid_t root_uuid, svclass_uuid, ga_svclass_uuid;
+ uuid_t l2cap_uuid, rfcomm_uuid;
+ sdp_profile_desc_t profile;
+ sdp_list_t *aproto, *proto[2];
+ sdp_record_t *record;
+ sdp_data_t *channel, *features;
+ uint16_t sdpfeat;
+ uint8_t ch = HFP_HF_CHANNEL;
+
+ record = sdp_record_alloc();
+ if (!record)
+ return NULL;
+
+ sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
+ root = sdp_list_append(NULL, &root_uuid);
+ sdp_set_browse_groups(record, root);
+
+ sdp_uuid16_create(&svclass_uuid, HANDSFREE_SVCLASS_ID);
+ svclass_id = sdp_list_append(NULL, &svclass_uuid);
+ sdp_uuid16_create(&ga_svclass_uuid, GENERIC_AUDIO_SVCLASS_ID);
+ svclass_id = sdp_list_append(svclass_id, &ga_svclass_uuid);
+ sdp_set_service_classes(record, svclass_id);
+
+ sdp_uuid16_create(&profile.uuid, HANDSFREE_PROFILE_ID);
+ profile.version = 0x0106;
+ pfseq = sdp_list_append(NULL, &profile);
+ sdp_set_profile_descs(record, pfseq);
+
+ sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID);
+ proto[0] = sdp_list_append(NULL, &l2cap_uuid);
+ apseq = sdp_list_append(NULL, proto[0]);
+
+ sdp_uuid16_create(&rfcomm_uuid, RFCOMM_UUID);
+ proto[1] = sdp_list_append(NULL, &rfcomm_uuid);
+ channel = sdp_data_alloc(SDP_UINT8, &ch);
+ proto[1] = sdp_list_append(proto[1], channel);
+ apseq = sdp_list_append(apseq, proto[1]);
+
+ /* Codec Negotiation bit in SDP feature is different then in BRSF */
+ sdpfeat = hfp_hf_features & 0x0000003F;
+ if (hfp_hf_features & HFP_HF_FEAT_CODEC)
+ sdpfeat |= 0x00000020;
+ else
+ sdpfeat &= ~0x00000020;
+
+ features = sdp_data_alloc(SDP_UINT16, &sdpfeat);
+ sdp_attr_add(record, SDP_ATTR_SUPPORTED_FEATURES, features);
+
+ aproto = sdp_list_append(NULL, apseq);
+ sdp_set_access_protos(record, aproto);
+
+ sdp_set_info_attr(record, "Hands-Free unit", NULL, NULL);
+
+ sdp_data_free(channel);
+ sdp_list_free(proto[0], NULL);
+ sdp_list_free(proto[1], NULL);
+ sdp_list_free(apseq, NULL);
+ sdp_list_free(pfseq, NULL);
+ sdp_list_free(aproto, NULL);
+ sdp_list_free(root, NULL);
+ sdp_list_free(svclass_id, NULL);
+
+ return record;
+}
+
+static bool enable_hf_client(void)
+{
+ sdp_record_t *rec;
+ GError *err = NULL;
+
+ hfp_hf_server = bt_io_listen(NULL, confirm_cb, NULL, NULL, &err,
+ BT_IO_OPT_SOURCE_BDADDR, &adapter_addr,
+ BT_IO_OPT_CHANNEL, HFP_HF_CHANNEL,
+ BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_MEDIUM,
+ BT_IO_OPT_INVALID);
+ if (!hfp_hf_server) {
+ error("hf-client: Failed to listen on Handsfree rfcomm: %s",
+ err->message);
+ g_error_free(err);
+ return false;
+ }
+
+ hfp_hf_features = HFP_HF_FEATURES;
+
+ rec = hfp_hf_record();
+ if (!rec) {
+ error("hf-client: Could not create service record");
+ goto failed;
+ }
+
+ if (bt_adapter_add_record(rec, 0) < 0) {
+ error("hf-client: Failed to register service record");
+ sdp_record_free(rec);
+ goto failed;
+ }
+
+ hfp_hf_record_id = rec->handle;
+
+ return true;
+
+failed:
+ g_io_channel_shutdown(hfp_hf_server, TRUE, NULL);
+ g_io_channel_unref(hfp_hf_server);
+ hfp_hf_server = NULL;
+
+ return false;
+}
+
+static void cleanup_hfp_hf(void)
+{
+ if (hfp_hf_server) {
+ g_io_channel_shutdown(hfp_hf_server, TRUE, NULL);
+ g_io_channel_unref(hfp_hf_server);
+ hfp_hf_server = NULL;
+ }
+
+ if (hfp_hf_record_id > 0) {
+ bt_adapter_remove_record(hfp_hf_record_id);
+ hfp_hf_record_id = 0;
+ }
+
+ if (sco) {
+ bt_sco_unref(sco);
+ sco = NULL;
+ }
+}
+
+static bool confirm_sco_cb(const bdaddr_t *addr, uint16_t *voice_settings)
+{
+ struct device *dev;
+
+ DBG("");
+
+ dev = find_device(addr);
+ if (!dev || dev->state != HAL_HF_CLIENT_CONN_STATE_SLC_CONNECTED) {
+ error("hf-client: No device or SLC not ready");
+ return false;
+ }
+
+ set_audio_state(dev, HAL_HF_CLIENT_AUDIO_STATE_CONNECTING);
+
+ if (codec_negotiation_supported(dev) &&
+ dev->negotiated_codec != CODEC_ID_CVSD)
+ *voice_settings = BT_VOICE_TRANSPARENT;
+ else
+ *voice_settings = BT_VOICE_CVSD_16BIT;
+
+ return true;
+}
+
+static void connect_sco_cb(enum sco_status status, const bdaddr_t *addr)
+{
+ struct device *dev;
+ uint8_t audio_state;
+
+ DBG("SCO Status %u", status);
+
+ /* Device shall be there, just sanity check */
+ dev = find_device(addr);
+ if (!dev) {
+ error("hf-client: There is no device?");
+ return;
+ }
+
+ if (status != SCO_STATUS_OK) {
+ audio_state = HAL_HF_CLIENT_AUDIO_STATE_DISCONNECTED;
+ goto done;
+ }
+
+ if (dev->negotiated_codec == CODEC_ID_MSBC)
+ audio_state = HAL_HF_CLIENT_AUDIO_STATE_CONNECTED_MSBC;
+ else
+ audio_state = HAL_HF_CLIENT_AUDIO_STATE_CONNECTED;
+
+done:
+ set_audio_state(dev, audio_state);
+}
+
+static void disconnect_sco_cb(const bdaddr_t *addr)
+{
+ struct device *dev;
+
+ DBG("");
+
+ dev = find_device(addr);
+ if (!dev) {
+ error("hf-client: No device");
+ return;
+ }
+
+ set_audio_state(dev, HAL_HF_CLIENT_AUDIO_STATE_DISCONNECTED);
+}
+
+bool bt_hf_client_register(struct ipc *ipc, const bdaddr_t *addr)
+{
+ DBG("");
+
+ devices = queue_new();
+
+ bacpy(&adapter_addr, addr);
+
+ if (!enable_hf_client())
+ goto failed;
+
+ sco = bt_sco_new(addr);
+ if (!sco) {
+ error("hf-client: Cannot create SCO. HFP AG is in use ?");
+ goto failed;
+ }
+
+ bt_sco_set_confirm_cb(sco, confirm_sco_cb);
+ bt_sco_set_connect_cb(sco, connect_sco_cb);
+ bt_sco_set_disconnect_cb(sco, disconnect_sco_cb);
+
+ hal_ipc = ipc;
+ ipc_register(hal_ipc, HAL_SERVICE_ID_HANDSFREE_CLIENT, cmd_handlers,
+ G_N_ELEMENTS(cmd_handlers));
+
+ return true;
+
+failed:
+ cleanup_hfp_hf();
+ queue_destroy(devices, free);
+ devices = NULL;
+
+ return false;
+}
+
+void bt_hf_client_unregister(void)
+{
+ DBG("");
+
+ cleanup_hfp_hf();
+
+ queue_destroy(devices, (void *) device_destroy);
+ devices = NULL;
+
+ ipc_unregister(hal_ipc, HAL_SERVICE_ID_HANDSFREE);
+ hal_ipc = NULL;
+}
diff --git a/repo/android/handsfree-client.h b/repo/android/handsfree-client.h
new file mode 100644
index 0000000..1eb69ff
--- /dev/null
+++ b/repo/android/handsfree-client.h
@@ -0,0 +1,25 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2014 Intel Corporation. All rights reserved.
+ *
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+bool bt_hf_client_register(struct ipc *ipc, const bdaddr_t *addr);
+void bt_hf_client_unregister(void);
diff --git a/repo/android/handsfree.c b/repo/android/handsfree.c
new file mode 100644
index 0000000..cb348ab
--- /dev/null
+++ b/repo/android/handsfree.c
@@ -0,0 +1,3031 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2013-2014 Intel Corporation. All rights reserved.
+ *
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdlib.h>
+#include <stdbool.h>
+#include <errno.h>
+#include <unistd.h>
+#include <glib.h>
+
+#include "lib/bluetooth.h"
+#include "lib/sdp.h"
+#include "lib/sdp_lib.h"
+#include "src/sdp-client.h"
+#include "src/uuid-helper.h"
+#include "src/shared/hfp.h"
+#include "src/shared/queue.h"
+#include "src/shared/util.h"
+#include "btio/btio.h"
+#include "hal-msg.h"
+#include "ipc-common.h"
+#include "ipc.h"
+#include "handsfree.h"
+#include "bluetooth.h"
+#include "src/log.h"
+#include "utils.h"
+#include "sco-msg.h"
+#include "sco.h"
+
+#define HSP_AG_CHANNEL 12
+#define HFP_AG_CHANNEL 13
+
+#define HFP_AG_FEAT_3WAY 0x00000001
+#define HFP_AG_FEAT_ECNR 0x00000002
+#define HFP_AG_FEAT_VR 0x00000004
+#define HFP_AG_FEAT_INBAND 0x00000008
+#define HFP_AG_FEAT_VTAG 0x00000010
+#define HFP_AG_FEAT_REJ_CALL 0x00000020
+#define HFP_AG_FEAT_ECS 0x00000040
+#define HFP_AG_FEAT_ECC 0x00000080
+#define HFP_AG_FEAT_EXT_ERR 0x00000100
+#define HFP_AG_FEAT_CODEC 0x00000200
+
+#define HFP_HF_FEAT_ECNR 0x00000001
+#define HFP_HF_FEAT_3WAY 0x00000002
+#define HFP_HF_FEAT_CLI 0x00000004
+#define HFP_HF_FEAT_VR 0x00000008
+#define HFP_HF_FEAT_RVC 0x00000010
+#define HFP_HF_FEAT_ECS 0x00000020
+#define HFP_HF_FEAT_ECC 0x00000040
+#define HFP_HF_FEAT_CODEC 0x00000080
+
+#define HFP_AG_FEATURES (HFP_AG_FEAT_3WAY | HFP_AG_FEAT_ECNR |\
+ HFP_AG_FEAT_VR | HFP_AG_FEAT_REJ_CALL |\
+ HFP_AG_FEAT_ECS | HFP_AG_FEAT_EXT_ERR)
+
+#define HFP_AG_CHLD "0,1,2,3"
+
+/* offsets in indicators table, should be incremented when sending CIEV */
+#define IND_SERVICE 0
+#define IND_CALL 1
+#define IND_CALLSETUP 2
+#define IND_CALLHELD 3
+#define IND_SIGNAL 4
+#define IND_ROAM 5
+#define IND_BATTCHG 6
+#define IND_COUNT (IND_BATTCHG + 1)
+
+#define RING_TIMEOUT 2
+
+#define CVSD_OFFSET 0
+#define MSBC_OFFSET 1
+#define CODECS_COUNT (MSBC_OFFSET + 1)
+
+#define CODEC_ID_CVSD 0x01
+#define CODEC_ID_MSBC 0x02
+
+struct indicator {
+ const char *name;
+ int min;
+ int max;
+ int val;
+ bool always_active;
+ bool active;
+};
+
+struct hfp_codec {
+ uint8_t type;
+ bool local_supported;
+ bool remote_supported;
+};
+
+struct hf_device {
+ bdaddr_t bdaddr;
+ uint8_t state;
+ uint8_t audio_state;
+ uint32_t features;
+
+ bool clip_enabled;
+ bool cmee_enabled;
+ bool ccwa_enabled;
+ bool indicators_enabled;
+ struct indicator inds[IND_COUNT];
+ int num_active;
+ int num_held;
+ int setup_state;
+ guint call_hanging_up;
+
+ uint8_t negotiated_codec;
+ uint8_t proposed_codec;
+ struct hfp_codec codecs[CODECS_COUNT];
+
+ guint ring;
+ char *clip;
+ bool hsp;
+
+ struct hfp_gw *gw;
+ guint delay_sco;
+};
+
+static const struct indicator inds_defaults[] = {
+ { "service", 0, 1, 0, false, true },
+ { "call", 0, 1, 0, true, true },
+ { "callsetup", 0, 3, 0, true, true },
+ { "callheld", 0, 2, 0, true, true },
+ { "signal", 0, 5, 0, false, true },
+ { "roam", 0, 1, 0, false, true },
+ { "battchg", 0, 5, 0, false, true },
+};
+
+static const struct hfp_codec codecs_defaults[] = {
+ { CODEC_ID_CVSD, true, false},
+ { CODEC_ID_MSBC, false, false},
+};
+
+static struct queue *devices = NULL;
+
+static uint32_t hfp_ag_features = 0;
+
+static bdaddr_t adapter_addr;
+
+static struct ipc *hal_ipc = NULL;
+static struct ipc *sco_ipc = NULL;
+
+static uint32_t hfp_record_id = 0;
+static GIOChannel *hfp_server = NULL;
+
+static uint32_t hsp_record_id = 0;
+static GIOChannel *hsp_server = NULL;
+
+static struct bt_sco *sco = NULL;
+
+static unsigned int max_hfp_clients = 0;
+
+static void set_state(struct hf_device *dev, uint8_t state)
+{
+ struct hal_ev_handsfree_conn_state ev;
+ char address[18];
+
+ if (dev->state == state)
+ return;
+
+ dev->state = state;
+
+ ba2str(&dev->bdaddr, address);
+ DBG("device %s state %u", address, state);
+
+ bdaddr2android(&dev->bdaddr, ev.bdaddr);
+ ev.state = state;
+
+ ipc_send_notif(hal_ipc, HAL_SERVICE_ID_HANDSFREE,
+ HAL_EV_HANDSFREE_CONN_STATE, sizeof(ev), &ev);
+}
+
+static void set_audio_state(struct hf_device *dev, uint8_t state)
+{
+ struct hal_ev_handsfree_audio_state ev;
+ char address[18];
+
+ if (dev->audio_state == state)
+ return;
+
+ dev->audio_state = state;
+
+ ba2str(&dev->bdaddr, address);
+ DBG("device %s audio state %u", address, state);
+
+ bdaddr2android(&dev->bdaddr, ev.bdaddr);
+ ev.state = state;
+
+ ipc_send_notif(hal_ipc, HAL_SERVICE_ID_HANDSFREE,
+ HAL_EV_HANDSFREE_AUDIO_STATE, sizeof(ev), &ev);
+}
+
+static void init_codecs(struct hf_device *dev)
+{
+ memcpy(dev->codecs, codecs_defaults, sizeof(dev->codecs));
+
+ if (hfp_ag_features & HFP_AG_FEAT_CODEC)
+ dev->codecs[MSBC_OFFSET].local_supported = true;
+}
+
+static struct hf_device *device_create(const bdaddr_t *bdaddr)
+{
+ struct hf_device *dev;
+
+ dev = new0(struct hf_device, 1);
+
+ bacpy(&dev->bdaddr, bdaddr);
+ dev->setup_state = HAL_HANDSFREE_CALL_STATE_IDLE;
+ dev->state = HAL_EV_HANDSFREE_CONN_STATE_DISCONNECTED;
+ dev->audio_state = HAL_EV_HANDSFREE_AUDIO_STATE_DISCONNECTED;
+
+ memcpy(dev->inds, inds_defaults, sizeof(dev->inds));
+
+ init_codecs(dev);
+
+ queue_push_head(devices, dev);
+
+ return dev;
+}
+
+static void device_destroy(struct hf_device *dev)
+{
+ hfp_gw_unref(dev->gw);
+
+ if (dev->delay_sco)
+ g_source_remove(dev->delay_sco);
+
+ if (dev->audio_state == HAL_EV_HANDSFREE_AUDIO_STATE_CONNECTED)
+ bt_sco_disconnect(sco);
+
+ if (dev->ring)
+ g_source_remove(dev->ring);
+
+ g_free(dev->clip);
+
+ if (dev->call_hanging_up)
+ g_source_remove(dev->call_hanging_up);
+
+ set_audio_state(dev, HAL_EV_HANDSFREE_AUDIO_STATE_DISCONNECTED);
+ set_state(dev, HAL_EV_HANDSFREE_CONN_STATE_DISCONNECTED);
+
+ queue_remove(devices, dev);
+ free(dev);
+}
+
+static bool match_by_bdaddr(const void *data, const void *match_data)
+{
+ const struct hf_device *dev = data;
+ const bdaddr_t *addr = match_data;
+
+ return !bacmp(&dev->bdaddr, addr);
+}
+
+static struct hf_device *find_device(const bdaddr_t *bdaddr)
+{
+ if (!bacmp(bdaddr, BDADDR_ANY))
+ return queue_peek_head(devices);
+
+ return queue_find(devices, match_by_bdaddr, bdaddr);
+}
+
+static struct hf_device *get_device(const bdaddr_t *bdaddr)
+{
+ struct hf_device *dev;
+
+ dev = find_device(bdaddr);
+ if (dev)
+ return dev;
+
+ if (queue_length(devices) == max_hfp_clients)
+ return NULL;
+
+ return device_create(bdaddr);
+}
+
+static void disconnect_watch(void *user_data)
+{
+ struct hf_device *dev = user_data;
+
+ DBG("");
+
+ device_destroy(dev);
+}
+
+static void at_cmd_unknown(const char *command, void *user_data)
+{
+ struct hf_device *dev = user_data;
+ uint8_t buf[IPC_MTU];
+ struct hal_ev_handsfree_unknown_at *ev = (void *) buf;
+
+ bdaddr2android(&dev->bdaddr, ev->bdaddr);
+
+ /* copy while string including terminating NULL */
+ ev->len = strlen(command) + 1;
+
+ if (ev->len > IPC_MTU - sizeof(*ev)) {
+ hfp_gw_send_result(dev->gw, HFP_RESULT_ERROR);
+ return;
+ }
+
+ memcpy(ev->buf, command, ev->len);
+
+ ipc_send_notif(hal_ipc, HAL_SERVICE_ID_HANDSFREE,
+ HAL_EV_HANDSFREE_UNKNOWN_AT, sizeof(*ev) + ev->len, ev);
+}
+
+static void at_cmd_vgm(struct hfp_context *context,
+ enum hfp_gw_cmd_type type, void *user_data)
+{
+ struct hf_device *dev = user_data;
+ struct hal_ev_handsfree_volume ev;
+ unsigned int val;
+
+ DBG("");
+
+ switch (type) {
+ case HFP_GW_CMD_TYPE_SET:
+ if (!hfp_context_get_number(context, &val) || val > 15)
+ break;
+
+ if (hfp_context_has_next(context))
+ break;
+
+ ev.type = HAL_HANDSFREE_VOLUME_TYPE_MIC;
+ ev.volume = val;
+ bdaddr2android(&dev->bdaddr, ev.bdaddr);
+
+ ipc_send_notif(hal_ipc, HAL_SERVICE_ID_HANDSFREE,
+ HAL_EV_HANDSFREE_VOLUME, sizeof(ev), &ev);
+
+ /* Framework is not replying with result for AT+VGM */
+ hfp_gw_send_result(dev->gw, HFP_RESULT_OK);
+ return;
+ case HFP_GW_CMD_TYPE_READ:
+ case HFP_GW_CMD_TYPE_TEST:
+ case HFP_GW_CMD_TYPE_COMMAND:
+ break;
+ }
+
+ hfp_gw_send_result(dev->gw, HFP_RESULT_ERROR);
+}
+
+static void at_cmd_vgs(struct hfp_context *context,
+ enum hfp_gw_cmd_type type, void *user_data)
+{
+ struct hf_device *dev = user_data;
+ struct hal_ev_handsfree_volume ev;
+ unsigned int val;
+
+ DBG("");
+
+ switch (type) {
+ case HFP_GW_CMD_TYPE_SET:
+ if (!hfp_context_get_number(context, &val) || val > 15)
+ break;
+
+ if (hfp_context_has_next(context))
+ break;
+
+ ev.type = HAL_HANDSFREE_VOLUME_TYPE_SPEAKER;
+ ev.volume = val;
+ bdaddr2android(&dev->bdaddr, ev.bdaddr);
+
+ ipc_send_notif(hal_ipc, HAL_SERVICE_ID_HANDSFREE,
+ HAL_EV_HANDSFREE_VOLUME, sizeof(ev), &ev);
+
+ /* Framework is not replying with result for AT+VGS */
+ hfp_gw_send_result(dev->gw, HFP_RESULT_OK);
+ return;
+ case HFP_GW_CMD_TYPE_READ:
+ case HFP_GW_CMD_TYPE_TEST:
+ case HFP_GW_CMD_TYPE_COMMAND:
+ break;
+ }
+
+ hfp_gw_send_result(dev->gw, HFP_RESULT_ERROR);
+}
+
+static void at_cmd_cops(struct hfp_context *context,
+ enum hfp_gw_cmd_type type, void *user_data)
+{
+ struct hf_device *dev = user_data;
+ struct hal_ev_handsfree_cops ev;
+ unsigned int val;
+
+ switch (type) {
+ case HFP_GW_CMD_TYPE_SET:
+ if (!hfp_context_get_number(context, &val) || val != 3)
+ break;
+
+ if (!hfp_context_get_number(context, &val) || val != 0)
+ break;
+
+ if (hfp_context_has_next(context))
+ break;
+
+ hfp_gw_send_result(dev->gw, HFP_RESULT_OK);
+ return;
+ case HFP_GW_CMD_TYPE_READ:
+ bdaddr2android(&dev->bdaddr, ev.bdaddr);
+
+ ipc_send_notif(hal_ipc, HAL_SERVICE_ID_HANDSFREE,
+ HAL_EV_HANDSFREE_COPS, sizeof(ev), &ev);
+ return;
+ case HFP_GW_CMD_TYPE_TEST:
+ case HFP_GW_CMD_TYPE_COMMAND:
+ break;
+ }
+
+ hfp_gw_send_result(dev->gw, HFP_RESULT_ERROR);
+}
+
+static void at_cmd_bia(struct hfp_context *context,
+ enum hfp_gw_cmd_type type, void *user_data)
+{
+ struct hf_device *dev = user_data;
+ unsigned int val, i, def;
+ bool tmp[IND_COUNT];
+
+ DBG("");
+
+ switch (type) {
+ case HFP_GW_CMD_TYPE_SET:
+ for (i = 0; i < IND_COUNT; i++)
+ tmp[i] = dev->inds[i].active;
+
+ i = 0;
+
+ do {
+ def = (i < IND_COUNT) ? dev->inds[i].active : 0;
+
+ if (!hfp_context_get_number_default(context, &val, def))
+ goto failed;
+
+ if (val > 1)
+ goto failed;
+
+ if (i < IND_COUNT) {
+ tmp[i] = val || dev->inds[i].always_active;
+ i++;
+ }
+ } while (hfp_context_has_next(context));
+
+ for (i = 0; i < IND_COUNT; i++)
+ dev->inds[i].active = tmp[i];
+
+ hfp_gw_send_result(dev->gw, HFP_RESULT_OK);
+ return;
+ case HFP_GW_CMD_TYPE_TEST:
+ case HFP_GW_CMD_TYPE_READ:
+ case HFP_GW_CMD_TYPE_COMMAND:
+ break;
+ }
+
+failed:
+ hfp_gw_send_result(dev->gw, HFP_RESULT_ERROR);
+}
+
+static void at_cmd_a(struct hfp_context *context,
+ enum hfp_gw_cmd_type type, void *user_data)
+{
+ struct hf_device *dev = user_data;
+ struct hal_ev_handsfree_answer ev;
+
+ DBG("");
+
+ switch (type) {
+ case HFP_GW_CMD_TYPE_COMMAND:
+ if (hfp_context_has_next(context))
+ break;
+
+ bdaddr2android(&dev->bdaddr, ev.bdaddr);
+
+ ipc_send_notif(hal_ipc, HAL_SERVICE_ID_HANDSFREE,
+ HAL_EV_HANDSFREE_ANSWER, sizeof(ev), &ev);
+
+ /* Framework is not replying with result for ATA */
+ hfp_gw_send_result(dev->gw, HFP_RESULT_OK);
+ return;
+ case HFP_GW_CMD_TYPE_SET:
+ case HFP_GW_CMD_TYPE_READ:
+ case HFP_GW_CMD_TYPE_TEST:
+ break;
+ }
+
+ hfp_gw_send_result(dev->gw, HFP_RESULT_ERROR);
+}
+
+static void at_cmd_d(struct hfp_context *context,
+ enum hfp_gw_cmd_type type, void *user_data)
+{
+ struct hf_device *dev = user_data;
+ char buf[IPC_MTU];
+ struct hal_ev_handsfree_dial *ev = (void *) buf;
+ int cnt;
+
+ DBG("");
+
+ switch (type) {
+ case HFP_GW_CMD_TYPE_SET:
+ if (!hfp_context_get_unquoted_string(context,
+ (char *) ev->number, 255))
+ break;
+
+ bdaddr2android(&dev->bdaddr, ev->bdaddr);
+
+ ev->number_len = strlen((char *) ev->number);
+
+ if (ev->number[ev->number_len - 1] != ';')
+ break;
+
+ if (ev->number[0] == '>')
+ cnt = strspn((char *) ev->number + 1, "0123456789") + 1;
+ else
+ cnt = strspn((char *) ev->number, "0123456789ABC*#+");
+
+ if (cnt != ev->number_len - 1)
+ break;
+
+ ev->number_len++;
+
+ ipc_send_notif(hal_ipc, HAL_SERVICE_ID_HANDSFREE,
+ HAL_EV_HANDSFREE_DIAL,
+ sizeof(*ev) + ev->number_len, ev);
+ return;
+ case HFP_GW_CMD_TYPE_READ:
+ case HFP_GW_CMD_TYPE_TEST:
+ case HFP_GW_CMD_TYPE_COMMAND:
+ break;
+ }
+
+ hfp_gw_send_result(dev->gw, HFP_RESULT_ERROR);
+}
+
+static void at_cmd_ccwa(struct hfp_context *context,
+ enum hfp_gw_cmd_type type, void *user_data)
+{
+ struct hf_device *dev = user_data;
+ unsigned int val;
+
+ DBG("");
+
+ switch (type) {
+ case HFP_GW_CMD_TYPE_SET:
+ if (!hfp_context_get_number(context, &val) || val > 1)
+ break;
+
+ if (hfp_context_has_next(context))
+ break;
+
+ dev->ccwa_enabled = val;
+
+ hfp_gw_send_result(dev->gw, HFP_RESULT_OK);
+ return;
+ case HFP_GW_CMD_TYPE_READ:
+ case HFP_GW_CMD_TYPE_TEST:
+ case HFP_GW_CMD_TYPE_COMMAND:
+ break;
+ }
+
+ hfp_gw_send_result(dev->gw, HFP_RESULT_ERROR);
+}
+
+static void at_cmd_chup(struct hfp_context *context,
+ enum hfp_gw_cmd_type type, void *user_data)
+{
+ struct hf_device *dev = user_data;
+ struct hal_ev_handsfree_hangup ev;
+
+ DBG("");
+
+ switch (type) {
+ case HFP_GW_CMD_TYPE_COMMAND:
+ if (hfp_context_has_next(context))
+ break;
+
+ bdaddr2android(&dev->bdaddr, ev.bdaddr);
+
+ ipc_send_notif(hal_ipc, HAL_SERVICE_ID_HANDSFREE,
+ HAL_EV_HANDSFREE_HANGUP, sizeof(ev), &ev);
+
+ /* Framework is not replying with result for AT+CHUP */
+ hfp_gw_send_result(dev->gw, HFP_RESULT_OK);
+ return;
+ case HFP_GW_CMD_TYPE_READ:
+ case HFP_GW_CMD_TYPE_TEST:
+ case HFP_GW_CMD_TYPE_SET:
+ break;
+ }
+
+ hfp_gw_send_result(dev->gw, HFP_RESULT_ERROR);
+}
+
+static void at_cmd_clcc(struct hfp_context *context,
+ enum hfp_gw_cmd_type type, void *user_data)
+{
+ struct hf_device *dev = user_data;
+ struct hal_ev_handsfree_clcc ev;
+
+ DBG("");
+
+ switch (type) {
+ case HFP_GW_CMD_TYPE_COMMAND:
+ if (hfp_context_has_next(context))
+ break;
+
+ bdaddr2android(&dev->bdaddr, ev.bdaddr);
+
+ ipc_send_notif(hal_ipc, HAL_SERVICE_ID_HANDSFREE,
+ HAL_EV_HANDSFREE_CLCC, sizeof(ev), &ev);
+ return;
+ case HFP_GW_CMD_TYPE_READ:
+ case HFP_GW_CMD_TYPE_TEST:
+ case HFP_GW_CMD_TYPE_SET:
+ break;
+ }
+
+ hfp_gw_send_result(dev->gw, HFP_RESULT_ERROR);
+}
+
+static void at_cmd_cmee(struct hfp_context *context,
+ enum hfp_gw_cmd_type type, void *user_data)
+{
+ struct hf_device *dev = user_data;
+ unsigned int val;
+
+ DBG("");
+
+ switch (type) {
+ case HFP_GW_CMD_TYPE_SET:
+ if (!hfp_context_get_number(context, &val) || val > 1)
+ break;
+
+ if (hfp_context_has_next(context))
+ break;
+
+ dev->cmee_enabled = val;
+
+ hfp_gw_send_result(dev->gw, HFP_RESULT_OK);
+ return;
+ case HFP_GW_CMD_TYPE_READ:
+ case HFP_GW_CMD_TYPE_TEST:
+ case HFP_GW_CMD_TYPE_COMMAND:
+ break;
+ }
+
+ hfp_gw_send_result(dev->gw, HFP_RESULT_ERROR);
+}
+
+static void at_cmd_clip(struct hfp_context *context,
+ enum hfp_gw_cmd_type type, void *user_data)
+{
+ struct hf_device *dev = user_data;
+ unsigned int val;
+
+ DBG("");
+
+ switch (type) {
+ case HFP_GW_CMD_TYPE_SET:
+ if (!hfp_context_get_number(context, &val) || val > 1)
+ break;
+
+ if (hfp_context_has_next(context))
+ break;
+
+ dev->clip_enabled = val;
+
+ hfp_gw_send_result(dev->gw, HFP_RESULT_OK);
+ return;
+ case HFP_GW_CMD_TYPE_READ:
+ case HFP_GW_CMD_TYPE_TEST:
+ case HFP_GW_CMD_TYPE_COMMAND:
+ break;
+ }
+
+ hfp_gw_send_result(dev->gw, HFP_RESULT_ERROR);
+}
+
+static void at_cmd_vts(struct hfp_context *context,
+ enum hfp_gw_cmd_type type, void *user_data)
+{
+ struct hf_device *dev = user_data;
+ struct hal_ev_handsfree_dtmf ev;
+ char str[2];
+
+ DBG("");
+
+ switch (type) {
+ case HFP_GW_CMD_TYPE_SET:
+ if (!hfp_context_get_unquoted_string(context, str, 2))
+ break;
+
+ if (!((str[0] >= '0' && str[0] <= '9') ||
+ (str[0] >= 'A' && str[0] <= 'D') ||
+ str[0] == '*' || str[0] == '#'))
+ break;
+
+ if (hfp_context_has_next(context))
+ break;
+
+ bdaddr2android(&dev->bdaddr, ev.bdaddr);
+ ev.tone = str[0];
+
+ ipc_send_notif(hal_ipc, HAL_SERVICE_ID_HANDSFREE,
+ HAL_EV_HANDSFREE_DTMF, sizeof(ev), &ev);
+
+ /* Framework is not replying with result for AT+VTS */
+ hfp_gw_send_result(dev->gw, HFP_RESULT_OK);
+ return;
+ case HFP_GW_CMD_TYPE_READ:
+ case HFP_GW_CMD_TYPE_TEST:
+ case HFP_GW_CMD_TYPE_COMMAND:
+ break;
+ }
+
+ hfp_gw_send_result(dev->gw, HFP_RESULT_ERROR);
+}
+
+static void at_cmd_cnum(struct hfp_context *context,
+ enum hfp_gw_cmd_type type, void *user_data)
+{
+ struct hf_device *dev = user_data;
+ struct hal_ev_handsfree_cnum ev;
+
+ DBG("");
+
+ switch (type) {
+ case HFP_GW_CMD_TYPE_COMMAND:
+ if (hfp_context_has_next(context))
+ break;
+
+ bdaddr2android(&dev->bdaddr, ev.bdaddr);
+
+ ipc_send_notif(hal_ipc, HAL_SERVICE_ID_HANDSFREE,
+ HAL_EV_HANDSFREE_CNUM, sizeof(ev), &ev);
+ return;
+ case HFP_GW_CMD_TYPE_SET:
+ case HFP_GW_CMD_TYPE_READ:
+ case HFP_GW_CMD_TYPE_TEST:
+ break;
+ }
+
+ hfp_gw_send_result(dev->gw, HFP_RESULT_ERROR);
+}
+
+static void at_cmd_binp(struct hfp_context *context,
+ enum hfp_gw_cmd_type type, void *user_data)
+{
+ struct hf_device *dev = user_data;
+
+ DBG("");
+
+ /* TODO */
+
+ hfp_gw_send_result(dev->gw, HFP_RESULT_ERROR);
+}
+
+static void at_cmd_bldn(struct hfp_context *context,
+ enum hfp_gw_cmd_type type, void *user_data)
+{
+ struct hf_device *dev = user_data;
+ struct hal_ev_handsfree_dial ev;
+
+ DBG("");
+
+ switch (type) {
+ case HFP_GW_CMD_TYPE_COMMAND:
+ if (hfp_context_has_next(context))
+ break;
+
+ bdaddr2android(&dev->bdaddr, ev.bdaddr);
+ ev.number_len = 0;
+
+ ipc_send_notif(hal_ipc, HAL_SERVICE_ID_HANDSFREE,
+ HAL_EV_HANDSFREE_DIAL, sizeof(ev), &ev);
+ return;
+ case HFP_GW_CMD_TYPE_READ:
+ case HFP_GW_CMD_TYPE_TEST:
+ case HFP_GW_CMD_TYPE_SET:
+ break;
+ }
+
+ hfp_gw_send_result(dev->gw, HFP_RESULT_ERROR);
+}
+
+static void at_cmd_bvra(struct hfp_context *context,
+ enum hfp_gw_cmd_type type, void *user_data)
+{
+ struct hf_device *dev = user_data;
+ struct hal_ev_handsfree_vr_state ev;
+ unsigned int val;
+
+ DBG("");
+
+ switch (type) {
+ case HFP_GW_CMD_TYPE_SET:
+ if (!hfp_context_get_number(context, &val) || val > 1)
+ break;
+
+ if (hfp_context_has_next(context))
+ break;
+
+ if (val)
+ ev.state = HAL_HANDSFREE_VR_STARTED;
+ else
+ ev.state = HAL_HANDSFREE_VR_STOPPED;
+
+ bdaddr2android(&dev->bdaddr, ev.bdaddr);
+
+ ipc_send_notif(hal_ipc, HAL_SERVICE_ID_HANDSFREE,
+ HAL_EV_HANDSFREE_VR, sizeof(ev), &ev);
+ return;
+ case HFP_GW_CMD_TYPE_READ:
+ case HFP_GW_CMD_TYPE_TEST:
+ case HFP_GW_CMD_TYPE_COMMAND:
+ break;
+ }
+
+ hfp_gw_send_result(dev->gw, HFP_RESULT_ERROR);
+}
+
+static void at_cmd_nrec(struct hfp_context *context,
+ enum hfp_gw_cmd_type type, void *user_data)
+{
+ struct hf_device *dev = user_data;
+ struct hal_ev_handsfree_nrec ev;
+ unsigned int val;
+
+ DBG("");
+
+ switch (type) {
+ case HFP_GW_CMD_TYPE_SET:
+ /*
+ * Android HAL defines start and stop parameter for NREC
+ * callback, but spec allows HF to only disable AG's NREC
+ * feature for SLC duration. Follow spec here.
+ */
+ if (!hfp_context_get_number(context, &val) || val != 0)
+ break;
+
+ if (hfp_context_has_next(context))
+ break;
+
+ ev.nrec = HAL_HANDSFREE_NREC_STOP;
+ bdaddr2android(&dev->bdaddr, ev.bdaddr);
+
+ ipc_send_notif(hal_ipc, HAL_SERVICE_ID_HANDSFREE,
+ HAL_EV_HANDSFREE_NREC, sizeof(ev), &ev);
+
+ /* Framework is not replying with context for AT+NREC */
+ hfp_gw_send_result(dev->gw, HFP_RESULT_OK);
+ return;
+ case HFP_GW_CMD_TYPE_READ:
+ case HFP_GW_CMD_TYPE_TEST:
+ case HFP_GW_CMD_TYPE_COMMAND:
+ break;
+ }
+
+ hfp_gw_send_result(dev->gw, HFP_RESULT_ERROR);
+}
+
+static void at_cmd_bsir(struct hfp_context *context,
+ enum hfp_gw_cmd_type type, void *user_data)
+{
+ struct hf_device *dev = user_data;
+
+ DBG("");
+
+ /* TODO */
+
+ hfp_gw_send_result(dev->gw, HFP_RESULT_ERROR);
+}
+
+static void at_cmd_btrh(struct hfp_context *context,
+ enum hfp_gw_cmd_type type, void *user_data)
+{
+ struct hf_device *dev = user_data;
+
+ DBG("");
+
+ /* TODO */
+
+ hfp_gw_send_result(dev->gw, HFP_RESULT_ERROR);
+}
+
+static void disconnect_sco_cb(const bdaddr_t *addr)
+{
+ struct hf_device *dev;
+
+ DBG("");
+
+ dev = find_device(addr);
+ if (!dev) {
+ error("handsfree: Could not find device");
+ return;
+ }
+
+ set_audio_state(dev, HAL_EV_HANDSFREE_AUDIO_STATE_DISCONNECTED);
+}
+
+static void select_codec(struct hf_device *dev, uint8_t codec_type)
+{
+ uint8_t type = CODEC_ID_CVSD;
+ int i;
+
+ if (codec_type > 0) {
+ type = codec_type;
+ goto done;
+ }
+
+ for (i = CODECS_COUNT - 1; i >= CVSD_OFFSET; i--) {
+ if (!dev->codecs[i].local_supported)
+ continue;
+
+ if (!dev->codecs[i].remote_supported)
+ continue;
+
+ type = dev->codecs[i].type;
+ break;
+ }
+
+done:
+ dev->proposed_codec = type;
+
+ hfp_gw_send_info(dev->gw, "+BCS: %u", type);
+}
+
+static bool codec_negotiation_supported(struct hf_device *dev)
+{
+ return (dev->features & HFP_HF_FEAT_CODEC) &&
+ (hfp_ag_features & HFP_AG_FEAT_CODEC);
+}
+
+static void connect_sco_cb(enum sco_status status, const bdaddr_t *addr)
+{
+ struct hf_device *dev;
+
+ dev = find_device(addr);
+ if (!dev) {
+ error("handsfree: Connect sco failed, no device?");
+ return;
+ }
+
+ if (status == SCO_STATUS_OK) {
+ set_audio_state(dev, HAL_EV_HANDSFREE_AUDIO_STATE_CONNECTED);
+ return;
+ }
+
+ /* Try fallback to CVSD first */
+ if (codec_negotiation_supported(dev) &&
+ dev->negotiated_codec != CODEC_ID_CVSD) {
+ info("handsfree: trying fallback with CVSD");
+ select_codec(dev, CODEC_ID_CVSD);
+ return;
+ }
+
+ error("handsfree: audio connect failed");
+ set_audio_state(dev, HAL_EV_HANDSFREE_AUDIO_STATE_DISCONNECTED);
+}
+
+static bool connect_sco(struct hf_device *dev)
+{
+ uint16_t voice_settings;
+
+ if (codec_negotiation_supported(dev) &&
+ dev->negotiated_codec != CODEC_ID_CVSD)
+ voice_settings = BT_VOICE_TRANSPARENT;
+ else
+ voice_settings = BT_VOICE_CVSD_16BIT;
+
+ if (!bt_sco_connect(sco, &dev->bdaddr, voice_settings))
+ return false;
+
+ set_audio_state(dev, HAL_EV_HANDSFREE_AUDIO_STATE_CONNECTING);
+
+ return true;
+}
+
+static gboolean connect_sco_delayed(void *data)
+{
+ struct hf_device *dev = data;
+
+ DBG("");
+
+ dev->delay_sco = 0;
+
+ if (connect_sco(dev))
+ return FALSE;
+
+ /*
+ * we try connect to negotiated codec. If it fails, and it isn't
+ * CVSD codec, try connect CVSD
+ */
+ if (dev->negotiated_codec != CODEC_ID_CVSD)
+ select_codec(dev, CODEC_ID_CVSD);
+
+ return FALSE;
+}
+
+static void at_cmd_bcc(struct hfp_context *result, enum hfp_gw_cmd_type type,
+ void *user_data)
+{
+ struct hf_device *dev = user_data;
+
+ DBG("");
+
+ switch (type) {
+ case HFP_GW_CMD_TYPE_COMMAND:
+ if (!codec_negotiation_supported(dev))
+ break;
+
+ if (hfp_context_has_next(result))
+ break;
+
+ hfp_gw_send_result(dev->gw, HFP_RESULT_OK);
+
+ /* we haven't negotiated codec, start selection */
+ if (!dev->negotiated_codec) {
+ select_codec(dev, 0);
+ return;
+ }
+
+ /* Delay SCO connection so that OK response is send first */
+ if (dev->delay_sco == 0)
+ dev->delay_sco = g_idle_add(connect_sco_delayed, dev);
+ return;
+ case HFP_GW_CMD_TYPE_READ:
+ case HFP_GW_CMD_TYPE_TEST:
+ case HFP_GW_CMD_TYPE_SET:
+ break;
+ }
+
+ hfp_gw_send_result(dev->gw, HFP_RESULT_ERROR);
+}
+
+static void at_cmd_bcs(struct hfp_context *result, enum hfp_gw_cmd_type type,
+ void *user_data)
+{
+ struct hf_device *dev = user_data;
+ struct hal_ev_handsfree_wbs ev;
+ unsigned int val;
+
+ DBG("");
+
+ switch (type) {
+ case HFP_GW_CMD_TYPE_SET:
+ if (!hfp_context_get_number(result, &val))
+ break;
+
+ if (hfp_context_has_next(result))
+ break;
+
+ /* Remote replied with other codec. Reply with error */
+ if (dev->proposed_codec != val) {
+ dev->proposed_codec = 0;
+ break;
+ }
+
+ ev.wbs = val;
+ bdaddr2android(&dev->bdaddr, ev.bdaddr);
+
+ ipc_send_notif(hal_ipc, HAL_SERVICE_ID_HANDSFREE,
+ HAL_EV_HANDSFREE_WBS, sizeof(ev), &ev);
+
+ dev->proposed_codec = 0;
+ dev->negotiated_codec = val;
+
+ hfp_gw_send_result(dev->gw, HFP_RESULT_OK);
+
+ /*
+ * Delay SCO connection so that OK response is send first,
+ * then connect with negotiated parameters.
+ */
+ if (dev->delay_sco == 0)
+ dev->delay_sco = g_idle_add(connect_sco_delayed, dev);
+ return;
+ case HFP_GW_CMD_TYPE_READ:
+ case HFP_GW_CMD_TYPE_TEST:
+ case HFP_GW_CMD_TYPE_COMMAND:
+ break;
+ }
+
+ hfp_gw_send_result(dev->gw, HFP_RESULT_ERROR);
+}
+
+static void at_cmd_ckpd(struct hfp_context *result, enum hfp_gw_cmd_type type,
+ void *user_data)
+{
+ struct hf_device *dev = user_data;
+ struct hal_ev_handsfree_hsp_key_press ev;
+ unsigned int val;
+
+ DBG("");
+
+ switch (type) {
+ case HFP_GW_CMD_TYPE_SET:
+ if (!hfp_context_get_number(result, &val) || val != 200)
+ break;
+
+ if (hfp_context_has_next(result))
+ break;
+
+ bdaddr2android(&dev->bdaddr, ev.bdaddr);
+
+ ipc_send_notif(hal_ipc, HAL_SERVICE_ID_HANDSFREE,
+ HAL_EV_HANDSFREE_HSP_KEY_PRESS,
+ sizeof(ev), &ev);
+
+ hfp_gw_send_result(dev->gw, HFP_RESULT_OK);
+ return;
+ case HFP_GW_CMD_TYPE_READ:
+ case HFP_GW_CMD_TYPE_TEST:
+ case HFP_GW_CMD_TYPE_COMMAND:
+ break;
+ }
+
+ hfp_gw_send_result(dev->gw, HFP_RESULT_ERROR);
+}
+
+static void register_post_slc_at(struct hf_device *dev)
+{
+ hfp_gw_set_command_handler(dev->gw, at_cmd_unknown, dev, NULL);
+
+ if (dev->hsp) {
+ hfp_gw_register(dev->gw, at_cmd_ckpd, "+CKPD", dev, NULL);
+ hfp_gw_register(dev->gw, at_cmd_vgs, "+VGS", dev, NULL);
+ hfp_gw_register(dev->gw, at_cmd_vgm, "+VGM", dev, NULL);
+ return;
+ }
+
+ hfp_gw_register(dev->gw, at_cmd_a, "A", dev, NULL);
+ hfp_gw_register(dev->gw, at_cmd_d, "D", dev, NULL);
+ hfp_gw_register(dev->gw, at_cmd_ccwa, "+CCWA", dev, NULL);
+ hfp_gw_register(dev->gw, at_cmd_chup, "+CHUP", dev, NULL);
+ hfp_gw_register(dev->gw, at_cmd_clcc, "+CLCC", dev, NULL);
+ hfp_gw_register(dev->gw, at_cmd_cops, "+COPS", dev, NULL);
+ hfp_gw_register(dev->gw, at_cmd_cmee, "+CMEE", dev, NULL);
+ hfp_gw_register(dev->gw, at_cmd_clip, "+CLIP", dev, NULL);
+ hfp_gw_register(dev->gw, at_cmd_vts, "+VTS", dev, NULL);
+ hfp_gw_register(dev->gw, at_cmd_cnum, "+CNUM", dev, NULL);
+ hfp_gw_register(dev->gw, at_cmd_bia, "+BIA", dev, NULL);
+ hfp_gw_register(dev->gw, at_cmd_binp, "+BINP", dev, NULL);
+ hfp_gw_register(dev->gw, at_cmd_bldn, "+BLDN", dev, NULL);
+ hfp_gw_register(dev->gw, at_cmd_bvra, "+BVRA", dev, NULL);
+ hfp_gw_register(dev->gw, at_cmd_nrec, "+NREC", dev, NULL);
+ hfp_gw_register(dev->gw, at_cmd_vgs, "+VGS", dev, NULL);
+ hfp_gw_register(dev->gw, at_cmd_vgm, "+VGM", dev, NULL);
+ hfp_gw_register(dev->gw, at_cmd_bsir, "+BSIR", dev, NULL);
+ hfp_gw_register(dev->gw, at_cmd_btrh, "+BTRH", dev, NULL);
+ hfp_gw_register(dev->gw, at_cmd_bcc, "+BCC", dev, NULL);
+ hfp_gw_register(dev->gw, at_cmd_bcs, "+BCS", dev, NULL);
+}
+
+static void at_cmd_cmer(struct hfp_context *result, enum hfp_gw_cmd_type type,
+ void *user_data)
+{
+ struct hf_device *dev = user_data;
+ unsigned int val;
+
+ switch (type) {
+ case HFP_GW_CMD_TYPE_SET:
+ /* mode must be =3 */
+ if (!hfp_context_get_number(result, &val) || val != 3)
+ break;
+
+ /* keyp is don't care */
+ if (!hfp_context_get_number(result, &val))
+ break;
+
+ /* disp is don't care */
+ if (!hfp_context_get_number(result, &val))
+ break;
+
+ /* ind must be 0 or 1 */
+ if (!hfp_context_get_number(result, &val) || val > 1)
+ break;
+
+ dev->indicators_enabled = val;
+
+ /* skip bfr if present */
+ hfp_context_get_number(result, &val);
+
+ if (hfp_context_has_next(result))
+ break;
+
+ hfp_gw_send_result(dev->gw, HFP_RESULT_OK);
+
+ if (dev->features & HFP_HF_FEAT_3WAY)
+ return;
+
+ register_post_slc_at(dev);
+ set_state(dev, HAL_EV_HANDSFREE_CONN_STATE_SLC_CONNECTED);
+ return;
+ case HFP_GW_CMD_TYPE_TEST:
+ case HFP_GW_CMD_TYPE_READ:
+ case HFP_GW_CMD_TYPE_COMMAND:
+ break;
+ }
+
+ hfp_gw_send_result(dev->gw, HFP_RESULT_ERROR);
+
+ if (dev->state != HAL_EV_HANDSFREE_CONN_STATE_SLC_CONNECTED)
+ hfp_gw_disconnect(dev->gw);
+}
+
+static void at_cmd_cind(struct hfp_context *result, enum hfp_gw_cmd_type type,
+ void *user_data)
+{
+ struct hf_device *dev = user_data;
+ struct hal_ev_handsfree_cind ev;
+ char *buf, *ptr;
+ int len;
+ unsigned int i;
+
+ switch (type) {
+ case HFP_GW_CMD_TYPE_TEST:
+
+ /*
+ * If device supports Codec Negotiation, AT+BAC should be
+ * received first
+ */
+ if (codec_negotiation_supported(dev) &&
+ !dev->codecs[CVSD_OFFSET].remote_supported)
+ break;
+
+ len = strlen("+CIND:") + 1;
+
+ for (i = 0; i < IND_COUNT; i++) {
+ len += strlen("(\"\",(X,X)),");
+ len += strlen(dev->inds[i].name);
+ }
+
+ buf = g_malloc(len);
+
+ ptr = buf + sprintf(buf, "+CIND:");
+
+ for (i = 0; i < IND_COUNT; i++) {
+ ptr += sprintf(ptr, "(\"%s\",(%d%c%d)),",
+ dev->inds[i].name,
+ dev->inds[i].min,
+ dev->inds[i].max == 1 ? ',' : '-',
+ dev->inds[i].max);
+ }
+
+ ptr--;
+ *ptr = '\0';
+
+ hfp_gw_send_info(dev->gw, "%s", buf);
+ hfp_gw_send_result(dev->gw, HFP_RESULT_OK);
+
+ g_free(buf);
+ return;
+ case HFP_GW_CMD_TYPE_READ:
+ bdaddr2android(&dev->bdaddr, ev.bdaddr);
+
+ ipc_send_notif(hal_ipc, HAL_SERVICE_ID_HANDSFREE,
+ HAL_EV_HANDSFREE_CIND, sizeof(ev), &ev);
+ return;
+ case HFP_GW_CMD_TYPE_SET:
+ case HFP_GW_CMD_TYPE_COMMAND:
+ break;
+ }
+
+ hfp_gw_send_result(dev->gw, HFP_RESULT_ERROR);
+
+ if (dev->state != HAL_EV_HANDSFREE_CONN_STATE_SLC_CONNECTED)
+ hfp_gw_disconnect(dev->gw);
+}
+
+static void at_cmd_brsf(struct hfp_context *result, enum hfp_gw_cmd_type type,
+ void *user_data)
+{
+ struct hf_device *dev = user_data;
+ unsigned int feat;
+
+ switch (type) {
+ case HFP_GW_CMD_TYPE_SET:
+ if (!hfp_context_get_number(result, &feat))
+ break;
+
+ if (hfp_context_has_next(result))
+ break;
+
+ /* TODO verify features */
+ dev->features = feat;
+
+ hfp_gw_send_info(dev->gw, "+BRSF: %u", hfp_ag_features);
+ hfp_gw_send_result(dev->gw, HFP_RESULT_OK);
+ return;
+ case HFP_GW_CMD_TYPE_READ:
+ case HFP_GW_CMD_TYPE_TEST:
+ case HFP_GW_CMD_TYPE_COMMAND:
+ break;
+ }
+
+ hfp_gw_send_result(dev->gw, HFP_RESULT_ERROR);
+
+ if (dev->state != HAL_EV_HANDSFREE_CONN_STATE_SLC_CONNECTED)
+ hfp_gw_disconnect(dev->gw);
+}
+
+static void at_cmd_chld(struct hfp_context *result, enum hfp_gw_cmd_type type,
+ void *user_data)
+{
+ struct hf_device *dev = user_data;
+ struct hal_ev_handsfree_chld ev;
+ unsigned int val;
+
+ DBG("");
+
+ switch (type) {
+ case HFP_GW_CMD_TYPE_SET:
+ if (!hfp_context_get_number(result, &val) || val > 3)
+ break;
+
+ /* No ECC support */
+ if (hfp_context_has_next(result))
+ break;
+
+ /* value match HAL type */
+ ev.chld = val;
+ bdaddr2android(&dev->bdaddr, ev.bdaddr);
+
+ ipc_send_notif(hal_ipc, HAL_SERVICE_ID_HANDSFREE,
+ HAL_EV_HANDSFREE_CHLD, sizeof(ev), &ev);
+ return;
+ case HFP_GW_CMD_TYPE_TEST:
+ hfp_gw_send_info(dev->gw, "+CHLD: (%s)", HFP_AG_CHLD);
+ hfp_gw_send_result(dev->gw, HFP_RESULT_OK);
+
+ register_post_slc_at(dev);
+ set_state(dev, HAL_EV_HANDSFREE_CONN_STATE_SLC_CONNECTED);
+ return;
+ case HFP_GW_CMD_TYPE_READ:
+ case HFP_GW_CMD_TYPE_COMMAND:
+ break;
+ }
+
+ hfp_gw_send_result(dev->gw, HFP_RESULT_ERROR);
+
+ if (dev->state != HAL_EV_HANDSFREE_CONN_STATE_SLC_CONNECTED)
+ hfp_gw_disconnect(dev->gw);
+}
+
+static struct hfp_codec *find_codec_by_type(struct hf_device *dev, uint8_t type)
+{
+ int i;
+
+ for (i = 0; i < CODECS_COUNT; i++)
+ if (type == dev->codecs[i].type)
+ return &dev->codecs[i];
+
+ return NULL;
+}
+
+static void at_cmd_bac(struct hfp_context *result, enum hfp_gw_cmd_type type,
+ void *user_data)
+{
+ struct hf_device *dev = user_data;
+ unsigned int val;
+
+ DBG("");
+
+ switch (type) {
+ case HFP_GW_CMD_TYPE_SET:
+ if (!codec_negotiation_supported(dev))
+ goto failed;
+
+ /* set codecs to defaults */
+ init_codecs(dev);
+ dev->negotiated_codec = 0;
+
+ /*
+ * At least CVSD mandatory codec must exist
+ * HFP V1.6 4.34.1
+ */
+ if (!hfp_context_get_number(result, &val) ||
+ val != CODEC_ID_CVSD)
+ goto failed;
+
+ dev->codecs[CVSD_OFFSET].remote_supported = true;
+
+ if (hfp_context_get_number(result, &val)) {
+ if (val != CODEC_ID_MSBC)
+ goto failed;
+
+ dev->codecs[MSBC_OFFSET].remote_supported = true;
+ }
+
+ while (hfp_context_has_next(result)) {
+ struct hfp_codec *codec;
+
+ if (!hfp_context_get_number(result, &val))
+ goto failed;
+
+ codec = find_codec_by_type(dev, val);
+ if (!codec)
+ continue;
+
+ codec->remote_supported = true;
+ }
+
+ hfp_gw_send_result(dev->gw, HFP_RESULT_OK);
+
+ if (dev->proposed_codec)
+ select_codec(dev, 0);
+ return;
+ case HFP_GW_CMD_TYPE_TEST:
+ case HFP_GW_CMD_TYPE_READ:
+ case HFP_GW_CMD_TYPE_COMMAND:
+ break;
+ }
+
+failed:
+ hfp_gw_send_result(dev->gw, HFP_RESULT_ERROR);
+
+ if (dev->state != HAL_EV_HANDSFREE_CONN_STATE_SLC_CONNECTED)
+ hfp_gw_disconnect(dev->gw);
+}
+
+static void register_slc_at(struct hf_device *dev)
+{
+ hfp_gw_register(dev->gw, at_cmd_brsf, "+BRSF", dev, NULL);
+ hfp_gw_register(dev->gw, at_cmd_cind, "+CIND", dev, NULL);
+ hfp_gw_register(dev->gw, at_cmd_cmer, "+CMER", dev, NULL);
+ hfp_gw_register(dev->gw, at_cmd_chld, "+CHLD", dev, NULL);
+ hfp_gw_register(dev->gw, at_cmd_bac, "+BAC", dev, NULL);
+}
+
+static void connect_cb(GIOChannel *chan, GError *err, gpointer user_data)
+{
+ struct hf_device *dev = user_data;
+
+ DBG("");
+
+ if (err) {
+ error("handsfree: connect failed (%s)", err->message);
+ goto failed;
+ }
+
+ dev->gw = hfp_gw_new(g_io_channel_unix_get_fd(chan));
+ if (!dev->gw)
+ goto failed;
+
+ g_io_channel_set_close_on_unref(chan, FALSE);
+
+ hfp_gw_set_close_on_unref(dev->gw, true);
+ hfp_gw_set_disconnect_handler(dev->gw, disconnect_watch, dev, NULL);
+
+ if (dev->hsp) {
+ register_post_slc_at(dev);
+ set_state(dev, HAL_EV_HANDSFREE_CONN_STATE_CONNECTED);
+ set_state(dev, HAL_EV_HANDSFREE_CONN_STATE_SLC_CONNECTED);
+ return;
+ }
+
+ register_slc_at(dev);
+ set_state(dev, HAL_EV_HANDSFREE_CONN_STATE_CONNECTED);
+ return;
+
+failed:
+ g_io_channel_shutdown(chan, TRUE, NULL);
+ device_destroy(dev);
+}
+
+static void confirm_cb(GIOChannel *chan, gpointer data)
+{
+ char address[18];
+ bdaddr_t bdaddr;
+ GError *err = NULL;
+ struct hf_device *dev;
+
+ bt_io_get(chan, &err,
+ BT_IO_OPT_DEST, address,
+ BT_IO_OPT_DEST_BDADDR, &bdaddr,
+ BT_IO_OPT_INVALID);
+ if (err) {
+ error("handsfree: confirm failed (%s)", err->message);
+ g_error_free(err);
+ goto drop;
+ }
+
+ DBG("incoming connect from %s", address);
+
+ dev = get_device(&bdaddr);
+ if (!dev) {
+ error("handsfree: Failed to get device object for %s", address);
+ goto drop;
+ }
+
+ if (dev->state != HAL_EV_HANDSFREE_CONN_STATE_DISCONNECTED) {
+ info("handsfree: refusing connection from %s", address);
+ goto drop;
+ }
+
+ if (!bt_io_accept(chan, connect_cb, dev, NULL, NULL)) {
+ error("handsfree: failed to accept connection");
+ device_destroy(dev);
+ goto drop;
+ }
+
+ dev->hsp = GPOINTER_TO_INT(data);
+
+ set_state(dev, HAL_EV_HANDSFREE_CONN_STATE_CONNECTING);
+
+ return;
+
+drop:
+ g_io_channel_shutdown(chan, TRUE, NULL);
+}
+
+static void sdp_hsp_search_cb(sdp_list_t *recs, int err, gpointer data)
+{
+ struct hf_device *dev = data;
+ sdp_list_t *protos;
+ GError *gerr = NULL;
+ GIOChannel *io;
+ uuid_t class;
+ int channel;
+
+ DBG("");
+
+ if (err < 0) {
+ error("handsfree: unable to get SDP record: %s",
+ strerror(-err));
+ goto fail;
+ }
+
+ sdp_uuid16_create(&class, HEADSET_SVCLASS_ID);
+
+ /* Find record with proper service class */
+ for (; recs; recs = recs->next) {
+ sdp_record_t *rec = recs->data;
+
+ if (rec && !sdp_uuid_cmp(&rec->svclass, &class))
+ break;
+ }
+
+ if (!recs || !recs->data) {
+ info("handsfree: no valid HSP SDP records found");
+ goto fail;
+ }
+
+ if (sdp_get_access_protos(recs->data, &protos) < 0) {
+ error("handsfree: unable to get access protocols from record");
+ goto fail;
+ }
+
+ /* TODO read remote version? */
+ /* TODO read volume control support */
+
+ channel = sdp_get_proto_port(protos, RFCOMM_UUID);
+ sdp_list_foreach(protos, (sdp_list_func_t) sdp_list_free, NULL);
+ sdp_list_free(protos, NULL);
+ if (channel <= 0) {
+ error("handsfree: unable to get RFCOMM channel from record");
+ goto fail;
+ }
+
+ io = bt_io_connect(connect_cb, dev, NULL, &gerr,
+ BT_IO_OPT_SOURCE_BDADDR, &adapter_addr,
+ BT_IO_OPT_DEST_BDADDR, &dev->bdaddr,
+ BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_MEDIUM,
+ BT_IO_OPT_CHANNEL, channel,
+ BT_IO_OPT_INVALID);
+ if (!io) {
+ error("handsfree: unable to connect: %s", gerr->message);
+ g_error_free(gerr);
+ goto fail;
+ }
+
+ dev->hsp = true;
+
+ g_io_channel_unref(io);
+ return;
+
+fail:
+ device_destroy(dev);
+}
+
+static int sdp_search_hsp(struct hf_device *dev)
+{
+ uuid_t uuid;
+
+ sdp_uuid16_create(&uuid, HEADSET_SVCLASS_ID);
+
+ return bt_search_service(&adapter_addr, &dev->bdaddr, &uuid,
+ sdp_hsp_search_cb, dev, NULL, 0);
+}
+
+static void sdp_hfp_search_cb(sdp_list_t *recs, int err, gpointer data)
+{
+ struct hf_device *dev = data;
+ sdp_list_t *protos;
+ GError *gerr = NULL;
+ GIOChannel *io;
+ uuid_t class;
+ int channel;
+
+ DBG("");
+
+ if (err < 0) {
+ error("handsfree: unable to get SDP record: %s",
+ strerror(-err));
+ goto fail;
+ }
+
+ sdp_uuid16_create(&class, HANDSFREE_SVCLASS_ID);
+
+ /* Find record with proper service class */
+ for (; recs; recs = recs->next) {
+ sdp_record_t *rec = recs->data;
+
+ if (rec && !sdp_uuid_cmp(&rec->svclass, &class))
+ break;
+ }
+
+ if (!recs || !recs->data) {
+ info("handsfree: no HFP SDP records found, trying HSP");
+
+ if (sdp_search_hsp(dev) < 0) {
+ error("handsfree: HSP SDP search failed");
+ goto fail;
+ }
+
+ return;
+ }
+
+ if (sdp_get_access_protos(recs->data, &protos) < 0) {
+ error("handsfree: unable to get access protocols from record");
+ goto fail;
+ }
+
+ channel = sdp_get_proto_port(protos, RFCOMM_UUID);
+ sdp_list_foreach(protos, (sdp_list_func_t) sdp_list_free, NULL);
+ sdp_list_free(protos, NULL);
+ if (channel <= 0) {
+ error("handsfree: unable to get RFCOMM channel from record");
+ goto fail;
+ }
+
+ /* TODO read remote version? */
+
+ io = bt_io_connect(connect_cb, dev, NULL, &gerr,
+ BT_IO_OPT_SOURCE_BDADDR, &adapter_addr,
+ BT_IO_OPT_DEST_BDADDR, &dev->bdaddr,
+ BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_MEDIUM,
+ BT_IO_OPT_CHANNEL, channel,
+ BT_IO_OPT_INVALID);
+ if (!io) {
+ error("handsfree: unable to connect: %s", gerr->message);
+ g_error_free(gerr);
+ goto fail;
+ }
+
+ g_io_channel_unref(io);
+ return;
+
+fail:
+ device_destroy(dev);
+}
+
+static int sdp_search_hfp(struct hf_device *dev)
+{
+ uuid_t uuid;
+
+ sdp_uuid16_create(&uuid, HANDSFREE_SVCLASS_ID);
+
+ return bt_search_service(&adapter_addr, &dev->bdaddr, &uuid,
+ sdp_hfp_search_cb, dev, NULL, 0);
+}
+
+static void handle_connect(const void *buf, uint16_t len)
+{
+ const struct hal_cmd_handsfree_connect *cmd = buf;
+ struct hf_device *dev;
+ char addr[18];
+ uint8_t status;
+ bdaddr_t bdaddr;
+ int ret;
+
+ DBG("");
+
+ android2bdaddr(&cmd->bdaddr, &bdaddr);
+
+ dev = get_device(&bdaddr);
+ if (!dev) {
+ status = HAL_STATUS_FAILED;
+ goto failed;
+ }
+
+ if (dev->state != HAL_EV_HANDSFREE_CONN_STATE_DISCONNECTED) {
+ status = HAL_STATUS_FAILED;
+ goto failed;
+ }
+
+ ba2str(&bdaddr, addr);
+ DBG("connecting to %s", addr);
+
+ /* prefer HFP over HSP */
+ ret = hfp_server ? sdp_search_hfp(dev) : sdp_search_hsp(dev);
+ if (ret < 0) {
+ error("handsfree: SDP search failed");
+ device_destroy(dev);
+ status = HAL_STATUS_FAILED;
+ goto failed;
+ }
+
+ set_state(dev, HAL_EV_HANDSFREE_CONN_STATE_CONNECTING);
+
+ status = HAL_STATUS_SUCCESS;
+
+failed:
+ ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_HANDSFREE,
+ HAL_OP_HANDSFREE_CONNECT, status);
+}
+
+static void handle_disconnect(const void *buf, uint16_t len)
+{
+ const struct hal_cmd_handsfree_disconnect *cmd = buf;
+ struct hf_device *dev;
+ bdaddr_t bdaddr;
+ uint8_t status;
+
+ DBG("");
+
+ android2bdaddr(cmd->bdaddr, &bdaddr);
+
+ dev = find_device(&bdaddr);
+ if (!dev) {
+ status = HAL_STATUS_FAILED;
+ goto failed;
+ }
+
+ if (dev->state == HAL_EV_HANDSFREE_CONN_STATE_DISCONNECTED) {
+ status = HAL_STATUS_FAILED;
+ goto failed;
+ }
+
+ if (dev->state == HAL_EV_HANDSFREE_CONN_STATE_DISCONNECTING) {
+ status = HAL_STATUS_SUCCESS;
+ goto failed;
+ }
+
+ if (dev->state == HAL_EV_HANDSFREE_CONN_STATE_CONNECTING) {
+ device_destroy(dev);
+ } else {
+ set_state(dev, HAL_EV_HANDSFREE_CONN_STATE_DISCONNECTING);
+ hfp_gw_disconnect(dev->gw);
+ }
+
+ status = HAL_STATUS_SUCCESS;
+
+failed:
+ ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_HANDSFREE,
+ HAL_OP_HANDSFREE_DISCONNECT, status);
+}
+
+static bool disconnect_sco(struct hf_device *dev)
+{
+ if (dev->audio_state == HAL_EV_HANDSFREE_AUDIO_STATE_DISCONNECTED ||
+ dev->audio_state == HAL_EV_HANDSFREE_AUDIO_STATE_DISCONNECTING)
+ return false;
+
+ bt_sco_disconnect(sco);
+ set_audio_state(dev, HAL_EV_HANDSFREE_AUDIO_STATE_DISCONNECTING);
+
+ return true;
+}
+
+static bool connect_audio(struct hf_device *dev)
+{
+ if (dev->audio_state != HAL_EV_HANDSFREE_AUDIO_STATE_DISCONNECTED)
+ return false;
+
+ /* we haven't negotiated codec, start selection */
+ if (codec_negotiation_supported(dev) && !dev->negotiated_codec) {
+ select_codec(dev, 0);
+ return true;
+ }
+
+ return connect_sco(dev);
+}
+
+static void handle_connect_audio(const void *buf, uint16_t len)
+{
+ const struct hal_cmd_handsfree_connect_audio *cmd = buf;
+ struct hf_device *dev;
+ bdaddr_t bdaddr;
+ uint8_t status;
+
+ DBG("");
+
+ android2bdaddr(cmd->bdaddr, &bdaddr);
+
+ dev = find_device(&bdaddr);
+ if (!dev) {
+ status = HAL_STATUS_FAILED;
+ goto done;
+ }
+
+ if (dev->audio_state != HAL_EV_HANDSFREE_AUDIO_STATE_DISCONNECTED) {
+ status = HAL_STATUS_FAILED;
+ goto done;
+ }
+
+ status = connect_audio(dev) ? HAL_STATUS_SUCCESS : HAL_STATUS_FAILED;
+
+done:
+ ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_HANDSFREE,
+ HAL_OP_HANDSFREE_CONNECT_AUDIO, status);
+}
+
+static void handle_disconnect_audio(const void *buf, uint16_t len)
+{
+ const struct hal_cmd_handsfree_disconnect_audio *cmd = buf;
+ struct hf_device *dev;
+ bdaddr_t bdaddr;
+ uint8_t status;
+
+ DBG("");
+
+ android2bdaddr(cmd->bdaddr, &bdaddr);
+
+ dev = find_device(&bdaddr);
+ if (!dev) {
+ status = HAL_STATUS_FAILED;
+ goto done;
+ }
+
+ status = disconnect_sco(dev) ? HAL_STATUS_SUCCESS : HAL_STATUS_FAILED;
+
+done:
+ ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_HANDSFREE,
+ HAL_OP_HANDSFREE_DISCONNECT_AUDIO, status);
+}
+
+static void handle_start_vr(const void *buf, uint16_t len)
+{
+ const struct hal_cmd_handsfree_start_vr *cmd = buf;
+ struct hf_device *dev;
+ bdaddr_t bdaddr;
+ uint8_t status;
+
+ DBG("");
+
+ android2bdaddr(cmd->bdaddr, &bdaddr);
+
+ dev = find_device(&bdaddr);
+ if (!dev) {
+ status = HAL_STATUS_FAILED;
+ goto done;
+ }
+
+ if (dev->features & HFP_HF_FEAT_VR) {
+ hfp_gw_send_info(dev->gw, "+BVRA: 1");
+ status = HAL_STATUS_SUCCESS;
+ } else {
+ status = HAL_STATUS_FAILED;
+ }
+
+done:
+ ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_HANDSFREE,
+ HAL_OP_HANDSFREE_START_VR, status);
+}
+
+static void handle_stop_vr(const void *buf, uint16_t len)
+{
+ const struct hal_cmd_handsfree_stop_vr *cmd = buf;
+ struct hf_device *dev;
+ bdaddr_t bdaddr;
+ uint8_t status;
+
+ DBG("");
+
+ android2bdaddr(cmd->bdaddr, &bdaddr);
+
+ dev = find_device(&bdaddr);
+ if (!dev) {
+ status = HAL_STATUS_FAILED;
+ goto done;
+ }
+
+ if (dev->features & HFP_HF_FEAT_VR) {
+ hfp_gw_send_info(dev->gw, "+BVRA: 0");
+ status = HAL_STATUS_SUCCESS;
+ } else {
+ status = HAL_STATUS_FAILED;
+ }
+
+done:
+ ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_HANDSFREE,
+ HAL_OP_HANDSFREE_STOP_VR, status);
+}
+
+static void handle_volume_control(const void *buf, uint16_t len)
+{
+ const struct hal_cmd_handsfree_volume_control *cmd = buf;
+ struct hf_device *dev;
+ uint8_t status, volume;
+ bdaddr_t bdaddr;
+
+ DBG("type=%u volume=%u", cmd->type, cmd->volume);
+
+ android2bdaddr(cmd->bdaddr, &bdaddr);
+
+ dev = find_device(&bdaddr);
+ if (!dev) {
+ status = HAL_STATUS_FAILED;
+ goto done;
+ }
+
+ volume = cmd->volume > 15 ? 15 : cmd->volume;
+
+ switch (cmd->type) {
+ case HAL_HANDSFREE_VOLUME_TYPE_MIC:
+ hfp_gw_send_info(dev->gw, "+VGM: %u", volume);
+
+ status = HAL_STATUS_SUCCESS;
+ break;
+ case HAL_HANDSFREE_VOLUME_TYPE_SPEAKER:
+ hfp_gw_send_info(dev->gw, "+VGS: %u", volume);
+
+ status = HAL_STATUS_SUCCESS;
+ break;
+ default:
+ status = HAL_STATUS_FAILED;
+ break;
+ }
+
+done:
+ ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_HANDSFREE,
+ HAL_OP_HANDSFREE_VOLUME_CONTROL, status);
+}
+
+static void update_indicator(struct hf_device *dev, int ind, uint8_t val)
+{
+ DBG("ind=%u new=%u old=%u", ind, val, dev->inds[ind].val);
+
+ if (dev->inds[ind].val == val)
+ return;
+
+ dev->inds[ind].val = val;
+
+ if (!dev->indicators_enabled)
+ return;
+
+ if (!dev->inds[ind].active)
+ return;
+
+ /* indicator numbers in CIEV start from 1 */
+ hfp_gw_send_info(dev->gw, "+CIEV: %u,%u", ind + 1, val);
+}
+
+static void device_status_notif(void *data, void *user_data)
+{
+ struct hf_device *dev = data;
+ struct hal_cmd_handsfree_device_status_notif *cmd = user_data;
+
+ update_indicator(dev, IND_SERVICE, cmd->state);
+ update_indicator(dev, IND_ROAM, cmd->type);
+ update_indicator(dev, IND_SIGNAL, cmd->signal);
+ update_indicator(dev, IND_BATTCHG, cmd->battery);
+}
+
+static void handle_device_status_notif(const void *buf, uint16_t len)
+{
+ const struct hal_cmd_handsfree_device_status_notif *cmd = buf;
+ uint8_t status;
+
+ DBG("");
+
+ if (queue_isempty(devices)) {
+ status = HAL_STATUS_FAILED;
+ goto done;
+ }
+
+ /* Cast cmd to void as queue api needs that */
+ queue_foreach(devices, device_status_notif, (void *) cmd);
+
+ status = HAL_STATUS_SUCCESS;
+
+done:
+ ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_HANDSFREE,
+ HAL_OP_HANDSFREE_DEVICE_STATUS_NOTIF, status);
+}
+
+static void handle_cops(const void *buf, uint16_t len)
+{
+ const struct hal_cmd_handsfree_cops_response *cmd = buf;
+ struct hf_device *dev;
+ bdaddr_t bdaddr;
+ uint8_t status;
+
+ if (len != sizeof(*cmd) + cmd->len ||
+ (cmd->len != 0 && cmd->buf[cmd->len - 1] != '\0')) {
+ error("Invalid cops response command, terminating");
+ raise(SIGTERM);
+ return;
+ }
+
+ DBG("");
+
+ android2bdaddr(cmd->bdaddr, &bdaddr);
+
+ dev = find_device(&bdaddr);
+ if (!dev) {
+ status = HAL_STATUS_FAILED;
+ goto done;
+ }
+
+ hfp_gw_send_info(dev->gw, "+COPS: 0,0,\"%.16s\"",
+ cmd->len ? (char *) cmd->buf : "");
+
+ hfp_gw_send_result(dev->gw, HFP_RESULT_OK);
+
+ status = HAL_STATUS_SUCCESS;
+
+done:
+ ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_HANDSFREE,
+ HAL_OP_HANDSFREE_COPS_RESPONSE, status);
+}
+
+static unsigned int get_callsetup(uint8_t state)
+{
+ switch (state) {
+ case HAL_HANDSFREE_CALL_STATE_INCOMING:
+ return 1;
+ case HAL_HANDSFREE_CALL_STATE_DIALING:
+ return 2;
+ case HAL_HANDSFREE_CALL_STATE_ALERTING:
+ return 3;
+ default:
+ return 0;
+ }
+}
+
+static void handle_cind(const void *buf, uint16_t len)
+{
+ const struct hal_cmd_handsfree_cind_response *cmd = buf;
+ struct hf_device *dev;
+ bdaddr_t bdaddr;
+ uint8_t status;
+
+ DBG("");
+
+ android2bdaddr(cmd->bdaddr, &bdaddr);
+
+ dev = find_device(&bdaddr);
+ if (!dev) {
+ status = HAL_STATUS_FAILED;
+ goto done;
+ }
+
+ /* HAL doesn't provide indicators values so need to convert here */
+ dev->inds[IND_SERVICE].val = cmd->svc;
+ dev->inds[IND_CALL].val = !!(cmd->num_active + cmd->num_held);
+ dev->inds[IND_CALLSETUP].val = get_callsetup(cmd->state);
+ dev->inds[IND_CALLHELD].val = cmd->num_held ?
+ (cmd->num_active ? 1 : 2) : 0;
+ dev->inds[IND_SIGNAL].val = cmd->signal;
+ dev->inds[IND_ROAM].val = cmd->roam;
+ dev->inds[IND_BATTCHG].val = cmd->batt_chg;
+
+ /* Order must match indicators_defaults table */
+ hfp_gw_send_info(dev->gw, "+CIND: %u,%u,%u,%u,%u,%u,%u",
+ dev->inds[IND_SERVICE].val,
+ dev->inds[IND_CALL].val,
+ dev->inds[IND_CALLSETUP].val,
+ dev->inds[IND_CALLHELD].val,
+ dev->inds[IND_SIGNAL].val,
+ dev->inds[IND_ROAM].val,
+ dev->inds[IND_BATTCHG].val);
+
+ hfp_gw_send_result(dev->gw, HFP_RESULT_OK);
+
+ status = HAL_STATUS_SUCCESS;
+
+done:
+ ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_HANDSFREE,
+ HAL_OP_HANDSFREE_CIND_RESPONSE, status);
+}
+
+static void handle_formatted_at_resp(const void *buf, uint16_t len)
+{
+ const struct hal_cmd_handsfree_formatted_at_response *cmd = buf;
+ struct hf_device *dev;
+ bdaddr_t bdaddr;
+ uint8_t status;
+
+ DBG("");
+
+ if (len != sizeof(*cmd) + cmd->len ||
+ (cmd->len != 0 && cmd->buf[cmd->len - 1] != '\0')) {
+ error("Invalid formatted AT response command, terminating");
+ raise(SIGTERM);
+ return;
+ }
+
+ android2bdaddr(cmd->bdaddr, &bdaddr);
+
+ dev = find_device(&bdaddr);
+ if (!dev) {
+ status = HAL_STATUS_FAILED;
+ goto done;
+ }
+
+ hfp_gw_send_info(dev->gw, "%s", cmd->len ? (char *) cmd->buf : "");
+
+ status = HAL_STATUS_SUCCESS;
+
+done:
+ ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_HANDSFREE,
+ HAL_OP_HANDSFREE_FORMATTED_AT_RESPONSE, status);
+}
+
+static void handle_at_resp(const void *buf, uint16_t len)
+{
+ const struct hal_cmd_handsfree_at_response *cmd = buf;
+ struct hf_device *dev;
+ bdaddr_t bdaddr;
+ uint8_t status;
+
+ DBG("");
+
+ android2bdaddr(cmd->bdaddr, &bdaddr);
+
+ dev = find_device(&bdaddr);
+ if (!dev) {
+ status = HAL_STATUS_FAILED;
+ goto done;
+ }
+
+ if (cmd->response == HAL_HANDSFREE_AT_RESPONSE_OK)
+ hfp_gw_send_result(dev->gw, HFP_RESULT_OK);
+ else if (dev->cmee_enabled)
+ hfp_gw_send_error(dev->gw, cmd->error);
+ else
+ hfp_gw_send_result(dev->gw, HFP_RESULT_ERROR);
+
+ status = HAL_STATUS_SUCCESS;
+
+done:
+ ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_HANDSFREE,
+ HAL_OP_HANDSFREE_AT_RESPONSE, status);
+}
+
+static void handle_clcc_resp(const void *buf, uint16_t len)
+{
+ const struct hal_cmd_handsfree_clcc_response *cmd = buf;
+ struct hf_device *dev;
+ uint8_t status;
+ bdaddr_t bdaddr;
+ char *number;
+
+ if (len != sizeof(*cmd) + cmd->number_len || (cmd->number_len != 0 &&
+ cmd->number[cmd->number_len - 1] != '\0')) {
+ error("Invalid CLCC response command, terminating");
+ raise(SIGTERM);
+ return;
+ }
+
+ DBG("");
+
+ android2bdaddr(cmd->bdaddr, &bdaddr);
+
+ dev = find_device(&bdaddr);
+ if (!dev) {
+ status = HAL_STATUS_FAILED;
+ goto done;
+ }
+
+ if (!cmd->index) {
+ hfp_gw_send_result(dev->gw, HFP_RESULT_OK);
+
+ status = HAL_STATUS_SUCCESS;
+ goto done;
+ }
+
+ number = cmd->number_len ? (char *) cmd->number : "";
+
+ switch (cmd->state) {
+ case HAL_HANDSFREE_CALL_STATE_INCOMING:
+ case HAL_HANDSFREE_CALL_STATE_WAITING:
+ case HAL_HANDSFREE_CALL_STATE_ACTIVE:
+ case HAL_HANDSFREE_CALL_STATE_HELD:
+ case HAL_HANDSFREE_CALL_STATE_DIALING:
+ case HAL_HANDSFREE_CALL_STATE_ALERTING:
+ if (cmd->type == HAL_HANDSFREE_CALL_ADDRTYPE_INTERNATIONAL &&
+ number[0] != '+')
+ hfp_gw_send_info(dev->gw,
+ "+CLCC: %u,%u,%u,%u,%u,\"+%s\",%u",
+ cmd->index, cmd->dir, cmd->state,
+ cmd->mode, cmd->mpty, number,
+ cmd->type);
+ else
+ hfp_gw_send_info(dev->gw,
+ "+CLCC: %u,%u,%u,%u,%u,\"%s\",%u",
+ cmd->index, cmd->dir, cmd->state,
+ cmd->mode, cmd->mpty, number,
+ cmd->type);
+
+ status = HAL_STATUS_SUCCESS;
+ break;
+ case HAL_HANDSFREE_CALL_STATE_IDLE:
+ default:
+ status = HAL_STATUS_FAILED;
+ break;
+ }
+
+done:
+ ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_HANDSFREE,
+ HAL_OP_HANDSFREE_CLCC_RESPONSE, status);
+}
+
+static gboolean ring_cb(gpointer user_data)
+{
+ struct hf_device *dev = user_data;
+
+ hfp_gw_send_info(dev->gw, "RING");
+
+ if (dev->clip_enabled && dev->clip)
+ hfp_gw_send_info(dev->gw, "%s", dev->clip);
+
+ return TRUE;
+}
+
+static void phone_state_dialing(struct hf_device *dev, int num_active,
+ int num_held)
+{
+ if (dev->call_hanging_up) {
+ g_source_remove(dev->call_hanging_up);
+ dev->call_hanging_up = 0;
+ }
+
+ update_indicator(dev, IND_CALLSETUP, 2);
+
+ if (num_active == 0 && num_held > 0)
+ update_indicator(dev, IND_CALLHELD, 2);
+
+ if (dev->num_active == 0 && dev->num_held == 0)
+ connect_audio(dev);
+}
+
+static void phone_state_alerting(struct hf_device *dev, int num_active,
+ int num_held)
+{
+ if (dev->call_hanging_up) {
+ g_source_remove(dev->call_hanging_up);
+ dev->call_hanging_up = 0;
+ }
+
+ update_indicator(dev, IND_CALLSETUP, 3);
+}
+
+static void phone_state_waiting(struct hf_device *dev, int num_active,
+ int num_held, uint8_t type,
+ const uint8_t *number, int number_len)
+{
+ char *num;
+
+ if (!dev->ccwa_enabled)
+ return;
+
+ num = number_len ? (char *) number : "";
+
+ if (type == HAL_HANDSFREE_CALL_ADDRTYPE_INTERNATIONAL && num[0] != '+')
+ hfp_gw_send_info(dev->gw, "+CCWA: \"+%s\",%u", num, type);
+ else
+ hfp_gw_send_info(dev->gw, "+CCWA: \"%s\",%u", num, type);
+
+ update_indicator(dev, IND_CALLSETUP, 1);
+}
+
+static void phone_state_incoming(struct hf_device *dev, int num_active,
+ int num_held, uint8_t type,
+ const uint8_t *number, int number_len)
+{
+ char *num;
+
+ if (dev->setup_state == HAL_HANDSFREE_CALL_STATE_INCOMING) {
+ if (dev->num_active != num_active ||
+ dev->num_held != num_held) {
+ if (dev->num_active == num_held &&
+ dev->num_held == num_active)
+ return;
+ /*
+ * calls changed while waiting call ie. due to
+ * termination of active call
+ */
+ update_indicator(dev, IND_CALLHELD,
+ num_held ? (num_active ? 1 : 2) : 0);
+ update_indicator(dev, IND_CALL,
+ !!(num_active + num_held));
+ }
+
+ return;
+ }
+
+ if (dev->call_hanging_up)
+ return;
+
+ if (num_active > 0 || num_held > 0) {
+ phone_state_waiting(dev, num_active, num_held, type, number,
+ number_len);
+ return;
+ }
+
+ update_indicator(dev, IND_CALLSETUP, 1);
+
+ num = number_len ? (char *) number : "";
+
+ if (type == HAL_HANDSFREE_CALL_ADDRTYPE_INTERNATIONAL && num[0] != '+')
+ dev->clip = g_strdup_printf("+CLIP: \"+%s\",%u", num, type);
+ else
+ dev->clip = g_strdup_printf("+CLIP: \"%s\",%u", num, type);
+
+ /* send first RING */
+ ring_cb(dev);
+
+ dev->ring = g_timeout_add_seconds_full(G_PRIORITY_DEFAULT,
+ RING_TIMEOUT, ring_cb,
+ dev, NULL);
+ if (!dev->ring) {
+ g_free(dev->clip);
+ dev->clip = NULL;
+ }
+}
+
+static gboolean hang_up_cb(gpointer user_data)
+{
+ struct hf_device *dev = user_data;
+
+ DBG("");
+
+ dev->call_hanging_up = 0;
+
+ return FALSE;
+}
+
+static void phone_state_idle(struct hf_device *dev, int num_active,
+ int num_held)
+{
+ if (dev->ring) {
+ g_source_remove(dev->ring);
+ dev->ring = 0;
+
+ if (dev->clip) {
+ g_free(dev->clip);
+ dev->clip = NULL;
+ }
+ }
+
+ switch (dev->setup_state) {
+ case HAL_HANDSFREE_CALL_STATE_INCOMING:
+ if (num_active > dev->num_active) {
+ update_indicator(dev, IND_CALL, 1);
+
+ if (dev->num_active == 0 && dev->num_held == 0)
+ connect_audio(dev);
+ }
+
+ if (num_held >= dev->num_held && num_held != 0)
+ update_indicator(dev, IND_CALLHELD, 1);
+
+ update_indicator(dev, IND_CALLSETUP, 0);
+
+ if (num_active == 0 && num_held == 0 &&
+ num_active == dev->num_active &&
+ num_held == dev->num_held)
+ dev->call_hanging_up = g_timeout_add(800, hang_up_cb,
+ dev);
+ break;
+ case HAL_HANDSFREE_CALL_STATE_DIALING:
+ case HAL_HANDSFREE_CALL_STATE_ALERTING:
+ if (num_active > dev->num_active)
+ update_indicator(dev, IND_CALL, 1);
+
+ update_indicator(dev, IND_CALLHELD,
+ num_held ? (num_active ? 1 : 2) : 0);
+
+ update_indicator(dev, IND_CALLSETUP, 0);
+
+ /* disconnect SCO if we hang up while dialing or alerting */
+ if (num_active == 0 && num_held == 0)
+ disconnect_sco(dev);
+ break;
+ case HAL_HANDSFREE_CALL_STATE_IDLE:
+ if (dev->call_hanging_up) {
+ g_source_remove(dev->call_hanging_up);
+ dev->call_hanging_up = 0;
+ return;
+ }
+
+ /* check if calls swapped */
+ if (num_held != 0 && num_active != 0 &&
+ dev->num_active == num_held &&
+ dev->num_held == num_active) {
+ /* TODO better way for forcing indicator */
+ dev->inds[IND_CALLHELD].val = 0;
+ } else if ((num_active > 0 || num_held > 0) &&
+ dev->num_active == 0 &&
+ dev->num_held == 0) {
+ /*
+ * If number of active or held calls change but there
+ * was no call setup change this means that there were
+ * calls present when headset was connected.
+ */
+ connect_audio(dev);
+ } else if (num_active == 0 && num_held == 0) {
+ disconnect_sco(dev);
+ }
+
+ update_indicator(dev, IND_CALLHELD,
+ num_held ? (num_active ? 1 : 2) : 0);
+ update_indicator(dev, IND_CALL, !!(num_active + num_held));
+ update_indicator(dev, IND_CALLSETUP, 0);
+
+ /* If call was terminated due to carrier lost send NO CARRIER */
+ if (num_active == 0 && num_held == 0 &&
+ dev->inds[IND_SERVICE].val == 0 &&
+ (dev->num_active > 0 || dev->num_held > 0))
+ hfp_gw_send_info(dev->gw, "NO CARRIER");
+
+ break;
+ default:
+ DBG("unhandled state %u", dev->setup_state);
+ break;
+ }
+}
+
+static void phone_state_change(void *data, void *user_data)
+{
+ struct hf_device *dev = data;
+ struct hal_cmd_handsfree_phone_state_change *cmd = user_data;
+
+ switch (cmd->state) {
+ case HAL_HANDSFREE_CALL_STATE_DIALING:
+ phone_state_dialing(dev, cmd->num_active, cmd->num_held);
+ break;
+ case HAL_HANDSFREE_CALL_STATE_ALERTING:
+ phone_state_alerting(dev, cmd->num_active, cmd->num_held);
+ break;
+ case HAL_HANDSFREE_CALL_STATE_INCOMING:
+ phone_state_incoming(dev, cmd->num_active, cmd->num_held,
+ cmd->type, cmd->number,
+ cmd->number_len);
+ break;
+ case HAL_HANDSFREE_CALL_STATE_IDLE:
+ phone_state_idle(dev, cmd->num_active, cmd->num_held);
+ break;
+ default:
+ DBG("unhandled new state %u (current state %u)", cmd->state,
+ dev->setup_state);
+
+ return;
+ }
+
+ dev->num_active = cmd->num_active;
+ dev->num_held = cmd->num_held;
+ dev->setup_state = cmd->state;
+
+}
+
+static void handle_phone_state_change(const void *buf, uint16_t len)
+{
+ const struct hal_cmd_handsfree_phone_state_change *cmd = buf;
+ uint8_t status;
+
+ if (len != sizeof(*cmd) + cmd->number_len || (cmd->number_len != 0 &&
+ cmd->number[cmd->number_len - 1] != '\0')) {
+ error("Invalid phone state change command, terminating");
+ raise(SIGTERM);
+ return;
+ }
+
+ DBG("active=%u hold=%u state=%u", cmd->num_active, cmd->num_held,
+ cmd->state);
+
+ if (queue_isempty(devices)) {
+ status = HAL_STATUS_FAILED;
+ goto failed;
+ }
+
+ /* Cast cmd to void as queue api needs that */
+ queue_foreach(devices, phone_state_change, (void *) cmd);
+
+ status = HAL_STATUS_SUCCESS;
+
+failed:
+ ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_HANDSFREE,
+ HAL_OP_HANDSFREE_PHONE_STATE_CHANGE, status);
+}
+
+static void handle_configure_wbs(const void *buf, uint16_t len)
+{
+ const struct hal_cmd_handsfree_configure_wbs *cmd = buf;
+ struct hf_device *dev;
+ bdaddr_t bdaddr;
+ uint8_t status;
+
+ if (!(hfp_ag_features & HFP_AG_FEAT_CODEC)) {
+ status = HAL_STATUS_FAILED;
+ goto done;
+ }
+
+ android2bdaddr(cmd->bdaddr, &bdaddr);
+
+ dev = find_device(&bdaddr);
+ if (!dev) {
+ status = HAL_STATUS_FAILED;
+ goto done;
+ }
+
+ if (dev->audio_state != HAL_EV_HANDSFREE_AUDIO_STATE_DISCONNECTED) {
+ status = HAL_STATUS_FAILED;
+ goto done;
+ }
+
+ switch (cmd->config) {
+ case HAL_HANDSFREE_WBS_NO:
+ dev->codecs[MSBC_OFFSET].local_supported = false;
+ break;
+ case HAL_HANDSFREE_WBS_YES:
+ dev->codecs[MSBC_OFFSET].local_supported = true;
+ break;
+ case HAL_HANDSFREE_WBS_NONE:
+ /* TODO */
+ default:
+ status = HAL_STATUS_FAILED;
+ goto done;
+ }
+
+ /*
+ * cleanup negotiated codec if WBS support was changed, it will be
+ * renegotiated on next audio connection based on currently supported
+ * codecs
+ */
+ dev->negotiated_codec = 0;
+ status = HAL_STATUS_SUCCESS;
+
+done:
+ ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_HANDSFREE,
+ HAL_OP_HANDSFREE_CONFIGURE_WBS, status);
+}
+
+static const struct ipc_handler cmd_handlers[] = {
+ /* HAL_OP_HANDSFREE_CONNECT */
+ { handle_connect, false,
+ sizeof(struct hal_cmd_handsfree_connect) },
+ /* HAL_OP_HANDSFREE_DISCONNECT */
+ { handle_disconnect, false,
+ sizeof(struct hal_cmd_handsfree_disconnect) },
+ /* HAL_OP_HANDSFREE_CONNECT_AUDIO */
+ { handle_connect_audio, false,
+ sizeof(struct hal_cmd_handsfree_connect_audio) },
+ /* HAL_OP_HANDSFREE_DISCONNECT_AUDIO */
+ { handle_disconnect_audio, false,
+ sizeof(struct hal_cmd_handsfree_disconnect_audio) },
+ /* define HAL_OP_HANDSFREE_START_VR */
+ { handle_start_vr, false, sizeof(struct hal_cmd_handsfree_start_vr) },
+ /* define HAL_OP_HANDSFREE_STOP_VR */
+ { handle_stop_vr, false, sizeof(struct hal_cmd_handsfree_stop_vr) },
+ /* HAL_OP_HANDSFREE_VOLUME_CONTROL */
+ { handle_volume_control, false,
+ sizeof(struct hal_cmd_handsfree_volume_control) },
+ /* HAL_OP_HANDSFREE_DEVICE_STATUS_NOTIF */
+ { handle_device_status_notif, false,
+ sizeof(struct hal_cmd_handsfree_device_status_notif) },
+ /* HAL_OP_HANDSFREE_COPS_RESPONSE */
+ { handle_cops, true,
+ sizeof(struct hal_cmd_handsfree_cops_response) },
+ /* HAL_OP_HANDSFREE_CIND_RESPONSE */
+ { handle_cind, false,
+ sizeof(struct hal_cmd_handsfree_cind_response) },
+ /* HAL_OP_HANDSFREE_FORMATTED_AT_RESPONSE */
+ { handle_formatted_at_resp, true,
+ sizeof(struct hal_cmd_handsfree_formatted_at_response) },
+ /* HAL_OP_HANDSFREE_AT_RESPONSE */
+ { handle_at_resp, false,
+ sizeof(struct hal_cmd_handsfree_at_response) },
+ /* HAL_OP_HANDSFREE_CLCC_RESPONSE */
+ { handle_clcc_resp, true,
+ sizeof(struct hal_cmd_handsfree_clcc_response) },
+ /* HAL_OP_HANDSFREE_PHONE_STATE_CHANGE */
+ { handle_phone_state_change, true,
+ sizeof(struct hal_cmd_handsfree_phone_state_change) },
+ /* HAL_OP_HANDSFREE_CONFIGURE_WBS */
+ { handle_configure_wbs, false,
+ sizeof(struct hal_cmd_handsfree_configure_wbs) },
+};
+
+static sdp_record_t *headset_ag_record(void)
+{
+ sdp_list_t *svclass_id, *pfseq, *apseq, *root;
+ uuid_t root_uuid, svclass_uuid, ga_svclass_uuid;
+ uuid_t l2cap_uuid, rfcomm_uuid;
+ sdp_profile_desc_t profile;
+ sdp_list_t *aproto, *proto[2];
+ sdp_record_t *record;
+ sdp_data_t *channel;
+ uint8_t netid = 0x01;
+ sdp_data_t *network;
+ uint8_t ch = HSP_AG_CHANNEL;
+
+ record = sdp_record_alloc();
+ if (!record)
+ return NULL;
+
+ network = sdp_data_alloc(SDP_UINT8, &netid);
+ if (!network) {
+ sdp_record_free(record);
+ return NULL;
+ }
+
+ sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
+ root = sdp_list_append(NULL, &root_uuid);
+ sdp_set_browse_groups(record, root);
+
+ sdp_uuid16_create(&svclass_uuid, HEADSET_AGW_SVCLASS_ID);
+ svclass_id = sdp_list_append(NULL, &svclass_uuid);
+ sdp_uuid16_create(&ga_svclass_uuid, GENERIC_AUDIO_SVCLASS_ID);
+ svclass_id = sdp_list_append(svclass_id, &ga_svclass_uuid);
+ sdp_set_service_classes(record, svclass_id);
+
+ sdp_uuid16_create(&profile.uuid, HEADSET_PROFILE_ID);
+ profile.version = 0x0102;
+ pfseq = sdp_list_append(NULL, &profile);
+ sdp_set_profile_descs(record, pfseq);
+
+ sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID);
+ proto[0] = sdp_list_append(NULL, &l2cap_uuid);
+ apseq = sdp_list_append(NULL, proto[0]);
+
+ sdp_uuid16_create(&rfcomm_uuid, RFCOMM_UUID);
+ proto[1] = sdp_list_append(NULL, &rfcomm_uuid);
+ channel = sdp_data_alloc(SDP_UINT8, &ch);
+ proto[1] = sdp_list_append(proto[1], channel);
+ apseq = sdp_list_append(apseq, proto[1]);
+
+ aproto = sdp_list_append(NULL, apseq);
+ sdp_set_access_protos(record, aproto);
+
+ sdp_set_info_attr(record, "Voice Gateway", NULL, NULL);
+
+ sdp_attr_add(record, SDP_ATTR_EXTERNAL_NETWORK, network);
+
+ sdp_data_free(channel);
+ sdp_list_free(proto[0], NULL);
+ sdp_list_free(proto[1], NULL);
+ sdp_list_free(apseq, NULL);
+ sdp_list_free(pfseq, NULL);
+ sdp_list_free(aproto, NULL);
+ sdp_list_free(root, NULL);
+ sdp_list_free(svclass_id, NULL);
+
+ return record;
+}
+
+static bool confirm_sco_cb(const bdaddr_t *addr, uint16_t *voice_settings)
+{
+ char address[18];
+ struct hf_device *dev;
+
+ ba2str(addr, address);
+
+ DBG("incoming SCO connection from %s", address);
+
+ dev = find_device(addr);
+ if (!dev || dev->state != HAL_EV_HANDSFREE_CONN_STATE_SLC_CONNECTED) {
+ error("handsfree: audio connection from %s rejected", address);
+ return false;
+ }
+
+ /* If HF initiate SCO there must be no WBS used */
+ *voice_settings = 0;
+
+ set_audio_state(dev, HAL_EV_HANDSFREE_AUDIO_STATE_CONNECTING);
+ return true;
+}
+
+static bool enable_hsp_ag(void)
+{
+ sdp_record_t *rec;
+ GError *err = NULL;
+
+ DBG("");
+
+ hsp_server = bt_io_listen(NULL, confirm_cb, GINT_TO_POINTER(true),
+ NULL, &err,
+ BT_IO_OPT_SOURCE_BDADDR, &adapter_addr,
+ BT_IO_OPT_CHANNEL, HSP_AG_CHANNEL,
+ BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_MEDIUM,
+ BT_IO_OPT_INVALID);
+ if (!hsp_server) {
+ error("Failed to listen on Headset rfcomm: %s", err->message);
+ g_error_free(err);
+ return false;
+ }
+
+ rec = headset_ag_record();
+ if (!rec) {
+ error("Failed to allocate Headset record");
+ goto failed;
+ }
+
+ if (bt_adapter_add_record(rec, 0) < 0) {
+ error("Failed to register Headset record");
+ sdp_record_free(rec);
+ goto failed;
+ }
+
+ hsp_record_id = rec->handle;
+ return true;
+
+failed:
+ g_io_channel_shutdown(hsp_server, TRUE, NULL);
+ g_io_channel_unref(hsp_server);
+ hsp_server = NULL;
+
+ return false;
+}
+
+static void cleanup_hsp_ag(void)
+{
+ if (hsp_server) {
+ g_io_channel_shutdown(hsp_server, TRUE, NULL);
+ g_io_channel_unref(hsp_server);
+ hsp_server = NULL;
+ }
+
+ if (hsp_record_id > 0) {
+ bt_adapter_remove_record(hsp_record_id);
+ hsp_record_id = 0;
+ }
+}
+
+static sdp_record_t *hfp_ag_record(void)
+{
+ sdp_list_t *svclass_id, *pfseq, *apseq, *root;
+ uuid_t root_uuid, svclass_uuid, ga_svclass_uuid;
+ uuid_t l2cap_uuid, rfcomm_uuid;
+ sdp_profile_desc_t profile;
+ sdp_list_t *aproto, *proto[2];
+ sdp_record_t *record;
+ sdp_data_t *channel, *features;
+ uint8_t netid = 0x01;
+ uint16_t sdpfeat;
+ sdp_data_t *network;
+ uint8_t ch = HFP_AG_CHANNEL;
+
+ record = sdp_record_alloc();
+ if (!record)
+ return NULL;
+
+ network = sdp_data_alloc(SDP_UINT8, &netid);
+ if (!network) {
+ sdp_record_free(record);
+ return NULL;
+ }
+
+ sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
+ root = sdp_list_append(NULL, &root_uuid);
+ sdp_set_browse_groups(record, root);
+
+ sdp_uuid16_create(&svclass_uuid, HANDSFREE_AGW_SVCLASS_ID);
+ svclass_id = sdp_list_append(NULL, &svclass_uuid);
+ sdp_uuid16_create(&ga_svclass_uuid, GENERIC_AUDIO_SVCLASS_ID);
+ svclass_id = sdp_list_append(svclass_id, &ga_svclass_uuid);
+ sdp_set_service_classes(record, svclass_id);
+
+ sdp_uuid16_create(&profile.uuid, HANDSFREE_PROFILE_ID);
+ profile.version = 0x0106;
+ pfseq = sdp_list_append(NULL, &profile);
+ sdp_set_profile_descs(record, pfseq);
+
+ sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID);
+ proto[0] = sdp_list_append(NULL, &l2cap_uuid);
+ apseq = sdp_list_append(NULL, proto[0]);
+
+ sdp_uuid16_create(&rfcomm_uuid, RFCOMM_UUID);
+ proto[1] = sdp_list_append(NULL, &rfcomm_uuid);
+ channel = sdp_data_alloc(SDP_UINT8, &ch);
+ proto[1] = sdp_list_append(proto[1], channel);
+ apseq = sdp_list_append(apseq, proto[1]);
+
+ /* Codec Negotiation bit in SDP feature is different then in BRSF */
+ sdpfeat = hfp_ag_features & 0x0000003F;
+ if (hfp_ag_features & HFP_AG_FEAT_CODEC)
+ sdpfeat |= 0x00000020;
+ else
+ sdpfeat &= ~0x00000020;
+
+ features = sdp_data_alloc(SDP_UINT16, &sdpfeat);
+ sdp_attr_add(record, SDP_ATTR_SUPPORTED_FEATURES, features);
+
+ aproto = sdp_list_append(NULL, apseq);
+ sdp_set_access_protos(record, aproto);
+
+ sdp_set_info_attr(record, "Hands-Free Audio Gateway", NULL, NULL);
+
+ sdp_attr_add(record, SDP_ATTR_EXTERNAL_NETWORK, network);
+
+ sdp_data_free(channel);
+ sdp_list_free(proto[0], NULL);
+ sdp_list_free(proto[1], NULL);
+ sdp_list_free(apseq, NULL);
+ sdp_list_free(pfseq, NULL);
+ sdp_list_free(aproto, NULL);
+ sdp_list_free(root, NULL);
+ sdp_list_free(svclass_id, NULL);
+
+ return record;
+}
+
+static bool enable_hfp_ag(void)
+{
+ sdp_record_t *rec;
+ GError *err = NULL;
+
+ DBG("");
+
+ if (hfp_server)
+ return false;
+
+ hfp_server = bt_io_listen(NULL, confirm_cb, GINT_TO_POINTER(false),
+ NULL, &err,
+ BT_IO_OPT_SOURCE_BDADDR, &adapter_addr,
+ BT_IO_OPT_CHANNEL, HFP_AG_CHANNEL,
+ BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_MEDIUM,
+ BT_IO_OPT_INVALID);
+ if (!hfp_server) {
+ error("Failed to listen on Handsfree rfcomm: %s", err->message);
+ g_error_free(err);
+ return false;
+ }
+
+ rec = hfp_ag_record();
+ if (!rec) {
+ error("Failed to allocate Handsfree record");
+ goto failed;
+ }
+
+ if (bt_adapter_add_record(rec, 0) < 0) {
+ error("Failed to register Handsfree record");
+ sdp_record_free(rec);
+ goto failed;
+ }
+
+ hfp_record_id = rec->handle;
+ return true;
+
+failed:
+ g_io_channel_shutdown(hfp_server, TRUE, NULL);
+ g_io_channel_unref(hfp_server);
+ hfp_server = NULL;
+
+ return false;
+}
+
+static void cleanup_hfp_ag(void)
+{
+ if (hfp_server) {
+ g_io_channel_shutdown(hfp_server, TRUE, NULL);
+ g_io_channel_unref(hfp_server);
+ hfp_server = NULL;
+ }
+
+ if (hfp_record_id > 0) {
+ bt_adapter_remove_record(hfp_record_id);
+ hfp_record_id = 0;
+ }
+}
+
+static void bt_sco_get_fd(const void *buf, uint16_t len)
+{
+ const struct sco_cmd_get_fd *cmd = buf;
+ struct sco_rsp_get_fd rsp;
+ struct hf_device *dev;
+ bdaddr_t bdaddr;
+ int fd;
+
+ DBG("");
+
+ android2bdaddr(cmd->bdaddr, &bdaddr);
+
+ dev = find_device(&bdaddr);
+ if (!dev || !bt_sco_get_fd_and_mtu(sco, &fd, &rsp.mtu))
+ goto failed;
+
+ DBG("fd %d mtu %u", fd, rsp.mtu);
+
+ ipc_send_rsp_full(sco_ipc, SCO_SERVICE_ID, SCO_OP_GET_FD,
+ sizeof(rsp), &rsp, fd);
+
+ return;
+
+failed:
+ ipc_send_rsp(sco_ipc, SCO_SERVICE_ID, SCO_OP_STATUS, SCO_STATUS_FAILED);
+}
+
+static const struct ipc_handler sco_handlers[] = {
+ /* SCO_OP_GET_FD */
+ { bt_sco_get_fd, false, sizeof(struct sco_cmd_get_fd) }
+};
+
+static void bt_sco_unregister(void)
+{
+ DBG("");
+
+ ipc_cleanup(sco_ipc);
+ sco_ipc = NULL;
+}
+
+static bool bt_sco_register(ipc_disconnect_cb disconnect)
+{
+ DBG("");
+
+ sco_ipc = ipc_init(BLUEZ_SCO_SK_PATH, sizeof(BLUEZ_SCO_SK_PATH),
+ SCO_SERVICE_ID, false, disconnect, NULL);
+ if (!sco_ipc)
+ return false;
+
+ ipc_register(sco_ipc, SCO_SERVICE_ID, sco_handlers,
+ G_N_ELEMENTS(sco_handlers));
+
+ return true;
+}
+
+bool bt_handsfree_register(struct ipc *ipc, const bdaddr_t *addr, uint8_t mode,
+ int max_clients)
+{
+ DBG("mode 0x%x max_clients %d", mode, max_clients);
+
+ bacpy(&adapter_addr, addr);
+
+ if (max_clients < 1)
+ return false;
+
+ devices = queue_new();
+
+ if (!enable_hsp_ag())
+ goto failed_queue;
+
+ sco = bt_sco_new(addr);
+ if (!sco)
+ goto failed_hsp;
+
+ bt_sco_set_confirm_cb(sco, confirm_sco_cb);
+ bt_sco_set_connect_cb(sco, connect_sco_cb);
+ bt_sco_set_disconnect_cb(sco, disconnect_sco_cb);
+
+ if (mode == HAL_MODE_HANDSFREE_HSP_ONLY)
+ goto done;
+
+ hfp_ag_features = HFP_AG_FEATURES;
+
+ if (mode == HAL_MODE_HANDSFREE_HFP_WBS)
+ hfp_ag_features |= HFP_AG_FEAT_CODEC;
+
+ if (enable_hfp_ag())
+ goto done;
+
+ bt_sco_unref(sco);
+ sco = NULL;
+ hfp_ag_features = 0;
+failed_hsp:
+ cleanup_hsp_ag();
+failed_queue:
+ queue_destroy(devices, NULL);
+ devices = NULL;
+
+ return false;
+
+done:
+ hal_ipc = ipc;
+ ipc_register(hal_ipc, HAL_SERVICE_ID_HANDSFREE, cmd_handlers,
+ G_N_ELEMENTS(cmd_handlers));
+
+ bt_sco_register(NULL);
+
+ max_hfp_clients = max_clients;
+
+ return true;
+}
+
+void bt_handsfree_unregister(void)
+{
+ DBG("");
+
+ bt_sco_unregister();
+ ipc_unregister(hal_ipc, HAL_SERVICE_ID_HANDSFREE);
+ hal_ipc = NULL;
+
+ cleanup_hfp_ag();
+ cleanup_hsp_ag();
+ bt_sco_unref(sco);
+ sco = NULL;
+
+ hfp_ag_features = 0;
+
+ queue_destroy(devices, (queue_destroy_func_t) device_destroy);
+ devices = NULL;
+
+ max_hfp_clients = 0;
+}
diff --git a/repo/android/handsfree.h b/repo/android/handsfree.h
new file mode 100644
index 0000000..d4fd649
--- /dev/null
+++ b/repo/android/handsfree.h
@@ -0,0 +1,26 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2013-2014 Intel Corporation. All rights reserved.
+ *
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+bool bt_handsfree_register(struct ipc *ipc, const bdaddr_t *addr, uint8_t mode,
+ int max_clients);
+void bt_handsfree_unregister(void);
diff --git a/repo/android/hardware/audio.h b/repo/android/hardware/audio.h
new file mode 100644
index 0000000..3cc2be5
--- /dev/null
+++ b/repo/android/hardware/audio.h
@@ -0,0 +1,667 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+#ifndef ANDROID_AUDIO_HAL_INTERFACE_H
+#define ANDROID_AUDIO_HAL_INTERFACE_H
+
+#include <stdint.h>
+#include <strings.h>
+#include <sys/cdefs.h>
+#include <sys/types.h>
+
+#include <hardware/hardware.h>
+#include <system/audio.h>
+#include <hardware/audio_effect.h>
+
+__BEGIN_DECLS
+
+/**
+ * The id of this module
+ */
+#define AUDIO_HARDWARE_MODULE_ID "audio"
+
+/**
+ * Name of the audio devices to open
+ */
+#define AUDIO_HARDWARE_INTERFACE "audio_hw_if"
+
+
+/* Use version 0.1 to be compatible with first generation of audio hw module with version_major
+ * hardcoded to 1. No audio module API change.
+ */
+#define AUDIO_MODULE_API_VERSION_0_1 HARDWARE_MODULE_API_VERSION(0, 1)
+#define AUDIO_MODULE_API_VERSION_CURRENT AUDIO_MODULE_API_VERSION_0_1
+
+/* First generation of audio devices had version hardcoded to 0. all devices with versions < 1.0
+ * will be considered of first generation API.
+ */
+#define AUDIO_DEVICE_API_VERSION_0_0 HARDWARE_DEVICE_API_VERSION(0, 0)
+#define AUDIO_DEVICE_API_VERSION_1_0 HARDWARE_DEVICE_API_VERSION(1, 0)
+#define AUDIO_DEVICE_API_VERSION_2_0 HARDWARE_DEVICE_API_VERSION(2, 0)
+#define AUDIO_DEVICE_API_VERSION_3_0 HARDWARE_DEVICE_API_VERSION(3, 0)
+#define AUDIO_DEVICE_API_VERSION_CURRENT AUDIO_DEVICE_API_VERSION_3_0
+/* Minimal audio HAL version supported by the audio framework */
+#define AUDIO_DEVICE_API_VERSION_MIN AUDIO_DEVICE_API_VERSION_2_0
+
+/**
+ * List of known audio HAL modules. This is the base name of the audio HAL
+ * library composed of the "audio." prefix, one of the base names below and
+ * a suffix specific to the device.
+ * e.g: audio.primary.goldfish.so or audio.a2dp.default.so
+ */
+
+#define AUDIO_HARDWARE_MODULE_ID_PRIMARY "primary"
+#define AUDIO_HARDWARE_MODULE_ID_A2DP "a2dp"
+#define AUDIO_HARDWARE_MODULE_ID_USB "usb"
+#define AUDIO_HARDWARE_MODULE_ID_REMOTE_SUBMIX "r_submix"
+#define AUDIO_HARDWARE_MODULE_ID_CODEC_OFFLOAD "codec_offload"
+
+/**************************************/
+
+/**
+ * standard audio parameters that the HAL may need to handle
+ */
+
+/**
+ * audio device parameters
+ */
+
+/* BT SCO Noise Reduction + Echo Cancellation parameters */
+#define AUDIO_PARAMETER_KEY_BT_NREC "bt_headset_nrec"
+#define AUDIO_PARAMETER_VALUE_ON "on"
+#define AUDIO_PARAMETER_VALUE_OFF "off"
+
+/* TTY mode selection */
+#define AUDIO_PARAMETER_KEY_TTY_MODE "tty_mode"
+#define AUDIO_PARAMETER_VALUE_TTY_OFF "tty_off"
+#define AUDIO_PARAMETER_VALUE_TTY_VCO "tty_vco"
+#define AUDIO_PARAMETER_VALUE_TTY_HCO "tty_hco"
+#define AUDIO_PARAMETER_VALUE_TTY_FULL "tty_full"
+
+/* Hearing Aid Compatibility - Telecoil (HAC-T) mode on/off
+ Strings must be in sync with CallFeaturesSetting.java */
+#define AUDIO_PARAMETER_KEY_HAC "HACSetting"
+#define AUDIO_PARAMETER_VALUE_HAC_ON "ON"
+#define AUDIO_PARAMETER_VALUE_HAC_OFF "OFF"
+
+/* A2DP sink address set by framework */
+#define AUDIO_PARAMETER_A2DP_SINK_ADDRESS "a2dp_sink_address"
+
+/* A2DP source address set by framework */
+#define AUDIO_PARAMETER_A2DP_SOURCE_ADDRESS "a2dp_source_address"
+
+/* Screen state */
+#define AUDIO_PARAMETER_KEY_SCREEN_STATE "screen_state"
+
+/* Bluetooth SCO wideband */
+#define AUDIO_PARAMETER_KEY_BT_SCO_WB "bt_wbs"
+
+
+/**
+ * audio stream parameters
+ */
+
+#define AUDIO_PARAMETER_STREAM_ROUTING "routing" /* audio_devices_t */
+#define AUDIO_PARAMETER_STREAM_FORMAT "format" /* audio_format_t */
+#define AUDIO_PARAMETER_STREAM_CHANNELS "channels" /* audio_channel_mask_t */
+#define AUDIO_PARAMETER_STREAM_FRAME_COUNT "frame_count" /* size_t */
+#define AUDIO_PARAMETER_STREAM_INPUT_SOURCE "input_source" /* audio_source_t */
+#define AUDIO_PARAMETER_STREAM_SAMPLING_RATE "sampling_rate" /* uint32_t */
+
+#define AUDIO_PARAMETER_DEVICE_DISCONNECT "disconnect" /* audio_devices_t */
+
+/* Query supported formats. The response is a '|' separated list of strings from
+ * audio_format_t enum e.g: "sup_formats=AUDIO_FORMAT_PCM_16_BIT" */
+#define AUDIO_PARAMETER_STREAM_SUP_FORMATS "sup_formats"
+/* Query supported channel masks. The response is a '|' separated list of strings from
+ * audio_channel_mask_t enum e.g: "sup_channels=AUDIO_CHANNEL_OUT_STEREO|AUDIO_CHANNEL_OUT_MONO" */
+#define AUDIO_PARAMETER_STREAM_SUP_CHANNELS "sup_channels"
+/* Query supported sampling rates. The response is a '|' separated list of integer values e.g:
+ * "sup_sampling_rates=44100|48000" */
+#define AUDIO_PARAMETER_STREAM_SUP_SAMPLING_RATES "sup_sampling_rates"
+
+/* Get the HW synchronization source used for an output stream.
+ * Return a valid source (positive integer) or AUDIO_HW_SYNC_INVALID if an error occurs
+ * or no HW sync source is used. */
+#define AUDIO_PARAMETER_STREAM_HW_AV_SYNC "hw_av_sync"
+
+/**
+ * audio codec parameters
+ */
+
+#define AUDIO_OFFLOAD_CODEC_PARAMS "music_offload_codec_param"
+#define AUDIO_OFFLOAD_CODEC_BIT_PER_SAMPLE "music_offload_bit_per_sample"
+#define AUDIO_OFFLOAD_CODEC_BIT_RATE "music_offload_bit_rate"
+#define AUDIO_OFFLOAD_CODEC_AVG_BIT_RATE "music_offload_avg_bit_rate"
+#define AUDIO_OFFLOAD_CODEC_ID "music_offload_codec_id"
+#define AUDIO_OFFLOAD_CODEC_BLOCK_ALIGN "music_offload_block_align"
+#define AUDIO_OFFLOAD_CODEC_SAMPLE_RATE "music_offload_sample_rate"
+#define AUDIO_OFFLOAD_CODEC_ENCODE_OPTION "music_offload_encode_option"
+#define AUDIO_OFFLOAD_CODEC_NUM_CHANNEL "music_offload_num_channels"
+#define AUDIO_OFFLOAD_CODEC_DOWN_SAMPLING "music_offload_down_sampling"
+#define AUDIO_OFFLOAD_CODEC_DELAY_SAMPLES "delay_samples"
+#define AUDIO_OFFLOAD_CODEC_PADDING_SAMPLES "padding_samples"
+
+/**************************************/
+
+/* common audio stream parameters and operations */
+struct audio_stream {
+
+ /**
+ * Return the sampling rate in Hz - eg. 44100.
+ */
+ uint32_t (*get_sample_rate)(const struct audio_stream *stream);
+
+ /* currently unused - use set_parameters with key
+ * AUDIO_PARAMETER_STREAM_SAMPLING_RATE
+ */
+ int (*set_sample_rate)(struct audio_stream *stream, uint32_t rate);
+
+ /**
+ * Return size of input/output buffer in bytes for this stream - eg. 4800.
+ * It should be a multiple of the frame size. See also get_input_buffer_size.
+ */
+ size_t (*get_buffer_size)(const struct audio_stream *stream);
+
+ /**
+ * Return the channel mask -
+ * e.g. AUDIO_CHANNEL_OUT_STEREO or AUDIO_CHANNEL_IN_STEREO
+ */
+ audio_channel_mask_t (*get_channels)(const struct audio_stream *stream);
+
+ /**
+ * Return the audio format - e.g. AUDIO_FORMAT_PCM_16_BIT
+ */
+ audio_format_t (*get_format)(const struct audio_stream *stream);
+
+ /* currently unused - use set_parameters with key
+ * AUDIO_PARAMETER_STREAM_FORMAT
+ */
+ int (*set_format)(struct audio_stream *stream, audio_format_t format);
+
+ /**
+ * Put the audio hardware input/output into standby mode.
+ * Driver should exit from standby mode at the next I/O operation.
+ * Returns 0 on success and <0 on failure.
+ */
+ int (*standby)(struct audio_stream *stream);
+
+ /** dump the state of the audio input/output device */
+ int (*dump)(const struct audio_stream *stream, int fd);
+
+ /** Return the set of device(s) which this stream is connected to */
+ audio_devices_t (*get_device)(const struct audio_stream *stream);
+
+ /**
+ * Currently unused - set_device() corresponds to set_parameters() with key
+ * AUDIO_PARAMETER_STREAM_ROUTING for both input and output.
+ * AUDIO_PARAMETER_STREAM_INPUT_SOURCE is an additional information used by
+ * input streams only.
+ */
+ int (*set_device)(struct audio_stream *stream, audio_devices_t device);
+
+ /**
+ * set/get audio stream parameters. The function accepts a list of
+ * parameter key value pairs in the form: key1=value1;key2=value2;...
+ *
+ * Some keys are reserved for standard parameters (See AudioParameter class)
+ *
+ * If the implementation does not accept a parameter change while
+ * the output is active but the parameter is acceptable otherwise, it must
+ * return -ENOSYS.
+ *
+ * The audio flinger will put the stream in standby and then change the
+ * parameter value.
+ */
+ int (*set_parameters)(struct audio_stream *stream, const char *kv_pairs);
+
+ /*
+ * Returns a pointer to a heap allocated string. The caller is responsible
+ * for freeing the memory for it using free().
+ */
+ char * (*get_parameters)(const struct audio_stream *stream,
+ const char *keys);
+ int (*add_audio_effect)(const struct audio_stream *stream,
+ effect_handle_t effect);
+ int (*remove_audio_effect)(const struct audio_stream *stream,
+ effect_handle_t effect);
+};
+typedef struct audio_stream audio_stream_t;
+
+/* type of asynchronous write callback events. Mutually exclusive */
+typedef enum {
+ STREAM_CBK_EVENT_WRITE_READY, /* non blocking write completed */
+ STREAM_CBK_EVENT_DRAIN_READY /* drain completed */
+} stream_callback_event_t;
+
+typedef int (*stream_callback_t)(stream_callback_event_t event, void *param, void *cookie);
+
+/* type of drain requested to audio_stream_out->drain(). Mutually exclusive */
+typedef enum {
+ AUDIO_DRAIN_ALL, /* drain() returns when all data has been played */
+ AUDIO_DRAIN_EARLY_NOTIFY /* drain() returns a short time before all data
+ from the current track has been played to
+ give time for gapless track switch */
+} audio_drain_type_t;
+
+/**
+ * audio_stream_out is the abstraction interface for the audio output hardware.
+ *
+ * It provides information about various properties of the audio output
+ * hardware driver.
+ */
+
+struct audio_stream_out {
+ /**
+ * Common methods of the audio stream out. This *must* be the first member of audio_stream_out
+ * as users of this structure will cast a audio_stream to audio_stream_out pointer in contexts
+ * where it's known the audio_stream references an audio_stream_out.
+ */
+ struct audio_stream common;
+
+ /**
+ * Return the audio hardware driver estimated latency in milliseconds.
+ */
+ uint32_t (*get_latency)(const struct audio_stream_out *stream);
+
+ /**
+ * Use this method in situations where audio mixing is done in the
+ * hardware. This method serves as a direct interface with hardware,
+ * allowing you to directly set the volume as apposed to via the framework.
+ * This method might produce multiple PCM outputs or hardware accelerated
+ * codecs, such as MP3 or AAC.
+ */
+ int (*set_volume)(struct audio_stream_out *stream, float left, float right);
+
+ /**
+ * Write audio buffer to driver. Returns number of bytes written, or a
+ * negative status_t. If at least one frame was written successfully prior to the error,
+ * it is suggested that the driver return that successful (short) byte count
+ * and then return an error in the subsequent call.
+ *
+ * If set_callback() has previously been called to enable non-blocking mode
+ * the write() is not allowed to block. It must write only the number of
+ * bytes that currently fit in the driver/hardware buffer and then return
+ * this byte count. If this is less than the requested write size the
+ * callback function must be called when more space is available in the
+ * driver/hardware buffer.
+ */
+ ssize_t (*write)(struct audio_stream_out *stream, const void* buffer,
+ size_t bytes);
+
+ /* return the number of audio frames written by the audio dsp to DAC since
+ * the output has exited standby
+ */
+ int (*get_render_position)(const struct audio_stream_out *stream,
+ uint32_t *dsp_frames);
+
+ /**
+ * get the local time at which the next write to the audio driver will be presented.
+ * The units are microseconds, where the epoch is decided by the local audio HAL.
+ */
+ int (*get_next_write_timestamp)(const struct audio_stream_out *stream,
+ int64_t *timestamp);
+
+ /**
+ * set the callback function for notifying completion of non-blocking
+ * write and drain.
+ * Calling this function implies that all future write() and drain()
+ * must be non-blocking and use the callback to signal completion.
+ */
+ int (*set_callback)(struct audio_stream_out *stream,
+ stream_callback_t callback, void *cookie);
+
+ /**
+ * Notifies to the audio driver to stop playback however the queued buffers are
+ * retained by the hardware. Useful for implementing pause/resume. Empty implementation
+ * if not supported however should be implemented for hardware with non-trivial
+ * latency. In the pause state audio hardware could still be using power. User may
+ * consider calling suspend after a timeout.
+ *
+ * Implementation of this function is mandatory for offloaded playback.
+ */
+ int (*pause)(struct audio_stream_out* stream);
+
+ /**
+ * Notifies to the audio driver to resume playback following a pause.
+ * Returns error if called without matching pause.
+ *
+ * Implementation of this function is mandatory for offloaded playback.
+ */
+ int (*resume)(struct audio_stream_out* stream);
+
+ /**
+ * Requests notification when data buffered by the driver/hardware has
+ * been played. If set_callback() has previously been called to enable
+ * non-blocking mode, the drain() must not block, instead it should return
+ * quickly and completion of the drain is notified through the callback.
+ * If set_callback() has not been called, the drain() must block until
+ * completion.
+ * If type==AUDIO_DRAIN_ALL, the drain completes when all previously written
+ * data has been played.
+ * If type==AUDIO_DRAIN_EARLY_NOTIFY, the drain completes shortly before all
+ * data for the current track has played to allow time for the framework
+ * to perform a gapless track switch.
+ *
+ * Drain must return immediately on stop() and flush() call
+ *
+ * Implementation of this function is mandatory for offloaded playback.
+ */
+ int (*drain)(struct audio_stream_out* stream, audio_drain_type_t type );
+
+ /**
+ * Notifies to the audio driver to flush the queued data. Stream must already
+ * be paused before calling flush().
+ *
+ * Implementation of this function is mandatory for offloaded playback.
+ */
+ int (*flush)(struct audio_stream_out* stream);
+
+ /**
+ * Return a recent count of the number of audio frames presented to an external observer.
+ * This excludes frames which have been written but are still in the pipeline.
+ * The count is not reset to zero when output enters standby.
+ * Also returns the value of CLOCK_MONOTONIC as of this presentation count.
+ * The returned count is expected to be 'recent',
+ * but does not need to be the most recent possible value.
+ * However, the associated time should correspond to whatever count is returned.
+ * Example: assume that N+M frames have been presented, where M is a 'small' number.
+ * Then it is permissible to return N instead of N+M,
+ * and the timestamp should correspond to N rather than N+M.
+ * The terms 'recent' and 'small' are not defined.
+ * They reflect the quality of the implementation.
+ *
+ * 3.0 and higher only.
+ */
+ int (*get_presentation_position)(const struct audio_stream_out *stream,
+ uint64_t *frames, struct timespec *timestamp);
+
+};
+typedef struct audio_stream_out audio_stream_out_t;
+
+struct audio_stream_in {
+ /**
+ * Common methods of the audio stream in. This *must* be the first member of audio_stream_in
+ * as users of this structure will cast a audio_stream to audio_stream_in pointer in contexts
+ * where it's known the audio_stream references an audio_stream_in.
+ */
+ struct audio_stream common;
+
+ /** set the input gain for the audio driver. This method is for
+ * for future use */
+ int (*set_gain)(struct audio_stream_in *stream, float gain);
+
+ /** Read audio buffer in from audio driver. Returns number of bytes read, or a
+ * negative status_t. If at least one frame was read prior to the error,
+ * read should return that byte count and then return an error in the subsequent call.
+ */
+ ssize_t (*read)(struct audio_stream_in *stream, void* buffer,
+ size_t bytes);
+
+ /**
+ * Return the amount of input frames lost in the audio driver since the
+ * last call of this function.
+ * Audio driver is expected to reset the value to 0 and restart counting
+ * upon returning the current value by this function call.
+ * Such loss typically occurs when the user space process is blocked
+ * longer than the capacity of audio driver buffers.
+ *
+ * Unit: the number of input audio frames
+ */
+ uint32_t (*get_input_frames_lost)(struct audio_stream_in *stream);
+};
+typedef struct audio_stream_in audio_stream_in_t;
+
+/**
+ * return the frame size (number of bytes per sample).
+ *
+ * Deprecated: use audio_stream_out_frame_size() or audio_stream_in_frame_size() instead.
+ */
+__attribute__((__deprecated__))
+static inline size_t audio_stream_frame_size(const struct audio_stream *s)
+{
+ size_t chan_samp_sz;
+ audio_format_t format = s->get_format(s);
+
+ if (audio_is_linear_pcm(format)) {
+ chan_samp_sz = audio_bytes_per_sample(format);
+ return popcount(s->get_channels(s)) * chan_samp_sz;
+ }
+
+ return sizeof(int8_t);
+}
+
+/**
+ * return the frame size (number of bytes per sample) of an output stream.
+ */
+static inline size_t audio_stream_out_frame_size(const struct audio_stream_out *s)
+{
+ size_t chan_samp_sz;
+ audio_format_t format = s->common.get_format(&s->common);
+
+ if (audio_is_linear_pcm(format)) {
+ chan_samp_sz = audio_bytes_per_sample(format);
+ return audio_channel_count_from_out_mask(s->common.get_channels(&s->common)) * chan_samp_sz;
+ }
+
+ return sizeof(int8_t);
+}
+
+/**
+ * return the frame size (number of bytes per sample) of an input stream.
+ */
+static inline size_t audio_stream_in_frame_size(const struct audio_stream_in *s)
+{
+ size_t chan_samp_sz;
+ audio_format_t format = s->common.get_format(&s->common);
+
+ if (audio_is_linear_pcm(format)) {
+ chan_samp_sz = audio_bytes_per_sample(format);
+ return audio_channel_count_from_in_mask(s->common.get_channels(&s->common)) * chan_samp_sz;
+ }
+
+ return sizeof(int8_t);
+}
+
+/**********************************************************************/
+
+/**
+ * Every hardware module must have a data structure named HAL_MODULE_INFO_SYM
+ * and the fields of this data structure must begin with hw_module_t
+ * followed by module specific information.
+ */
+struct audio_module {
+ struct hw_module_t common;
+};
+
+struct audio_hw_device {
+ /**
+ * Common methods of the audio device. This *must* be the first member of audio_hw_device
+ * as users of this structure will cast a hw_device_t to audio_hw_device pointer in contexts
+ * where it's known the hw_device_t references an audio_hw_device.
+ */
+ struct hw_device_t common;
+
+ /**
+ * used by audio flinger to enumerate what devices are supported by
+ * each audio_hw_device implementation.
+ *
+ * Return value is a bitmask of 1 or more values of audio_devices_t
+ *
+ * NOTE: audio HAL implementations starting with
+ * AUDIO_DEVICE_API_VERSION_2_0 do not implement this function.
+ * All supported devices should be listed in audio_policy.conf
+ * file and the audio policy manager must choose the appropriate
+ * audio module based on information in this file.
+ */
+ uint32_t (*get_supported_devices)(const struct audio_hw_device *dev);
+
+ /**
+ * check to see if the audio hardware interface has been initialized.
+ * returns 0 on success, -ENODEV on failure.
+ */
+ int (*init_check)(const struct audio_hw_device *dev);
+
+ /** set the audio volume of a voice call. Range is between 0.0 and 1.0 */
+ int (*set_voice_volume)(struct audio_hw_device *dev, float volume);
+
+ /**
+ * set the audio volume for all audio activities other than voice call.
+ * Range between 0.0 and 1.0. If any value other than 0 is returned,
+ * the software mixer will emulate this capability.
+ */
+ int (*set_master_volume)(struct audio_hw_device *dev, float volume);
+
+ /**
+ * Get the current master volume value for the HAL, if the HAL supports
+ * master volume control. AudioFlinger will query this value from the
+ * primary audio HAL when the service starts and use the value for setting
+ * the initial master volume across all HALs. HALs which do not support
+ * this method may leave it set to NULL.
+ */
+ int (*get_master_volume)(struct audio_hw_device *dev, float *volume);
+
+ /**
+ * set_mode is called when the audio mode changes. AUDIO_MODE_NORMAL mode
+ * is for standard audio playback, AUDIO_MODE_RINGTONE when a ringtone is
+ * playing, and AUDIO_MODE_IN_CALL when a call is in progress.
+ */
+ int (*set_mode)(struct audio_hw_device *dev, audio_mode_t mode);
+
+ /* mic mute */
+ int (*set_mic_mute)(struct audio_hw_device *dev, bool state);
+ int (*get_mic_mute)(const struct audio_hw_device *dev, bool *state);
+
+ /* set/get global audio parameters */
+ int (*set_parameters)(struct audio_hw_device *dev, const char *kv_pairs);
+
+ /*
+ * Returns a pointer to a heap allocated string. The caller is responsible
+ * for freeing the memory for it using free().
+ */
+ char * (*get_parameters)(const struct audio_hw_device *dev,
+ const char *keys);
+
+ /* Returns audio input buffer size according to parameters passed or
+ * 0 if one of the parameters is not supported.
+ * See also get_buffer_size which is for a particular stream.
+ */
+ size_t (*get_input_buffer_size)(const struct audio_hw_device *dev,
+ const struct audio_config *config);
+
+ /** This method creates and opens the audio hardware output stream.
+ * The "address" parameter qualifies the "devices" audio device type if needed.
+ * The format format depends on the device type:
+ * - Bluetooth devices use the MAC address of the device in the form "00:11:22:AA:BB:CC"
+ * - USB devices use the ALSA card and device numbers in the form "card=X;device=Y"
+ * - Other devices may use a number or any other string.
+ */
+
+ int (*open_output_stream)(struct audio_hw_device *dev,
+ audio_io_handle_t handle,
+ audio_devices_t devices,
+ audio_output_flags_t flags,
+ struct audio_config *config,
+ struct audio_stream_out **stream_out,
+ const char *address);
+
+ void (*close_output_stream)(struct audio_hw_device *dev,
+ struct audio_stream_out* stream_out);
+
+ /** This method creates and opens the audio hardware input stream */
+ int (*open_input_stream)(struct audio_hw_device *dev,
+ audio_io_handle_t handle,
+ audio_devices_t devices,
+ struct audio_config *config,
+ struct audio_stream_in **stream_in,
+ audio_input_flags_t flags,
+ const char *address,
+ audio_source_t source);
+
+ void (*close_input_stream)(struct audio_hw_device *dev,
+ struct audio_stream_in *stream_in);
+
+ /** This method dumps the state of the audio hardware */
+ int (*dump)(const struct audio_hw_device *dev, int fd);
+
+ /**
+ * set the audio mute status for all audio activities. If any value other
+ * than 0 is returned, the software mixer will emulate this capability.
+ */
+ int (*set_master_mute)(struct audio_hw_device *dev, bool mute);
+
+ /**
+ * Get the current master mute status for the HAL, if the HAL supports
+ * master mute control. AudioFlinger will query this value from the primary
+ * audio HAL when the service starts and use the value for setting the
+ * initial master mute across all HALs. HALs which do not support this
+ * method may leave it set to NULL.
+ */
+ int (*get_master_mute)(struct audio_hw_device *dev, bool *mute);
+
+ /**
+ * Routing control
+ */
+
+ /* Creates an audio patch between several source and sink ports.
+ * The handle is allocated by the HAL and should be unique for this
+ * audio HAL module. */
+ int (*create_audio_patch)(struct audio_hw_device *dev,
+ unsigned int num_sources,
+ const struct audio_port_config *sources,
+ unsigned int num_sinks,
+ const struct audio_port_config *sinks,
+ audio_patch_handle_t *handle);
+
+ /* Release an audio patch */
+ int (*release_audio_patch)(struct audio_hw_device *dev,
+ audio_patch_handle_t handle);
+
+ /* Fills the list of supported attributes for a given audio port.
+ * As input, "port" contains the information (type, role, address etc...)
+ * needed by the HAL to identify the port.
+ * As output, "port" contains possible attributes (sampling rates, formats,
+ * channel masks, gain controllers...) for this port.
+ */
+ int (*get_audio_port)(struct audio_hw_device *dev,
+ struct audio_port *port);
+
+ /* Set audio port configuration */
+ int (*set_audio_port_config)(struct audio_hw_device *dev,
+ const struct audio_port_config *config);
+
+};
+typedef struct audio_hw_device audio_hw_device_t;
+
+/** convenience API for opening and closing a supported device */
+
+static inline int audio_hw_device_open(const struct hw_module_t* module,
+ struct audio_hw_device** device)
+{
+ return module->methods->open(module, AUDIO_HARDWARE_INTERFACE,
+ (struct hw_device_t**)device);
+}
+
+static inline int audio_hw_device_close(struct audio_hw_device* device)
+{
+ return device->common.close(&device->common);
+}
+
+
+__END_DECLS
+
+#endif // ANDROID_AUDIO_INTERFACE_H
diff --git a/repo/android/hardware/audio_effect.h b/repo/android/hardware/audio_effect.h
new file mode 100644
index 0000000..69ea896
--- /dev/null
+++ b/repo/android/hardware/audio_effect.h
@@ -0,0 +1,1010 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+#ifndef ANDROID_AUDIO_EFFECT_H
+#define ANDROID_AUDIO_EFFECT_H
+
+#include <errno.h>
+#include <stdint.h>
+#include <strings.h>
+#include <sys/cdefs.h>
+#include <sys/types.h>
+
+#include <system/audio.h>
+
+
+__BEGIN_DECLS
+
+
+/////////////////////////////////////////////////
+// Common Definitions
+/////////////////////////////////////////////////
+
+//
+//--- Effect descriptor structure effect_descriptor_t
+//
+
+// Unique effect ID (can be generated from the following site:
+// http://www.itu.int/ITU-T/asn1/uuid.html)
+// This format is used for both "type" and "uuid" fields of the effect descriptor structure.
+// - When used for effect type and the engine is implementing and effect corresponding to a standard
+// OpenSL ES interface, this ID must be the one defined in OpenSLES_IID.h for that interface.
+// - When used as uuid, it should be a unique UUID for this particular implementation.
+typedef struct effect_uuid_s {
+ uint32_t timeLow;
+ uint16_t timeMid;
+ uint16_t timeHiAndVersion;
+ uint16_t clockSeq;
+ uint8_t node[6];
+} effect_uuid_t;
+
+// Maximum length of character strings in structures defines by this API.
+#define EFFECT_STRING_LEN_MAX 64
+
+// NULL UUID definition (matches SL_IID_NULL_)
+#define EFFECT_UUID_INITIALIZER { 0xec7178ec, 0xe5e1, 0x4432, 0xa3f4, \
+ { 0x46, 0x57, 0xe6, 0x79, 0x52, 0x10 } }
+static const effect_uuid_t EFFECT_UUID_NULL_ = EFFECT_UUID_INITIALIZER;
+static const effect_uuid_t * const EFFECT_UUID_NULL = &EFFECT_UUID_NULL_;
+static const char * const EFFECT_UUID_NULL_STR = "ec7178ec-e5e1-4432-a3f4-4657e6795210";
+
+
+// The effect descriptor contains necessary information to facilitate the enumeration of the effect
+// engines present in a library.
+typedef struct effect_descriptor_s {
+ effect_uuid_t type; // UUID of to the OpenSL ES interface implemented by this effect
+ effect_uuid_t uuid; // UUID for this particular implementation
+ uint32_t apiVersion; // Version of the effect control API implemented
+ uint32_t flags; // effect engine capabilities/requirements flags (see below)
+ uint16_t cpuLoad; // CPU load indication (see below)
+ uint16_t memoryUsage; // Data Memory usage (see below)
+ char name[EFFECT_STRING_LEN_MAX]; // human readable effect name
+ char implementor[EFFECT_STRING_LEN_MAX]; // human readable effect implementor name
+} effect_descriptor_t;
+
+// CPU load and memory usage indication: each effect implementation must provide an indication of
+// its CPU and memory usage for the audio effect framework to limit the number of effects
+// instantiated at a given time on a given platform.
+// The CPU load is expressed in 0.1 MIPS units as estimated on an ARM9E core (ARMv5TE) with 0 WS.
+// The memory usage is expressed in KB and includes only dynamically allocated memory
+
+// Definitions for flags field of effect descriptor.
+// +---------------------------+-----------+-----------------------------------
+// | description | bits | values
+// +---------------------------+-----------+-----------------------------------
+// | connection mode | 0..2 | 0 insert: after track process
+// | | | 1 auxiliary: connect to track auxiliary
+// | | | output and use send level
+// | | | 2 replace: replaces track process function;
+// | | | must implement SRC, volume and mono to stereo.
+// | | | 3 pre processing: applied below audio HAL on input
+// | | | 4 post processing: applied below audio HAL on output
+// | | | 5 - 7 reserved
+// +---------------------------+-----------+-----------------------------------
+// | insertion preference | 3..5 | 0 none
+// | | | 1 first of the chain
+// | | | 2 last of the chain
+// | | | 3 exclusive (only effect in the insert chain)
+// | | | 4..7 reserved
+// +---------------------------+-----------+-----------------------------------
+// | Volume management | 6..8 | 0 none
+// | | | 1 implements volume control
+// | | | 2 requires volume indication
+// | | | 4 reserved
+// +---------------------------+-----------+-----------------------------------
+// | Device indication | 9..11 | 0 none
+// | | | 1 requires device updates
+// | | | 2, 4 reserved
+// +---------------------------+-----------+-----------------------------------
+// | Sample input mode | 12..13 | 1 direct: process() function or EFFECT_CMD_SET_CONFIG
+// | | | command must specify a buffer descriptor
+// | | | 2 provider: process() function uses the
+// | | | bufferProvider indicated by the
+// | | | EFFECT_CMD_SET_CONFIG command to request input.
+// | | | buffers.
+// | | | 3 both: both input modes are supported
+// +---------------------------+-----------+-----------------------------------
+// | Sample output mode | 14..15 | 1 direct: process() function or EFFECT_CMD_SET_CONFIG
+// | | | command must specify a buffer descriptor
+// | | | 2 provider: process() function uses the
+// | | | bufferProvider indicated by the
+// | | | EFFECT_CMD_SET_CONFIG command to request output
+// | | | buffers.
+// | | | 3 both: both output modes are supported
+// +---------------------------+-----------+-----------------------------------
+// | Hardware acceleration | 16..17 | 0 No hardware acceleration
+// | | | 1 non tunneled hw acceleration: the process() function
+// | | | reads the samples, send them to HW accelerated
+// | | | effect processor, reads back the processed samples
+// | | | and returns them to the output buffer.
+// | | | 2 tunneled hw acceleration: the process() function is
+// | | | transparent. The effect interface is only used to
+// | | | control the effect engine. This mode is relevant for
+// | | | global effects actually applied by the audio
+// | | | hardware on the output stream.
+// +---------------------------+-----------+-----------------------------------
+// | Audio Mode indication | 18..19 | 0 none
+// | | | 1 requires audio mode updates
+// | | | 2..3 reserved
+// +---------------------------+-----------+-----------------------------------
+// | Audio source indication | 20..21 | 0 none
+// | | | 1 requires audio source updates
+// | | | 2..3 reserved
+// +---------------------------+-----------+-----------------------------------
+// | Effect offload supported | 22 | 0 The effect cannot be offloaded to an audio DSP
+// | | | 1 The effect can be offloaded to an audio DSP
+// +---------------------------+-----------+-----------------------------------
+
+// Insert mode
+#define EFFECT_FLAG_TYPE_SHIFT 0
+#define EFFECT_FLAG_TYPE_SIZE 3
+#define EFFECT_FLAG_TYPE_MASK (((1 << EFFECT_FLAG_TYPE_SIZE) -1) \
+ << EFFECT_FLAG_TYPE_SHIFT)
+#define EFFECT_FLAG_TYPE_INSERT (0 << EFFECT_FLAG_TYPE_SHIFT)
+#define EFFECT_FLAG_TYPE_AUXILIARY (1 << EFFECT_FLAG_TYPE_SHIFT)
+#define EFFECT_FLAG_TYPE_REPLACE (2 << EFFECT_FLAG_TYPE_SHIFT)
+#define EFFECT_FLAG_TYPE_PRE_PROC (3 << EFFECT_FLAG_TYPE_SHIFT)
+#define EFFECT_FLAG_TYPE_POST_PROC (4 << EFFECT_FLAG_TYPE_SHIFT)
+
+// Insert preference
+#define EFFECT_FLAG_INSERT_SHIFT (EFFECT_FLAG_TYPE_SHIFT + EFFECT_FLAG_TYPE_SIZE)
+#define EFFECT_FLAG_INSERT_SIZE 3
+#define EFFECT_FLAG_INSERT_MASK (((1 << EFFECT_FLAG_INSERT_SIZE) -1) \
+ << EFFECT_FLAG_INSERT_SHIFT)
+#define EFFECT_FLAG_INSERT_ANY (0 << EFFECT_FLAG_INSERT_SHIFT)
+#define EFFECT_FLAG_INSERT_FIRST (1 << EFFECT_FLAG_INSERT_SHIFT)
+#define EFFECT_FLAG_INSERT_LAST (2 << EFFECT_FLAG_INSERT_SHIFT)
+#define EFFECT_FLAG_INSERT_EXCLUSIVE (3 << EFFECT_FLAG_INSERT_SHIFT)
+
+
+// Volume control
+#define EFFECT_FLAG_VOLUME_SHIFT (EFFECT_FLAG_INSERT_SHIFT + EFFECT_FLAG_INSERT_SIZE)
+#define EFFECT_FLAG_VOLUME_SIZE 3
+#define EFFECT_FLAG_VOLUME_MASK (((1 << EFFECT_FLAG_VOLUME_SIZE) -1) \
+ << EFFECT_FLAG_VOLUME_SHIFT)
+#define EFFECT_FLAG_VOLUME_CTRL (1 << EFFECT_FLAG_VOLUME_SHIFT)
+#define EFFECT_FLAG_VOLUME_IND (2 << EFFECT_FLAG_VOLUME_SHIFT)
+#define EFFECT_FLAG_VOLUME_NONE (0 << EFFECT_FLAG_VOLUME_SHIFT)
+
+// Device indication
+#define EFFECT_FLAG_DEVICE_SHIFT (EFFECT_FLAG_VOLUME_SHIFT + EFFECT_FLAG_VOLUME_SIZE)
+#define EFFECT_FLAG_DEVICE_SIZE 3
+#define EFFECT_FLAG_DEVICE_MASK (((1 << EFFECT_FLAG_DEVICE_SIZE) -1) \
+ << EFFECT_FLAG_DEVICE_SHIFT)
+#define EFFECT_FLAG_DEVICE_IND (1 << EFFECT_FLAG_DEVICE_SHIFT)
+#define EFFECT_FLAG_DEVICE_NONE (0 << EFFECT_FLAG_DEVICE_SHIFT)
+
+// Sample input modes
+#define EFFECT_FLAG_INPUT_SHIFT (EFFECT_FLAG_DEVICE_SHIFT + EFFECT_FLAG_DEVICE_SIZE)
+#define EFFECT_FLAG_INPUT_SIZE 2
+#define EFFECT_FLAG_INPUT_MASK (((1 << EFFECT_FLAG_INPUT_SIZE) -1) \
+ << EFFECT_FLAG_INPUT_SHIFT)
+#define EFFECT_FLAG_INPUT_DIRECT (1 << EFFECT_FLAG_INPUT_SHIFT)
+#define EFFECT_FLAG_INPUT_PROVIDER (2 << EFFECT_FLAG_INPUT_SHIFT)
+#define EFFECT_FLAG_INPUT_BOTH (3 << EFFECT_FLAG_INPUT_SHIFT)
+
+// Sample output modes
+#define EFFECT_FLAG_OUTPUT_SHIFT (EFFECT_FLAG_INPUT_SHIFT + EFFECT_FLAG_INPUT_SIZE)
+#define EFFECT_FLAG_OUTPUT_SIZE 2
+#define EFFECT_FLAG_OUTPUT_MASK (((1 << EFFECT_FLAG_OUTPUT_SIZE) -1) \
+ << EFFECT_FLAG_OUTPUT_SHIFT)
+#define EFFECT_FLAG_OUTPUT_DIRECT (1 << EFFECT_FLAG_OUTPUT_SHIFT)
+#define EFFECT_FLAG_OUTPUT_PROVIDER (2 << EFFECT_FLAG_OUTPUT_SHIFT)
+#define EFFECT_FLAG_OUTPUT_BOTH (3 << EFFECT_FLAG_OUTPUT_SHIFT)
+
+// Hardware acceleration mode
+#define EFFECT_FLAG_HW_ACC_SHIFT (EFFECT_FLAG_OUTPUT_SHIFT + EFFECT_FLAG_OUTPUT_SIZE)
+#define EFFECT_FLAG_HW_ACC_SIZE 2
+#define EFFECT_FLAG_HW_ACC_MASK (((1 << EFFECT_FLAG_HW_ACC_SIZE) -1) \
+ << EFFECT_FLAG_HW_ACC_SHIFT)
+#define EFFECT_FLAG_HW_ACC_SIMPLE (1 << EFFECT_FLAG_HW_ACC_SHIFT)
+#define EFFECT_FLAG_HW_ACC_TUNNEL (2 << EFFECT_FLAG_HW_ACC_SHIFT)
+
+// Audio mode indication
+#define EFFECT_FLAG_AUDIO_MODE_SHIFT (EFFECT_FLAG_HW_ACC_SHIFT + EFFECT_FLAG_HW_ACC_SIZE)
+#define EFFECT_FLAG_AUDIO_MODE_SIZE 2
+#define EFFECT_FLAG_AUDIO_MODE_MASK (((1 << EFFECT_FLAG_AUDIO_MODE_SIZE) -1) \
+ << EFFECT_FLAG_AUDIO_MODE_SHIFT)
+#define EFFECT_FLAG_AUDIO_MODE_IND (1 << EFFECT_FLAG_AUDIO_MODE_SHIFT)
+#define EFFECT_FLAG_AUDIO_MODE_NONE (0 << EFFECT_FLAG_AUDIO_MODE_SHIFT)
+
+// Audio source indication
+#define EFFECT_FLAG_AUDIO_SOURCE_SHIFT (EFFECT_FLAG_AUDIO_MODE_SHIFT + EFFECT_FLAG_AUDIO_MODE_SIZE)
+#define EFFECT_FLAG_AUDIO_SOURCE_SIZE 2
+#define EFFECT_FLAG_AUDIO_SOURCE_MASK (((1 << EFFECT_FLAG_AUDIO_SOURCE_SIZE) -1) \
+ << EFFECT_FLAG_AUDIO_SOURCE_SHIFT)
+#define EFFECT_FLAG_AUDIO_SOURCE_IND (1 << EFFECT_FLAG_AUDIO_SOURCE_SHIFT)
+#define EFFECT_FLAG_AUDIO_SOURCE_NONE (0 << EFFECT_FLAG_AUDIO_SOURCE_SHIFT)
+
+// Effect offload indication
+#define EFFECT_FLAG_OFFLOAD_SHIFT (EFFECT_FLAG_AUDIO_SOURCE_SHIFT + \
+ EFFECT_FLAG_AUDIO_SOURCE_SIZE)
+#define EFFECT_FLAG_OFFLOAD_SIZE 1
+#define EFFECT_FLAG_OFFLOAD_MASK (((1 << EFFECT_FLAG_OFFLOAD_SIZE) -1) \
+ << EFFECT_FLAG_OFFLOAD_SHIFT)
+#define EFFECT_FLAG_OFFLOAD_SUPPORTED (1 << EFFECT_FLAG_OFFLOAD_SHIFT)
+
+#define EFFECT_MAKE_API_VERSION(M, m) (((M)<<16) | ((m) & 0xFFFF))
+#define EFFECT_API_VERSION_MAJOR(v) ((v)>>16)
+#define EFFECT_API_VERSION_MINOR(v) ((m) & 0xFFFF)
+
+
+
+/////////////////////////////////////////////////
+// Effect control interface
+/////////////////////////////////////////////////
+
+// Effect control interface version 2.0
+#define EFFECT_CONTROL_API_VERSION EFFECT_MAKE_API_VERSION(2,0)
+
+// Effect control interface structure: effect_interface_s
+// The effect control interface is exposed by each effect engine implementation. It consists of
+// a set of functions controlling the configuration, activation and process of the engine.
+// The functions are grouped in a structure of type effect_interface_s.
+//
+// Effect control interface handle: effect_handle_t
+// The effect_handle_t serves two purposes regarding the implementation of the effect engine:
+// - 1 it is the address of a pointer to an effect_interface_s structure where the functions
+// of the effect control API for a particular effect are located.
+// - 2 it is the address of the context of a particular effect instance.
+// A typical implementation in the effect library would define a structure as follows:
+// struct effect_module_s {
+// const struct effect_interface_s *itfe;
+// effect_config_t config;
+// effect_context_t context;
+// }
+// The implementation of EffectCreate() function would then allocate a structure of this
+// type and return its address as effect_handle_t
+typedef struct effect_interface_s **effect_handle_t;
+
+
+// Forward definition of type audio_buffer_t
+typedef struct audio_buffer_s audio_buffer_t;
+
+
+
+
+
+
+// Effect control interface definition
+struct effect_interface_s {
+ ////////////////////////////////////////////////////////////////////////////////
+ //
+ // Function: process
+ //
+ // Description: Effect process function. Takes input samples as specified
+ // (count and location) in input buffer descriptor and output processed
+ // samples as specified in output buffer descriptor. If the buffer descriptor
+ // is not specified the function must use either the buffer or the
+ // buffer provider function installed by the EFFECT_CMD_SET_CONFIG command.
+ // The effect framework will call the process() function after the EFFECT_CMD_ENABLE
+ // command is received and until the EFFECT_CMD_DISABLE is received. When the engine
+ // receives the EFFECT_CMD_DISABLE command it should turn off the effect gracefully
+ // and when done indicate that it is OK to stop calling the process() function by
+ // returning the -ENODATA status.
+ //
+ // NOTE: the process() function implementation should be "real-time safe" that is
+ // it should not perform blocking calls: malloc/free, sleep, read/write/open/close,
+ // pthread_cond_wait/pthread_mutex_lock...
+ //
+ // Input:
+ // self: handle to the effect interface this function
+ // is called on.
+ // inBuffer: buffer descriptor indicating where to read samples to process.
+ // If NULL, use the configuration passed by EFFECT_CMD_SET_CONFIG command.
+ //
+ // outBuffer: buffer descriptor indicating where to write processed samples.
+ // If NULL, use the configuration passed by EFFECT_CMD_SET_CONFIG command.
+ //
+ // Output:
+ // returned value: 0 successful operation
+ // -ENODATA the engine has finished the disable phase and the framework
+ // can stop calling process()
+ // -EINVAL invalid interface handle or
+ // invalid input/output buffer description
+ ////////////////////////////////////////////////////////////////////////////////
+ int32_t (*process)(effect_handle_t self,
+ audio_buffer_t *inBuffer,
+ audio_buffer_t *outBuffer);
+ ////////////////////////////////////////////////////////////////////////////////
+ //
+ // Function: command
+ //
+ // Description: Send a command and receive a response to/from effect engine.
+ //
+ // Input:
+ // self: handle to the effect interface this function
+ // is called on.
+ // cmdCode: command code: the command can be a standardized command defined in
+ // effect_command_e (see below) or a proprietary command.
+ // cmdSize: size of command in bytes
+ // pCmdData: pointer to command data
+ // pReplyData: pointer to reply data
+ //
+ // Input/Output:
+ // replySize: maximum size of reply data as input
+ // actual size of reply data as output
+ //
+ // Output:
+ // returned value: 0 successful operation
+ // -EINVAL invalid interface handle or
+ // invalid command/reply size or format according to command code
+ // The return code should be restricted to indicate problems related to the this
+ // API specification. Status related to the execution of a particular command should be
+ // indicated as part of the reply field.
+ //
+ // *pReplyData updated with command response
+ //
+ ////////////////////////////////////////////////////////////////////////////////
+ int32_t (*command)(effect_handle_t self,
+ uint32_t cmdCode,
+ uint32_t cmdSize,
+ void *pCmdData,
+ uint32_t *replySize,
+ void *pReplyData);
+ ////////////////////////////////////////////////////////////////////////////////
+ //
+ // Function: get_descriptor
+ //
+ // Description: Returns the effect descriptor
+ //
+ // Input:
+ // self: handle to the effect interface this function
+ // is called on.
+ //
+ // Input/Output:
+ // pDescriptor: address where to return the effect descriptor.
+ //
+ // Output:
+ // returned value: 0 successful operation.
+ // -EINVAL invalid interface handle or invalid pDescriptor
+ // *pDescriptor: updated with the effect descriptor.
+ //
+ ////////////////////////////////////////////////////////////////////////////////
+ int32_t (*get_descriptor)(effect_handle_t self,
+ effect_descriptor_t *pDescriptor);
+ ////////////////////////////////////////////////////////////////////////////////
+ //
+ // Function: process_reverse
+ //
+ // Description: Process reverse stream function. This function is used to pass
+ // a reference stream to the effect engine. If the engine does not need a reference
+ // stream, this function pointer can be set to NULL.
+ // This function would typically implemented by an Echo Canceler.
+ //
+ // Input:
+ // self: handle to the effect interface this function
+ // is called on.
+ // inBuffer: buffer descriptor indicating where to read samples to process.
+ // If NULL, use the configuration passed by EFFECT_CMD_SET_CONFIG_REVERSE command.
+ //
+ // outBuffer: buffer descriptor indicating where to write processed samples.
+ // If NULL, use the configuration passed by EFFECT_CMD_SET_CONFIG_REVERSE command.
+ // If the buffer and buffer provider in the configuration received by
+ // EFFECT_CMD_SET_CONFIG_REVERSE are also NULL, do not return modified reverse
+ // stream data
+ //
+ // Output:
+ // returned value: 0 successful operation
+ // -ENODATA the engine has finished the disable phase and the framework
+ // can stop calling process_reverse()
+ // -EINVAL invalid interface handle or
+ // invalid input/output buffer description
+ ////////////////////////////////////////////////////////////////////////////////
+ int32_t (*process_reverse)(effect_handle_t self,
+ audio_buffer_t *inBuffer,
+ audio_buffer_t *outBuffer);
+};
+
+
+//
+//--- Standardized command codes for command() function
+//
+enum effect_command_e {
+ EFFECT_CMD_INIT, // initialize effect engine
+ EFFECT_CMD_SET_CONFIG, // configure effect engine (see effect_config_t)
+ EFFECT_CMD_RESET, // reset effect engine
+ EFFECT_CMD_ENABLE, // enable effect process
+ EFFECT_CMD_DISABLE, // disable effect process
+ EFFECT_CMD_SET_PARAM, // set parameter immediately (see effect_param_t)
+ EFFECT_CMD_SET_PARAM_DEFERRED, // set parameter deferred
+ EFFECT_CMD_SET_PARAM_COMMIT, // commit previous set parameter deferred
+ EFFECT_CMD_GET_PARAM, // get parameter
+ EFFECT_CMD_SET_DEVICE, // set audio device (see audio.h, audio_devices_t)
+ EFFECT_CMD_SET_VOLUME, // set volume
+ EFFECT_CMD_SET_AUDIO_MODE, // set the audio mode (normal, ring, ...)
+ EFFECT_CMD_SET_CONFIG_REVERSE, // configure effect engine reverse stream(see effect_config_t)
+ EFFECT_CMD_SET_INPUT_DEVICE, // set capture device (see audio.h, audio_devices_t)
+ EFFECT_CMD_GET_CONFIG, // read effect engine configuration
+ EFFECT_CMD_GET_CONFIG_REVERSE, // read configure effect engine reverse stream configuration
+ EFFECT_CMD_GET_FEATURE_SUPPORTED_CONFIGS,// get all supported configurations for a feature.
+ EFFECT_CMD_GET_FEATURE_CONFIG, // get current feature configuration
+ EFFECT_CMD_SET_FEATURE_CONFIG, // set current feature configuration
+ EFFECT_CMD_SET_AUDIO_SOURCE, // set the audio source (see audio.h, audio_source_t)
+ EFFECT_CMD_OFFLOAD, // set if effect thread is an offload one,
+ // send the ioHandle of the effect thread
+ EFFECT_CMD_FIRST_PROPRIETARY = 0x10000 // first proprietary command code
+};
+
+//==================================================================================================
+// command: EFFECT_CMD_INIT
+//--------------------------------------------------------------------------------------------------
+// description:
+// Initialize effect engine: All configurations return to default
+//--------------------------------------------------------------------------------------------------
+// command format:
+// size: 0
+// data: N/A
+//--------------------------------------------------------------------------------------------------
+// reply format:
+// size: sizeof(int)
+// data: status
+//==================================================================================================
+// command: EFFECT_CMD_SET_CONFIG
+//--------------------------------------------------------------------------------------------------
+// description:
+// Apply new audio parameters configurations for input and output buffers
+//--------------------------------------------------------------------------------------------------
+// command format:
+// size: sizeof(effect_config_t)
+// data: effect_config_t
+//--------------------------------------------------------------------------------------------------
+// reply format:
+// size: sizeof(int)
+// data: status
+//==================================================================================================
+// command: EFFECT_CMD_RESET
+//--------------------------------------------------------------------------------------------------
+// description:
+// Reset the effect engine. Keep configuration but resets state and buffer content
+//--------------------------------------------------------------------------------------------------
+// command format:
+// size: 0
+// data: N/A
+//--------------------------------------------------------------------------------------------------
+// reply format:
+// size: 0
+// data: N/A
+//==================================================================================================
+// command: EFFECT_CMD_ENABLE
+//--------------------------------------------------------------------------------------------------
+// description:
+// Enable the process. Called by the framework before the first call to process()
+//--------------------------------------------------------------------------------------------------
+// command format:
+// size: 0
+// data: N/A
+//--------------------------------------------------------------------------------------------------
+// reply format:
+// size: sizeof(int)
+// data: status
+//==================================================================================================
+// command: EFFECT_CMD_DISABLE
+//--------------------------------------------------------------------------------------------------
+// description:
+// Disable the process. Called by the framework after the last call to process()
+//--------------------------------------------------------------------------------------------------
+// command format:
+// size: 0
+// data: N/A
+//--------------------------------------------------------------------------------------------------
+// reply format:
+// size: sizeof(int)
+// data: status
+//==================================================================================================
+// command: EFFECT_CMD_SET_PARAM
+//--------------------------------------------------------------------------------------------------
+// description:
+// Set a parameter and apply it immediately
+//--------------------------------------------------------------------------------------------------
+// command format:
+// size: sizeof(effect_param_t) + size of param and value
+// data: effect_param_t + param + value. See effect_param_t definition below for value offset
+//--------------------------------------------------------------------------------------------------
+// reply format:
+// size: sizeof(int)
+// data: status
+//==================================================================================================
+// command: EFFECT_CMD_SET_PARAM_DEFERRED
+//--------------------------------------------------------------------------------------------------
+// description:
+// Set a parameter but apply it only when receiving EFFECT_CMD_SET_PARAM_COMMIT command
+//--------------------------------------------------------------------------------------------------
+// command format:
+// size: sizeof(effect_param_t) + size of param and value
+// data: effect_param_t + param + value. See effect_param_t definition below for value offset
+//--------------------------------------------------------------------------------------------------
+// reply format:
+// size: 0
+// data: N/A
+//==================================================================================================
+// command: EFFECT_CMD_SET_PARAM_COMMIT
+//--------------------------------------------------------------------------------------------------
+// description:
+// Apply all previously received EFFECT_CMD_SET_PARAM_DEFERRED commands
+//--------------------------------------------------------------------------------------------------
+// command format:
+// size: 0
+// data: N/A
+//--------------------------------------------------------------------------------------------------
+// reply format:
+// size: sizeof(int)
+// data: status
+//==================================================================================================
+// command: EFFECT_CMD_GET_PARAM
+//--------------------------------------------------------------------------------------------------
+// description:
+// Get a parameter value
+//--------------------------------------------------------------------------------------------------
+// command format:
+// size: sizeof(effect_param_t) + size of param
+// data: effect_param_t + param
+//--------------------------------------------------------------------------------------------------
+// reply format:
+// size: sizeof(effect_param_t) + size of param and value
+// data: effect_param_t + param + value. See effect_param_t definition below for value offset
+//==================================================================================================
+// command: EFFECT_CMD_SET_DEVICE
+//--------------------------------------------------------------------------------------------------
+// description:
+// Set the rendering device the audio output path is connected to. See audio.h, audio_devices_t
+// for device values.
+// The effect implementation must set EFFECT_FLAG_DEVICE_IND flag in its descriptor to receive this
+// command when the device changes
+//--------------------------------------------------------------------------------------------------
+// command format:
+// size: sizeof(uint32_t)
+// data: uint32_t
+//--------------------------------------------------------------------------------------------------
+// reply format:
+// size: 0
+// data: N/A
+//==================================================================================================
+// command: EFFECT_CMD_SET_VOLUME
+//--------------------------------------------------------------------------------------------------
+// description:
+// Set and get volume. Used by audio framework to delegate volume control to effect engine.
+// The effect implementation must set EFFECT_FLAG_VOLUME_IND or EFFECT_FLAG_VOLUME_CTRL flag in
+// its descriptor to receive this command before every call to process() function
+// If EFFECT_FLAG_VOLUME_CTRL flag is set in the effect descriptor, the effect engine must return
+// the volume that should be applied before the effect is processed. The overall volume (the volume
+// actually applied by the effect engine multiplied by the returned value) should match the value
+// indicated in the command.
+//--------------------------------------------------------------------------------------------------
+// command format:
+// size: n * sizeof(uint32_t)
+// data: volume for each channel defined in effect_config_t for output buffer expressed in
+// 8.24 fixed point format
+//--------------------------------------------------------------------------------------------------
+// reply format:
+// size: n * sizeof(uint32_t) / 0
+// data: - if EFFECT_FLAG_VOLUME_CTRL is set in effect descriptor:
+// volume for each channel defined in effect_config_t for output buffer expressed in
+// 8.24 fixed point format
+// - if EFFECT_FLAG_VOLUME_CTRL is not set in effect descriptor:
+// N/A
+// It is legal to receive a null pointer as pReplyData in which case the effect framework has
+// delegated volume control to another effect
+//==================================================================================================
+// command: EFFECT_CMD_SET_AUDIO_MODE
+//--------------------------------------------------------------------------------------------------
+// description:
+// Set the audio mode. The effect implementation must set EFFECT_FLAG_AUDIO_MODE_IND flag in its
+// descriptor to receive this command when the audio mode changes.
+//--------------------------------------------------------------------------------------------------
+// command format:
+// size: sizeof(uint32_t)
+// data: audio_mode_t
+//--------------------------------------------------------------------------------------------------
+// reply format:
+// size: 0
+// data: N/A
+//==================================================================================================
+// command: EFFECT_CMD_SET_CONFIG_REVERSE
+//--------------------------------------------------------------------------------------------------
+// description:
+// Apply new audio parameters configurations for input and output buffers of reverse stream.
+// An example of reverse stream is the echo reference supplied to an Acoustic Echo Canceler.
+//--------------------------------------------------------------------------------------------------
+// command format:
+// size: sizeof(effect_config_t)
+// data: effect_config_t
+//--------------------------------------------------------------------------------------------------
+// reply format:
+// size: sizeof(int)
+// data: status
+//==================================================================================================
+// command: EFFECT_CMD_SET_INPUT_DEVICE
+//--------------------------------------------------------------------------------------------------
+// description:
+// Set the capture device the audio input path is connected to. See audio.h, audio_devices_t
+// for device values.
+// The effect implementation must set EFFECT_FLAG_DEVICE_IND flag in its descriptor to receive this
+// command when the device changes
+//--------------------------------------------------------------------------------------------------
+// command format:
+// size: sizeof(uint32_t)
+// data: uint32_t
+//--------------------------------------------------------------------------------------------------
+// reply format:
+// size: 0
+// data: N/A
+//==================================================================================================
+// command: EFFECT_CMD_GET_CONFIG
+//--------------------------------------------------------------------------------------------------
+// description:
+// Read audio parameters configurations for input and output buffers
+//--------------------------------------------------------------------------------------------------
+// command format:
+// size: 0
+// data: N/A
+//--------------------------------------------------------------------------------------------------
+// reply format:
+// size: sizeof(effect_config_t)
+// data: effect_config_t
+//==================================================================================================
+// command: EFFECT_CMD_GET_CONFIG_REVERSE
+//--------------------------------------------------------------------------------------------------
+// description:
+// Read audio parameters configurations for input and output buffers of reverse stream
+//--------------------------------------------------------------------------------------------------
+// command format:
+// size: 0
+// data: N/A
+//--------------------------------------------------------------------------------------------------
+// reply format:
+// size: sizeof(effect_config_t)
+// data: effect_config_t
+//==================================================================================================
+// command: EFFECT_CMD_GET_FEATURE_SUPPORTED_CONFIGS
+//--------------------------------------------------------------------------------------------------
+// description:
+// Queries for supported configurations for a particular feature (e.g. get the supported
+// combinations of main and auxiliary channels for a noise suppressor).
+// The command parameter is the feature identifier (See effect_feature_e for a list of defined
+// features) followed by the maximum number of configuration descriptor to return.
+// The reply is composed of:
+// - status (uint32_t):
+// - 0 if feature is supported
+// - -ENOSYS if the feature is not supported,
+// - -ENOMEM if the feature is supported but the total number of supported configurations
+// exceeds the maximum number indicated by the caller.
+// - total number of supported configurations (uint32_t)
+// - an array of configuration descriptors.
+// The actual number of descriptors returned must not exceed the maximum number indicated by
+// the caller.
+//--------------------------------------------------------------------------------------------------
+// command format:
+// size: 2 x sizeof(uint32_t)
+// data: effect_feature_e + maximum number of configurations to return
+//--------------------------------------------------------------------------------------------------
+// reply format:
+// size: 2 x sizeof(uint32_t) + n x sizeof (<config descriptor>)
+// data: status + total number of configurations supported + array of n config descriptors
+//==================================================================================================
+// command: EFFECT_CMD_GET_FEATURE_CONFIG
+//--------------------------------------------------------------------------------------------------
+// description:
+// Retrieves current configuration for a given feature.
+// The reply status is:
+// - 0 if feature is supported
+// - -ENOSYS if the feature is not supported,
+//--------------------------------------------------------------------------------------------------
+// command format:
+// size: sizeof(uint32_t)
+// data: effect_feature_e
+//--------------------------------------------------------------------------------------------------
+// reply format:
+// size: sizeof(uint32_t) + sizeof (<config descriptor>)
+// data: status + config descriptor
+//==================================================================================================
+// command: EFFECT_CMD_SET_FEATURE_CONFIG
+//--------------------------------------------------------------------------------------------------
+// description:
+// Sets current configuration for a given feature.
+// The reply status is:
+// - 0 if feature is supported
+// - -ENOSYS if the feature is not supported,
+// - -EINVAL if the configuration is invalid
+//--------------------------------------------------------------------------------------------------
+// command format:
+// size: sizeof(uint32_t) + sizeof (<config descriptor>)
+// data: effect_feature_e + config descriptor
+//--------------------------------------------------------------------------------------------------
+// reply format:
+// size: sizeof(uint32_t)
+// data: status
+//==================================================================================================
+// command: EFFECT_CMD_SET_AUDIO_SOURCE
+//--------------------------------------------------------------------------------------------------
+// description:
+// Set the audio source the capture path is configured for (Camcorder, voice recognition...).
+// See audio.h, audio_source_t for values.
+//--------------------------------------------------------------------------------------------------
+// command format:
+// size: sizeof(uint32_t)
+// data: uint32_t
+//--------------------------------------------------------------------------------------------------
+// reply format:
+// size: 0
+// data: N/A
+//==================================================================================================
+// command: EFFECT_CMD_OFFLOAD
+//--------------------------------------------------------------------------------------------------
+// description:
+// 1.indicate if the playback thread the effect is attached to is offloaded or not
+// 2.update the io handle of the playback thread the effect is attached to
+//--------------------------------------------------------------------------------------------------
+// command format:
+// size: sizeof(effect_offload_param_t)
+// data: effect_offload_param_t
+//--------------------------------------------------------------------------------------------------
+// reply format:
+// size: sizeof(uint32_t)
+// data: uint32_t
+//--------------------------------------------------------------------------------------------------
+// command: EFFECT_CMD_FIRST_PROPRIETARY
+//--------------------------------------------------------------------------------------------------
+// description:
+// All proprietary effect commands must use command codes above this value. The size and format of
+// command and response fields is free in this case
+//==================================================================================================
+
+
+// Audio buffer descriptor used by process(), bufferProvider() functions and buffer_config_t
+// structure. Multi-channel audio is always interleaved. The channel order is from LSB to MSB with
+// regard to the channel mask definition in audio.h, audio_channel_mask_t e.g :
+// Stereo: left, right
+// 5 point 1: front left, front right, front center, low frequency, back left, back right
+// The buffer size is expressed in frame count, a frame being composed of samples for all
+// channels at a given time. Frame size for unspecified format (AUDIO_FORMAT_OTHER) is 8 bit by
+// definition
+struct audio_buffer_s {
+ size_t frameCount; // number of frames in buffer
+ union {
+ void* raw; // raw pointer to start of buffer
+ int32_t* s32; // pointer to signed 32 bit data at start of buffer
+ int16_t* s16; // pointer to signed 16 bit data at start of buffer
+ uint8_t* u8; // pointer to unsigned 8 bit data at start of buffer
+ };
+};
+
+// The buffer_provider_s structure contains functions that can be used
+// by the effect engine process() function to query and release input
+// or output audio buffer.
+// The getBuffer() function is called to retrieve a buffer where data
+// should read from or written to by process() function.
+// The releaseBuffer() function MUST be called when the buffer retrieved
+// with getBuffer() is not needed anymore.
+// The process function should use the buffer provider mechanism to retrieve
+// input or output buffer if the inBuffer or outBuffer passed as argument is NULL
+// and the buffer configuration (buffer_config_t) given by the EFFECT_CMD_SET_CONFIG
+// command did not specify an audio buffer.
+
+typedef int32_t (* buffer_function_t)(void *cookie, audio_buffer_t *buffer);
+
+typedef struct buffer_provider_s {
+ buffer_function_t getBuffer; // retrieve next buffer
+ buffer_function_t releaseBuffer; // release used buffer
+ void *cookie; // for use by client of buffer provider functions
+} buffer_provider_t;
+
+
+// The buffer_config_s structure specifies the input or output audio format
+// to be used by the effect engine. It is part of the effect_config_t
+// structure that defines both input and output buffer configurations and is
+// passed by the EFFECT_CMD_SET_CONFIG or EFFECT_CMD_SET_CONFIG_REVERSE command.
+typedef struct buffer_config_s {
+ audio_buffer_t buffer; // buffer for use by process() function if not passed explicitly
+ uint32_t samplingRate; // sampling rate
+ uint32_t channels; // channel mask (see audio_channel_mask_t in audio.h)
+ buffer_provider_t bufferProvider; // buffer provider
+ uint8_t format; // Audio format (see audio_format_t in audio.h)
+ uint8_t accessMode; // read/write or accumulate in buffer (effect_buffer_access_e)
+ uint16_t mask; // indicates which of the above fields is valid
+} buffer_config_t;
+
+// Values for "accessMode" field of buffer_config_t:
+// overwrite, read only, accumulate (read/modify/write)
+enum effect_buffer_access_e {
+ EFFECT_BUFFER_ACCESS_WRITE,
+ EFFECT_BUFFER_ACCESS_READ,
+ EFFECT_BUFFER_ACCESS_ACCUMULATE
+
+};
+
+// feature identifiers for EFFECT_CMD_GET_FEATURE_SUPPORTED_CONFIGS command
+enum effect_feature_e {
+ EFFECT_FEATURE_AUX_CHANNELS, // supports auxiliary channels (e.g. dual mic noise suppressor)
+ EFFECT_FEATURE_CNT
+};
+
+// EFFECT_FEATURE_AUX_CHANNELS feature configuration descriptor. Describe a combination
+// of main and auxiliary channels supported
+typedef struct channel_config_s {
+ audio_channel_mask_t main_channels; // channel mask for main channels
+ audio_channel_mask_t aux_channels; // channel mask for auxiliary channels
+} channel_config_t;
+
+
+// Values for bit field "mask" in buffer_config_t. If a bit is set, the corresponding field
+// in buffer_config_t must be taken into account when executing the EFFECT_CMD_SET_CONFIG command
+#define EFFECT_CONFIG_BUFFER 0x0001 // buffer field must be taken into account
+#define EFFECT_CONFIG_SMP_RATE 0x0002 // samplingRate field must be taken into account
+#define EFFECT_CONFIG_CHANNELS 0x0004 // channels field must be taken into account
+#define EFFECT_CONFIG_FORMAT 0x0008 // format field must be taken into account
+#define EFFECT_CONFIG_ACC_MODE 0x0010 // accessMode field must be taken into account
+#define EFFECT_CONFIG_PROVIDER 0x0020 // bufferProvider field must be taken into account
+#define EFFECT_CONFIG_ALL (EFFECT_CONFIG_BUFFER | EFFECT_CONFIG_SMP_RATE | \
+ EFFECT_CONFIG_CHANNELS | EFFECT_CONFIG_FORMAT | \
+ EFFECT_CONFIG_ACC_MODE | EFFECT_CONFIG_PROVIDER)
+
+
+// effect_config_s structure describes the format of the pCmdData argument of EFFECT_CMD_SET_CONFIG
+// command to configure audio parameters and buffers for effect engine input and output.
+typedef struct effect_config_s {
+ buffer_config_t inputCfg;
+ buffer_config_t outputCfg;
+} effect_config_t;
+
+
+// effect_param_s structure describes the format of the pCmdData argument of EFFECT_CMD_SET_PARAM
+// command and pCmdData and pReplyData of EFFECT_CMD_GET_PARAM command.
+// psize and vsize represent the actual size of parameter and value.
+//
+// NOTE: the start of value field inside the data field is always on a 32 bit boundary:
+//
+// +-----------+
+// | status | sizeof(int)
+// +-----------+
+// | psize | sizeof(int)
+// +-----------+
+// | vsize | sizeof(int)
+// +-----------+
+// | | | |
+// ~ parameter ~ > psize |
+// | | | > ((psize - 1)/sizeof(int) + 1) * sizeof(int)
+// +-----------+ |
+// | padding | |
+// +-----------+
+// | | |
+// ~ value ~ > vsize
+// | | |
+// +-----------+
+
+typedef struct effect_param_s {
+ int32_t status; // Transaction status (unused for command, used for reply)
+ uint32_t psize; // Parameter size
+ uint32_t vsize; // Value size
+ char data[]; // Start of Parameter + Value data
+} effect_param_t;
+
+// structure used by EFFECT_CMD_OFFLOAD command
+typedef struct effect_offload_param_s {
+ bool isOffload; // true if the playback thread the effect is attached to is offloaded
+ int ioHandle; // io handle of the playback thread the effect is attached to
+} effect_offload_param_t;
+
+
+/////////////////////////////////////////////////
+// Effect library interface
+/////////////////////////////////////////////////
+
+// Effect library interface version 3.0
+// Note that EffectsFactory.c only checks the major version component, so changes to the minor
+// number can only be used for fully backwards compatible changes
+#define EFFECT_LIBRARY_API_VERSION EFFECT_MAKE_API_VERSION(3,0)
+
+#define AUDIO_EFFECT_LIBRARY_TAG ((('A') << 24) | (('E') << 16) | (('L') << 8) | ('T'))
+
+// Every effect library must have a data structure named AUDIO_EFFECT_LIBRARY_INFO_SYM
+// and the fields of this data structure must begin with audio_effect_library_t
+
+typedef struct audio_effect_library_s {
+ // tag must be initialized to AUDIO_EFFECT_LIBRARY_TAG
+ uint32_t tag;
+ // Version of the effect library API : 0xMMMMmmmm MMMM: Major, mmmm: minor
+ uint32_t version;
+ // Name of this library
+ const char *name;
+ // Author/owner/implementor of the library
+ const char *implementor;
+
+ ////////////////////////////////////////////////////////////////////////////////
+ //
+ // Function: create_effect
+ //
+ // Description: Creates an effect engine of the specified implementation uuid and
+ // returns an effect control interface on this engine. The function will allocate the
+ // resources for an instance of the requested effect engine and return
+ // a handle on the effect control interface.
+ //
+ // Input:
+ // uuid: pointer to the effect uuid.
+ // sessionId: audio session to which this effect instance will be attached. All effects
+ // created with the same session ID are connected in series and process the same signal
+ // stream. Knowing that two effects are part of the same effect chain can help the
+ // library implement some kind of optimizations.
+ // ioId: identifies the output or input stream this effect is directed to at audio HAL.
+ // For future use especially with tunneled HW accelerated effects
+ //
+ // Input/Output:
+ // pHandle: address where to return the effect interface handle.
+ //
+ // Output:
+ // returned value: 0 successful operation.
+ // -ENODEV library failed to initialize
+ // -EINVAL invalid pEffectUuid or pHandle
+ // -ENOENT no effect with this uuid found
+ // *pHandle: updated with the effect interface handle.
+ //
+ ////////////////////////////////////////////////////////////////////////////////
+ int32_t (*create_effect)(const effect_uuid_t *uuid,
+ int32_t sessionId,
+ int32_t ioId,
+ effect_handle_t *pHandle);
+
+ ////////////////////////////////////////////////////////////////////////////////
+ //
+ // Function: release_effect
+ //
+ // Description: Releases the effect engine whose handle is given as argument.
+ // All resources allocated to this particular instance of the effect are
+ // released.
+ //
+ // Input:
+ // handle: handle on the effect interface to be released.
+ //
+ // Output:
+ // returned value: 0 successful operation.
+ // -ENODEV library failed to initialize
+ // -EINVAL invalid interface handle
+ //
+ ////////////////////////////////////////////////////////////////////////////////
+ int32_t (*release_effect)(effect_handle_t handle);
+
+ ////////////////////////////////////////////////////////////////////////////////
+ //
+ // Function: get_descriptor
+ //
+ // Description: Returns the descriptor of the effect engine which implementation UUID is
+ // given as argument.
+ //
+ // Input/Output:
+ // uuid: pointer to the effect uuid.
+ // pDescriptor: address where to return the effect descriptor.
+ //
+ // Output:
+ // returned value: 0 successful operation.
+ // -ENODEV library failed to initialize
+ // -EINVAL invalid pDescriptor or uuid
+ // *pDescriptor: updated with the effect descriptor.
+ //
+ ////////////////////////////////////////////////////////////////////////////////
+ int32_t (*get_descriptor)(const effect_uuid_t *uuid,
+ effect_descriptor_t *pDescriptor);
+} audio_effect_library_t;
+
+// Name of the hal_module_info
+#define AUDIO_EFFECT_LIBRARY_INFO_SYM AELI
+
+// Name of the hal_module_info as a string
+#define AUDIO_EFFECT_LIBRARY_INFO_SYM_AS_STR "AELI"
+
+__END_DECLS
+
+#endif // ANDROID_AUDIO_EFFECT_H
diff --git a/repo/android/hardware/bluetooth.h b/repo/android/hardware/bluetooth.h
new file mode 100644
index 0000000..74cd1fc
--- /dev/null
+++ b/repo/android/hardware/bluetooth.h
@@ -0,0 +1,550 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_INCLUDE_BLUETOOTH_H
+#define ANDROID_INCLUDE_BLUETOOTH_H
+
+#include <stdbool.h>
+#include <stdint.h>
+#include <sys/cdefs.h>
+#include <sys/types.h>
+
+#include <hardware/hardware.h>
+
+__BEGIN_DECLS
+
+/**
+ * The Bluetooth Hardware Module ID
+ */
+
+#define BT_HARDWARE_MODULE_ID "bluetooth"
+#define BT_STACK_MODULE_ID "bluetooth"
+#define BT_STACK_TEST_MODULE_ID "bluetooth_test"
+
+
+/* Bluetooth profile interface IDs */
+
+#define BT_PROFILE_HANDSFREE_ID "handsfree"
+#define BT_PROFILE_HANDSFREE_CLIENT_ID "handsfree_client"
+#define BT_PROFILE_ADVANCED_AUDIO_ID "a2dp"
+#define BT_PROFILE_ADVANCED_AUDIO_SINK_ID "a2dp_sink"
+#define BT_PROFILE_HEALTH_ID "health"
+#define BT_PROFILE_SOCKETS_ID "socket"
+#define BT_PROFILE_HIDHOST_ID "hidhost"
+#define BT_PROFILE_PAN_ID "pan"
+#define BT_PROFILE_MAP_CLIENT_ID "map_client"
+
+#define BT_PROFILE_GATT_ID "gatt"
+#define BT_PROFILE_AV_RC_ID "avrcp"
+#define BT_PROFILE_AV_RC_CTRL_ID "avrcp_ctrl"
+
+/** Bluetooth Address */
+typedef struct {
+ uint8_t address[6];
+} __attribute__((packed))bt_bdaddr_t;
+
+/** Bluetooth Device Name */
+typedef struct {
+ uint8_t name[249];
+} __attribute__((packed))bt_bdname_t;
+
+/** Bluetooth Adapter Visibility Modes*/
+typedef enum {
+ BT_SCAN_MODE_NONE,
+ BT_SCAN_MODE_CONNECTABLE,
+ BT_SCAN_MODE_CONNECTABLE_DISCOVERABLE
+} bt_scan_mode_t;
+
+/** Bluetooth Adapter State */
+typedef enum {
+ BT_STATE_OFF,
+ BT_STATE_ON
+} bt_state_t;
+
+/** Bluetooth Error Status */
+/** We need to build on this */
+
+typedef enum {
+ BT_STATUS_SUCCESS,
+ BT_STATUS_FAIL,
+ BT_STATUS_NOT_READY,
+ BT_STATUS_NOMEM,
+ BT_STATUS_BUSY,
+ BT_STATUS_DONE, /* request already completed */
+ BT_STATUS_UNSUPPORTED,
+ BT_STATUS_PARM_INVALID,
+ BT_STATUS_UNHANDLED,
+ BT_STATUS_AUTH_FAILURE,
+ BT_STATUS_RMT_DEV_DOWN,
+ BT_STATUS_AUTH_REJECTED
+
+} bt_status_t;
+
+/** Bluetooth PinKey Code */
+typedef struct {
+ uint8_t pin[16];
+} __attribute__((packed))bt_pin_code_t;
+
+typedef struct {
+ uint8_t status;
+ uint8_t ctrl_state; /* stack reported state */
+ uint64_t tx_time; /* in ms */
+ uint64_t rx_time; /* in ms */
+ uint64_t idle_time; /* in ms */
+ uint64_t energy_used; /* a product of mA, V and ms */
+} __attribute__((packed))bt_activity_energy_info;
+
+/** Bluetooth Adapter Discovery state */
+typedef enum {
+ BT_DISCOVERY_STOPPED,
+ BT_DISCOVERY_STARTED
+} bt_discovery_state_t;
+
+/** Bluetooth ACL connection state */
+typedef enum {
+ BT_ACL_STATE_CONNECTED,
+ BT_ACL_STATE_DISCONNECTED
+} bt_acl_state_t;
+
+/** Bluetooth 128-bit UUID */
+typedef struct {
+ uint8_t uu[16];
+} bt_uuid_t;
+
+/** Bluetooth SDP service record */
+typedef struct
+{
+ bt_uuid_t uuid;
+ uint16_t channel;
+ char name[256]; // what's the maximum length
+} bt_service_record_t;
+
+
+/** Bluetooth Remote Version info */
+typedef struct
+{
+ int version;
+ int sub_ver;
+ int manufacturer;
+} bt_remote_version_t;
+
+typedef struct
+{
+ uint8_t local_privacy_enabled;
+ uint8_t max_adv_instance;
+ uint8_t rpa_offload_supported;
+ uint8_t max_irk_list_size;
+ uint8_t max_adv_filter_supported;
+ uint8_t scan_result_storage_size_lobyte;
+ uint8_t scan_result_storage_size_hibyte;
+ uint8_t activity_energy_info_supported;
+}bt_local_le_features_t;
+
+/* Bluetooth Adapter and Remote Device property types */
+typedef enum {
+ /* Properties common to both adapter and remote device */
+ /**
+ * Description - Bluetooth Device Name
+ * Access mode - Adapter name can be GET/SET. Remote device can be GET
+ * Data type - bt_bdname_t
+ */
+ BT_PROPERTY_BDNAME = 0x1,
+ /**
+ * Description - Bluetooth Device Address
+ * Access mode - Only GET.
+ * Data type - bt_bdaddr_t
+ */
+ BT_PROPERTY_BDADDR,
+ /**
+ * Description - Bluetooth Service 128-bit UUIDs
+ * Access mode - Only GET.
+ * Data type - Array of bt_uuid_t (Array size inferred from property length).
+ */
+ BT_PROPERTY_UUIDS,
+ /**
+ * Description - Bluetooth Class of Device as found in Assigned Numbers
+ * Access mode - Only GET.
+ * Data type - uint32_t.
+ */
+ BT_PROPERTY_CLASS_OF_DEVICE,
+ /**
+ * Description - Device Type - BREDR, BLE or DUAL Mode
+ * Access mode - Only GET.
+ * Data type - bt_device_type_t
+ */
+ BT_PROPERTY_TYPE_OF_DEVICE,
+ /**
+ * Description - Bluetooth Service Record
+ * Access mode - Only GET.
+ * Data type - bt_service_record_t
+ */
+ BT_PROPERTY_SERVICE_RECORD,
+
+ /* Properties unique to adapter */
+ /**
+ * Description - Bluetooth Adapter scan mode
+ * Access mode - GET and SET
+ * Data type - bt_scan_mode_t.
+ */
+ BT_PROPERTY_ADAPTER_SCAN_MODE,
+ /**
+ * Description - List of bonded devices
+ * Access mode - Only GET.
+ * Data type - Array of bt_bdaddr_t of the bonded remote devices
+ * (Array size inferred from property length).
+ */
+ BT_PROPERTY_ADAPTER_BONDED_DEVICES,
+ /**
+ * Description - Bluetooth Adapter Discovery timeout (in seconds)
+ * Access mode - GET and SET
+ * Data type - uint32_t
+ */
+ BT_PROPERTY_ADAPTER_DISCOVERY_TIMEOUT,
+
+ /* Properties unique to remote device */
+ /**
+ * Description - User defined friendly name of the remote device
+ * Access mode - GET and SET
+ * Data type - bt_bdname_t.
+ */
+ BT_PROPERTY_REMOTE_FRIENDLY_NAME,
+ /**
+ * Description - RSSI value of the inquired remote device
+ * Access mode - Only GET.
+ * Data type - int32_t.
+ */
+ BT_PROPERTY_REMOTE_RSSI,
+ /**
+ * Description - Remote version info
+ * Access mode - SET/GET.
+ * Data type - bt_remote_version_t.
+ */
+
+ BT_PROPERTY_REMOTE_VERSION_INFO,
+
+ /**
+ * Description - Local LE features
+ * Access mode - GET.
+ * Data type - bt_local_le_features_t.
+ */
+ BT_PROPERTY_LOCAL_LE_FEATURES,
+
+ BT_PROPERTY_REMOTE_DEVICE_TIMESTAMP = 0xFF,
+} bt_property_type_t;
+
+/** Bluetooth Adapter Property data structure */
+typedef struct
+{
+ bt_property_type_t type;
+ int len;
+ void *val;
+} bt_property_t;
+
+
+/** Bluetooth Device Type */
+typedef enum {
+ BT_DEVICE_DEVTYPE_BREDR = 0x1,
+ BT_DEVICE_DEVTYPE_BLE,
+ BT_DEVICE_DEVTYPE_DUAL
+} bt_device_type_t;
+/** Bluetooth Bond state */
+typedef enum {
+ BT_BOND_STATE_NONE,
+ BT_BOND_STATE_BONDING,
+ BT_BOND_STATE_BONDED
+} bt_bond_state_t;
+
+/** Bluetooth SSP Bonding Variant */
+typedef enum {
+ BT_SSP_VARIANT_PASSKEY_CONFIRMATION,
+ BT_SSP_VARIANT_PASSKEY_ENTRY,
+ BT_SSP_VARIANT_CONSENT,
+ BT_SSP_VARIANT_PASSKEY_NOTIFICATION
+} bt_ssp_variant_t;
+
+#define BT_MAX_NUM_UUIDS 32
+
+/** Bluetooth Interface callbacks */
+
+/** Bluetooth Enable/Disable Callback. */
+typedef void (*adapter_state_changed_callback)(bt_state_t state);
+
+/** GET/SET Adapter Properties callback */
+/* TODO: For the GET/SET property APIs/callbacks, we may need a session
+ * identifier to associate the call with the callback. This would be needed
+ * whenever more than one simultaneous instance of the same adapter_type
+ * is get/set.
+ *
+ * If this is going to be handled in the Java framework, then we do not need
+ * to manage sessions here.
+ */
+typedef void (*adapter_properties_callback)(bt_status_t status,
+ int num_properties,
+ bt_property_t *properties);
+
+/** GET/SET Remote Device Properties callback */
+/** TODO: For remote device properties, do not see a need to get/set
+ * multiple properties - num_properties shall be 1
+ */
+typedef void (*remote_device_properties_callback)(bt_status_t status,
+ bt_bdaddr_t *bd_addr,
+ int num_properties,
+ bt_property_t *properties);
+
+/** New device discovered callback */
+/** If EIR data is not present, then BD_NAME and RSSI shall be NULL and -1
+ * respectively */
+typedef void (*device_found_callback)(int num_properties,
+ bt_property_t *properties);
+
+/** Discovery state changed callback */
+typedef void (*discovery_state_changed_callback)(bt_discovery_state_t state);
+
+/** Bluetooth Legacy PinKey Request callback */
+typedef void (*pin_request_callback)(bt_bdaddr_t *remote_bd_addr,
+ bt_bdname_t *bd_name, uint32_t cod);
+
+/** Bluetooth SSP Request callback - Just Works & Numeric Comparison*/
+/** pass_key - Shall be 0 for BT_SSP_PAIRING_VARIANT_CONSENT &
+ * BT_SSP_PAIRING_PASSKEY_ENTRY */
+/* TODO: Passkey request callback shall not be needed for devices with display
+ * capability. We still need support this in the stack for completeness */
+typedef void (*ssp_request_callback)(bt_bdaddr_t *remote_bd_addr,
+ bt_bdname_t *bd_name,
+ uint32_t cod,
+ bt_ssp_variant_t pairing_variant,
+ uint32_t pass_key);
+
+/** Bluetooth Bond state changed callback */
+/* Invoked in response to create_bond, cancel_bond or remove_bond */
+typedef void (*bond_state_changed_callback)(bt_status_t status,
+ bt_bdaddr_t *remote_bd_addr,
+ bt_bond_state_t state);
+
+/** Bluetooth ACL connection state changed callback */
+typedef void (*acl_state_changed_callback)(bt_status_t status, bt_bdaddr_t *remote_bd_addr,
+ bt_acl_state_t state);
+
+typedef enum {
+ ASSOCIATE_JVM,
+ DISASSOCIATE_JVM
+} bt_cb_thread_evt;
+
+/** Thread Associate/Disassociate JVM Callback */
+/* Callback that is invoked by the callback thread to allow upper layer to attach/detach to/from
+ * the JVM */
+typedef void (*callback_thread_event)(bt_cb_thread_evt evt);
+
+/** Bluetooth Test Mode Callback */
+/* Receive any HCI event from controller. Must be in DUT Mode for this callback to be received */
+typedef void (*dut_mode_recv_callback)(uint16_t opcode, uint8_t *buf, uint8_t len);
+
+/* LE Test mode callbacks
+* This callback shall be invoked whenever the le_tx_test, le_rx_test or le_test_end is invoked
+* The num_packets is valid only for le_test_end command */
+typedef void (*le_test_mode_callback)(bt_status_t status, uint16_t num_packets);
+
+/** Callback invoked when energy details are obtained */
+/* Ctrl_state-Current controller state-Active-1,scan-2,or idle-3 state as defined by HCI spec.
+ * If the ctrl_state value is 0, it means the API call failed
+ * Time values-In milliseconds as returned by the controller
+ * Energy used-Value as returned by the controller
+ * Status-Provides the status of the read_energy_info API call */
+typedef void (*energy_info_callback)(bt_activity_energy_info *energy_info);
+
+/** TODO: Add callbacks for Link Up/Down and other generic
+ * notifications/callbacks */
+
+/** Bluetooth DM callback structure. */
+typedef struct {
+ /** set to sizeof(bt_callbacks_t) */
+ size_t size;
+ adapter_state_changed_callback adapter_state_changed_cb;
+ adapter_properties_callback adapter_properties_cb;
+ remote_device_properties_callback remote_device_properties_cb;
+ device_found_callback device_found_cb;
+ discovery_state_changed_callback discovery_state_changed_cb;
+ pin_request_callback pin_request_cb;
+ ssp_request_callback ssp_request_cb;
+ bond_state_changed_callback bond_state_changed_cb;
+ acl_state_changed_callback acl_state_changed_cb;
+ callback_thread_event thread_evt_cb;
+ dut_mode_recv_callback dut_mode_recv_cb;
+ le_test_mode_callback le_test_mode_cb;
+ energy_info_callback energy_info_cb;
+} bt_callbacks_t;
+
+typedef void (*alarm_cb)(void *data);
+typedef bool (*set_wake_alarm_callout)(uint64_t delay_millis, bool should_wake, alarm_cb cb, void *data);
+typedef int (*acquire_wake_lock_callout)(const char *lock_name);
+typedef int (*release_wake_lock_callout)(const char *lock_name);
+
+/** The set of functions required by bluedroid to set wake alarms and
+ * grab wake locks. This struct is passed into the stack through the
+ * |set_os_callouts| function on |bt_interface_t|.
+ */
+typedef struct {
+ /* set to sizeof(bt_os_callouts_t) */
+ size_t size;
+
+ set_wake_alarm_callout set_wake_alarm;
+ acquire_wake_lock_callout acquire_wake_lock;
+ release_wake_lock_callout release_wake_lock;
+} bt_os_callouts_t;
+
+/** NOTE: By default, no profiles are initialized at the time of init/enable.
+ * Whenever the application invokes the 'init' API of a profile, then one of
+ * the following shall occur:
+ *
+ * 1.) If Bluetooth is not enabled, then the Bluetooth core shall mark the
+ * profile as enabled. Subsequently, when the application invokes the
+ * Bluetooth 'enable', as part of the enable sequence the profile that were
+ * marked shall be enabled by calling appropriate stack APIs. The
+ * 'adapter_properties_cb' shall return the list of UUIDs of the
+ * enabled profiles.
+ *
+ * 2.) If Bluetooth is enabled, then the Bluetooth core shall invoke the stack
+ * profile API to initialize the profile and trigger a
+ * 'adapter_properties_cb' with the current list of UUIDs including the
+ * newly added profile's UUID.
+ *
+ * The reverse shall occur whenever the profile 'cleanup' APIs are invoked
+ */
+
+/** Represents the standard Bluetooth DM interface. */
+typedef struct {
+ /** set to sizeof(bt_interface_t) */
+ size_t size;
+ /**
+ * Opens the interface and provides the callback routines
+ * to the implemenation of this interface.
+ */
+ int (*init)(bt_callbacks_t* callbacks );
+
+ /** Enable Bluetooth. */
+ int (*enable)(void);
+
+ /** Disable Bluetooth. */
+ int (*disable)(void);
+
+ /** Closes the interface. */
+ void (*cleanup)(void);
+
+ /** Get all Bluetooth Adapter properties at init */
+ int (*get_adapter_properties)(void);
+
+ /** Get Bluetooth Adapter property of 'type' */
+ int (*get_adapter_property)(bt_property_type_t type);
+
+ /** Set Bluetooth Adapter property of 'type' */
+ /* Based on the type, val shall be one of
+ * bt_bdaddr_t or bt_bdname_t or bt_scanmode_t etc
+ */
+ int (*set_adapter_property)(const bt_property_t *property);
+
+ /** Get all Remote Device properties */
+ int (*get_remote_device_properties)(bt_bdaddr_t *remote_addr);
+
+ /** Get Remote Device property of 'type' */
+ int (*get_remote_device_property)(bt_bdaddr_t *remote_addr,
+ bt_property_type_t type);
+
+ /** Set Remote Device property of 'type' */
+ int (*set_remote_device_property)(bt_bdaddr_t *remote_addr,
+ const bt_property_t *property);
+
+ /** Get Remote Device's service record for the given UUID */
+ int (*get_remote_service_record)(bt_bdaddr_t *remote_addr,
+ bt_uuid_t *uuid);
+
+ /** Start SDP to get remote services */
+ int (*get_remote_services)(bt_bdaddr_t *remote_addr);
+
+ /** Start Discovery */
+ int (*start_discovery)(void);
+
+ /** Cancel Discovery */
+ int (*cancel_discovery)(void);
+
+ /** Create Bluetooth Bonding */
+ int (*create_bond)(const bt_bdaddr_t *bd_addr, int transport);
+
+ /** Remove Bond */
+ int (*remove_bond)(const bt_bdaddr_t *bd_addr);
+
+ /** Cancel Bond */
+ int (*cancel_bond)(const bt_bdaddr_t *bd_addr);
+
+ /**
+ * Get the connection status for a given remote device.
+ * return value of 0 means the device is not connected,
+ * non-zero return status indicates an active connection.
+ */
+ int (*get_connection_state)(const bt_bdaddr_t *bd_addr);
+
+ /** BT Legacy PinKey Reply */
+ /** If accept==FALSE, then pin_len and pin_code shall be 0x0 */
+ int (*pin_reply)(const bt_bdaddr_t *bd_addr, uint8_t accept,
+ uint8_t pin_len, bt_pin_code_t *pin_code);
+
+ /** BT SSP Reply - Just Works, Numeric Comparison and Passkey
+ * passkey shall be zero for BT_SSP_VARIANT_PASSKEY_COMPARISON &
+ * BT_SSP_VARIANT_CONSENT
+ * For BT_SSP_VARIANT_PASSKEY_ENTRY, if accept==FALSE, then passkey
+ * shall be zero */
+ int (*ssp_reply)(const bt_bdaddr_t *bd_addr, bt_ssp_variant_t variant,
+ uint8_t accept, uint32_t passkey);
+
+ /** Get Bluetooth profile interface */
+ const void* (*get_profile_interface) (const char *profile_id);
+
+ /** Bluetooth Test Mode APIs - Bluetooth must be enabled for these APIs */
+ /* Configure DUT Mode - Use this mode to enter/exit DUT mode */
+ int (*dut_mode_configure)(uint8_t enable);
+
+ /* Send any test HCI (vendor-specific) command to the controller. Must be in DUT Mode */
+ int (*dut_mode_send)(uint16_t opcode, uint8_t *buf, uint8_t len);
+ /** BLE Test Mode APIs */
+ /* opcode MUST be one of: LE_Receiver_Test, LE_Transmitter_Test, LE_Test_End */
+ int (*le_test_mode)(uint16_t opcode, uint8_t *buf, uint8_t len);
+
+ /* enable or disable bluetooth HCI snoop log */
+ int (*config_hci_snoop_log)(uint8_t enable);
+
+ /** Sets the OS call-out functions that bluedroid needs for alarms and wake locks.
+ * This should be called immediately after a successful |init|.
+ */
+ int (*set_os_callouts)(bt_os_callouts_t *callouts);
+
+ /** Read Energy info details - return value indicates BT_STATUS_SUCCESS or BT_STATUS_NOT_READY
+ * Success indicates that the VSC command was sent to controller
+ */
+ int (*read_energy_info)();
+} bt_interface_t;
+
+/** TODO: Need to add APIs for Service Discovery, Service authorization and
+ * connection management. Also need to add APIs for configuring
+ * properties of remote bonded devices such as name, UUID etc. */
+
+typedef struct {
+ struct hw_device_t common;
+ const bt_interface_t* (*get_bluetooth_interface)();
+} bluetooth_device_t;
+
+typedef bluetooth_device_t bluetooth_module_t;
+__END_DECLS
+
+#endif /* ANDROID_INCLUDE_BLUETOOTH_H */
diff --git a/repo/android/hardware/bt_av.h b/repo/android/hardware/bt_av.h
new file mode 100644
index 0000000..5252a17
--- /dev/null
+++ b/repo/android/hardware/bt_av.h
@@ -0,0 +1,103 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_INCLUDE_BT_AV_H
+#define ANDROID_INCLUDE_BT_AV_H
+
+__BEGIN_DECLS
+
+/* Bluetooth AV connection states */
+typedef enum {
+ BTAV_CONNECTION_STATE_DISCONNECTED = 0,
+ BTAV_CONNECTION_STATE_CONNECTING,
+ BTAV_CONNECTION_STATE_CONNECTED,
+ BTAV_CONNECTION_STATE_DISCONNECTING
+} btav_connection_state_t;
+
+/* Bluetooth AV datapath states */
+typedef enum {
+ BTAV_AUDIO_STATE_REMOTE_SUSPEND = 0,
+ BTAV_AUDIO_STATE_STOPPED,
+ BTAV_AUDIO_STATE_STARTED,
+} btav_audio_state_t;
+
+
+/** Callback for connection state change.
+ * state will have one of the values from btav_connection_state_t
+ */
+typedef void (* btav_connection_state_callback)(btav_connection_state_t state,
+ bt_bdaddr_t *bd_addr);
+
+/** Callback for audiopath state change.
+ * state will have one of the values from btav_audio_state_t
+ */
+typedef void (* btav_audio_state_callback)(btav_audio_state_t state,
+ bt_bdaddr_t *bd_addr);
+
+/** Callback for audio configuration change.
+ * Used only for the A2DP sink interface.
+ * state will have one of the values from btav_audio_state_t
+ * sample_rate: sample rate in Hz
+ * channel_count: number of channels (1 for mono, 2 for stereo)
+ */
+typedef void (* btav_audio_config_callback)(bt_bdaddr_t *bd_addr,
+ uint32_t sample_rate,
+ uint8_t channel_count);
+
+/** BT-AV callback structure. */
+typedef struct {
+ /** set to sizeof(btav_callbacks_t) */
+ size_t size;
+ btav_connection_state_callback connection_state_cb;
+ btav_audio_state_callback audio_state_cb;
+ btav_audio_config_callback audio_config_cb;
+} btav_callbacks_t;
+
+/**
+ * NOTE:
+ *
+ * 1. AVRCP 1.0 shall be supported initially. AVRCP passthrough commands
+ * shall be handled internally via uinput
+ *
+ * 2. A2DP data path shall be handled via a socket pipe between the AudioFlinger
+ * android_audio_hw library and the Bluetooth stack.
+ *
+ */
+/** Represents the standard BT-AV interface.
+ * Used for both the A2DP source and sink interfaces.
+ */
+typedef struct {
+
+ /** set to sizeof(btav_interface_t) */
+ size_t size;
+ /**
+ * Register the BtAv callbacks
+ */
+ bt_status_t (*init)( btav_callbacks_t* callbacks );
+
+ /** connect to headset */
+ bt_status_t (*connect)( bt_bdaddr_t *bd_addr );
+
+ /** dis-connect from headset */
+ bt_status_t (*disconnect)( bt_bdaddr_t *bd_addr );
+
+ /** Closes the interface. */
+ void (*cleanup)( void );
+} btav_interface_t;
+
+__END_DECLS
+
+#endif /* ANDROID_INCLUDE_BT_AV_H */
diff --git a/repo/android/hardware/bt_gatt.h b/repo/android/hardware/bt_gatt.h
new file mode 100644
index 0000000..42e14c2
--- /dev/null
+++ b/repo/android/hardware/bt_gatt.h
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+#ifndef ANDROID_INCLUDE_BT_GATT_H
+#define ANDROID_INCLUDE_BT_GATT_H
+
+#include <stdint.h>
+#include "bt_gatt_client.h"
+#include "bt_gatt_server.h"
+
+__BEGIN_DECLS
+
+/** BT-GATT callbacks */
+typedef struct {
+ /** Set to sizeof(btgatt_callbacks_t) */
+ size_t size;
+
+ /** GATT Client callbacks */
+ const btgatt_client_callbacks_t* client;
+
+ /** GATT Server callbacks */
+ const btgatt_server_callbacks_t* server;
+} btgatt_callbacks_t;
+
+/** Represents the standard Bluetooth GATT interface. */
+typedef struct {
+ /** Set to sizeof(btgatt_interface_t) */
+ size_t size;
+
+ /**
+ * Initializes the interface and provides callback routines
+ */
+ bt_status_t (*init)( const btgatt_callbacks_t* callbacks );
+
+ /** Closes the interface */
+ void (*cleanup)( void );
+
+ /** Pointer to the GATT client interface methods.*/
+ const btgatt_client_interface_t* client;
+
+ /** Pointer to the GATT server interface methods.*/
+ const btgatt_server_interface_t* server;
+} btgatt_interface_t;
+
+__END_DECLS
+
+#endif /* ANDROID_INCLUDE_BT_GATT_H */
diff --git a/repo/android/hardware/bt_gatt_client.h b/repo/android/hardware/bt_gatt_client.h
new file mode 100644
index 0000000..8073dd1
--- /dev/null
+++ b/repo/android/hardware/bt_gatt_client.h
@@ -0,0 +1,417 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+#ifndef ANDROID_INCLUDE_BT_GATT_CLIENT_H
+#define ANDROID_INCLUDE_BT_GATT_CLIENT_H
+
+#include <stdint.h>
+#include "bt_gatt_types.h"
+
+__BEGIN_DECLS
+
+/**
+ * Buffer sizes for maximum attribute length and maximum read/write
+ * operation buffer size.
+ */
+#define BTGATT_MAX_ATTR_LEN 600
+
+/** Buffer type for unformatted reads/writes */
+typedef struct
+{
+ uint8_t value[BTGATT_MAX_ATTR_LEN];
+ uint16_t len;
+} btgatt_unformatted_value_t;
+
+/** Parameters for GATT read operations */
+typedef struct
+{
+ btgatt_srvc_id_t srvc_id;
+ btgatt_gatt_id_t char_id;
+ btgatt_gatt_id_t descr_id;
+ btgatt_unformatted_value_t value;
+ uint16_t value_type;
+ uint8_t status;
+} btgatt_read_params_t;
+
+/** Parameters for GATT write operations */
+typedef struct
+{
+ btgatt_srvc_id_t srvc_id;
+ btgatt_gatt_id_t char_id;
+ btgatt_gatt_id_t descr_id;
+ uint8_t status;
+} btgatt_write_params_t;
+
+/** Attribute change notification parameters */
+typedef struct
+{
+ uint8_t value[BTGATT_MAX_ATTR_LEN];
+ bt_bdaddr_t bda;
+ btgatt_srvc_id_t srvc_id;
+ btgatt_gatt_id_t char_id;
+ uint16_t len;
+ uint8_t is_notify;
+} btgatt_notify_params_t;
+
+typedef struct
+{
+ bt_bdaddr_t *bda1;
+ bt_uuid_t *uuid1;
+ uint16_t u1;
+ uint16_t u2;
+ uint16_t u3;
+ uint16_t u4;
+ uint16_t u5;
+} btgatt_test_params_t;
+
+/** BT-GATT Client callback structure. */
+
+/** Callback invoked in response to register_client */
+typedef void (*register_client_callback)(int status, int client_if,
+ bt_uuid_t *app_uuid);
+
+/** Callback for scan results */
+typedef void (*scan_result_callback)(bt_bdaddr_t* bda, int rssi, uint8_t* adv_data);
+
+/** GATT open callback invoked in response to open */
+typedef void (*connect_callback)(int conn_id, int status, int client_if, bt_bdaddr_t* bda);
+
+/** Callback invoked in response to close */
+typedef void (*disconnect_callback)(int conn_id, int status,
+ int client_if, bt_bdaddr_t* bda);
+
+/**
+ * Invoked in response to search_service when the GATT service search
+ * has been completed.
+ */
+typedef void (*search_complete_callback)(int conn_id, int status);
+
+/** Reports GATT services on a remote device */
+typedef void (*search_result_callback)( int conn_id, btgatt_srvc_id_t *srvc_id);
+
+/** GATT characteristic enumeration result callback */
+typedef void (*get_characteristic_callback)(int conn_id, int status,
+ btgatt_srvc_id_t *srvc_id, btgatt_gatt_id_t *char_id,
+ int char_prop);
+
+/** GATT descriptor enumeration result callback */
+typedef void (*get_descriptor_callback)(int conn_id, int status,
+ btgatt_srvc_id_t *srvc_id, btgatt_gatt_id_t *char_id,
+ btgatt_gatt_id_t *descr_id);
+
+/** GATT included service enumeration result callback */
+typedef void (*get_included_service_callback)(int conn_id, int status,
+ btgatt_srvc_id_t *srvc_id, btgatt_srvc_id_t *incl_srvc_id);
+
+/** Callback invoked in response to [de]register_for_notification */
+typedef void (*register_for_notification_callback)(int conn_id,
+ int registered, int status, btgatt_srvc_id_t *srvc_id,
+ btgatt_gatt_id_t *char_id);
+
+/**
+ * Remote device notification callback, invoked when a remote device sends
+ * a notification or indication that a client has registered for.
+ */
+typedef void (*notify_callback)(int conn_id, btgatt_notify_params_t *p_data);
+
+/** Reports result of a GATT read operation */
+typedef void (*read_characteristic_callback)(int conn_id, int status,
+ btgatt_read_params_t *p_data);
+
+/** GATT write characteristic operation callback */
+typedef void (*write_characteristic_callback)(int conn_id, int status,
+ btgatt_write_params_t *p_data);
+
+/** GATT execute prepared write callback */
+typedef void (*execute_write_callback)(int conn_id, int status);
+
+/** Callback invoked in response to read_descriptor */
+typedef void (*read_descriptor_callback)(int conn_id, int status,
+ btgatt_read_params_t *p_data);
+
+/** Callback invoked in response to write_descriptor */
+typedef void (*write_descriptor_callback)(int conn_id, int status,
+ btgatt_write_params_t *p_data);
+
+/** Callback triggered in response to read_remote_rssi */
+typedef void (*read_remote_rssi_callback)(int client_if, bt_bdaddr_t* bda,
+ int rssi, int status);
+
+/**
+ * Callback indicating the status of a listen() operation
+ */
+typedef void (*listen_callback)(int status, int server_if);
+
+/** Callback invoked when the MTU for a given connection changes */
+typedef void (*configure_mtu_callback)(int conn_id, int status, int mtu);
+
+/** Callback invoked when a scan filter configuration command has completed */
+typedef void (*scan_filter_cfg_callback)(int action, int client_if, int status, int filt_type,
+ int avbl_space);
+
+/** Callback invoked when scan param has been added, cleared, or deleted */
+typedef void (*scan_filter_param_callback)(int action, int client_if, int status,
+ int avbl_space);
+
+/** Callback invoked when a scan filter configuration command has completed */
+typedef void (*scan_filter_status_callback)(int enable, int client_if, int status);
+
+/** Callback invoked when multi-adv enable operation has completed */
+typedef void (*multi_adv_enable_callback)(int client_if, int status);
+
+/** Callback invoked when multi-adv param update operation has completed */
+typedef void (*multi_adv_update_callback)(int client_if, int status);
+
+/** Callback invoked when multi-adv instance data set operation has completed */
+typedef void (*multi_adv_data_callback)(int client_if, int status);
+
+/** Callback invoked when multi-adv disable operation has completed */
+typedef void (*multi_adv_disable_callback)(int client_if, int status);
+
+/**
+ * Callback notifying an application that a remote device connection is currently congested
+ * and cannot receive any more data. An application should avoid sending more data until
+ * a further callback is received indicating the congestion status has been cleared.
+ */
+typedef void (*congestion_callback)(int conn_id, bool congested);
+/** Callback invoked when batchscan storage config operation has completed */
+typedef void (*batchscan_cfg_storage_callback)(int client_if, int status);
+
+/** Callback invoked when batchscan enable / disable operation has completed */
+typedef void (*batchscan_enable_disable_callback)(int action, int client_if, int status);
+
+/** Callback invoked when batchscan reports are obtained */
+typedef void (*batchscan_reports_callback)(int client_if, int status, int report_format,
+ int num_records, int data_len, uint8_t* rep_data);
+
+/** Callback invoked when batchscan storage threshold limit is crossed */
+typedef void (*batchscan_threshold_callback)(int client_if);
+
+/** Track ADV VSE callback invoked when tracked device is found or lost */
+typedef void (*track_adv_event_callback)(int client_if, int filt_index, int addr_type,
+ bt_bdaddr_t* bda, int adv_state);
+
+typedef struct {
+ register_client_callback register_client_cb;
+ scan_result_callback scan_result_cb;
+ connect_callback open_cb;
+ disconnect_callback close_cb;
+ search_complete_callback search_complete_cb;
+ search_result_callback search_result_cb;
+ get_characteristic_callback get_characteristic_cb;
+ get_descriptor_callback get_descriptor_cb;
+ get_included_service_callback get_included_service_cb;
+ register_for_notification_callback register_for_notification_cb;
+ notify_callback notify_cb;
+ read_characteristic_callback read_characteristic_cb;
+ write_characteristic_callback write_characteristic_cb;
+ read_descriptor_callback read_descriptor_cb;
+ write_descriptor_callback write_descriptor_cb;
+ execute_write_callback execute_write_cb;
+ read_remote_rssi_callback read_remote_rssi_cb;
+ listen_callback listen_cb;
+ configure_mtu_callback configure_mtu_cb;
+ scan_filter_cfg_callback scan_filter_cfg_cb;
+ scan_filter_param_callback scan_filter_param_cb;
+ scan_filter_status_callback scan_filter_status_cb;
+ multi_adv_enable_callback multi_adv_enable_cb;
+ multi_adv_update_callback multi_adv_update_cb;
+ multi_adv_data_callback multi_adv_data_cb;
+ multi_adv_disable_callback multi_adv_disable_cb;
+ congestion_callback congestion_cb;
+ batchscan_cfg_storage_callback batchscan_cfg_storage_cb;
+ batchscan_enable_disable_callback batchscan_enb_disable_cb;
+ batchscan_reports_callback batchscan_reports_cb;
+ batchscan_threshold_callback batchscan_threshold_cb;
+ track_adv_event_callback track_adv_event_cb;
+} btgatt_client_callbacks_t;
+
+/** Represents the standard BT-GATT client interface. */
+
+typedef struct {
+ /** Registers a GATT client application with the stack */
+ bt_status_t (*register_client)( bt_uuid_t *uuid );
+
+ /** Unregister a client application from the stack */
+ bt_status_t (*unregister_client)(int client_if );
+
+ /** Start or stop LE device scanning */
+ bt_status_t (*scan)( bool start );
+
+ /** Create a connection to a remote LE or dual-mode device */
+ bt_status_t (*connect)( int client_if, const bt_bdaddr_t *bd_addr,
+ bool is_direct, int transport );
+
+ /** Disconnect a remote device or cancel a pending connection */
+ bt_status_t (*disconnect)( int client_if, const bt_bdaddr_t *bd_addr,
+ int conn_id);
+
+ /** Start or stop advertisements to listen for incoming connections */
+ bt_status_t (*listen)(int client_if, bool start);
+
+ /** Clear the attribute cache for a given device */
+ bt_status_t (*refresh)( int client_if, const bt_bdaddr_t *bd_addr );
+
+ /**
+ * Enumerate all GATT services on a connected device.
+ * Optionally, the results can be filtered for a given UUID.
+ */
+ bt_status_t (*search_service)(int conn_id, bt_uuid_t *filter_uuid );
+
+ /**
+ * Enumerate included services for a given service.
+ * Set start_incl_srvc_id to NULL to get the first included service.
+ */
+ bt_status_t (*get_included_service)( int conn_id, btgatt_srvc_id_t *srvc_id,
+ btgatt_srvc_id_t *start_incl_srvc_id);
+
+ /**
+ * Enumerate characteristics for a given service.
+ * Set start_char_id to NULL to get the first characteristic.
+ */
+ bt_status_t (*get_characteristic)( int conn_id,
+ btgatt_srvc_id_t *srvc_id, btgatt_gatt_id_t *start_char_id);
+
+ /**
+ * Enumerate descriptors for a given characteristic.
+ * Set start_descr_id to NULL to get the first descriptor.
+ */
+ bt_status_t (*get_descriptor)( int conn_id,
+ btgatt_srvc_id_t *srvc_id, btgatt_gatt_id_t *char_id,
+ btgatt_gatt_id_t *start_descr_id);
+
+ /** Read a characteristic on a remote device */
+ bt_status_t (*read_characteristic)( int conn_id,
+ btgatt_srvc_id_t *srvc_id, btgatt_gatt_id_t *char_id,
+ int auth_req );
+
+ /** Write a remote characteristic */
+ bt_status_t (*write_characteristic)(int conn_id,
+ btgatt_srvc_id_t *srvc_id, btgatt_gatt_id_t *char_id,
+ int write_type, int len, int auth_req,
+ char* p_value);
+
+ /** Read the descriptor for a given characteristic */
+ bt_status_t (*read_descriptor)(int conn_id,
+ btgatt_srvc_id_t *srvc_id, btgatt_gatt_id_t *char_id,
+ btgatt_gatt_id_t *descr_id, int auth_req);
+
+ /** Write a remote descriptor for a given characteristic */
+ bt_status_t (*write_descriptor)( int conn_id,
+ btgatt_srvc_id_t *srvc_id, btgatt_gatt_id_t *char_id,
+ btgatt_gatt_id_t *descr_id, int write_type, int len,
+ int auth_req, char* p_value);
+
+ /** Execute a prepared write operation */
+ bt_status_t (*execute_write)(int conn_id, int execute);
+
+ /**
+ * Register to receive notifications or indications for a given
+ * characteristic
+ */
+ bt_status_t (*register_for_notification)( int client_if,
+ const bt_bdaddr_t *bd_addr, btgatt_srvc_id_t *srvc_id,
+ btgatt_gatt_id_t *char_id);
+
+ /** Deregister a previous request for notifications/indications */
+ bt_status_t (*deregister_for_notification)( int client_if,
+ const bt_bdaddr_t *bd_addr, btgatt_srvc_id_t *srvc_id,
+ btgatt_gatt_id_t *char_id);
+
+ /** Request RSSI for a given remote device */
+ bt_status_t (*read_remote_rssi)( int client_if, const bt_bdaddr_t *bd_addr);
+
+ /** Setup scan filter params */
+ bt_status_t (*scan_filter_param_setup)(int client_if, int action, int filt_index, int feat_seln,
+ int list_logic_type, int filt_logic_type, int rssi_high_thres,
+ int rssi_low_thres, int dely_mode, int found_timeout,
+ int lost_timeout, int found_timeout_cnt);
+
+
+ /** Configure a scan filter condition */
+ bt_status_t (*scan_filter_add_remove)(int client_if, int action, int filt_type,
+ int filt_index, int company_id,
+ int company_id_mask, const bt_uuid_t *p_uuid,
+ const bt_uuid_t *p_uuid_mask, const bt_bdaddr_t *bd_addr,
+ char addr_type, int data_len, char* p_data, int mask_len,
+ char* p_mask);
+
+ /** Clear all scan filter conditions for specific filter index*/
+ bt_status_t (*scan_filter_clear)(int client_if, int filt_index);
+
+ /** Enable / disable scan filter feature*/
+ bt_status_t (*scan_filter_enable)(int client_if, bool enable);
+
+ /** Determine the type of the remote device (LE, BR/EDR, Dual-mode) */
+ int (*get_device_type)( const bt_bdaddr_t *bd_addr );
+
+ /** Set the advertising data or scan response data */
+ bt_status_t (*set_adv_data)(int client_if, bool set_scan_rsp, bool include_name,
+ bool include_txpower, int min_interval, int max_interval, int appearance,
+ uint16_t manufacturer_len, char* manufacturer_data,
+ uint16_t service_data_len, char* service_data,
+ uint16_t service_uuid_len, char* service_uuid);
+
+ /** Configure the MTU for a given connection */
+ bt_status_t (*configure_mtu)(int conn_id, int mtu);
+
+ /** Request a connection parameter update */
+ bt_status_t (*conn_parameter_update)(const bt_bdaddr_t *bd_addr, int min_interval,
+ int max_interval, int latency, int timeout);
+
+ /** Sets the LE scan interval and window in units of N*0.625 msec */
+ bt_status_t (*set_scan_parameters)(int scan_interval, int scan_window);
+
+ /* Setup the parameters as per spec, user manual specified values and enable multi ADV */
+ bt_status_t (*multi_adv_enable)(int client_if, int min_interval,int max_interval,int adv_type,
+ int chnl_map, int tx_power, int timeout_s);
+
+ /* Update the parameters as per spec, user manual specified values and restart multi ADV */
+ bt_status_t (*multi_adv_update)(int client_if, int min_interval,int max_interval,int adv_type,
+ int chnl_map, int tx_power, int timeout_s);
+
+ /* Setup the data for the specified instance */
+ bt_status_t (*multi_adv_set_inst_data)(int client_if, bool set_scan_rsp, bool include_name,
+ bool incl_txpower, int appearance, int manufacturer_len,
+ char* manufacturer_data, int service_data_len,
+ char* service_data, int service_uuid_len, char* service_uuid);
+
+ /* Disable the multi adv instance */
+ bt_status_t (*multi_adv_disable)(int client_if);
+
+ /* Configure the batchscan storage */
+ bt_status_t (*batchscan_cfg_storage)(int client_if, int batch_scan_full_max,
+ int batch_scan_trunc_max, int batch_scan_notify_threshold);
+
+ /* Enable batchscan */
+ bt_status_t (*batchscan_enb_batch_scan)(int client_if, int scan_mode,
+ int scan_interval, int scan_window, int addr_type, int discard_rule);
+
+ /* Disable batchscan */
+ bt_status_t (*batchscan_dis_batch_scan)(int client_if);
+
+ /* Read out batchscan reports */
+ bt_status_t (*batchscan_read_reports)(int client_if, int scan_mode);
+
+ /** Test mode interface */
+ bt_status_t (*test_command)( int command, btgatt_test_params_t* params);
+
+} btgatt_client_interface_t;
+
+__END_DECLS
+
+#endif /* ANDROID_INCLUDE_BT_GATT_CLIENT_H */
diff --git a/repo/android/hardware/bt_gatt_server.h b/repo/android/hardware/bt_gatt_server.h
new file mode 100644
index 0000000..0d6cc1e
--- /dev/null
+++ b/repo/android/hardware/bt_gatt_server.h
@@ -0,0 +1,196 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+#ifndef ANDROID_INCLUDE_BT_GATT_SERVER_H
+#define ANDROID_INCLUDE_BT_GATT_SERVER_H
+
+#include <stdint.h>
+
+#include "bt_gatt_types.h"
+
+__BEGIN_DECLS
+
+/** GATT value type used in response to remote read requests */
+typedef struct
+{
+ uint8_t value[BTGATT_MAX_ATTR_LEN];
+ uint16_t handle;
+ uint16_t offset;
+ uint16_t len;
+ uint8_t auth_req;
+} btgatt_value_t;
+
+/** GATT remote read request response type */
+typedef union
+{
+ btgatt_value_t attr_value;
+ uint16_t handle;
+} btgatt_response_t;
+
+/** BT-GATT Server callback structure. */
+
+/** Callback invoked in response to register_server */
+typedef void (*register_server_callback)(int status, int server_if,
+ bt_uuid_t *app_uuid);
+
+/** Callback indicating that a remote device has connected or been disconnected */
+typedef void (*connection_callback)(int conn_id, int server_if, int connected,
+ bt_bdaddr_t *bda);
+
+/** Callback invoked in response to create_service */
+typedef void (*service_added_callback)(int status, int server_if,
+ btgatt_srvc_id_t *srvc_id, int srvc_handle);
+
+/** Callback indicating that an included service has been added to a service */
+typedef void (*included_service_added_callback)(int status, int server_if,
+ int srvc_handle, int incl_srvc_handle);
+
+/** Callback invoked when a characteristic has been added to a service */
+typedef void (*characteristic_added_callback)(int status, int server_if,
+ bt_uuid_t *uuid, int srvc_handle, int char_handle);
+
+/** Callback invoked when a descriptor has been added to a characteristic */
+typedef void (*descriptor_added_callback)(int status, int server_if,
+ bt_uuid_t *uuid, int srvc_handle, int descr_handle);
+
+/** Callback invoked in response to start_service */
+typedef void (*service_started_callback)(int status, int server_if,
+ int srvc_handle);
+
+/** Callback invoked in response to stop_service */
+typedef void (*service_stopped_callback)(int status, int server_if,
+ int srvc_handle);
+
+/** Callback triggered when a service has been deleted */
+typedef void (*service_deleted_callback)(int status, int server_if,
+ int srvc_handle);
+
+/**
+ * Callback invoked when a remote device has requested to read a characteristic
+ * or descriptor. The application must respond by calling send_response
+ */
+typedef void (*request_read_callback)(int conn_id, int trans_id, bt_bdaddr_t *bda,
+ int attr_handle, int offset, bool is_long);
+
+/**
+ * Callback invoked when a remote device has requested to write to a
+ * characteristic or descriptor.
+ */
+typedef void (*request_write_callback)(int conn_id, int trans_id, bt_bdaddr_t *bda,
+ int attr_handle, int offset, int length,
+ bool need_rsp, bool is_prep, uint8_t* value);
+
+/** Callback invoked when a previously prepared write is to be executed */
+typedef void (*request_exec_write_callback)(int conn_id, int trans_id,
+ bt_bdaddr_t *bda, int exec_write);
+
+/**
+ * Callback triggered in response to send_response if the remote device
+ * sends a confirmation.
+ */
+typedef void (*response_confirmation_callback)(int status, int handle);
+
+/**
+ * Callback confirming that a notification or indication has been sent
+ * to a remote device.
+ */
+typedef void (*indication_sent_callback)(int conn_id, int status);
+
+/**
+ * Callback notifying an application that a remote device connection is currently congested
+ * and cannot receive any more data. An application should avoid sending more data until
+ * a further callback is received indicating the congestion status has been cleared.
+ */
+typedef void (*congestion_callback)(int conn_id, bool congested);
+
+/** Callback invoked when the MTU for a given connection changes */
+typedef void (*mtu_changed_callback)(int conn_id, int mtu);
+
+typedef struct {
+ register_server_callback register_server_cb;
+ connection_callback connection_cb;
+ service_added_callback service_added_cb;
+ included_service_added_callback included_service_added_cb;
+ characteristic_added_callback characteristic_added_cb;
+ descriptor_added_callback descriptor_added_cb;
+ service_started_callback service_started_cb;
+ service_stopped_callback service_stopped_cb;
+ service_deleted_callback service_deleted_cb;
+ request_read_callback request_read_cb;
+ request_write_callback request_write_cb;
+ request_exec_write_callback request_exec_write_cb;
+ response_confirmation_callback response_confirmation_cb;
+ indication_sent_callback indication_sent_cb;
+ congestion_callback congestion_cb;
+ mtu_changed_callback mtu_changed_cb;
+} btgatt_server_callbacks_t;
+
+/** Represents the standard BT-GATT server interface. */
+typedef struct {
+ /** Registers a GATT server application with the stack */
+ bt_status_t (*register_server)( bt_uuid_t *uuid );
+
+ /** Unregister a server application from the stack */
+ bt_status_t (*unregister_server)(int server_if );
+
+ /** Create a connection to a remote peripheral */
+ bt_status_t (*connect)(int server_if, const bt_bdaddr_t *bd_addr,
+ bool is_direct, int transport);
+
+ /** Disconnect an established connection or cancel a pending one */
+ bt_status_t (*disconnect)(int server_if, const bt_bdaddr_t *bd_addr,
+ int conn_id );
+
+ /** Create a new service */
+ bt_status_t (*add_service)( int server_if, btgatt_srvc_id_t *srvc_id, int num_handles);
+
+ /** Assign an included service to it's parent service */
+ bt_status_t (*add_included_service)( int server_if, int service_handle, int included_handle);
+
+ /** Add a characteristic to a service */
+ bt_status_t (*add_characteristic)( int server_if,
+ int service_handle, bt_uuid_t *uuid,
+ int properties, int permissions);
+
+ /** Add a descriptor to a given service */
+ bt_status_t (*add_descriptor)(int server_if, int service_handle,
+ bt_uuid_t *uuid, int permissions);
+
+ /** Starts a local service */
+ bt_status_t (*start_service)(int server_if, int service_handle,
+ int transport);
+
+ /** Stops a local service */
+ bt_status_t (*stop_service)(int server_if, int service_handle);
+
+ /** Delete a local service */
+ bt_status_t (*delete_service)(int server_if, int service_handle);
+
+ /** Send value indication to a remote device */
+ bt_status_t (*send_indication)(int server_if, int attribute_handle,
+ int conn_id, int len, int confirm,
+ char* p_value);
+
+ /** Send a response to a read/write operation */
+ bt_status_t (*send_response)(int conn_id, int trans_id,
+ int status, btgatt_response_t *response);
+
+} btgatt_server_interface_t;
+
+__END_DECLS
+
+#endif /* ANDROID_INCLUDE_BT_GATT_CLIENT_H */
diff --git a/repo/android/hardware/bt_gatt_types.h b/repo/android/hardware/bt_gatt_types.h
new file mode 100644
index 0000000..e037ddc
--- /dev/null
+++ b/repo/android/hardware/bt_gatt_types.h
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+#ifndef ANDROID_INCLUDE_BT_GATT_TYPES_H
+#define ANDROID_INCLUDE_BT_GATT_TYPES_H
+
+#include <stdint.h>
+#include <stdbool.h>
+
+__BEGIN_DECLS
+
+/**
+ * GATT Service types
+ */
+#define BTGATT_SERVICE_TYPE_PRIMARY 0
+#define BTGATT_SERVICE_TYPE_SECONDARY 1
+
+/** GATT ID adding instance id tracking to the UUID */
+typedef struct
+{
+ bt_uuid_t uuid;
+ uint8_t inst_id;
+} btgatt_gatt_id_t;
+
+/** GATT Service ID also identifies the service type (primary/secondary) */
+typedef struct
+{
+ btgatt_gatt_id_t id;
+ uint8_t is_primary;
+} btgatt_srvc_id_t;
+
+/** Preferred physical Transport for GATT connection */
+typedef enum
+{
+ GATT_TRANSPORT_AUTO,
+ GATT_TRANSPORT_BREDR,
+ GATT_TRANSPORT_LE
+} btgatt_transport_t;
+
+__END_DECLS
+
+#endif /* ANDROID_INCLUDE_BT_GATT_TYPES_H */
diff --git a/repo/android/hardware/bt_hf.h b/repo/android/hardware/bt_hf.h
new file mode 100644
index 0000000..7dcb40a
--- /dev/null
+++ b/repo/android/hardware/bt_hf.h
@@ -0,0 +1,301 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_INCLUDE_BT_HF_H
+#define ANDROID_INCLUDE_BT_HF_H
+
+__BEGIN_DECLS
+
+/* AT response code - OK/Error */
+typedef enum {
+ BTHF_AT_RESPONSE_ERROR = 0,
+ BTHF_AT_RESPONSE_OK
+} bthf_at_response_t;
+
+typedef enum {
+ BTHF_CONNECTION_STATE_DISCONNECTED = 0,
+ BTHF_CONNECTION_STATE_CONNECTING,
+ BTHF_CONNECTION_STATE_CONNECTED,
+ BTHF_CONNECTION_STATE_SLC_CONNECTED,
+ BTHF_CONNECTION_STATE_DISCONNECTING
+} bthf_connection_state_t;
+
+typedef enum {
+ BTHF_AUDIO_STATE_DISCONNECTED = 0,
+ BTHF_AUDIO_STATE_CONNECTING,
+ BTHF_AUDIO_STATE_CONNECTED,
+ BTHF_AUDIO_STATE_DISCONNECTING
+} bthf_audio_state_t;
+
+typedef enum {
+ BTHF_VR_STATE_STOPPED = 0,
+ BTHF_VR_STATE_STARTED
+} bthf_vr_state_t;
+
+typedef enum {
+ BTHF_VOLUME_TYPE_SPK = 0,
+ BTHF_VOLUME_TYPE_MIC
+} bthf_volume_type_t;
+
+/* Noise Reduction and Echo Cancellation */
+typedef enum
+{
+ BTHF_NREC_STOP,
+ BTHF_NREC_START
+} bthf_nrec_t;
+
+/* WBS codec setting */
+typedef enum
+{
+ BTHF_WBS_NONE,
+ BTHF_WBS_NO,
+ BTHF_WBS_YES
+}bthf_wbs_config_t;
+
+/* CHLD - Call held handling */
+typedef enum
+{
+ BTHF_CHLD_TYPE_RELEASEHELD, // Terminate all held or set UDUB("busy") to a waiting call
+ BTHF_CHLD_TYPE_RELEASEACTIVE_ACCEPTHELD, // Terminate all active calls and accepts a waiting/held call
+ BTHF_CHLD_TYPE_HOLDACTIVE_ACCEPTHELD, // Hold all active calls and accepts a waiting/held call
+ BTHF_CHLD_TYPE_ADDHELDTOCONF, // Add all held calls to a conference
+} bthf_chld_type_t;
+
+/** Callback for connection state change.
+ * state will have one of the values from BtHfConnectionState
+ */
+typedef void (* bthf_connection_state_callback)(bthf_connection_state_t state, bt_bdaddr_t *bd_addr);
+
+/** Callback for audio connection state change.
+ * state will have one of the values from BtHfAudioState
+ */
+typedef void (* bthf_audio_state_callback)(bthf_audio_state_t state, bt_bdaddr_t *bd_addr);
+
+/** Callback for VR connection state change.
+ * state will have one of the values from BtHfVRState
+ */
+typedef void (* bthf_vr_cmd_callback)(bthf_vr_state_t state, bt_bdaddr_t *bd_addr);
+
+/** Callback for answer incoming call (ATA)
+ */
+typedef void (* bthf_answer_call_cmd_callback)(bt_bdaddr_t *bd_addr);
+
+/** Callback for disconnect call (AT+CHUP)
+ */
+typedef void (* bthf_hangup_call_cmd_callback)(bt_bdaddr_t *bd_addr);
+
+/** Callback for disconnect call (AT+CHUP)
+ * type will denote Speaker/Mic gain (BtHfVolumeControl).
+ */
+typedef void (* bthf_volume_cmd_callback)(bthf_volume_type_t type, int volume, bt_bdaddr_t *bd_addr);
+
+/** Callback for dialing an outgoing call
+ * If number is NULL, redial
+ */
+typedef void (* bthf_dial_call_cmd_callback)(char *number, bt_bdaddr_t *bd_addr);
+
+/** Callback for sending DTMF tones
+ * tone contains the dtmf character to be sent
+ */
+typedef void (* bthf_dtmf_cmd_callback)(char tone, bt_bdaddr_t *bd_addr);
+
+/** Callback for enabling/disabling noise reduction/echo cancellation
+ * value will be 1 to enable, 0 to disable
+ */
+typedef void (* bthf_nrec_cmd_callback)(bthf_nrec_t nrec, bt_bdaddr_t *bd_addr);
+
+/** Callback for AT+BCS and event from BAC
+ * WBS enable, WBS disable
+ */
+typedef void (* bthf_wbs_callback)(bthf_wbs_config_t wbs, bt_bdaddr_t *bd_addr);
+
+/** Callback for call hold handling (AT+CHLD)
+ * value will contain the call hold command (0, 1, 2, 3)
+ */
+typedef void (* bthf_chld_cmd_callback)(bthf_chld_type_t chld, bt_bdaddr_t *bd_addr);
+
+/** Callback for CNUM (subscriber number)
+ */
+typedef void (* bthf_cnum_cmd_callback)(bt_bdaddr_t *bd_addr);
+
+/** Callback for indicators (CIND)
+ */
+typedef void (* bthf_cind_cmd_callback)(bt_bdaddr_t *bd_addr);
+
+/** Callback for operator selection (COPS)
+ */
+typedef void (* bthf_cops_cmd_callback)(bt_bdaddr_t *bd_addr);
+
+/** Callback for call list (AT+CLCC)
+ */
+typedef void (* bthf_clcc_cmd_callback) (bt_bdaddr_t *bd_addr);
+
+/** Callback for unknown AT command recd from HF
+ * at_string will contain the unparsed AT string
+ */
+typedef void (* bthf_unknown_at_cmd_callback)(char *at_string, bt_bdaddr_t *bd_addr);
+
+/** Callback for keypressed (HSP) event.
+ */
+typedef void (* bthf_key_pressed_cmd_callback)(bt_bdaddr_t *bd_addr);
+
+/** BT-HF callback structure. */
+typedef struct {
+ /** set to sizeof(BtHfCallbacks) */
+ size_t size;
+ bthf_connection_state_callback connection_state_cb;
+ bthf_audio_state_callback audio_state_cb;
+ bthf_vr_cmd_callback vr_cmd_cb;
+ bthf_answer_call_cmd_callback answer_call_cmd_cb;
+ bthf_hangup_call_cmd_callback hangup_call_cmd_cb;
+ bthf_volume_cmd_callback volume_cmd_cb;
+ bthf_dial_call_cmd_callback dial_call_cmd_cb;
+ bthf_dtmf_cmd_callback dtmf_cmd_cb;
+ bthf_nrec_cmd_callback nrec_cmd_cb;
+ bthf_wbs_callback wbs_cb;
+ bthf_chld_cmd_callback chld_cmd_cb;
+ bthf_cnum_cmd_callback cnum_cmd_cb;
+ bthf_cind_cmd_callback cind_cmd_cb;
+ bthf_cops_cmd_callback cops_cmd_cb;
+ bthf_clcc_cmd_callback clcc_cmd_cb;
+ bthf_unknown_at_cmd_callback unknown_at_cmd_cb;
+ bthf_key_pressed_cmd_callback key_pressed_cmd_cb;
+} bthf_callbacks_t;
+
+/** Network Status */
+typedef enum
+{
+ BTHF_NETWORK_STATE_NOT_AVAILABLE = 0,
+ BTHF_NETWORK_STATE_AVAILABLE
+} bthf_network_state_t;
+
+/** Service type */
+typedef enum
+{
+ BTHF_SERVICE_TYPE_HOME = 0,
+ BTHF_SERVICE_TYPE_ROAMING
+} bthf_service_type_t;
+
+typedef enum {
+ BTHF_CALL_STATE_ACTIVE = 0,
+ BTHF_CALL_STATE_HELD,
+ BTHF_CALL_STATE_DIALING,
+ BTHF_CALL_STATE_ALERTING,
+ BTHF_CALL_STATE_INCOMING,
+ BTHF_CALL_STATE_WAITING,
+ BTHF_CALL_STATE_IDLE
+} bthf_call_state_t;
+
+typedef enum {
+ BTHF_CALL_DIRECTION_OUTGOING = 0,
+ BTHF_CALL_DIRECTION_INCOMING
+} bthf_call_direction_t;
+
+typedef enum {
+ BTHF_CALL_TYPE_VOICE = 0,
+ BTHF_CALL_TYPE_DATA,
+ BTHF_CALL_TYPE_FAX
+} bthf_call_mode_t;
+
+typedef enum {
+ BTHF_CALL_MPTY_TYPE_SINGLE = 0,
+ BTHF_CALL_MPTY_TYPE_MULTI
+} bthf_call_mpty_type_t;
+
+typedef enum {
+ BTHF_CALL_ADDRTYPE_UNKNOWN = 0x81,
+ BTHF_CALL_ADDRTYPE_INTERNATIONAL = 0x91
+} bthf_call_addrtype_t;
+/** Represents the standard BT-HF interface. */
+typedef struct {
+
+ /** set to sizeof(BtHfInterface) */
+ size_t size;
+ /**
+ * Register the BtHf callbacks
+ */
+ bt_status_t (*init)( bthf_callbacks_t* callbacks, int max_hf_clients);
+
+ /** connect to headset */
+ bt_status_t (*connect)( bt_bdaddr_t *bd_addr );
+
+ /** dis-connect from headset */
+ bt_status_t (*disconnect)( bt_bdaddr_t *bd_addr );
+
+ /** create an audio connection */
+ bt_status_t (*connect_audio)( bt_bdaddr_t *bd_addr );
+
+ /** close the audio connection */
+ bt_status_t (*disconnect_audio)( bt_bdaddr_t *bd_addr );
+
+ /** start voice recognition */
+ bt_status_t (*start_voice_recognition)( bt_bdaddr_t *bd_addr );
+
+ /** stop voice recognition */
+ bt_status_t (*stop_voice_recognition)( bt_bdaddr_t *bd_addr );
+
+ /** volume control */
+ bt_status_t (*volume_control) (bthf_volume_type_t type, int volume, bt_bdaddr_t *bd_addr );
+
+ /** Combined device status change notification */
+ bt_status_t (*device_status_notification)(bthf_network_state_t ntk_state, bthf_service_type_t svc_type, int signal,
+ int batt_chg);
+
+ /** Response for COPS command */
+ bt_status_t (*cops_response)(const char *cops, bt_bdaddr_t *bd_addr );
+
+ /** Response for CIND command */
+ bt_status_t (*cind_response)(int svc, int num_active, int num_held, bthf_call_state_t call_setup_state,
+ int signal, int roam, int batt_chg, bt_bdaddr_t *bd_addr );
+
+ /** Pre-formatted AT response, typically in response to unknown AT cmd */
+ bt_status_t (*formatted_at_response)(const char *rsp, bt_bdaddr_t *bd_addr );
+
+ /** ok/error response
+ * ERROR (0)
+ * OK (1)
+ */
+ bt_status_t (*at_response) (bthf_at_response_t response_code, int error_code, bt_bdaddr_t *bd_addr );
+
+ /** response for CLCC command
+ * Can be iteratively called for each call index
+ * Call index of 0 will be treated as NULL termination (Completes response)
+ */
+ bt_status_t (*clcc_response) (int index, bthf_call_direction_t dir,
+ bthf_call_state_t state, bthf_call_mode_t mode,
+ bthf_call_mpty_type_t mpty, const char *number,
+ bthf_call_addrtype_t type, bt_bdaddr_t *bd_addr );
+
+ /** notify of a call state change
+ * Each update notifies
+ * 1. Number of active/held/ringing calls
+ * 2. call_state: This denotes the state change that triggered this msg
+ * This will take one of the values from BtHfCallState
+ * 3. number & type: valid only for incoming & waiting call
+ */
+ bt_status_t (*phone_state_change) (int num_active, int num_held, bthf_call_state_t call_setup_state,
+ const char *number, bthf_call_addrtype_t type);
+
+ /** Closes the interface. */
+ void (*cleanup)( void );
+
+ /** configureation for the SCO codec */
+ bt_status_t (*configure_wbs)( bt_bdaddr_t *bd_addr ,bthf_wbs_config_t config );
+} bthf_interface_t;
+
+__END_DECLS
+
+#endif /* ANDROID_INCLUDE_BT_HF_H */
diff --git a/repo/android/hardware/bt_hf_client.h b/repo/android/hardware/bt_hf_client.h
new file mode 100644
index 0000000..8acf1b2
--- /dev/null
+++ b/repo/android/hardware/bt_hf_client.h
@@ -0,0 +1,363 @@
+/*
+ * Copyright (C) 2012-2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_INCLUDE_BT_HF_CLIENT_H
+#define ANDROID_INCLUDE_BT_HF_CLIENT_H
+
+__BEGIN_DECLS
+
+typedef enum {
+ BTHF_CLIENT_CONNECTION_STATE_DISCONNECTED = 0,
+ BTHF_CLIENT_CONNECTION_STATE_CONNECTING,
+ BTHF_CLIENT_CONNECTION_STATE_CONNECTED,
+ BTHF_CLIENT_CONNECTION_STATE_SLC_CONNECTED,
+ BTHF_CLIENT_CONNECTION_STATE_DISCONNECTING
+} bthf_client_connection_state_t;
+
+typedef enum {
+ BTHF_CLIENT_AUDIO_STATE_DISCONNECTED = 0,
+ BTHF_CLIENT_AUDIO_STATE_CONNECTING,
+ BTHF_CLIENT_AUDIO_STATE_CONNECTED,
+ BTHF_CLIENT_AUDIO_STATE_CONNECTED_MSBC,
+} bthf_client_audio_state_t;
+
+typedef enum {
+ BTHF_CLIENT_VR_STATE_STOPPED = 0,
+ BTHF_CLIENT_VR_STATE_STARTED
+} bthf_client_vr_state_t;
+
+typedef enum {
+ BTHF_CLIENT_VOLUME_TYPE_SPK = 0,
+ BTHF_CLIENT_VOLUME_TYPE_MIC
+} bthf_client_volume_type_t;
+
+typedef enum
+{
+ BTHF_CLIENT_NETWORK_STATE_NOT_AVAILABLE = 0,
+ BTHF_CLIENT_NETWORK_STATE_AVAILABLE
+} bthf_client_network_state_t;
+
+typedef enum
+{
+ BTHF_CLIENT_SERVICE_TYPE_HOME = 0,
+ BTHF_CLIENT_SERVICE_TYPE_ROAMING
+} bthf_client_service_type_t;
+
+typedef enum {
+ BTHF_CLIENT_CALL_STATE_ACTIVE = 0,
+ BTHF_CLIENT_CALL_STATE_HELD,
+ BTHF_CLIENT_CALL_STATE_DIALING,
+ BTHF_CLIENT_CALL_STATE_ALERTING,
+ BTHF_CLIENT_CALL_STATE_INCOMING,
+ BTHF_CLIENT_CALL_STATE_WAITING,
+ BTHF_CLIENT_CALL_STATE_HELD_BY_RESP_HOLD,
+} bthf_client_call_state_t;
+
+typedef enum {
+ BTHF_CLIENT_CALL_NO_CALLS_IN_PROGRESS = 0,
+ BTHF_CLIENT_CALL_CALLS_IN_PROGRESS
+} bthf_client_call_t;
+
+typedef enum {
+ BTHF_CLIENT_CALLSETUP_NONE = 0,
+ BTHF_CLIENT_CALLSETUP_INCOMING,
+ BTHF_CLIENT_CALLSETUP_OUTGOING,
+ BTHF_CLIENT_CALLSETUP_ALERTING
+
+} bthf_client_callsetup_t;
+
+typedef enum {
+ BTHF_CLIENT_CALLHELD_NONE = 0,
+ BTHF_CLIENT_CALLHELD_HOLD_AND_ACTIVE,
+ BTHF_CLIENT_CALLHELD_HOLD,
+} bthf_client_callheld_t;
+
+typedef enum {
+ BTHF_CLIENT_RESP_AND_HOLD_HELD = 0,
+ BTRH_CLIENT_RESP_AND_HOLD_ACCEPT,
+ BTRH_CLIENT_RESP_AND_HOLD_REJECT,
+} bthf_client_resp_and_hold_t;
+
+typedef enum {
+ BTHF_CLIENT_CALL_DIRECTION_OUTGOING = 0,
+ BTHF_CLIENT_CALL_DIRECTION_INCOMING
+} bthf_client_call_direction_t;
+
+typedef enum {
+ BTHF_CLIENT_CALL_MPTY_TYPE_SINGLE = 0,
+ BTHF_CLIENT_CALL_MPTY_TYPE_MULTI
+} bthf_client_call_mpty_type_t;
+
+typedef enum {
+ BTHF_CLIENT_CMD_COMPLETE_OK = 0,
+ BTHF_CLIENT_CMD_COMPLETE_ERROR,
+ BTHF_CLIENT_CMD_COMPLETE_ERROR_NO_CARRIER,
+ BTHF_CLIENT_CMD_COMPLETE_ERROR_BUSY,
+ BTHF_CLIENT_CMD_COMPLETE_ERROR_NO_ANSWER,
+ BTHF_CLIENT_CMD_COMPLETE_ERROR_DELAYED,
+ BTHF_CLIENT_CMD_COMPLETE_ERROR_BLACKLISTED,
+ BTHF_CLIENT_CMD_COMPLETE_ERROR_CME
+} bthf_client_cmd_complete_t;
+
+typedef enum {
+ BTHF_CLIENT_CALL_ACTION_CHLD_0 = 0,
+ BTHF_CLIENT_CALL_ACTION_CHLD_1,
+ BTHF_CLIENT_CALL_ACTION_CHLD_2,
+ BTHF_CLIENT_CALL_ACTION_CHLD_3,
+ BTHF_CLIENT_CALL_ACTION_CHLD_4,
+ BTHF_CLIENT_CALL_ACTION_CHLD_1x,
+ BTHF_CLIENT_CALL_ACTION_CHLD_2x,
+ BTHF_CLIENT_CALL_ACTION_ATA,
+ BTHF_CLIENT_CALL_ACTION_CHUP,
+ BTHF_CLIENT_CALL_ACTION_BTRH_0,
+ BTHF_CLIENT_CALL_ACTION_BTRH_1,
+ BTHF_CLIENT_CALL_ACTION_BTRH_2,
+} bthf_client_call_action_t;
+
+typedef enum {
+ BTHF_CLIENT_SERVICE_UNKNOWN = 0,
+ BTHF_CLIENT_SERVICE_VOICE,
+ BTHF_CLIENT_SERVICE_FAX
+} bthf_client_subscriber_service_type_t;
+
+typedef enum {
+ BTHF_CLIENT_IN_BAND_RINGTONE_NOT_PROVIDED = 0,
+ BTHF_CLIENT_IN_BAND_RINGTONE_PROVIDED,
+} bthf_client_in_band_ring_state_t;
+
+/* Peer features masks */
+#define BTHF_CLIENT_PEER_FEAT_3WAY 0x00000001 /* Three-way calling */
+#define BTHF_CLIENT_PEER_FEAT_ECNR 0x00000002 /* Echo cancellation and/or noise reduction */
+#define BTHF_CLIENT_PEER_FEAT_VREC 0x00000004 /* Voice recognition */
+#define BTHF_CLIENT_PEER_FEAT_INBAND 0x00000008 /* In-band ring tone */
+#define BTHF_CLIENT_PEER_FEAT_VTAG 0x00000010 /* Attach a phone number to a voice tag */
+#define BTHF_CLIENT_PEER_FEAT_REJECT 0x00000020 /* Ability to reject incoming call */
+#define BTHF_CLIENT_PEER_FEAT_ECS 0x00000040 /* Enhanced Call Status */
+#define BTHF_CLIENT_PEER_FEAT_ECC 0x00000080 /* Enhanced Call Control */
+#define BTHF_CLIENT_PEER_FEAT_EXTERR 0x00000100 /* Extended error codes */
+#define BTHF_CLIENT_PEER_FEAT_CODEC 0x00000200 /* Codec Negotiation */
+
+/* Peer call handling features masks */
+#define BTHF_CLIENT_CHLD_FEAT_REL 0x00000001 /* 0 Release waiting call or held calls */
+#define BTHF_CLIENT_CHLD_FEAT_REL_ACC 0x00000002 /* 1 Release active calls and accept other
+ (waiting or held) cal */
+#define BTHF_CLIENT_CHLD_FEAT_REL_X 0x00000004 /* 1x Release specified active call only */
+#define BTHF_CLIENT_CHLD_FEAT_HOLD_ACC 0x00000008 /* 2 Active calls on hold and accept other
+ (waiting or held) call */
+#define BTHF_CLIENT_CHLD_FEAT_PRIV_X 0x00000010 /* 2x Request private mode with specified
+ call (put the rest on hold) */
+#define BTHF_CLIENT_CHLD_FEAT_MERGE 0x00000020 /* 3 Add held call to multiparty */
+#define BTHF_CLIENT_CHLD_FEAT_MERGE_DETACH 0x00000040 /* 4 Connect two calls and leave
+ (disconnect from) multiparty */
+
+/** Callback for connection state change.
+ * state will have one of the values from BtHfConnectionState
+ * peer/chld_features are valid only for BTHF_CLIENT_CONNECTION_STATE_SLC_CONNECTED state
+ */
+typedef void (* bthf_client_connection_state_callback)(bthf_client_connection_state_t state,
+ unsigned int peer_feat,
+ unsigned int chld_feat,
+ bt_bdaddr_t *bd_addr);
+
+/** Callback for audio connection state change.
+ * state will have one of the values from BtHfAudioState
+ */
+typedef void (* bthf_client_audio_state_callback)(bthf_client_audio_state_t state,
+ bt_bdaddr_t *bd_addr);
+
+/** Callback for VR connection state change.
+ * state will have one of the values from BtHfVRState
+ */
+typedef void (* bthf_client_vr_cmd_callback)(bthf_client_vr_state_t state);
+
+/** Callback for network state change
+ */
+typedef void (* bthf_client_network_state_callback) (bthf_client_network_state_t state);
+
+/** Callback for network roaming status change
+ */
+typedef void (* bthf_client_network_roaming_callback) (bthf_client_service_type_t type);
+
+/** Callback for signal strength indication
+ */
+typedef void (* bthf_client_network_signal_callback) (int signal_strength);
+
+/** Callback for battery level indication
+ */
+typedef void (* bthf_client_battery_level_callback) (int battery_level);
+
+/** Callback for current operator name
+ */
+typedef void (* bthf_client_current_operator_callback) (const char *name);
+
+/** Callback for call indicator
+ */
+typedef void (* bthf_client_call_callback) (bthf_client_call_t call);
+
+/** Callback for callsetup indicator
+ */
+typedef void (* bthf_client_callsetup_callback) (bthf_client_callsetup_t callsetup);
+
+/** Callback for callheld indicator
+ */
+typedef void (* bthf_client_callheld_callback) (bthf_client_callheld_t callheld);
+
+/** Callback for response and hold
+ */
+typedef void (* bthf_client_resp_and_hold_callback) (bthf_client_resp_and_hold_t resp_and_hold);
+
+/** Callback for Calling Line Identification notification
+ * Will be called only when there is an incoming call and number is provided.
+ */
+typedef void (* bthf_client_clip_callback) (const char *number);
+
+/**
+ * Callback for Call Waiting notification
+ */
+typedef void (* bthf_client_call_waiting_callback) (const char *number);
+
+/**
+ * Callback for listing current calls. Can be called multiple time.
+ * If number is unknown NULL is passed.
+ */
+typedef void (*bthf_client_current_calls) (int index, bthf_client_call_direction_t dir,
+ bthf_client_call_state_t state,
+ bthf_client_call_mpty_type_t mpty,
+ const char *number);
+
+/** Callback for audio volume change
+ */
+typedef void (*bthf_client_volume_change_callback) (bthf_client_volume_type_t type, int volume);
+
+/** Callback for command complete event
+ * cme is valid only for BTHF_CLIENT_CMD_COMPLETE_ERROR_CME type
+ */
+typedef void (*bthf_client_cmd_complete_callback) (bthf_client_cmd_complete_t type, int cme);
+
+/** Callback for subscriber information
+ */
+typedef void (* bthf_client_subscriber_info_callback) (const char *name,
+ bthf_client_subscriber_service_type_t type);
+
+/** Callback for in-band ring tone settings
+ */
+typedef void (* bthf_client_in_band_ring_tone_callback) (bthf_client_in_band_ring_state_t state);
+
+/**
+ * Callback for requested number from AG
+ */
+typedef void (* bthf_client_last_voice_tag_number_callback) (const char *number);
+
+/**
+ * Callback for sending ring indication to app
+ */
+typedef void (* bthf_client_ring_indication_callback) (void);
+
+/** BT-HF callback structure. */
+typedef struct {
+ /** set to sizeof(BtHfClientCallbacks) */
+ size_t size;
+ bthf_client_connection_state_callback connection_state_cb;
+ bthf_client_audio_state_callback audio_state_cb;
+ bthf_client_vr_cmd_callback vr_cmd_cb;
+ bthf_client_network_state_callback network_state_cb;
+ bthf_client_network_roaming_callback network_roaming_cb;
+ bthf_client_network_signal_callback network_signal_cb;
+ bthf_client_battery_level_callback battery_level_cb;
+ bthf_client_current_operator_callback current_operator_cb;
+ bthf_client_call_callback call_cb;
+ bthf_client_callsetup_callback callsetup_cb;
+ bthf_client_callheld_callback callheld_cb;
+ bthf_client_resp_and_hold_callback resp_and_hold_cb;
+ bthf_client_clip_callback clip_cb;
+ bthf_client_call_waiting_callback call_waiting_cb;
+ bthf_client_current_calls current_calls_cb;
+ bthf_client_volume_change_callback volume_change_cb;
+ bthf_client_cmd_complete_callback cmd_complete_cb;
+ bthf_client_subscriber_info_callback subscriber_info_cb;
+ bthf_client_in_band_ring_tone_callback in_band_ring_tone_cb;
+ bthf_client_last_voice_tag_number_callback last_voice_tag_number_callback;
+ bthf_client_ring_indication_callback ring_indication_cb;
+} bthf_client_callbacks_t;
+
+/** Represents the standard BT-HF interface. */
+typedef struct {
+
+ /** set to sizeof(BtHfClientInterface) */
+ size_t size;
+ /**
+ * Register the BtHf callbacks
+ */
+ bt_status_t (*init)(bthf_client_callbacks_t* callbacks);
+
+ /** connect to audio gateway */
+ bt_status_t (*connect)(bt_bdaddr_t *bd_addr);
+
+ /** disconnect from audio gateway */
+ bt_status_t (*disconnect)(bt_bdaddr_t *bd_addr);
+
+ /** create an audio connection */
+ bt_status_t (*connect_audio)(bt_bdaddr_t *bd_addr);
+
+ /** close the audio connection */
+ bt_status_t (*disconnect_audio)(bt_bdaddr_t *bd_addr);
+
+ /** start voice recognition */
+ bt_status_t (*start_voice_recognition)(void);
+
+ /** stop voice recognition */
+ bt_status_t (*stop_voice_recognition)(void);
+
+ /** volume control */
+ bt_status_t (*volume_control) (bthf_client_volume_type_t type, int volume);
+
+ /** place a call with number a number
+ * if number is NULL last called number is called (aka re-dial)*/
+ bt_status_t (*dial) (const char *number);
+
+ /** place a call with number specified by location (speed dial) */
+ bt_status_t (*dial_memory) (int location);
+
+ /** perform specified call related action
+ * idx is limited only for enhanced call control related action
+ */
+ bt_status_t (*handle_call_action) (bthf_client_call_action_t action, int idx);
+
+ /** query list of current calls */
+ bt_status_t (*query_current_calls) (void);
+
+ /** query name of current selected operator */
+ bt_status_t (*query_current_operator_name) (void);
+
+ /** Retrieve subscriber information */
+ bt_status_t (*retrieve_subscriber_info) (void);
+
+ /** Send DTMF code*/
+ bt_status_t (*send_dtmf) (char code);
+
+ /** Request a phone number from AG corresponding to last voice tag recorded */
+ bt_status_t (*request_last_voice_tag_number) (void);
+
+ /** Closes the interface. */
+ void (*cleanup)(void);
+
+ /** Send AT Command. */
+ bt_status_t (*send_at_cmd) (int cmd, int val1, int val2, const char *arg);
+} bthf_client_interface_t;
+
+__END_DECLS
+
+#endif /* ANDROID_INCLUDE_BT_HF_CLIENT_H */
diff --git a/repo/android/hardware/bt_hh.h b/repo/android/hardware/bt_hh.h
new file mode 100644
index 0000000..dad9586
--- /dev/null
+++ b/repo/android/hardware/bt_hh.h
@@ -0,0 +1,185 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_INCLUDE_BT_HH_H
+#define ANDROID_INCLUDE_BT_HH_H
+
+#include <stdint.h>
+
+__BEGIN_DECLS
+
+#define BTHH_MAX_DSC_LEN 884
+
+/* HH connection states */
+typedef enum
+{
+ BTHH_CONN_STATE_CONNECTED = 0,
+ BTHH_CONN_STATE_CONNECTING,
+ BTHH_CONN_STATE_DISCONNECTED,
+ BTHH_CONN_STATE_DISCONNECTING,
+ BTHH_CONN_STATE_FAILED_MOUSE_FROM_HOST,
+ BTHH_CONN_STATE_FAILED_KBD_FROM_HOST,
+ BTHH_CONN_STATE_FAILED_TOO_MANY_DEVICES,
+ BTHH_CONN_STATE_FAILED_NO_BTHID_DRIVER,
+ BTHH_CONN_STATE_FAILED_GENERIC,
+ BTHH_CONN_STATE_UNKNOWN
+} bthh_connection_state_t;
+
+typedef enum
+{
+ BTHH_OK = 0,
+ BTHH_HS_HID_NOT_READY, /* handshake error : device not ready */
+ BTHH_HS_INVALID_RPT_ID, /* handshake error : invalid report ID */
+ BTHH_HS_TRANS_NOT_SPT, /* handshake error : transaction not spt */
+ BTHH_HS_INVALID_PARAM, /* handshake error : invalid paremter */
+ BTHH_HS_ERROR, /* handshake error : unspecified HS error */
+ BTHH_ERR, /* general BTA HH error */
+ BTHH_ERR_SDP, /* SDP error */
+ BTHH_ERR_PROTO, /* SET_Protocol error,
+ only used in BTA_HH_OPEN_EVT callback */
+ BTHH_ERR_DB_FULL, /* device database full error, used */
+ BTHH_ERR_TOD_UNSPT, /* type of device not supported */
+ BTHH_ERR_NO_RES, /* out of system resources */
+ BTHH_ERR_AUTH_FAILED, /* authentication fail */
+ BTHH_ERR_HDL
+}bthh_status_t;
+
+/* Protocol modes */
+typedef enum {
+ BTHH_REPORT_MODE = 0x00,
+ BTHH_BOOT_MODE = 0x01,
+ BTHH_UNSUPPORTED_MODE = 0xff
+}bthh_protocol_mode_t;
+
+/* Report types */
+typedef enum {
+ BTHH_INPUT_REPORT = 1,
+ BTHH_OUTPUT_REPORT,
+ BTHH_FEATURE_REPORT
+}bthh_report_type_t;
+
+typedef struct
+{
+ int attr_mask;
+ uint8_t sub_class;
+ uint8_t app_id;
+ int vendor_id;
+ int product_id;
+ int version;
+ uint8_t ctry_code;
+ int dl_len;
+ uint8_t dsc_list[BTHH_MAX_DSC_LEN];
+} bthh_hid_info_t;
+
+/** Callback for connection state change.
+ * state will have one of the values from bthh_connection_state_t
+ */
+typedef void (* bthh_connection_state_callback)(bt_bdaddr_t *bd_addr, bthh_connection_state_t state);
+
+/** Callback for vitual unplug api.
+ * the status of the vitual unplug
+ */
+typedef void (* bthh_virtual_unplug_callback)(bt_bdaddr_t *bd_addr, bthh_status_t hh_status);
+
+/** Callback for get hid info
+ * hid_info will contain attr_mask, sub_class, app_id, vendor_id, product_id, version, ctry_code, len
+ */
+typedef void (* bthh_hid_info_callback)(bt_bdaddr_t *bd_addr, bthh_hid_info_t hid_info);
+
+/** Callback for get protocol api.
+ * the protocol mode is one of the value from bthh_protocol_mode_t
+ */
+typedef void (* bthh_protocol_mode_callback)(bt_bdaddr_t *bd_addr, bthh_status_t hh_status, bthh_protocol_mode_t mode);
+
+/** Callback for get/set_idle_time api.
+ */
+typedef void (* bthh_idle_time_callback)(bt_bdaddr_t *bd_addr, bthh_status_t hh_status, int idle_rate);
+
+
+/** Callback for get report api.
+ * if staus is ok rpt_data contains the report data
+ */
+typedef void (* bthh_get_report_callback)(bt_bdaddr_t *bd_addr, bthh_status_t hh_status, uint8_t* rpt_data, int rpt_size);
+
+/** Callback for set_report/set_protocol api and if error
+ * occurs for get_report/get_protocol api.
+ */
+typedef void (* bthh_handshake_callback)(bt_bdaddr_t *bd_addr, bthh_status_t hh_status);
+
+
+/** BT-HH callback structure. */
+typedef struct {
+ /** set to sizeof(BtHfCallbacks) */
+ size_t size;
+ bthh_connection_state_callback connection_state_cb;
+ bthh_hid_info_callback hid_info_cb;
+ bthh_protocol_mode_callback protocol_mode_cb;
+ bthh_idle_time_callback idle_time_cb;
+ bthh_get_report_callback get_report_cb;
+ bthh_virtual_unplug_callback virtual_unplug_cb;
+ bthh_handshake_callback handshake_cb;
+
+} bthh_callbacks_t;
+
+
+
+/** Represents the standard BT-HH interface. */
+typedef struct {
+
+ /** set to sizeof(BtHhInterface) */
+ size_t size;
+
+ /**
+ * Register the BtHh callbacks
+ */
+ bt_status_t (*init)( bthh_callbacks_t* callbacks );
+
+ /** connect to hid device */
+ bt_status_t (*connect)( bt_bdaddr_t *bd_addr);
+
+ /** dis-connect from hid device */
+ bt_status_t (*disconnect)( bt_bdaddr_t *bd_addr );
+
+ /** Virtual UnPlug (VUP) the specified HID device */
+ bt_status_t (*virtual_unplug)(bt_bdaddr_t *bd_addr);
+
+ /** Set the HID device descriptor for the specified HID device. */
+ bt_status_t (*set_info)(bt_bdaddr_t *bd_addr, bthh_hid_info_t hid_info );
+
+ /** Get the HID proto mode. */
+ bt_status_t (*get_protocol) (bt_bdaddr_t *bd_addr, bthh_protocol_mode_t protocolMode);
+
+ /** Set the HID proto mode. */
+ bt_status_t (*set_protocol)(bt_bdaddr_t *bd_addr, bthh_protocol_mode_t protocolMode);
+
+ /** Send a GET_REPORT to HID device. */
+ bt_status_t (*get_report)(bt_bdaddr_t *bd_addr, bthh_report_type_t reportType, uint8_t reportId, int bufferSize);
+
+ /** Send a SET_REPORT to HID device. */
+ bt_status_t (*set_report)(bt_bdaddr_t *bd_addr, bthh_report_type_t reportType, char* report);
+
+ /** Send data to HID device. */
+ bt_status_t (*send_data)(bt_bdaddr_t *bd_addr, char* data);
+
+ /** Closes the interface. */
+ void (*cleanup)( void );
+
+} bthh_interface_t;
+__END_DECLS
+
+#endif /* ANDROID_INCLUDE_BT_HH_H */
+
+
diff --git a/repo/android/hardware/bt_hl.h b/repo/android/hardware/bt_hl.h
new file mode 100644
index 0000000..bd29e3a
--- /dev/null
+++ b/repo/android/hardware/bt_hl.h
@@ -0,0 +1,123 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_INCLUDE_BT_HL_H
+#define ANDROID_INCLUDE_BT_HL_H
+
+__BEGIN_DECLS
+
+/* HL connection states */
+
+typedef enum
+{
+ BTHL_MDEP_ROLE_SOURCE,
+ BTHL_MDEP_ROLE_SINK
+} bthl_mdep_role_t;
+
+typedef enum {
+ BTHL_APP_REG_STATE_REG_SUCCESS,
+ BTHL_APP_REG_STATE_REG_FAILED,
+ BTHL_APP_REG_STATE_DEREG_SUCCESS,
+ BTHL_APP_REG_STATE_DEREG_FAILED
+} bthl_app_reg_state_t;
+
+typedef enum
+{
+ BTHL_CHANNEL_TYPE_RELIABLE,
+ BTHL_CHANNEL_TYPE_STREAMING,
+ BTHL_CHANNEL_TYPE_ANY
+} bthl_channel_type_t;
+
+
+/* HL connection states */
+typedef enum {
+ BTHL_CONN_STATE_CONNECTING,
+ BTHL_CONN_STATE_CONNECTED,
+ BTHL_CONN_STATE_DISCONNECTING,
+ BTHL_CONN_STATE_DISCONNECTED,
+ BTHL_CONN_STATE_DESTROYED
+} bthl_channel_state_t;
+
+typedef struct
+{
+ bthl_mdep_role_t mdep_role;
+ int data_type;
+ bthl_channel_type_t channel_type;
+ const char *mdep_description; /* MDEP description to be used in the SDP (optional); null terminated */
+} bthl_mdep_cfg_t;
+
+typedef struct
+{
+ const char *application_name;
+ const char *provider_name; /* provider name to be used in the SDP (optional); null terminated */
+ const char *srv_name; /* service name to be used in the SDP (optional); null terminated*/
+ const char *srv_desp; /* service description to be used in the SDP (optional); null terminated */
+ int number_of_mdeps;
+ bthl_mdep_cfg_t *mdep_cfg; /* Dynamic array */
+} bthl_reg_param_t;
+
+/** Callback for application registration status.
+ * state will have one of the values from bthl_app_reg_state_t
+ */
+typedef void (* bthl_app_reg_state_callback)(int app_id, bthl_app_reg_state_t state);
+
+/** Callback for channel connection state change.
+ * state will have one of the values from
+ * bthl_connection_state_t and fd (file descriptor)
+ */
+typedef void (* bthl_channel_state_callback)(int app_id, bt_bdaddr_t *bd_addr, int mdep_cfg_index, int channel_id, bthl_channel_state_t state, int fd);
+
+/** BT-HL callback structure. */
+typedef struct {
+ /** set to sizeof(bthl_callbacks_t) */
+ size_t size;
+ bthl_app_reg_state_callback app_reg_state_cb;
+ bthl_channel_state_callback channel_state_cb;
+} bthl_callbacks_t;
+
+
+/** Represents the standard BT-HL interface. */
+typedef struct {
+
+ /** set to sizeof(bthl_interface_t) */
+ size_t size;
+
+ /**
+ * Register the Bthl callbacks
+ */
+ bt_status_t (*init)( bthl_callbacks_t* callbacks );
+
+ /** Register HL application */
+ bt_status_t (*register_application) ( bthl_reg_param_t *p_reg_param, int *app_id);
+
+ /** Unregister HL application */
+ bt_status_t (*unregister_application) (int app_id);
+
+ /** connect channel */
+ bt_status_t (*connect_channel)(int app_id, bt_bdaddr_t *bd_addr, int mdep_cfg_index, int *channel_id);
+
+ /** destroy channel */
+ bt_status_t (*destroy_channel)(int channel_id);
+
+ /** Close the Bthl callback **/
+ void (*cleanup)(void);
+
+} bthl_interface_t;
+__END_DECLS
+
+#endif /* ANDROID_INCLUDE_BT_HL_H */
+
+
diff --git a/repo/android/hardware/bt_mce.h b/repo/android/hardware/bt_mce.h
new file mode 100644
index 0000000..5d159b3
--- /dev/null
+++ b/repo/android/hardware/bt_mce.h
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_INCLUDE_BT_MCE_H
+#define ANDROID_INCLUDE_BT_MCE_H
+
+__BEGIN_DECLS
+
+/** MAS instance description */
+typedef struct
+{
+ int id;
+ int scn;
+ int msg_types;
+ char *p_name;
+} btmce_mas_instance_t;
+
+/** callback for get_remote_mas_instances */
+typedef void (*btmce_remote_mas_instances_callback)(bt_status_t status, bt_bdaddr_t *bd_addr,
+ int num_instances, btmce_mas_instance_t *instances);
+
+typedef struct {
+ /** set to sizeof(btmce_callbacks_t) */
+ size_t size;
+ btmce_remote_mas_instances_callback remote_mas_instances_cb;
+} btmce_callbacks_t;
+
+typedef struct {
+ /** set to size of this struct */
+ size_t size;
+
+ /** register BT MCE callbacks */
+ bt_status_t (*init)(btmce_callbacks_t *callbacks);
+
+ /** search for MAS instances on remote device */
+ bt_status_t (*get_remote_mas_instances)(bt_bdaddr_t *bd_addr);
+} btmce_interface_t;
+
+__END_DECLS
+
+#endif /* ANDROID_INCLUDE_BT_MCE_H */
diff --git a/repo/android/hardware/bt_pan.h b/repo/android/hardware/bt_pan.h
new file mode 100644
index 0000000..83e7949
--- /dev/null
+++ b/repo/android/hardware/bt_pan.h
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_INCLUDE_BT_PAN_H
+#define ANDROID_INCLUDE_BT_PAN_H
+
+__BEGIN_DECLS
+
+#define BTPAN_ROLE_NONE 0
+#define BTPAN_ROLE_PANNAP 1
+#define BTPAN_ROLE_PANU 2
+
+typedef enum {
+ BTPAN_STATE_CONNECTED = 0,
+ BTPAN_STATE_CONNECTING = 1,
+ BTPAN_STATE_DISCONNECTED = 2,
+ BTPAN_STATE_DISCONNECTING = 3
+} btpan_connection_state_t;
+
+typedef enum {
+ BTPAN_STATE_ENABLED = 0,
+ BTPAN_STATE_DISABLED = 1
+} btpan_control_state_t;
+
+/**
+* Callback for pan connection state
+*/
+typedef void (*btpan_connection_state_callback)(btpan_connection_state_t state, bt_status_t error,
+ const bt_bdaddr_t *bd_addr, int local_role, int remote_role);
+typedef void (*btpan_control_state_callback)(btpan_control_state_t state, int local_role,
+ bt_status_t error, const char* ifname);
+
+typedef struct {
+ size_t size;
+ btpan_control_state_callback control_state_cb;
+ btpan_connection_state_callback connection_state_cb;
+} btpan_callbacks_t;
+typedef struct {
+ /** set to size of this struct*/
+ size_t size;
+ /**
+ * Initialize the pan interface and register the btpan callbacks
+ */
+ bt_status_t (*init)(const btpan_callbacks_t* callbacks);
+ /*
+ * enable the pan service by specified role. The result state of
+ * enabl will be returned by btpan_control_state_callback. when pan-nap is enabled,
+ * the state of connecting panu device will be notified by btpan_connection_state_callback
+ */
+ bt_status_t (*enable)(int local_role);
+ /*
+ * get current pan local role
+ */
+ int (*get_local_role)(void);
+ /**
+ * start bluetooth pan connection to the remote device by specified pan role. The result state will be
+ * returned by btpan_connection_state_callback
+ */
+ bt_status_t (*connect)(const bt_bdaddr_t *bd_addr, int local_role, int remote_role);
+ /**
+ * stop bluetooth pan connection. The result state will be returned by btpan_connection_state_callback
+ */
+ bt_status_t (*disconnect)(const bt_bdaddr_t *bd_addr);
+
+ /**
+ * Cleanup the pan interface
+ */
+ void (*cleanup)(void);
+
+} btpan_interface_t;
+
+__END_DECLS
+
+#endif /* ANDROID_INCLUDE_BT_PAN_H */
diff --git a/repo/android/hardware/bt_rc.h b/repo/android/hardware/bt_rc.h
new file mode 100644
index 0000000..c565c48
--- /dev/null
+++ b/repo/android/hardware/bt_rc.h
@@ -0,0 +1,296 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_INCLUDE_BT_RC_H
+#define ANDROID_INCLUDE_BT_RC_H
+
+__BEGIN_DECLS
+
+/* Macros */
+#define BTRC_MAX_ATTR_STR_LEN 255
+#define BTRC_UID_SIZE 8
+#define BTRC_MAX_APP_SETTINGS 8
+#define BTRC_MAX_FOLDER_DEPTH 4
+#define BTRC_MAX_APP_ATTR_SIZE 16
+#define BTRC_MAX_ELEM_ATTR_SIZE 7
+
+typedef uint8_t btrc_uid_t[BTRC_UID_SIZE];
+
+typedef enum {
+ BTRC_FEAT_NONE = 0x00, /* AVRCP 1.0 */
+ BTRC_FEAT_METADATA = 0x01, /* AVRCP 1.3 */
+ BTRC_FEAT_ABSOLUTE_VOLUME = 0x02, /* Supports TG role and volume sync */
+ BTRC_FEAT_BROWSE = 0x04, /* AVRCP 1.4 and up, with Browsing support */
+} btrc_remote_features_t;
+
+typedef enum {
+ BTRC_PLAYSTATE_STOPPED = 0x00, /* Stopped */
+ BTRC_PLAYSTATE_PLAYING = 0x01, /* Playing */
+ BTRC_PLAYSTATE_PAUSED = 0x02, /* Paused */
+ BTRC_PLAYSTATE_FWD_SEEK = 0x03, /* Fwd Seek*/
+ BTRC_PLAYSTATE_REV_SEEK = 0x04, /* Rev Seek*/
+ BTRC_PLAYSTATE_ERROR = 0xFF, /* Error */
+} btrc_play_status_t;
+
+typedef enum {
+ BTRC_EVT_PLAY_STATUS_CHANGED = 0x01,
+ BTRC_EVT_TRACK_CHANGE = 0x02,
+ BTRC_EVT_TRACK_REACHED_END = 0x03,
+ BTRC_EVT_TRACK_REACHED_START = 0x04,
+ BTRC_EVT_PLAY_POS_CHANGED = 0x05,
+ BTRC_EVT_APP_SETTINGS_CHANGED = 0x08,
+} btrc_event_id_t;
+
+typedef enum {
+ BTRC_NOTIFICATION_TYPE_INTERIM = 0,
+ BTRC_NOTIFICATION_TYPE_CHANGED = 1,
+} btrc_notification_type_t;
+
+typedef enum {
+ BTRC_PLAYER_ATTR_EQUALIZER = 0x01,
+ BTRC_PLAYER_ATTR_REPEAT = 0x02,
+ BTRC_PLAYER_ATTR_SHUFFLE = 0x03,
+ BTRC_PLAYER_ATTR_SCAN = 0x04,
+} btrc_player_attr_t;
+
+typedef enum {
+ BTRC_MEDIA_ATTR_TITLE = 0x01,
+ BTRC_MEDIA_ATTR_ARTIST = 0x02,
+ BTRC_MEDIA_ATTR_ALBUM = 0x03,
+ BTRC_MEDIA_ATTR_TRACK_NUM = 0x04,
+ BTRC_MEDIA_ATTR_NUM_TRACKS = 0x05,
+ BTRC_MEDIA_ATTR_GENRE = 0x06,
+ BTRC_MEDIA_ATTR_PLAYING_TIME = 0x07,
+} btrc_media_attr_t;
+
+typedef enum {
+ BTRC_PLAYER_VAL_OFF_REPEAT = 0x01,
+ BTRC_PLAYER_VAL_SINGLE_REPEAT = 0x02,
+ BTRC_PLAYER_VAL_ALL_REPEAT = 0x03,
+ BTRC_PLAYER_VAL_GROUP_REPEAT = 0x04
+} btrc_player_repeat_val_t;
+
+typedef enum {
+ BTRC_PLAYER_VAL_OFF_SHUFFLE = 0x01,
+ BTRC_PLAYER_VAL_ALL_SHUFFLE = 0x02,
+ BTRC_PLAYER_VAL_GROUP_SHUFFLE = 0x03
+} btrc_player_shuffle_val_t;
+
+typedef enum {
+ BTRC_STS_BAD_CMD = 0x00, /* Invalid command */
+ BTRC_STS_BAD_PARAM = 0x01, /* Invalid parameter */
+ BTRC_STS_NOT_FOUND = 0x02, /* Specified parameter is wrong or not found */
+ BTRC_STS_INTERNAL_ERR = 0x03, /* Internal Error */
+ BTRC_STS_NO_ERROR = 0x04 /* Operation Success */
+} btrc_status_t;
+
+typedef struct {
+ uint8_t num_attr;
+ uint8_t attr_ids[BTRC_MAX_APP_SETTINGS];
+ uint8_t attr_values[BTRC_MAX_APP_SETTINGS];
+} btrc_player_settings_t;
+
+typedef union
+{
+ btrc_play_status_t play_status;
+ btrc_uid_t track; /* queue position in NowPlaying */
+ uint32_t song_pos;
+ btrc_player_settings_t player_setting;
+} btrc_register_notification_t;
+
+typedef struct {
+ uint8_t id; /* can be attr_id or value_id */
+ uint8_t text[BTRC_MAX_ATTR_STR_LEN];
+} btrc_player_setting_text_t;
+
+typedef struct {
+ uint32_t attr_id;
+ uint8_t text[BTRC_MAX_ATTR_STR_LEN];
+} btrc_element_attr_val_t;
+
+/** Callback for the controller's supported feautres */
+typedef void (* btrc_remote_features_callback)(bt_bdaddr_t *bd_addr,
+ btrc_remote_features_t features);
+
+/** Callback for play status request */
+typedef void (* btrc_get_play_status_callback)();
+
+/** Callback for list player application attributes (Shuffle, Repeat,...) */
+typedef void (* btrc_list_player_app_attr_callback)();
+
+/** Callback for list player application attributes (Shuffle, Repeat,...) */
+typedef void (* btrc_list_player_app_values_callback)(btrc_player_attr_t attr_id);
+
+/** Callback for getting the current player application settings value
+** num_attr: specifies the number of attribute ids contained in p_attrs
+*/
+typedef void (* btrc_get_player_app_value_callback) (uint8_t num_attr, btrc_player_attr_t *p_attrs);
+
+/** Callback for getting the player application settings attributes' text
+** num_attr: specifies the number of attribute ids contained in p_attrs
+*/
+typedef void (* btrc_get_player_app_attrs_text_callback) (uint8_t num_attr, btrc_player_attr_t *p_attrs);
+
+/** Callback for getting the player application settings values' text
+** num_attr: specifies the number of value ids contained in p_vals
+*/
+typedef void (* btrc_get_player_app_values_text_callback) (uint8_t attr_id, uint8_t num_val, uint8_t *p_vals);
+
+/** Callback for setting the player application settings values */
+typedef void (* btrc_set_player_app_value_callback) (btrc_player_settings_t *p_vals);
+
+/** Callback to fetch the get element attributes of the current song
+** num_attr: specifies the number of attributes requested in p_attrs
+*/
+typedef void (* btrc_get_element_attr_callback) (uint8_t num_attr, btrc_media_attr_t *p_attrs);
+
+/** Callback for register notification (Play state change/track change/...)
+** param: Is only valid if event_id is BTRC_EVT_PLAY_POS_CHANGED
+*/
+typedef void (* btrc_register_notification_callback) (btrc_event_id_t event_id, uint32_t param);
+
+/* AVRCP 1.4 Enhancements */
+/** Callback for volume change on CT
+** volume: Current volume setting on the CT (0-127)
+*/
+typedef void (* btrc_volume_change_callback) (uint8_t volume, uint8_t ctype);
+
+/** Callback for passthrough commands */
+typedef void (* btrc_passthrough_cmd_callback) (int id, int key_state);
+
+/** BT-RC Target callback structure. */
+typedef struct {
+ /** set to sizeof(BtRcCallbacks) */
+ size_t size;
+ btrc_remote_features_callback remote_features_cb;
+ btrc_get_play_status_callback get_play_status_cb;
+ btrc_list_player_app_attr_callback list_player_app_attr_cb;
+ btrc_list_player_app_values_callback list_player_app_values_cb;
+ btrc_get_player_app_value_callback get_player_app_value_cb;
+ btrc_get_player_app_attrs_text_callback get_player_app_attrs_text_cb;
+ btrc_get_player_app_values_text_callback get_player_app_values_text_cb;
+ btrc_set_player_app_value_callback set_player_app_value_cb;
+ btrc_get_element_attr_callback get_element_attr_cb;
+ btrc_register_notification_callback register_notification_cb;
+ btrc_volume_change_callback volume_change_cb;
+ btrc_passthrough_cmd_callback passthrough_cmd_cb;
+} btrc_callbacks_t;
+
+/** Represents the standard BT-RC AVRCP Target interface. */
+typedef struct {
+
+ /** set to sizeof(BtRcInterface) */
+ size_t size;
+ /**
+ * Register the BtRc callbacks
+ */
+ bt_status_t (*init)( btrc_callbacks_t* callbacks );
+
+ /** Respose to GetPlayStatus request. Contains the current
+ ** 1. Play status
+ ** 2. Song duration/length
+ ** 3. Song position
+ */
+ bt_status_t (*get_play_status_rsp)( btrc_play_status_t play_status, uint32_t song_len, uint32_t song_pos);
+
+ /** Lists the support player application attributes (Shuffle/Repeat/...)
+ ** num_attr: Specifies the number of attributes contained in the pointer p_attrs
+ */
+ bt_status_t (*list_player_app_attr_rsp)( int num_attr, btrc_player_attr_t *p_attrs);
+
+ /** Lists the support player application attributes (Shuffle Off/On/Group)
+ ** num_val: Specifies the number of values contained in the pointer p_vals
+ */
+ bt_status_t (*list_player_app_value_rsp)( int num_val, uint8_t *p_vals);
+
+ /** Returns the current application attribute values for each of the specified attr_id */
+ bt_status_t (*get_player_app_value_rsp)( btrc_player_settings_t *p_vals);
+
+ /** Returns the application attributes text ("Shuffle"/"Repeat"/...)
+ ** num_attr: Specifies the number of attributes' text contained in the pointer p_attrs
+ */
+ bt_status_t (*get_player_app_attr_text_rsp)( int num_attr, btrc_player_setting_text_t *p_attrs);
+
+ /** Returns the application attributes text ("Shuffle"/"Repeat"/...)
+ ** num_attr: Specifies the number of attribute values' text contained in the pointer p_vals
+ */
+ bt_status_t (*get_player_app_value_text_rsp)( int num_val, btrc_player_setting_text_t *p_vals);
+
+ /** Returns the current songs' element attributes text ("Title"/"Album"/"Artist")
+ ** num_attr: Specifies the number of attributes' text contained in the pointer p_attrs
+ */
+ bt_status_t (*get_element_attr_rsp)( uint8_t num_attr, btrc_element_attr_val_t *p_attrs);
+
+ /** Response to set player attribute request ("Shuffle"/"Repeat")
+ ** rsp_status: Status of setting the player attributes for the current media player
+ */
+ bt_status_t (*set_player_app_value_rsp)(btrc_status_t rsp_status);
+
+ /* Response to the register notification request (Play state change/track change/...).
+ ** event_id: Refers to the event_id this notification change corresponds too
+ ** type: Response type - interim/changed
+ ** p_params: Based on the event_id, this parameter should be populated
+ */
+ bt_status_t (*register_notification_rsp)(btrc_event_id_t event_id,
+ btrc_notification_type_t type,
+ btrc_register_notification_t *p_param);
+
+ /* AVRCP 1.4 enhancements */
+
+ /**Send current volume setting to remote side. Support limited to SetAbsoluteVolume
+ ** This can be enhanced to support Relative Volume (AVRCP 1.0).
+ ** With RelateVolume, we will send VOLUME_UP/VOLUME_DOWN opposed to absolute volume level
+ ** volume: Should be in the range 0-127. bit7 is reseved and cannot be set
+ */
+ bt_status_t (*set_volume)(uint8_t volume);
+
+ /** Closes the interface. */
+ void (*cleanup)( void );
+} btrc_interface_t;
+
+
+typedef void (* btrc_passthrough_rsp_callback) (int id, int key_state);
+
+typedef void (* btrc_connection_state_callback) (bool state, bt_bdaddr_t *bd_addr);
+
+/** BT-RC Controller callback structure. */
+typedef struct {
+ /** set to sizeof(BtRcCallbacks) */
+ size_t size;
+ btrc_passthrough_rsp_callback passthrough_rsp_cb;
+ btrc_connection_state_callback connection_state_cb;
+} btrc_ctrl_callbacks_t;
+
+/** Represents the standard BT-RC AVRCP Controller interface. */
+typedef struct {
+
+ /** set to sizeof(BtRcInterface) */
+ size_t size;
+ /**
+ * Register the BtRc callbacks
+ */
+ bt_status_t (*init)( btrc_ctrl_callbacks_t* callbacks );
+
+ /** send pass through command to target */
+ bt_status_t (*send_pass_through_cmd) ( bt_bdaddr_t *bd_addr, uint8_t key_code, uint8_t key_state );
+
+ /** Closes the interface. */
+ void (*cleanup)( void );
+} btrc_ctrl_interface_t;
+
+__END_DECLS
+
+#endif /* ANDROID_INCLUDE_BT_RC_H */
diff --git a/repo/android/hardware/bt_sock.h b/repo/android/hardware/bt_sock.h
new file mode 100644
index 0000000..a4aa046
--- /dev/null
+++ b/repo/android/hardware/bt_sock.h
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_INCLUDE_BT_SOCK_H
+#define ANDROID_INCLUDE_BT_SOCK_H
+
+__BEGIN_DECLS
+
+#define BTSOCK_FLAG_ENCRYPT 1
+#define BTSOCK_FLAG_AUTH (1 << 1)
+
+typedef enum {
+ BTSOCK_RFCOMM = 1,
+ BTSOCK_SCO = 2,
+ BTSOCK_L2CAP = 3
+} btsock_type_t;
+
+/** Represents the standard BT SOCKET interface. */
+typedef struct {
+ short size;
+ bt_bdaddr_t bd_addr;
+ int channel;
+ int status;
+} __attribute__((packed)) sock_connect_signal_t;
+
+typedef struct {
+
+ /** set to size of this struct*/
+ size_t size;
+ /**
+ * listen to a rfcomm uuid or channel. It returns the socket fd from which
+ * btsock_connect_signal can be read out when a remote device connected
+ */
+ bt_status_t (*listen)(btsock_type_t type, const char* service_name, const uint8_t* service_uuid, int channel, int* sock_fd, int flags);
+ /*
+ * connect to a rfcomm uuid channel of remote device, It returns the socket fd from which
+ * the btsock_connect_signal and a new socket fd to be accepted can be read out when connected
+ */
+ bt_status_t (*connect)(const bt_bdaddr_t *bd_addr, btsock_type_t type, const uint8_t* uuid, int channel, int* sock_fd, int flags);
+
+} btsock_interface_t;
+
+__END_DECLS
+
+#endif /* ANDROID_INCLUDE_BT_SOCK_H */
diff --git a/repo/android/hardware/hardware.c b/repo/android/hardware/hardware.c
new file mode 100644
index 0000000..4bd5eba
--- /dev/null
+++ b/repo/android/hardware/hardware.c
@@ -0,0 +1,124 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <hardware/hardware.h>
+
+#include <dlfcn.h>
+#include <string.h>
+#include <pthread.h>
+#include <errno.h>
+#include <limits.h>
+#include <stdio.h>
+
+#define LOG_TAG "HAL"
+
+#define LOG_INFO " I"
+#define LOG_WARN " W"
+#define LOG_ERROR " E"
+#define LOG_DEBUG " D"
+#define ALOG(pri, tag, fmt, arg...) fprintf(stderr, tag pri": " fmt"\n", ##arg)
+
+#define info(fmt, arg...) ALOG(LOG_INFO, LOG_TAG, fmt, ##arg)
+#define warn(fmt, arg...) ALOG(LOG_WARN, LOG_TAG, fmt, ##arg)
+#define error(fmt, arg...) ALOG(LOG_ERROR, LOG_TAG, fmt, ##arg)
+
+/**
+ * Load the file defined by the variant and if successful
+ * return the dlopen handle and the hmi.
+ * @return 0 = success, !0 = failure.
+ */
+static int load(const char *id,
+ const char *path,
+ const struct hw_module_t **pHmi)
+{
+ int status;
+ void *handle;
+ struct hw_module_t *hmi;
+ const char *sym = HAL_MODULE_INFO_SYM_AS_STR;
+
+ /*
+ * load the symbols resolving undefined symbols before
+ * dlopen returns. Since RTLD_GLOBAL is not or'd in with
+ * RTLD_NOW the external symbols will not be global
+ */
+ handle = dlopen(path, RTLD_NOW);
+ if (handle == NULL) {
+ char const *err_str = dlerror();
+ error("load: module=%s\n%s", path, err_str?err_str:"unknown");
+ status = -EINVAL;
+ goto done;
+ }
+
+ /* Get the address of the struct hal_module_info. */
+ hmi = (struct hw_module_t *)dlsym(handle, sym);
+ if (hmi == NULL) {
+ error("load: couldn't find symbol %s", sym);
+ status = -EINVAL;
+ goto done;
+ }
+
+ /* Check that the id matches */
+ if (strcmp(id, hmi->id) != 0) {
+ error("load: id=%s != hmi->id=%s", id, hmi->id);
+ status = -EINVAL;
+ goto done;
+ }
+
+ hmi->dso = handle;
+
+ *pHmi = hmi;
+
+ info("loaded HAL id=%s path=%s hmi=%p handle=%p",
+ id, path, *pHmi, handle);
+
+ return 0;
+
+done:
+ hmi = NULL;
+ if (handle != NULL) {
+ dlclose(handle);
+ handle = NULL;
+ }
+
+ return status;
+}
+
+int hw_get_module_by_class(const char *class_id, const char *inst,
+ const struct hw_module_t **module)
+{
+ char path[PATH_MAX];
+ char name[PATH_MAX];
+
+ if (inst)
+ snprintf(name, PATH_MAX, "%s.%s", class_id, inst);
+ else
+ snprintf(name, PATH_MAX, "%s", class_id);
+
+ /*
+ * Here we rely on the fact that calling dlopen multiple times on
+ * the same .so will simply increment a refcount (and not load
+ * a new copy of the library).
+ * We also assume that dlopen() is thread-safe.
+ */
+ snprintf(path, sizeof(path), "%s/%s.default.so", PLUGINDIR, name);
+
+ return load(class_id, path, module);
+}
+
+int hw_get_module(const char *id, const struct hw_module_t **module)
+{
+ return hw_get_module_by_class(id, NULL, module);
+}
diff --git a/repo/android/hardware/hardware.h b/repo/android/hardware/hardware.h
new file mode 100644
index 0000000..c7e8cc7
--- /dev/null
+++ b/repo/android/hardware/hardware.h
@@ -0,0 +1,227 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_INCLUDE_HARDWARE_HARDWARE_H
+#define ANDROID_INCLUDE_HARDWARE_HARDWARE_H
+
+#include <stdint.h>
+#include <sys/cdefs.h>
+
+__BEGIN_DECLS
+
+/*
+ * Value for the hw_module_t.tag field
+ */
+
+#define MAKE_TAG_CONSTANT(A,B,C,D) (((A) << 24) | ((B) << 16) | ((C) << 8) | (D))
+
+#define HARDWARE_MODULE_TAG MAKE_TAG_CONSTANT('H', 'W', 'M', 'T')
+#define HARDWARE_DEVICE_TAG MAKE_TAG_CONSTANT('H', 'W', 'D', 'T')
+
+#define HARDWARE_MAKE_API_VERSION(maj,min) \
+ ((((maj) & 0xff) << 8) | ((min) & 0xff))
+
+#define HARDWARE_MAKE_API_VERSION_2(maj,min,hdr) \
+ ((((maj) & 0xff) << 24) | (((min) & 0xff) << 16) | ((hdr) & 0xffff))
+#define HARDWARE_API_VERSION_2_MAJ_MIN_MASK 0xffff0000
+#define HARDWARE_API_VERSION_2_HEADER_MASK 0x0000ffff
+
+
+/*
+ * The current HAL API version.
+ *
+ * All module implementations must set the hw_module_t.hal_api_version field
+ * to this value when declaring the module with HAL_MODULE_INFO_SYM.
+ *
+ * Note that previous implementations have always set this field to 0.
+ * Therefore, libhardware HAL API will always consider versions 0.0 and 1.0
+ * to be 100% binary compatible.
+ *
+ */
+#define HARDWARE_HAL_API_VERSION HARDWARE_MAKE_API_VERSION(1, 0)
+
+/*
+ * Helper macros for module implementors.
+ *
+ * The derived modules should provide convenience macros for supported
+ * versions so that implementations can explicitly specify module/device
+ * versions at definition time.
+ *
+ * Use this macro to set the hw_module_t.module_api_version field.
+ */
+#define HARDWARE_MODULE_API_VERSION(maj,min) HARDWARE_MAKE_API_VERSION(maj,min)
+#define HARDWARE_MODULE_API_VERSION_2(maj,min,hdr) HARDWARE_MAKE_API_VERSION_2(maj,min,hdr)
+
+/*
+ * Use this macro to set the hw_device_t.version field
+ */
+#define HARDWARE_DEVICE_API_VERSION(maj,min) HARDWARE_MAKE_API_VERSION(maj,min)
+#define HARDWARE_DEVICE_API_VERSION_2(maj,min,hdr) HARDWARE_MAKE_API_VERSION_2(maj,min,hdr)
+
+struct hw_module_t;
+struct hw_module_methods_t;
+struct hw_device_t;
+
+/**
+ * Every hardware module must have a data structure named HAL_MODULE_INFO_SYM
+ * and the fields of this data structure must begin with hw_module_t
+ * followed by module specific information.
+ */
+typedef struct hw_module_t {
+ /** tag must be initialized to HARDWARE_MODULE_TAG */
+ uint32_t tag;
+
+ /**
+ * The API version of the implemented module. The module owner is
+ * responsible for updating the version when a module interface has
+ * changed.
+ *
+ * The derived modules such as gralloc and audio own and manage this field.
+ * The module user must interpret the version field to decide whether or
+ * not to inter-operate with the supplied module implementation.
+ * For example, SurfaceFlinger is responsible for making sure that
+ * it knows how to manage different versions of the gralloc-module API,
+ * and AudioFlinger must know how to do the same for audio-module API.
+ *
+ * The module API version should include a major and a minor component.
+ * For example, version 1.0 could be represented as 0x0100. This format
+ * implies that versions 0x0100-0x01ff are all API-compatible.
+ *
+ * In the future, libhardware will expose a hw_get_module_version()
+ * (or equivalent) function that will take minimum/maximum supported
+ * versions as arguments and would be able to reject modules with
+ * versions outside of the supplied range.
+ */
+ uint16_t module_api_version;
+#define version_major module_api_version
+ /**
+ * version_major/version_minor defines are supplied here for temporary
+ * source code compatibility. They will be removed in the next version.
+ * ALL clients must convert to the new version format.
+ */
+
+ /**
+ * The API version of the HAL module interface. This is meant to
+ * version the hw_module_t, hw_module_methods_t, and hw_device_t
+ * structures and definitions.
+ *
+ * The HAL interface owns this field. Module users/implementations
+ * must NOT rely on this value for version information.
+ *
+ * Presently, 0 is the only valid value.
+ */
+ uint16_t hal_api_version;
+#define version_minor hal_api_version
+
+ /** Identifier of module */
+ const char *id;
+
+ /** Name of this module */
+ const char *name;
+
+ /** Author/owner/implementor of the module */
+ const char *author;
+
+ /** Modules methods */
+ struct hw_module_methods_t* methods;
+
+ /** module's dso */
+ void* dso;
+
+ /** padding to 128 bytes, reserved for future use */
+ uint32_t reserved[32-7];
+
+} hw_module_t;
+
+typedef struct hw_module_methods_t {
+ /** Open a specific device */
+ int (*open)(const struct hw_module_t* module, const char* id,
+ struct hw_device_t** device);
+
+} hw_module_methods_t;
+
+/**
+ * Every device data structure must begin with hw_device_t
+ * followed by module specific public methods and attributes.
+ */
+typedef struct hw_device_t {
+ /** tag must be initialized to HARDWARE_DEVICE_TAG */
+ uint32_t tag;
+
+ /**
+ * Version of the module-specific device API. This value is used by
+ * the derived-module user to manage different device implementations.
+ *
+ * The module user is responsible for checking the module_api_version
+ * and device version fields to ensure that the user is capable of
+ * communicating with the specific module implementation.
+ *
+ * One module can support multiple devices with different versions. This
+ * can be useful when a device interface changes in an incompatible way
+ * but it is still necessary to support older implementations at the same
+ * time. One such example is the Camera 2.0 API.
+ *
+ * This field is interpreted by the module user and is ignored by the
+ * HAL interface itself.
+ */
+ uint32_t version;
+
+ /** reference to the module this device belongs to */
+ struct hw_module_t* module;
+
+ /** padding reserved for future use */
+ uint32_t reserved[12];
+
+ /** Close this device */
+ int (*close)(struct hw_device_t* device);
+
+} hw_device_t;
+
+/**
+ * Name of the hal_module_info
+ */
+#define HAL_MODULE_INFO_SYM HMI
+
+/**
+ * Name of the hal_module_info as a string
+ */
+#define HAL_MODULE_INFO_SYM_AS_STR "HMI"
+
+/**
+ * Get the module info associated with a module by id.
+ *
+ * @return: 0 == success, <0 == error and *module == NULL
+ */
+int hw_get_module(const char *id, const struct hw_module_t **module);
+
+/**
+ * Get the module info associated with a module instance by class 'class_id'
+ * and instance 'inst'.
+ *
+ * Some modules types necessitate multiple instances. For example audio supports
+ * multiple concurrent interfaces and thus 'audio' is the module class
+ * and 'primary' or 'a2dp' are module interfaces. This implies that the files
+ * providing these modules would be named audio.primary.<variant>.so and
+ * audio.a2dp.<variant>.so
+ *
+ * @return: 0 == success, <0 == error and *module == NULL
+ */
+int hw_get_module_by_class(const char *class_id, const char *inst,
+ const struct hw_module_t **module);
+
+__END_DECLS
+
+#endif /* ANDROID_INCLUDE_HARDWARE_HARDWARE_H */
diff --git a/repo/android/health.c b/repo/android/health.c
new file mode 100644
index 0000000..eb02a64
--- /dev/null
+++ b/repo/android/health.c
@@ -0,0 +1,2047 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2014 Intel Corporation. All rights reserved.
+ * Copyright (C) 2010 GSyC/LibreSoft, Universidad Rey Juan Carlos.
+ *
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdint.h>
+#include <stdbool.h>
+#include <errno.h>
+#include <unistd.h>
+#include <glib.h>
+
+#include "btio/btio.h"
+#include "lib/bluetooth.h"
+#include "lib/sdp.h"
+#include "lib/sdp_lib.h"
+#include "lib/uuid.h"
+#include "lib/l2cap.h"
+#include "src/log.h"
+#include "src/shared/util.h"
+#include "src/shared/queue.h"
+#include "src/uuid-helper.h"
+#include "src/sdp-client.h"
+#include "profiles/health/mcap.h"
+
+#include "hal-msg.h"
+#include "ipc-common.h"
+#include "ipc.h"
+#include "utils.h"
+#include "bluetooth.h"
+#include "health.h"
+
+#define SVC_HINT_HEALTH 0x00
+#define HDP_VERSION 0x0101
+#define DATA_EXCHANGE_SPEC_11073 0x01
+
+#define CHANNEL_TYPE_ANY 0x00
+#define CHANNEL_TYPE_RELIABLE 0x01
+#define CHANNEL_TYPE_STREAM 0x02
+
+#define MDEP_ECHO 0x00
+#define MDEP_INITIAL 0x01
+#define MDEP_FINAL 0x7F
+
+static bdaddr_t adapter_addr;
+static struct ipc *hal_ipc = NULL;
+static struct queue *apps = NULL;
+static struct mcap_instance *mcap = NULL;
+static uint32_t record_id = 0;
+static uint32_t record_state = 0;
+
+struct mdep_cfg {
+ uint8_t role;
+ uint16_t data_type;
+ uint8_t channel_type;
+ char *descr;
+
+ uint8_t id; /* mdep id */
+};
+
+struct health_device {
+ bdaddr_t dst;
+ uint16_t app_id;
+
+ struct mcap_mcl *mcl;
+
+ struct queue *channels; /* data channels */
+
+ uint16_t ccpsm;
+ uint16_t dcpsm;
+};
+
+struct health_channel {
+ uint8_t mdep_id;
+ uint8_t type;
+
+ struct health_device *dev;
+
+ uint8_t remote_mdep;
+ struct mcap_mdl *mdl;
+ bool mdl_conn;
+ uint16_t mdl_id; /* MDL ID */
+
+ uint16_t id; /* channel id */
+};
+
+struct health_app {
+ char *app_name;
+ char *provider_name;
+ char *service_name;
+ char *service_descr;
+ uint8_t num_of_mdep;
+ struct queue *mdeps;
+
+ uint16_t id; /* app id */
+ struct queue *devices;
+};
+
+static void send_app_reg_notify(struct health_app *app, uint8_t state)
+{
+ struct hal_ev_health_app_reg_state ev;
+
+ DBG("");
+
+ ev.id = app->id;
+ ev.state = state;
+
+ ipc_send_notif(hal_ipc, HAL_SERVICE_ID_HEALTH,
+ HAL_EV_HEALTH_APP_REG_STATE, sizeof(ev), &ev);
+}
+
+static void send_channel_state_notify(struct health_channel *channel,
+ uint8_t state, int fd)
+{
+ struct hal_ev_health_channel_state ev;
+
+ DBG("");
+
+ bdaddr2android(&channel->dev->dst, ev.bdaddr);
+ ev.app_id = channel->dev->app_id;
+ ev.mdep_index = channel->mdep_id - 1;
+ ev.channel_id = channel->id;
+ ev.channel_state = state;
+
+ ipc_send_notif_with_fd(hal_ipc, HAL_SERVICE_ID_HEALTH,
+ HAL_EV_HEALTH_CHANNEL_STATE,
+ sizeof(ev), &ev, fd);
+}
+
+static void unref_mdl(struct health_channel *channel)
+{
+ if (!channel || !channel->mdl)
+ return;
+
+ mcap_mdl_unref(channel->mdl);
+ channel->mdl = NULL;
+ channel->mdl_conn = false;
+}
+
+static void free_health_channel(void *data)
+{
+ struct health_channel *channel = data;
+ int fd;
+
+ DBG("channel %p", channel);
+
+ if (!channel)
+ return;
+
+ fd = mcap_mdl_get_fd(channel->mdl);
+ if (fd >= 0)
+ shutdown(fd, SHUT_RDWR);
+
+ unref_mdl(channel);
+ free(channel);
+}
+
+static void destroy_channel(void *data)
+{
+ struct health_channel *channel = data;
+
+ if (!channel)
+ return;
+
+ send_channel_state_notify(channel, HAL_HEALTH_CHANNEL_DESTROYED, -1);
+ queue_remove(channel->dev->channels, channel);
+ free_health_channel(channel);
+}
+
+static void unref_mcl(struct health_device *dev)
+{
+ if (!dev || !dev->mcl)
+ return;
+
+ mcap_close_mcl(dev->mcl, FALSE);
+ mcap_mcl_unref(dev->mcl);
+ dev->mcl = NULL;
+}
+
+static void free_health_device(void *data)
+{
+ struct health_device *dev = data;
+
+ if (!dev)
+ return;
+
+ unref_mcl(dev);
+ queue_destroy(dev->channels, free_health_channel);
+ free(dev);
+}
+
+static void free_mdep_cfg(void *data)
+{
+ struct mdep_cfg *cfg = data;
+
+ if (!cfg)
+ return;
+
+ free(cfg->descr);
+ free(cfg);
+}
+
+static void free_health_app(void *data)
+{
+ struct health_app *app = data;
+
+ if (!app)
+ return;
+
+ free(app->app_name);
+ free(app->provider_name);
+ free(app->service_name);
+ free(app->service_descr);
+ queue_destroy(app->mdeps, free_mdep_cfg);
+ queue_destroy(app->devices, free_health_device);
+ free(app);
+}
+
+static bool match_channel_by_mdl(const void *data, const void *user_data)
+{
+ const struct health_channel *channel = data;
+ const struct mcap_mdl *mdl = user_data;
+
+ return channel->mdl == mdl;
+}
+
+static bool match_channel_by_id(const void *data, const void *user_data)
+{
+ const struct health_channel *channel = data;
+ uint16_t channel_id = PTR_TO_INT(user_data);
+
+ return channel->id == channel_id;
+}
+
+static bool match_dev_by_mcl(const void *data, const void *user_data)
+{
+ const struct health_device *dev = data;
+ const struct mcap_mcl *mcl = user_data;
+
+ return dev->mcl == mcl;
+}
+
+static bool match_dev_by_addr(const void *data, const void *user_data)
+{
+ const struct health_device *dev = data;
+ const bdaddr_t *addr = user_data;
+
+ return !bacmp(&dev->dst, addr);
+}
+
+static bool match_channel_by_mdep_id(const void *data, const void *user_data)
+{
+ const struct health_channel *channel = data;
+ uint16_t mdep_id = PTR_TO_INT(user_data);
+
+ return channel->mdep_id == mdep_id;
+}
+
+static bool match_mdep_by_role(const void *data, const void *user_data)
+{
+ const struct mdep_cfg *mdep = data;
+ uint16_t role = PTR_TO_INT(user_data);
+
+ return mdep->role == role;
+}
+
+static bool match_mdep_by_id(const void *data, const void *user_data)
+{
+ const struct mdep_cfg *mdep = data;
+ uint16_t mdep_id = PTR_TO_INT(user_data);
+
+ return mdep->id == mdep_id;
+}
+
+static bool match_app_by_id(const void *data, const void *user_data)
+{
+ const struct health_app *app = data;
+ uint16_t app_id = PTR_TO_INT(user_data);
+
+ return app->id == app_id;
+}
+
+static struct health_channel *search_channel_by_id(uint16_t id)
+{
+ const struct queue_entry *apps_entry, *devices_entry;
+ struct health_app *app;
+ struct health_channel *channel;
+ struct health_device *dev;
+
+ DBG("");
+
+ apps_entry = queue_get_entries(apps);
+ while (apps_entry) {
+ app = apps_entry->data;
+ devices_entry = queue_get_entries(app->devices);
+ while (devices_entry) {
+ dev = devices_entry->data;
+ channel = queue_find(dev->channels, match_channel_by_id,
+ INT_TO_PTR(id));
+
+ if (channel)
+ return channel;
+
+ devices_entry = devices_entry->next;
+ }
+
+ apps_entry = apps_entry->next;
+ }
+
+ return NULL;
+}
+
+static struct health_channel *search_channel_by_mdl(struct mcap_mdl *mdl)
+{
+ const struct queue_entry *apps_entry, *devices_entry;
+ struct health_app *app;
+ struct health_channel *channel;
+ struct health_device *dev;
+
+ DBG("");
+
+ apps_entry = queue_get_entries(apps);
+ while (apps_entry) {
+ app = apps_entry->data;
+ devices_entry = queue_get_entries(app->devices);
+ while (devices_entry) {
+ dev = devices_entry->data;
+ channel = queue_find(dev->channels,
+ match_channel_by_mdl, mdl);
+
+ if (channel)
+ return channel;
+
+ devices_entry = devices_entry->next;
+ }
+
+ apps_entry = apps_entry->next;
+ }
+
+ return NULL;
+}
+
+static struct health_device *search_dev_by_mcl(struct mcap_mcl *mcl)
+{
+ const struct queue_entry *apps_entry;
+ struct health_app *app;
+ struct health_device *dev;
+
+ DBG("");
+
+ apps_entry = queue_get_entries(apps);
+ while (apps_entry) {
+ app = apps_entry->data;
+
+ dev = queue_find(app->devices, match_dev_by_mcl, mcl);
+
+ if (dev)
+ return dev;
+
+ apps_entry = apps_entry->next;
+ }
+
+ return NULL;
+}
+
+static struct health_app *search_app_by_mdepid(uint8_t mdepid)
+{
+ const struct queue_entry *apps_entry;
+ struct health_app *app;
+
+ DBG("");
+
+ apps_entry = queue_get_entries(apps);
+ while (apps_entry) {
+ app = apps_entry->data;
+
+ if (queue_find(app->mdeps, match_mdep_by_id,
+ INT_TO_PTR(mdepid)))
+ return app;
+
+ apps_entry = apps_entry->next;
+ }
+
+ return NULL;
+}
+
+static int register_service_protocols(sdp_record_t *rec,
+ struct health_app *app)
+{
+ uuid_t l2cap_uuid, mcap_c_uuid;
+ sdp_list_t *l2cap_list, *proto_list = NULL, *mcap_list = NULL;
+ sdp_list_t *access_proto_list = NULL;
+ sdp_data_t *psm = NULL, *mcap_ver = NULL;
+ uint32_t ccpsm;
+ uint16_t version = MCAP_VERSION;
+ GError *err = NULL;
+ int ret = -1;
+
+ DBG("");
+
+ /* set l2cap information */
+ sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID);
+ l2cap_list = sdp_list_append(NULL, &l2cap_uuid);
+ if (!l2cap_list)
+ goto fail;
+
+ ccpsm = mcap_get_ctrl_psm(mcap, &err);
+ if (err)
+ goto fail;
+
+ psm = sdp_data_alloc(SDP_UINT16, &ccpsm);
+ if (!psm)
+ goto fail;
+
+ if (!sdp_list_append(l2cap_list, psm))
+ goto fail;
+
+ proto_list = sdp_list_append(NULL, l2cap_list);
+ if (!proto_list)
+ goto fail;
+
+ /* set mcap information */
+ sdp_uuid16_create(&mcap_c_uuid, MCAP_CTRL_UUID);
+ mcap_list = sdp_list_append(NULL, &mcap_c_uuid);
+ if (!mcap_list)
+ goto fail;
+
+ mcap_ver = sdp_data_alloc(SDP_UINT16, &version);
+ if (!mcap_ver)
+ goto fail;
+
+ if (!sdp_list_append(mcap_list, mcap_ver))
+ goto fail;
+
+ if (!sdp_list_append(proto_list, mcap_list))
+ goto fail;
+
+ /* attach protocol information to service record */
+ access_proto_list = sdp_list_append(NULL, proto_list);
+ if (!access_proto_list)
+ goto fail;
+
+ sdp_set_access_protos(rec, access_proto_list);
+ ret = 0;
+
+fail:
+ sdp_list_free(l2cap_list, NULL);
+ sdp_list_free(mcap_list, NULL);
+ sdp_list_free(proto_list, NULL);
+ sdp_list_free(access_proto_list, NULL);
+
+ if (psm)
+ sdp_data_free(psm);
+
+ if (mcap_ver)
+ sdp_data_free(mcap_ver);
+
+ if (err)
+ g_error_free(err);
+
+ return ret;
+}
+
+static int register_service_profiles(sdp_record_t *rec)
+{
+ int ret;
+ sdp_list_t *profile_list;
+ sdp_profile_desc_t hdp_profile;
+
+ DBG("");
+
+ /* set hdp information */
+ sdp_uuid16_create(&hdp_profile.uuid, HDP_SVCLASS_ID);
+ hdp_profile.version = HDP_VERSION;
+ profile_list = sdp_list_append(NULL, &hdp_profile);
+ if (!profile_list)
+ return -1;
+
+ /* set profile descriptor list */
+ ret = sdp_set_profile_descs(rec, profile_list);
+ sdp_list_free(profile_list, NULL);
+
+ return ret;
+}
+
+static int register_service_additional_protocols(sdp_record_t *rec,
+ struct health_app *app)
+{
+ int ret = -1;
+ uuid_t l2cap_uuid, mcap_d_uuid;
+ sdp_list_t *l2cap_list, *proto_list = NULL, *mcap_list = NULL;
+ sdp_list_t *access_proto_list = NULL;
+ sdp_data_t *psm = NULL;
+ uint32_t dcpsm;
+ GError *err = NULL;
+
+ DBG("");
+
+ /* set l2cap information */
+ sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID);
+ l2cap_list = sdp_list_append(NULL, &l2cap_uuid);
+ if (!l2cap_list)
+ goto fail;
+
+ dcpsm = mcap_get_data_psm(mcap, &err);
+ if (err)
+ goto fail;
+
+ psm = sdp_data_alloc(SDP_UINT16, &dcpsm);
+ if (!psm)
+ goto fail;
+
+ if (!sdp_list_append(l2cap_list, psm))
+ goto fail;
+
+ proto_list = sdp_list_append(NULL, l2cap_list);
+ if (!proto_list)
+ goto fail;
+
+ /* set mcap information */
+ sdp_uuid16_create(&mcap_d_uuid, MCAP_DATA_UUID);
+ mcap_list = sdp_list_append(NULL, &mcap_d_uuid);
+ if (!mcap_list)
+ goto fail;
+
+ if (!sdp_list_append(proto_list, mcap_list))
+ goto fail;
+
+ /* attach protocol information to service record */
+ access_proto_list = sdp_list_append(NULL, proto_list);
+ if (!access_proto_list)
+ goto fail;
+
+ sdp_set_add_access_protos(rec, access_proto_list);
+ ret = 0;
+
+fail:
+ sdp_list_free(l2cap_list, NULL);
+ sdp_list_free(mcap_list, NULL);
+ sdp_list_free(proto_list, NULL);
+ sdp_list_free(access_proto_list, NULL);
+
+ if (psm)
+ sdp_data_free(psm);
+
+ if (err)
+ g_error_free(err);
+
+ return ret;
+}
+
+static sdp_list_t *mdeps_to_sdp_features(struct mdep_cfg *mdep)
+{
+ sdp_data_t *mdepid, *dtype = NULL, *role = NULL, *descr = NULL;
+ sdp_list_t *f_list = NULL;
+
+ DBG("");
+
+ mdepid = sdp_data_alloc(SDP_UINT8, &mdep->id);
+ if (!mdepid)
+ return NULL;
+
+ dtype = sdp_data_alloc(SDP_UINT16, &mdep->data_type);
+ if (!dtype)
+ goto fail;
+
+ role = sdp_data_alloc(SDP_UINT8, &mdep->role);
+ if (!role)
+ goto fail;
+
+ if (mdep->descr) {
+ descr = sdp_data_alloc(SDP_TEXT_STR8, mdep->descr);
+ if (!descr)
+ goto fail;
+ }
+
+ f_list = sdp_list_append(NULL, mdepid);
+ if (!f_list)
+ goto fail;
+
+ if (!sdp_list_append(f_list, dtype))
+ goto fail;
+
+ if (!sdp_list_append(f_list, role))
+ goto fail;
+
+ if (descr && !sdp_list_append(f_list, descr))
+ goto fail;
+
+ return f_list;
+
+fail:
+ sdp_list_free(f_list, NULL);
+
+ if (mdepid)
+ sdp_data_free(mdepid);
+
+ if (dtype)
+ sdp_data_free(dtype);
+
+ if (role)
+ sdp_data_free(role);
+
+ if (descr)
+ sdp_data_free(descr);
+
+ return NULL;
+}
+
+static void free_hdp_list(void *list)
+{
+ sdp_list_t *hdp_list = list;
+
+ sdp_list_free(hdp_list, (sdp_free_func_t)sdp_data_free);
+}
+
+static void register_features(void *data, void *user_data)
+{
+ struct mdep_cfg *mdep = data;
+ sdp_list_t **sup_features = user_data;
+ sdp_list_t *hdp_feature;
+
+ DBG("");
+
+ hdp_feature = mdeps_to_sdp_features(mdep);
+ if (!hdp_feature)
+ return;
+
+ if (!*sup_features) {
+ *sup_features = sdp_list_append(NULL, hdp_feature);
+ if (!*sup_features)
+ sdp_list_free(hdp_feature,
+ (sdp_free_func_t)sdp_data_free);
+ } else if (!sdp_list_append(*sup_features, hdp_feature)) {
+ sdp_list_free(hdp_feature,
+ (sdp_free_func_t)sdp_data_free);
+ }
+}
+
+static int register_service_sup_features(sdp_record_t *rec,
+ struct health_app *app)
+{
+ sdp_list_t *sup_features = NULL;
+
+ DBG("");
+
+ queue_foreach(app->mdeps, register_features, &sup_features);
+ if (!sup_features)
+ return -1;
+
+ if (sdp_set_supp_feat(rec, sup_features) < 0) {
+ sdp_list_free(sup_features, free_hdp_list);
+ return -1;
+ }
+
+ sdp_list_free(sup_features, free_hdp_list);
+ return 0;
+}
+
+static int register_data_exchange_spec(sdp_record_t *rec)
+{
+ sdp_data_t *spec;
+ uint8_t data_spec = DATA_EXCHANGE_SPEC_11073;
+ /* As of now only 11073 is supported, so we set it as default */
+
+ DBG("");
+
+ spec = sdp_data_alloc(SDP_UINT8, &data_spec);
+ if (!spec)
+ return -1;
+
+ if (sdp_attr_add(rec, SDP_ATTR_DATA_EXCHANGE_SPEC, spec) < 0) {
+ sdp_data_free(spec);
+ return -1;
+ }
+
+ return 0;
+}
+
+static int register_mcap_features(sdp_record_t *rec)
+{
+ sdp_data_t *mcap_proc;
+ uint8_t mcap_sup_proc = MCAP_SUP_PROC;
+
+ DBG("");
+
+ mcap_proc = sdp_data_alloc(SDP_UINT8, &mcap_sup_proc);
+ if (!mcap_proc)
+ return -1;
+
+ if (sdp_attr_add(rec, SDP_ATTR_MCAP_SUPPORTED_PROCEDURES,
+ mcap_proc) < 0) {
+ sdp_data_free(mcap_proc);
+ return -1;
+ }
+
+ return 0;
+}
+
+static int set_sdp_services_uuid(sdp_record_t *rec, uint8_t role)
+{
+ uuid_t source, sink;
+ sdp_list_t *list = NULL;
+
+ sdp_uuid16_create(&sink, HDP_SINK_SVCLASS_ID);
+ sdp_uuid16_create(&source, HDP_SOURCE_SVCLASS_ID);
+ sdp_get_service_classes(rec, &list);
+
+ switch (role) {
+ case HAL_HEALTH_MDEP_ROLE_SOURCE:
+ if (!sdp_list_find(list, &source, sdp_uuid_cmp))
+ list = sdp_list_append(list, &source);
+ break;
+ case HAL_HEALTH_MDEP_ROLE_SINK:
+ if (!sdp_list_find(list, &sink, sdp_uuid_cmp))
+ list = sdp_list_append(list, &sink);
+ break;
+ }
+
+ if (sdp_set_service_classes(rec, list) < 0) {
+ sdp_list_free(list, NULL);
+ return -1;
+ }
+
+ sdp_list_free(list, NULL);
+
+ return 0;
+}
+
+static int update_sdp_record(struct health_app *app)
+{
+ sdp_record_t *rec;
+ uint8_t role;
+
+ DBG("");
+
+ if (record_id > 0) {
+ bt_adapter_remove_record(record_id);
+ record_id = 0;
+ }
+
+ rec = sdp_record_alloc();
+ if (!rec)
+ return -1;
+
+ role = HAL_HEALTH_MDEP_ROLE_SOURCE;
+ if (queue_find(app->mdeps, match_mdep_by_role, INT_TO_PTR(role)))
+ set_sdp_services_uuid(rec, role);
+
+ role = HAL_HEALTH_MDEP_ROLE_SINK;
+ if (queue_find(app->mdeps, match_mdep_by_role, INT_TO_PTR(role)))
+ set_sdp_services_uuid(rec, role);
+
+ sdp_set_info_attr(rec, app->service_name, app->provider_name,
+ app->service_descr);
+
+ if (register_service_protocols(rec, app) < 0)
+ goto fail;
+
+ if (register_service_profiles(rec) < 0)
+ goto fail;
+
+ if (register_service_additional_protocols(rec, app) < 0)
+ goto fail;
+
+ if (register_service_sup_features(rec, app) < 0)
+ goto fail;
+
+ if (register_data_exchange_spec(rec) < 0)
+ goto fail;
+
+ if (register_mcap_features(rec) < 0)
+ goto fail;
+
+ if (sdp_set_record_state(rec, record_state++) < 0)
+ goto fail;
+
+ if (bt_adapter_add_record(rec, SVC_HINT_HEALTH) < 0) {
+ error("health: Failed to register HEALTH record");
+ goto fail;
+ }
+
+ record_id = rec->handle;
+
+ return 0;
+
+fail:
+ sdp_record_free(rec);
+
+ return -1;
+}
+
+static struct health_app *create_health_app(const char *app_name,
+ const char *provider, const char *srv_name,
+ const char *srv_descr, uint8_t mdeps)
+{
+ struct health_app *app;
+ static unsigned int app_id = 1;
+
+ DBG("");
+
+ app = new0(struct health_app, 1);
+ app->id = app_id++;
+ app->num_of_mdep = mdeps;
+ app->app_name = strdup(app_name);
+
+ if (provider) {
+ app->provider_name = strdup(provider);
+ if (!app->provider_name)
+ goto fail;
+ }
+
+ if (srv_name) {
+ app->service_name = strdup(srv_name);
+ if (!app->service_name)
+ goto fail;
+ }
+
+ if (srv_descr) {
+ app->service_descr = strdup(srv_descr);
+ if (!app->service_descr)
+ goto fail;
+ }
+
+ app->mdeps = queue_new();
+ app->devices = queue_new();
+
+ return app;
+
+fail:
+ free_health_app(app);
+ return NULL;
+}
+
+static void bt_health_register_app(const void *buf, uint16_t len)
+{
+ const struct hal_cmd_health_reg_app *cmd = buf;
+ struct hal_rsp_health_reg_app rsp;
+ struct health_app *app;
+ uint16_t off;
+ uint16_t app_name_len, provider_len, srv_name_len, srv_descr_len;
+ char *app_name, *provider = NULL, *srv_name = NULL, *srv_descr = NULL;
+
+ DBG("");
+
+ if (len != sizeof(*cmd) + cmd->len ||
+ cmd->app_name_off > cmd->provider_name_off ||
+ cmd->provider_name_off > cmd->service_name_off ||
+ cmd->service_name_off > cmd->service_descr_off ||
+ cmd->service_descr_off > cmd->len) {
+ error("health: Invalid register app command, terminating");
+ raise(SIGTERM);
+ return;
+ }
+
+ app_name = (char *) cmd->data;
+ app_name_len = cmd->provider_name_off - cmd->app_name_off;
+
+ off = app_name_len;
+ provider_len = cmd->service_name_off - off;
+ if (provider_len > 0)
+ provider = (char *) cmd->data + off;
+
+ off += provider_len;
+ srv_name_len = cmd->service_descr_off - off;
+ if (srv_name_len > 0)
+ srv_name = (char *) cmd->data + off;
+
+ off += srv_name_len;
+ srv_descr_len = cmd->len - off;
+ if (srv_descr_len > 0)
+ srv_descr = (char *) cmd->data + off;
+
+ app = create_health_app(app_name, provider, srv_name, srv_descr,
+ cmd->num_of_mdep);
+ if (!app)
+ goto fail;
+
+ queue_push_tail(apps, app);
+
+ rsp.app_id = app->id;
+ ipc_send_rsp_full(hal_ipc, HAL_SERVICE_ID_HEALTH, HAL_OP_HEALTH_REG_APP,
+ sizeof(rsp), &rsp, -1);
+ return;
+
+fail:
+ free_health_app(app);
+ ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_HEALTH, HAL_OP_HEALTH_MDEP,
+ HAL_STATUS_FAILED);
+}
+
+static uint8_t android2channel_type(uint8_t type)
+{
+ switch (type) {
+ case HAL_HEALTH_CHANNEL_TYPE_RELIABLE:
+ return CHANNEL_TYPE_RELIABLE;
+ case HAL_HEALTH_CHANNEL_TYPE_STREAMING:
+ return CHANNEL_TYPE_STREAM;
+ default:
+ return CHANNEL_TYPE_ANY;
+ }
+}
+
+static void bt_health_mdep_cfg_data(const void *buf, uint16_t len)
+{
+ const struct hal_cmd_health_mdep *cmd = buf;
+ struct health_app *app;
+ struct mdep_cfg *mdep = NULL;
+ uint8_t status;
+
+ DBG("");
+
+ app = queue_find(apps, match_app_by_id, INT_TO_PTR(cmd->app_id));
+ if (!app) {
+ status = HAL_STATUS_INVALID;
+ goto fail;
+ }
+
+ mdep = new0(struct mdep_cfg, 1);
+ mdep->role = cmd->role;
+ mdep->data_type = cmd->data_type;
+ mdep->channel_type = android2channel_type(cmd->channel_type);
+ mdep->id = queue_length(app->mdeps) + 1;
+
+ if (cmd->descr_len > 0) {
+ mdep->descr = malloc0(cmd->descr_len);
+ memcpy(mdep->descr, cmd->descr, cmd->descr_len);
+ }
+
+ queue_push_tail(app->mdeps, mdep);
+
+ if (app->num_of_mdep != queue_length(app->mdeps))
+ goto send_rsp;
+
+ /* add sdp record from app configuration data */
+ /*
+ * TODO: Check what to be done if mupltple applications are trying to
+ * register with different role and different configurations.
+ * 1) Does device supports SOURCE and SINK at the same time ?
+ * 2) Does it require different SDP records or one record with
+ * multile MDEP configurations ?
+ */
+ if (update_sdp_record(app) < 0) {
+ error("health: HDP SDP record preparation failed");
+ status = HAL_STATUS_FAILED;
+ goto fail;
+ }
+
+ send_app_reg_notify(app, HAL_HEALTH_APP_REG_SUCCESS);
+
+send_rsp:
+ ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_HEALTH, HAL_OP_HEALTH_MDEP,
+ HAL_STATUS_SUCCESS);
+ return;
+
+fail:
+ if (status != HAL_STATUS_SUCCESS) {
+ free_mdep_cfg(mdep);
+ queue_remove(apps, app);
+ free_health_app(app);
+ }
+
+ ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_HEALTH, HAL_OP_HEALTH_MDEP,
+ status);
+}
+
+static void bt_health_unregister_app(const void *buf, uint16_t len)
+{
+ const struct hal_cmd_health_unreg_app *cmd = buf;
+ struct health_app *app;
+
+ DBG("");
+
+ app = queue_remove_if(apps, match_app_by_id, INT_TO_PTR(cmd->app_id));
+ if (!app) {
+ ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_HEALTH,
+ HAL_OP_HEALTH_UNREG_APP, HAL_STATUS_INVALID);
+ return;
+ }
+
+ send_app_reg_notify(app, HAL_HEALTH_APP_DEREG_SUCCESS);
+
+ if (record_id > 0) {
+ bt_adapter_remove_record(record_id);
+ record_id = 0;
+ }
+
+ free_health_app(app);
+ ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_HEALTH,
+ HAL_OP_HEALTH_UNREG_APP, HAL_STATUS_SUCCESS);
+}
+
+static int get_prot_desc_entry(sdp_data_t *entry, int type, guint16 *val)
+{
+ sdp_data_t *iter;
+ int proto;
+
+ if (!entry || !SDP_IS_SEQ(entry->dtd))
+ return -1;
+
+ iter = entry->val.dataseq;
+ if (!(iter->dtd & SDP_UUID_UNSPEC))
+ return -1;
+
+ proto = sdp_uuid_to_proto(&iter->val.uuid);
+ if (proto != type)
+ return -1;
+
+ if (!val)
+ return 0;
+
+ iter = iter->next;
+ if (iter->dtd != SDP_UINT16)
+ return -1;
+
+ *val = iter->val.uint16;
+
+ return 0;
+}
+
+static int get_prot_desc_list(const sdp_record_t *rec, uint16_t *psm,
+ uint16_t *version)
+{
+ sdp_data_t *pdl, *p0, *p1;
+
+ if (!psm && !version)
+ return -1;
+
+ pdl = sdp_data_get(rec, SDP_ATTR_PROTO_DESC_LIST);
+ if (!pdl || !SDP_IS_SEQ(pdl->dtd))
+ return -1;
+
+ p0 = pdl->val.dataseq;
+ if (get_prot_desc_entry(p0, L2CAP_UUID, psm) < 0)
+ return -1;
+
+ p1 = p0->next;
+ if (get_prot_desc_entry(p1, MCAP_CTRL_UUID, version) < 0)
+ return -1;
+
+ return 0;
+}
+
+static int get_ccpsm(sdp_list_t *recs, uint16_t *ccpsm)
+{
+ sdp_list_t *l;
+
+ for (l = recs; l; l = l->next) {
+ sdp_record_t *rec = l->data;
+
+ if (!get_prot_desc_list(rec, ccpsm, NULL))
+ return 0;
+ }
+
+ return -1;
+}
+
+static int get_add_prot_desc_list(const sdp_record_t *rec, uint16_t *psm)
+{
+ sdp_data_t *pdl, *p0, *p1;
+
+ if (!psm)
+ return -1;
+
+ pdl = sdp_data_get(rec, SDP_ATTR_ADD_PROTO_DESC_LIST);
+ if (!pdl || pdl->dtd != SDP_SEQ8)
+ return -1;
+
+ pdl = pdl->val.dataseq;
+ if (pdl->dtd != SDP_SEQ8)
+ return -1;
+
+ p0 = pdl->val.dataseq;
+
+ if (get_prot_desc_entry(p0, L2CAP_UUID, psm) < 0)
+ return -1;
+
+ p1 = p0->next;
+ if (get_prot_desc_entry(p1, MCAP_DATA_UUID, NULL) < 0)
+ return -1;
+
+ return 0;
+}
+
+static int get_dcpsm(sdp_list_t *recs, uint16_t *dcpsm)
+{
+ sdp_list_t *l;
+
+ for (l = recs; l; l = l->next) {
+ sdp_record_t *rec = l->data;
+
+ if (!get_add_prot_desc_list(rec, dcpsm))
+ return 0;
+ }
+
+ return -1;
+}
+
+static int send_echo_data(int sock, const void *buf, uint32_t size)
+{
+ const uint8_t *buf_b = buf;
+ uint32_t sent = 0;
+
+ while (sent < size) {
+ int n = write(sock, buf_b + sent, size - sent);
+ if (n < 0)
+ return -1;
+ sent += n;
+ }
+
+ return 0;
+}
+
+static gboolean serve_echo(GIOChannel *io, GIOCondition cond, gpointer data)
+{
+ struct health_channel *channel = data;
+ uint8_t buf[MCAP_DC_MTU];
+ int fd, len, ret;
+
+ DBG("channel %p", channel);
+
+ if (cond & (G_IO_ERR | G_IO_HUP | G_IO_NVAL)) {
+ DBG("Error condition on channel");
+ return FALSE;
+ }
+
+ fd = g_io_channel_unix_get_fd(io);
+
+ len = read(fd, buf, sizeof(buf));
+ if (len < 0) {
+ DBG("Error reading ECHO");
+ return FALSE;
+ }
+
+ ret = send_echo_data(fd, buf, len);
+ if (ret != len)
+ DBG("Error sending ECHO back");
+
+ return FALSE;
+}
+
+static void mcap_mdl_connected_cb(struct mcap_mdl *mdl, void *data)
+{
+ struct health_channel *channel = data;
+ int fd;
+
+ DBG("Data channel connected: mdl %p channel %p", mdl, channel);
+
+ if (!channel) {
+ channel = search_channel_by_mdl(mdl);
+ if (!channel) {
+ error("health: channel data does not exist");
+ return;
+ }
+ }
+
+ if (!channel->mdl)
+ channel->mdl = mcap_mdl_ref(mdl);
+
+ fd = mcap_mdl_get_fd(channel->mdl);
+ if (fd < 0) {
+ error("health: error retrieving fd");
+ goto fail;
+ }
+
+ if (channel->mdep_id == MDEP_ECHO) {
+ GIOChannel *io;
+
+ io = g_io_channel_unix_new(fd);
+ g_io_add_watch(io, G_IO_ERR | G_IO_HUP | G_IO_NVAL | G_IO_IN,
+ serve_echo, channel);
+ g_io_channel_unref(io);
+
+ return;
+ }
+
+ info("health: MDL connected");
+ send_channel_state_notify(channel, HAL_HEALTH_CHANNEL_CONNECTED, fd);
+
+ return;
+fail:
+ /* TODO: mcap_mdl_abort */
+ destroy_channel(channel);
+}
+
+static void mcap_mdl_closed_cb(struct mcap_mdl *mdl, void *data)
+{
+ struct health_channel *channel = data;
+
+ info("health: MDL closed");
+
+ if (!channel)
+ return;
+
+ channel->mdl_conn = false;
+}
+
+static void mcap_mdl_deleted_cb(struct mcap_mdl *mdl, void *data)
+{
+ struct health_channel *channel;
+
+ info("health: MDL deleted");
+
+ channel = search_channel_by_mdl(mdl);
+ if (!channel)
+ return;
+
+ DBG("channel %p mdl %p", channel, mdl);
+ destroy_channel(channel);
+}
+
+static void mcap_mdl_aborted_cb(struct mcap_mdl *mdl, void *data)
+{
+ DBG("Not Implemeneted");
+}
+
+static struct health_device *create_device(struct health_app *app,
+ const uint8_t *addr)
+{
+ struct health_device *dev;
+
+ /* create device and push it to devices queue */
+ dev = new0(struct health_device, 1);
+
+ android2bdaddr(addr, &dev->dst);
+ dev->channels = queue_new();
+ dev->app_id = app->id;
+
+ queue_push_tail(app->devices, dev);
+
+ return dev;
+}
+
+static struct health_device *get_device(struct health_app *app,
+ const uint8_t *addr)
+{
+ struct health_device *dev;
+ bdaddr_t bdaddr;
+
+ android2bdaddr(addr, &bdaddr);
+ dev = queue_find(app->devices, match_dev_by_addr, &bdaddr);
+ if (dev)
+ return dev;
+
+ return create_device(app, addr);
+}
+
+static struct health_channel *create_channel(struct health_app *app,
+ uint8_t mdep_index,
+ struct health_device *dev)
+{
+ struct mdep_cfg *mdep;
+ struct health_channel *channel;
+ static unsigned int channel_id = 1;
+
+ DBG("mdep %u", mdep_index);
+
+ if (!dev || !app)
+ return NULL;
+
+ mdep = queue_find(app->mdeps, match_mdep_by_id, INT_TO_PTR(mdep_index));
+ if (!mdep) {
+ if (mdep_index == MDEP_ECHO) {
+ mdep = new0(struct mdep_cfg, 1);
+
+ /* Leave other configuration zeroes */
+ mdep->id = MDEP_ECHO;
+
+ queue_push_tail(app->mdeps, mdep);
+ } else {
+ return NULL;
+ }
+ }
+
+ /* create channel and push it to device */
+ channel = new0(struct health_channel, 1);
+ channel->mdep_id = mdep->id;
+ channel->type = mdep->channel_type;
+ channel->id = channel_id++;
+ channel->dev = dev;
+
+ queue_push_tail(dev->channels, channel);
+
+ return channel;
+}
+
+static struct health_channel *connect_channel(struct health_app *app,
+ struct mcap_mcl *mcl,
+ uint8_t mdepid)
+{
+ struct health_device *device;
+ bdaddr_t addr;
+
+ DBG("app %p mdepid %u", app, mdepid);
+
+ mcap_mcl_get_addr(mcl, &addr);
+
+ if (!app) {
+ DBG("No app found for mdepid %u", mdepid);
+ return NULL;
+ }
+
+ device = get_device(app, (uint8_t *) &addr);
+
+ return create_channel(app, mdepid, device);
+}
+
+static uint8_t conf_to_l2cap(uint8_t conf)
+{
+ return conf == CHANNEL_TYPE_STREAM ? L2CAP_MODE_STREAMING :
+ L2CAP_MODE_ERTM;
+}
+
+static uint8_t mcap_mdl_conn_req_cb(struct mcap_mcl *mcl, uint8_t mdepid,
+ uint16_t mdlid, uint8_t *conf, void *data)
+{
+ GError *gerr = NULL;
+ struct health_channel *channel;
+ struct health_app *app;
+ struct mdep_cfg *mdep;
+
+ DBG("Data channel request: mdepid %u mdlid %u conf %u",
+ mdepid, mdlid, *conf);
+
+ if (mdepid == MDEP_ECHO)
+ /* For echo service take last app */
+ app = queue_peek_tail(apps);
+ else
+ app = search_app_by_mdepid(mdepid);
+
+ if (!app)
+ return MCAP_MDL_BUSY;
+
+ channel = connect_channel(app, mcl, mdepid);
+ if (!channel)
+ return MCAP_MDL_BUSY;
+
+ /* Channel is assigned here after creation */
+ mcl->cb->user_data = channel;
+
+ if (mdepid == MDEP_ECHO) {
+ switch (*conf) {
+ case CHANNEL_TYPE_ANY:
+ *conf = CHANNEL_TYPE_RELIABLE;
+ break;
+ case CHANNEL_TYPE_RELIABLE:
+ break;
+ case CHANNEL_TYPE_STREAM:
+ return MCAP_CONFIGURATION_REJECTED;
+ default:
+ /*
+ * Special case defined in HDP spec 3.4.
+ * When an invalid configuration is received we shall
+ * close the MCL when we are still processing the
+ * callback.
+ */
+ /* TODO close device */
+ return MCAP_CONFIGURATION_REJECTED; /* not processed */
+ }
+
+ if (!mcap_set_data_chan_mode(mcap, L2CAP_MODE_ERTM, &gerr)) {
+ error("Error: %s", gerr->message);
+ g_error_free(gerr);
+ return MCAP_MDL_BUSY;
+ }
+
+ /* TODO: Create channel */
+
+ return MCAP_SUCCESS;
+ }
+
+ mdep = queue_find(app->mdeps, match_mdep_by_id, INT_TO_PTR(mdepid));
+ if (!mdep)
+ return MCAP_MDL_BUSY;
+
+ switch (*conf) {
+ case CHANNEL_TYPE_ANY:
+ if (mdep->role == HAL_HEALTH_MDEP_ROLE_SINK) {
+ return MCAP_CONFIGURATION_REJECTED;
+ } else {
+ if (queue_length(channel->dev->channels) <= 1)
+ *conf = CHANNEL_TYPE_RELIABLE;
+ else
+ *conf = CHANNEL_TYPE_STREAM;
+ }
+ break;
+ case CHANNEL_TYPE_STREAM:
+ if (mdep->role == HAL_HEALTH_MDEP_ROLE_SOURCE)
+ return MCAP_CONFIGURATION_REJECTED;
+ break;
+ case CHANNEL_TYPE_RELIABLE:
+ if (mdep->role == HAL_HEALTH_MDEP_ROLE_SOURCE)
+ return MCAP_CONFIGURATION_REJECTED;
+ break;
+ default:
+ /*
+ * Special case defined in HDP spec 3.4. When an invalid
+ * configuration is received we shall close the MCL when
+ * we are still processing the callback.
+ */
+ /* TODO: close device */
+ return MCAP_CONFIGURATION_REJECTED; /* not processed */
+ }
+
+ if (!mcap_set_data_chan_mode(mcap, conf_to_l2cap(*conf), &gerr)) {
+ error("health: error setting L2CAP mode: %s", gerr->message);
+ g_error_free(gerr);
+ return MCAP_MDL_BUSY;
+ }
+
+ return MCAP_SUCCESS;
+}
+
+static uint8_t mcap_mdl_reconn_req_cb(struct mcap_mdl *mdl, void *data)
+{
+ struct health_channel *channel;
+ GError *err = NULL;
+
+ DBG("");
+
+ channel = search_channel_by_mdl(mdl);
+ if (!channel) {
+ error("health: channel data does not exist");
+ return MCAP_UNSPECIFIED_ERROR;
+ }
+
+ if (!mcap_set_data_chan_mode(mcap,
+ conf_to_l2cap(channel->type), &err)) {
+ error("health: %s", err->message);
+ g_error_free(err);
+ return MCAP_MDL_BUSY;
+ }
+
+ return MCAP_SUCCESS;
+}
+
+static void connect_mdl_cb(struct mcap_mdl *mdl, GError *gerr, gpointer data)
+{
+ struct health_channel *channel = data;
+ int fd;
+
+ DBG("");
+
+ if (gerr) {
+ error("health: error connecting to MDL %s", gerr->message);
+ goto fail;
+ }
+
+ fd = mcap_mdl_get_fd(channel->mdl);
+ if (fd < 0) {
+ error("health: error retrieving fd");
+ goto fail;
+ }
+
+ info("health: MDL connected");
+ channel->mdl_conn = true;
+
+ /* first data channel should be reliable data channel */
+ if (!queue_length(channel->dev->channels))
+ if (channel->type != CHANNEL_TYPE_RELIABLE)
+ goto fail;
+
+ send_channel_state_notify(channel, HAL_HEALTH_CHANNEL_CONNECTED, fd);
+
+ return;
+
+fail:
+ /* TODO: mcap_mdl_abort */
+ destroy_channel(channel);
+}
+
+static void reconnect_mdl_cb(struct mcap_mdl *mdl, GError *gerr, gpointer data)
+{
+ struct health_channel *channel = data;
+ uint8_t mode;
+ GError *err = NULL;
+
+ DBG("");
+
+ if (gerr) {
+ error("health: error reconnecting to MDL %s", gerr->message);
+ goto fail;
+ }
+
+ channel->mdl_id = mcap_mdl_get_mdlid(mdl);
+
+ if (channel->type == CHANNEL_TYPE_RELIABLE)
+ mode = L2CAP_MODE_ERTM;
+ else
+ mode = L2CAP_MODE_STREAMING;
+
+ if (!mcap_connect_mdl(channel->mdl, mode, channel->dev->dcpsm,
+ connect_mdl_cb, channel,
+ NULL, &err)) {
+ error("health: error connecting to mdl");
+ g_error_free(err);
+ goto fail;
+ }
+
+ return;
+
+fail:
+ /* TODO: mcap_mdl_abort */
+ destroy_channel(channel);
+}
+
+static int reconnect_mdl(struct health_channel *channel)
+{
+ GError *gerr = NULL;
+
+ DBG("");
+
+ if (!channel)
+ return -1;
+
+ if (!mcap_reconnect_mdl(channel->mdl, reconnect_mdl_cb, channel,
+ NULL, &gerr)){
+ error("health: reconnect failed %s", gerr->message);
+ destroy_channel(channel);
+ }
+
+ return 0;
+}
+
+static void create_mdl_cb(struct mcap_mdl *mdl, uint8_t type, GError *gerr,
+ gpointer data)
+{
+ struct health_channel *channel = data;
+ uint8_t mode;
+ GError *err = NULL;
+
+ DBG("");
+ if (gerr) {
+ error("health: error creating MDL %s", gerr->message);
+ goto fail;
+ }
+
+ if (channel->type == CHANNEL_TYPE_ANY && type != CHANNEL_TYPE_ANY)
+ channel->type = type;
+
+ /*
+ * if requested channel type is not same as preferred
+ * channel type from remote device, then abort the connection.
+ */
+ if (channel->type != type) {
+ /* TODO: abort mdl */
+ error("health: channel type requested %d preferred %d not same",
+ channel->type, type);
+ goto fail;
+ }
+
+ if (!channel->mdl)
+ channel->mdl = mcap_mdl_ref(mdl);
+
+ channel->type = type;
+ channel->mdl_id = mcap_mdl_get_mdlid(mdl);
+
+ if (channel->type == CHANNEL_TYPE_RELIABLE)
+ mode = L2CAP_MODE_ERTM;
+ else
+ mode = L2CAP_MODE_STREAMING;
+
+ if (!mcap_connect_mdl(channel->mdl, mode, channel->dev->dcpsm,
+ connect_mdl_cb, channel,
+ NULL, &err)) {
+ error("health: error connecting to mdl");
+ g_error_free(err);
+ goto fail;
+ }
+
+ return;
+
+fail:
+ destroy_channel(channel);
+}
+
+static bool check_role(uint8_t rec_role, uint8_t app_role)
+{
+ if ((rec_role == HAL_HEALTH_MDEP_ROLE_SINK &&
+ app_role == HAL_HEALTH_MDEP_ROLE_SOURCE) ||
+ (rec_role == HAL_HEALTH_MDEP_ROLE_SOURCE &&
+ app_role == HAL_HEALTH_MDEP_ROLE_SINK))
+ return true;
+
+ return false;
+}
+
+static bool get_mdep_from_rec(const sdp_record_t *rec, uint8_t role,
+ uint16_t d_type, uint8_t *mdep)
+{
+ sdp_data_t *list, *feat;
+
+ if (!mdep)
+ return false;
+
+ list = sdp_data_get(rec, SDP_ATTR_SUPPORTED_FEATURES_LIST);
+ if (!list || !SDP_IS_SEQ(list->dtd))
+ return false;
+
+ for (feat = list->val.dataseq; feat; feat = feat->next) {
+ sdp_data_t *data_type, *mdepid, *role_t;
+
+ if (!SDP_IS_SEQ(feat->dtd))
+ continue;
+
+ mdepid = feat->val.dataseq;
+ if (!mdepid)
+ continue;
+
+ data_type = mdepid->next;
+ if (!data_type)
+ continue;
+
+ role_t = data_type->next;
+ if (!role_t)
+ continue;
+
+ if (data_type->dtd != SDP_UINT16 || mdepid->dtd != SDP_UINT8 ||
+ role_t->dtd != SDP_UINT8)
+ continue;
+
+ if (data_type->val.uint16 != d_type ||
+ !check_role(role_t->val.uint8, role))
+ continue;
+
+ *mdep = mdepid->val.uint8;
+
+ return true;
+ }
+
+ return false;
+}
+
+static bool get_remote_mdep(sdp_list_t *recs, struct health_channel *channel)
+{
+ struct health_app *app;
+ struct mdep_cfg *mdep;
+ uint8_t mdep_id;
+
+ app = queue_find(apps, match_app_by_id,
+ INT_TO_PTR(channel->dev->app_id));
+ if (!app)
+ return false;
+
+ mdep = queue_find(app->mdeps, match_mdep_by_id,
+ INT_TO_PTR(channel->mdep_id));
+ if (!mdep)
+ return false;
+
+ if (!get_mdep_from_rec(recs->data, mdep->role, mdep->data_type,
+ &mdep_id)) {
+ error("health: no matching MDEP: %u", channel->mdep_id);
+ return false;
+ }
+
+ channel->remote_mdep = mdep_id;
+ return true;
+}
+
+static bool create_mdl(struct health_channel *channel)
+{
+ struct health_app *app;
+ struct mdep_cfg *mdep;
+ uint8_t type;
+ GError *gerr = NULL;
+
+ app = queue_find(apps, match_app_by_id,
+ INT_TO_PTR(channel->dev->app_id));
+ if (!app)
+ return false;
+
+ mdep = queue_find(app->mdeps, match_mdep_by_id,
+ INT_TO_PTR(channel->mdep_id));
+ if (!mdep)
+ return false;
+
+ if (mdep->role == HAL_HEALTH_MDEP_ROLE_SOURCE)
+ type = channel->type;
+ else
+ type = CHANNEL_TYPE_ANY;
+
+ if (!mcap_create_mdl(channel->dev->mcl, channel->remote_mdep,
+ type, create_mdl_cb, channel, NULL, &gerr)) {
+ error("health: error creating mdl %s", gerr->message);
+ g_error_free(gerr);
+ return false;
+ }
+
+ return true;
+}
+
+static bool set_mcl_cb(struct mcap_mcl *mcl, gpointer user_data, GError **err)
+{
+ return mcap_mcl_set_cb(mcl, user_data, err,
+ MCAP_MDL_CB_CONNECTED, mcap_mdl_connected_cb,
+ MCAP_MDL_CB_CLOSED, mcap_mdl_closed_cb,
+ MCAP_MDL_CB_DELETED, mcap_mdl_deleted_cb,
+ MCAP_MDL_CB_ABORTED, mcap_mdl_aborted_cb,
+ MCAP_MDL_CB_REMOTE_CONN_REQ, mcap_mdl_conn_req_cb,
+ MCAP_MDL_CB_REMOTE_RECONN_REQ, mcap_mdl_reconn_req_cb,
+ MCAP_MDL_CB_INVALID);
+}
+
+static void create_mcl_cb(struct mcap_mcl *mcl, GError *err, gpointer data)
+{
+ struct health_channel *channel = data;
+ gboolean ret;
+ GError *gerr = NULL;
+
+ DBG("");
+
+ if (err) {
+ error("health: error creating MCL : %s", err->message);
+ goto fail;
+ }
+
+ if (!channel->dev->mcl)
+ channel->dev->mcl = mcap_mcl_ref(mcl);
+
+ info("health: MCL connected");
+
+ ret = set_mcl_cb(channel->dev->mcl, channel, &gerr);
+ if (!ret) {
+ error("health: error setting mdl callbacks: %s", gerr->message);
+ g_error_free(gerr);
+ goto fail;
+ }
+
+ if (!create_mdl(channel))
+ goto fail;
+
+ return;
+
+fail:
+ destroy_channel(channel);
+}
+
+static void search_cb(sdp_list_t *recs, int err, gpointer data)
+{
+ struct health_channel *channel = data;
+ GError *gerr = NULL;
+
+ DBG("");
+
+ if (err < 0 || !recs) {
+ error("health: Error getting remote SDP records");
+ goto fail;
+ }
+
+ if (get_ccpsm(recs, &channel->dev->ccpsm) < 0) {
+ error("health: Can't get remote PSM for control channel");
+ goto fail;
+ }
+
+ if (get_dcpsm(recs, &channel->dev->dcpsm) < 0) {
+ error("health: Can't get remote PSM for data channel");
+ goto fail;
+ }
+
+ if (!get_remote_mdep(recs, channel)) {
+ error("health: Can't get remote MDEP data");
+ goto fail;
+ }
+
+ if (!mcap_create_mcl(mcap, &channel->dev->dst, channel->dev->ccpsm,
+ create_mcl_cb, channel, NULL, &gerr)) {
+ error("health: error creating mcl %s", gerr->message);
+ g_error_free(gerr);
+ goto fail;
+ }
+
+ return;
+
+fail:
+ destroy_channel(channel);
+}
+
+static int connect_mcl(struct health_channel *channel)
+{
+ uuid_t uuid;
+ int err;
+
+ DBG("");
+
+ bt_string2uuid(&uuid, HDP_UUID);
+
+ err = bt_search_service(&adapter_addr, &channel->dev->dst, &uuid,
+ search_cb, channel, NULL, 0);
+ if (!err)
+ send_channel_state_notify(channel,
+ HAL_HEALTH_CHANNEL_CONNECTING, -1);
+
+ return err;
+}
+
+static struct health_app *get_app(uint16_t app_id)
+{
+ return queue_find(apps, match_app_by_id, INT_TO_PTR(app_id));
+}
+
+static struct health_channel *get_channel(struct health_app *app,
+ uint8_t mdep_index,
+ struct health_device *dev)
+{
+ struct health_channel *channel;
+ uint8_t index;
+
+ if (!dev)
+ return NULL;
+
+ index = mdep_index + 1;
+ channel = queue_find(dev->channels, match_channel_by_mdep_id,
+ INT_TO_PTR(index));
+ if (channel)
+ return channel;
+
+ return create_channel(app, index, dev);
+}
+
+static void bt_health_connect_channel(const void *buf, uint16_t len)
+{
+ const struct hal_cmd_health_connect_channel *cmd = buf;
+ struct hal_rsp_health_connect_channel rsp;
+ struct health_device *dev = NULL;
+ struct health_channel *channel = NULL;
+ struct health_app *app;
+
+ DBG("");
+
+ app = get_app(cmd->app_id);
+ if (!app)
+ goto send_rsp;
+
+ dev = get_device(app, cmd->bdaddr);
+
+ channel = get_channel(app, cmd->mdep_index, dev);
+ if (!channel)
+ goto send_rsp;
+
+ if (!queue_length(dev->channels)) {
+ if (channel->type != CHANNEL_TYPE_RELIABLE) {
+ error("health: first data shannel should be reliable");
+ goto fail;
+ }
+ }
+
+ if (!dev->mcl) {
+ if (connect_mcl(channel) < 0) {
+ error("health: error retrieving HDP SDP record");
+ goto fail;
+ }
+ } else {
+ /* data channel is already connected */
+ if (channel->mdl && channel->mdl_conn)
+ goto fail;
+
+ /* create mdl if it does not exists */
+ if (!channel->mdl && !create_mdl(channel))
+ goto fail;
+
+ /* reconnect mdl if it exists */
+ if (channel->mdl && !channel->mdl_conn) {
+ if (reconnect_mdl(channel) < 0)
+ goto fail;
+ }
+
+ }
+
+ rsp.channel_id = channel->id;
+ ipc_send_rsp_full(hal_ipc, HAL_SERVICE_ID_HEALTH,
+ HAL_OP_HEALTH_CONNECT_CHANNEL,
+ sizeof(rsp), &rsp, -1);
+ return;
+
+fail:
+ queue_remove(channel->dev->channels, channel);
+ free_health_channel(channel);
+
+send_rsp:
+ ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_HEALTH,
+ HAL_OP_HEALTH_CONNECT_CHANNEL, HAL_STATUS_FAILED);
+}
+
+static void channel_delete_cb(GError *gerr, gpointer data)
+{
+ struct health_channel *channel = data;
+
+ DBG("");
+
+ if (gerr) {
+ error("health: channel delete failed %s", gerr->message);
+ return;
+ }
+
+ destroy_channel(channel);
+}
+
+static void bt_health_destroy_channel(const void *buf, uint16_t len)
+{
+ const struct hal_cmd_health_destroy_channel *cmd = buf;
+ struct health_channel *channel;
+ GError *gerr = NULL;
+
+ DBG("");
+
+ channel = search_channel_by_id(cmd->channel_id);
+ if (!channel)
+ goto fail;
+
+ if (!mcap_delete_mdl(channel->mdl, channel_delete_cb, channel,
+ NULL, &gerr)) {
+ error("health: channel delete failed %s", gerr->message);
+ goto fail;
+ }
+
+ ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_HEALTH,
+ HAL_OP_HEALTH_DESTROY_CHANNEL, HAL_STATUS_SUCCESS);
+
+ return;
+
+fail:
+ ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_HEALTH,
+ HAL_OP_HEALTH_DESTROY_CHANNEL, HAL_STATUS_INVALID);
+}
+
+static const struct ipc_handler cmd_handlers[] = {
+ /* HAL_OP_HEALTH_REG_APP */
+ { bt_health_register_app, true,
+ sizeof(struct hal_cmd_health_reg_app) },
+ /* HAL_OP_HEALTH_MDEP */
+ { bt_health_mdep_cfg_data, true,
+ sizeof(struct hal_cmd_health_mdep) },
+ /* HAL_OP_HEALTH_UNREG_APP */
+ { bt_health_unregister_app, false,
+ sizeof(struct hal_cmd_health_unreg_app) },
+ /* HAL_OP_HEALTH_CONNECT_CHANNEL */
+ { bt_health_connect_channel, false,
+ sizeof(struct hal_cmd_health_connect_channel) },
+ /* HAL_OP_HEALTH_DESTROY_CHANNEL */
+ { bt_health_destroy_channel, false,
+ sizeof(struct hal_cmd_health_destroy_channel) },
+};
+
+static void mcl_connected(struct mcap_mcl *mcl, gpointer data)
+{
+ GError *gerr = NULL;
+ bool ret;
+
+ DBG("");
+
+ info("health: MCL connected");
+ ret = set_mcl_cb(mcl, NULL, &gerr);
+ if (!ret) {
+ error("health: error setting mcl callbacks: %s", gerr->message);
+ g_error_free(gerr);
+ }
+}
+
+static void mcl_reconnected(struct mcap_mcl *mcl, gpointer data)
+{
+ struct health_device *dev;
+
+ DBG("");
+
+ info("health: MCL reconnected");
+ dev = search_dev_by_mcl(mcl);
+ if (!dev) {
+ error("device data does not exists");
+ return;
+ }
+}
+
+static void mcl_disconnected(struct mcap_mcl *mcl, gpointer data)
+{
+ struct health_device *dev;
+
+ DBG("");
+
+ info("health: MCL disconnected");
+ dev = search_dev_by_mcl(mcl);
+ unref_mcl(dev);
+}
+
+static void mcl_uncached(struct mcap_mcl *mcl, gpointer data)
+{
+ /* mcap library maintains cache of mcls, not required here */
+}
+
+bool bt_health_register(struct ipc *ipc, const bdaddr_t *addr, uint8_t mode)
+{
+ GError *err = NULL;
+
+ DBG("");
+
+ bacpy(&adapter_addr, addr);
+
+ mcap = mcap_create_instance(&adapter_addr, BT_IO_SEC_MEDIUM, 0, 0,
+ mcl_connected, mcl_reconnected,
+ mcl_disconnected, mcl_uncached,
+ NULL, /* CSP is not used right now */
+ NULL, &err);
+ if (!mcap) {
+ error("health: MCAP instance creation failed %s", err->message);
+ g_error_free(err);
+ return false;
+ }
+
+ hal_ipc = ipc;
+ apps = queue_new();
+
+ ipc_register(hal_ipc, HAL_SERVICE_ID_HEALTH, cmd_handlers,
+ G_N_ELEMENTS(cmd_handlers));
+
+ return true;
+}
+
+void bt_health_unregister(void)
+{
+ DBG("");
+
+ mcap_instance_unref(mcap);
+ queue_destroy(apps, free_health_app);
+ ipc_unregister(hal_ipc, HAL_SERVICE_ID_HEALTH);
+ hal_ipc = NULL;
+}
diff --git a/repo/android/health.h b/repo/android/health.h
new file mode 100644
index 0000000..0b32fd3
--- /dev/null
+++ b/repo/android/health.h
@@ -0,0 +1,25 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2014 Intel Corporation. All rights reserved.
+ *
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+bool bt_health_register(struct ipc *ipc, const bdaddr_t *addr, uint8_t mode);
+void bt_health_unregister(void);
diff --git a/repo/android/hidhost.c b/repo/android/hidhost.c
new file mode 100644
index 0000000..591ca95
--- /dev/null
+++ b/repo/android/hidhost.c
@@ -0,0 +1,1595 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2013-2014 Intel Corporation. All rights reserved.
+ *
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdint.h>
+#include <stdbool.h>
+#include <errno.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <ctype.h>
+
+#include <glib.h>
+
+#include "btio/btio.h"
+#include "lib/bluetooth.h"
+#include "lib/sdp.h"
+#include "lib/sdp_lib.h"
+#include "src/shared/mgmt.h"
+#include "src/shared/util.h"
+#include "src/shared/uhid.h"
+#include "src/sdp-client.h"
+#include "src/uuid-helper.h"
+#include "src/log.h"
+#include "profiles/input/hog-lib.h"
+
+#include "hal-msg.h"
+#include "ipc-common.h"
+#include "ipc.h"
+#include "bluetooth.h"
+#include "gatt.h"
+#include "hidhost.h"
+#include "utils.h"
+
+#define L2CAP_PSM_HIDP_CTRL 0x11
+#define L2CAP_PSM_HIDP_INTR 0x13
+
+/* HID message types */
+#define HID_MSG_HANDSHAKE 0x00
+#define HID_MSG_CONTROL 0x10
+#define HID_MSG_GET_REPORT 0x40
+#define HID_MSG_SET_REPORT 0x50
+#define HID_MSG_GET_PROTOCOL 0x60
+#define HID_MSG_SET_PROTOCOL 0x70
+#define HID_MSG_DATA 0xa0
+
+#define HID_MSG_TYPE_MASK 0xf0
+
+/* HID data types */
+#define HID_DATA_TYPE_INPUT 0x01
+#define HID_DATA_TYPE_OUTPUT 0x02
+#define HID_DATA_TYPE_FEATURE 0x03
+
+/* HID protocol header parameters */
+#define HID_PROTO_BOOT 0x00
+#define HID_PROTO_REPORT 0x01
+
+/* HID GET REPORT Size Field */
+#define HID_GET_REPORT_SIZE_FIELD 0x08
+
+/* HID Virtual Cable Unplug */
+#define HID_VIRTUAL_CABLE_UNPLUG 0x05
+
+#define HOG_UUID "00001812-0000-1000-8000-00805f9b34fb"
+
+static bdaddr_t adapter_addr;
+
+static GIOChannel *ctrl_io = NULL;
+static GIOChannel *intr_io = NULL;
+static GSList *devices = NULL;
+static unsigned int hog_app = 0;
+
+static struct ipc *hal_ipc = NULL;
+
+struct hid_device {
+ bdaddr_t dst;
+ uint8_t state;
+ uint8_t subclass;
+ uint16_t vendor;
+ uint16_t product;
+ uint16_t version;
+ uint8_t country;
+ int rd_size;
+ void *rd_data;
+ uint8_t boot_dev;
+ GIOChannel *ctrl_io;
+ GIOChannel *intr_io;
+ guint ctrl_watch;
+ guint intr_watch;
+ struct bt_uhid *uhid;
+ uint8_t last_hid_msg;
+ struct bt_hog *hog;
+ int sec_level;
+};
+
+static int device_cmp(gconstpointer s, gconstpointer user_data)
+{
+ const struct hid_device *dev = s;
+ const bdaddr_t *dst = user_data;
+
+ return bacmp(&dev->dst, dst);
+}
+
+static void hid_device_free(void *data)
+{
+ struct hid_device *dev = data;
+
+ if (dev->ctrl_watch > 0)
+ g_source_remove(dev->ctrl_watch);
+
+ if (dev->intr_watch > 0)
+ g_source_remove(dev->intr_watch);
+
+ if (dev->intr_io)
+ g_io_channel_unref(dev->intr_io);
+
+ if (dev->ctrl_io)
+ g_io_channel_unref(dev->ctrl_io);
+
+ if (dev->uhid)
+ bt_uhid_unref(dev->uhid);
+
+ if (dev->hog)
+ bt_hog_unref(dev->hog);
+
+ g_free(dev->rd_data);
+ g_free(dev);
+}
+
+static void hid_device_remove(struct hid_device *dev)
+{
+ devices = g_slist_remove(devices, dev);
+ hid_device_free(dev);
+}
+
+static struct hid_device *hid_device_new(const bdaddr_t *addr)
+{
+ struct hid_device *dev;
+
+ dev = g_new0(struct hid_device, 1);
+ bacpy(&dev->dst, addr);
+ dev->state = HAL_HIDHOST_STATE_DISCONNECTED;
+ dev->sec_level = BT_IO_SEC_LOW;
+
+ devices = g_slist_append(devices, dev);
+
+ return dev;
+}
+
+static bool hex2buf(const uint8_t *hex, uint8_t *buf, int buf_size)
+{
+ int i, j;
+ char c;
+ uint8_t b;
+
+ for (i = 0, j = 0; i < buf_size; i++, j++) {
+ c = toupper(hex[j]);
+
+ if (c >= '0' && c <= '9')
+ b = c - '0';
+ else if (c >= 'A' && c <= 'F')
+ b = 10 + c - 'A';
+ else
+ return false;
+
+ j++;
+
+ c = toupper(hex[j]);
+
+ if (c >= '0' && c <= '9')
+ b = b * 16 + c - '0';
+ else if (c >= 'A' && c <= 'F')
+ b = b * 16 + 10 + c - 'A';
+ else
+ return false;
+
+ buf[i] = b;
+ }
+
+ return true;
+}
+
+static void handle_uhid_output(struct uhid_event *event, void *user_data)
+{
+ struct uhid_output_req *output = &event->u.output;
+ struct hid_device *dev = user_data;
+ int fd, req_size;
+ uint8_t *req;
+
+ if (!dev->ctrl_io)
+ return;
+
+ req_size = 1 + output->size;
+ req = malloc0(req_size);
+ if (!req)
+ return;
+
+ req[0] = HID_MSG_SET_REPORT | output->rtype;
+ memcpy(req + 1, output->data, req_size - 1);
+
+ fd = g_io_channel_unix_get_fd(dev->ctrl_io);
+
+ if (write(fd, req, req_size) < 0)
+ error("hidhost: error writing set_report: %s (%d)",
+ strerror(errno), errno);
+
+ free(req);
+}
+
+static gboolean intr_io_watch_cb(GIOChannel *chan, gpointer data)
+{
+ struct hid_device *dev = data;
+ uint8_t buf[UHID_DATA_MAX];
+ struct uhid_event ev;
+ int fd, bread, err;
+
+ /* Wait uHID if not ready */
+ if (!dev->uhid)
+ return TRUE;
+
+ fd = g_io_channel_unix_get_fd(chan);
+ bread = read(fd, buf, sizeof(buf));
+ if (bread < 0) {
+ error("hidhost: read from interrupt failed: %s(%d)",
+ strerror(errno), -errno);
+ return TRUE;
+ }
+
+ /* Discard non-data packets */
+ if (bread == 0 || buf[0] != (HID_MSG_DATA | HID_DATA_TYPE_INPUT))
+ return TRUE;
+
+ /* send data to uHID device skipping HIDP header byte */
+ memset(&ev, 0, sizeof(ev));
+ ev.type = UHID_INPUT;
+ ev.u.input.size = bread - 1;
+ memcpy(ev.u.input.data, &buf[1], ev.u.input.size);
+
+ err = bt_uhid_send(dev->uhid, &ev);
+ if (err < 0)
+ DBG("bt_uhid_send: %s (%d)", strerror(-err), -err);
+
+ return TRUE;
+}
+
+static void bt_hid_notify_state(struct hid_device *dev, uint8_t state)
+{
+ struct hal_ev_hidhost_conn_state ev;
+ char address[18];
+
+ if (dev->state == state)
+ return;
+
+ dev->state = state;
+
+ ba2str(&dev->dst, address);
+ DBG("device %s state %u", address, state);
+
+ bdaddr2android(&dev->dst, ev.bdaddr);
+ ev.state = state;
+
+ ipc_send_notif(hal_ipc, HAL_SERVICE_ID_HIDHOST,
+ HAL_EV_HIDHOST_CONN_STATE, sizeof(ev), &ev);
+}
+
+static gboolean intr_watch_cb(GIOChannel *chan, GIOCondition cond,
+ gpointer data)
+{
+ struct hid_device *dev = data;
+
+ if (cond & (G_IO_HUP | G_IO_ERR | G_IO_NVAL))
+ goto error;
+
+ if (cond & G_IO_IN)
+ return intr_io_watch_cb(chan, data);
+
+error:
+ bt_hid_notify_state(dev, HAL_HIDHOST_STATE_DISCONNECTED);
+
+ /*
+ * Checking for ctrl_watch avoids a double g_io_channel_shutdown since
+ * it's likely that ctrl_watch_cb has been queued for dispatching in
+ * this mainloop iteration
+ */
+ if ((cond & (G_IO_HUP | G_IO_ERR)) && dev->ctrl_watch)
+ g_io_channel_shutdown(chan, TRUE, NULL);
+
+ /* Close control channel */
+ if (dev->ctrl_io && !(cond & G_IO_NVAL))
+ g_io_channel_shutdown(dev->ctrl_io, TRUE, NULL);
+
+ hid_device_remove(dev);
+
+ return FALSE;
+}
+
+static void bt_hid_notify_proto_mode(struct hid_device *dev, uint8_t *buf,
+ int len)
+{
+ struct hal_ev_hidhost_proto_mode ev;
+ char address[18];
+
+ ba2str(&dev->dst, address);
+ DBG("device %s", address);
+
+ memset(&ev, 0, sizeof(ev));
+ bdaddr2android(&dev->dst, ev.bdaddr);
+
+ if (buf[0] == HID_MSG_DATA) {
+ ev.status = HAL_HIDHOST_STATUS_OK;
+ if (buf[1] == HID_PROTO_REPORT)
+ ev.mode = HAL_HIDHOST_REPORT_PROTOCOL;
+ else if (buf[1] == HID_PROTO_BOOT)
+ ev.mode = HAL_HIDHOST_BOOT_PROTOCOL;
+ else
+ ev.mode = HAL_HIDHOST_UNSUPPORTED_PROTOCOL;
+
+ } else {
+ ev.status = buf[0];
+ ev.mode = HAL_HIDHOST_UNSUPPORTED_PROTOCOL;
+ }
+
+ ipc_send_notif(hal_ipc, HAL_SERVICE_ID_HIDHOST,
+ HAL_EV_HIDHOST_PROTO_MODE, sizeof(ev), &ev);
+}
+
+static void bt_hid_notify_get_report(struct hid_device *dev, uint8_t *buf,
+ int len)
+{
+ struct hal_ev_hidhost_get_report *ev;
+ int ev_len;
+ char address[18];
+
+ ba2str(&dev->dst, address);
+ DBG("device %s", address);
+
+ ev_len = sizeof(*ev);
+
+ if (!((buf[0] == (HID_MSG_DATA | HID_DATA_TYPE_INPUT)) ||
+ (buf[0] == (HID_MSG_DATA | HID_DATA_TYPE_OUTPUT)) ||
+ (buf[0] == (HID_MSG_DATA | HID_DATA_TYPE_FEATURE)))) {
+ ev = g_malloc0(ev_len);
+ ev->status = buf[0];
+ bdaddr2android(&dev->dst, ev->bdaddr);
+ goto send;
+ }
+
+ /*
+ * Report porotocol mode reply contains id after hdr, in boot
+ * protocol mode id doesn't exist
+ */
+ ev_len += (dev->boot_dev) ? (len - 1) : (len - 2);
+ ev = g_malloc0(ev_len);
+ ev->status = HAL_HIDHOST_STATUS_OK;
+ bdaddr2android(&dev->dst, ev->bdaddr);
+
+ /*
+ * Report porotocol mode reply contains id after hdr, in boot
+ * protocol mode id doesn't exist
+ */
+ if (dev->boot_dev) {
+ ev->len = len - 1;
+ memcpy(ev->data, buf + 1, ev->len);
+ } else {
+ ev->len = len - 2;
+ memcpy(ev->data, buf + 2, ev->len);
+ }
+
+send:
+ ipc_send_notif(hal_ipc, HAL_SERVICE_ID_HIDHOST,
+ HAL_EV_HIDHOST_GET_REPORT, ev_len, ev);
+ g_free(ev);
+}
+
+static void bt_hid_notify_handshake(struct hid_device *dev, uint8_t *buf,
+ int len)
+{
+ struct hal_ev_hidhost_handshake ev;
+
+ bdaddr2android(&dev->dst, ev.bdaddr);
+
+ /* crop result code to handshake status range from HAL */
+ ev.status = buf[0];
+ if (ev.status > HAL_HIDHOST_HS_ERROR)
+ ev.status = HAL_HIDHOST_HS_ERROR;
+
+ ipc_send_notif(hal_ipc, HAL_SERVICE_ID_HIDHOST,
+ HAL_EV_HIDHOST_HANDSHAKE, sizeof(ev), &ev);
+}
+
+static void bt_hid_notify_virtual_unplug(struct hid_device *dev,
+ uint8_t *buf, int len)
+{
+ struct hal_ev_hidhost_virtual_unplug ev;
+ char address[18];
+
+ ba2str(&dev->dst, address);
+ DBG("device %s", address);
+ bdaddr2android(&dev->dst, ev.bdaddr);
+
+ ev.status = HAL_HIDHOST_GENERAL_ERROR;
+
+ /* Wait either channels to HUP */
+ if (dev->intr_io && dev->ctrl_io) {
+ g_io_channel_shutdown(dev->intr_io, TRUE, NULL);
+ g_io_channel_shutdown(dev->ctrl_io, TRUE, NULL);
+ bt_hid_notify_state(dev, HAL_HIDHOST_STATE_DISCONNECTING);
+ ev.status = HAL_HIDHOST_STATUS_OK;
+ }
+
+ ipc_send_notif(hal_ipc, HAL_SERVICE_ID_HIDHOST,
+ HAL_EV_HIDHOST_VIRTUAL_UNPLUG, sizeof(ev), &ev);
+}
+
+static gboolean ctrl_io_watch_cb(GIOChannel *chan, gpointer data)
+{
+ struct hid_device *dev = data;
+ int fd, bread;
+ uint8_t buf[UHID_DATA_MAX];
+
+ DBG("");
+
+ fd = g_io_channel_unix_get_fd(chan);
+ bread = read(fd, buf, sizeof(buf));
+ if (bread < 0) {
+ error("hidhost: read from control failed: %s(%d)",
+ strerror(errno), -errno);
+ return TRUE;
+ }
+
+ switch (dev->last_hid_msg) {
+ case HID_MSG_GET_PROTOCOL:
+ case HID_MSG_SET_PROTOCOL:
+ bt_hid_notify_proto_mode(dev, buf, bread);
+ break;
+ case HID_MSG_GET_REPORT:
+ bt_hid_notify_get_report(dev, buf, bread);
+ break;
+ }
+
+ switch (buf[0] & HID_MSG_TYPE_MASK) {
+ case HID_MSG_HANDSHAKE:
+ bt_hid_notify_handshake(dev, buf, bread);
+ break;
+ case HID_MSG_CONTROL:
+ if ((buf[0] & ~HID_MSG_TYPE_MASK) == HID_VIRTUAL_CABLE_UNPLUG)
+ bt_hid_notify_virtual_unplug(dev, buf, bread);
+ break;
+ default:
+ break;
+ }
+
+ /* reset msg type request */
+ dev->last_hid_msg = 0;
+
+ return TRUE;
+}
+
+static gboolean ctrl_watch_cb(GIOChannel *chan, GIOCondition cond,
+ gpointer data)
+{
+ struct hid_device *dev = data;
+
+ if (cond & (G_IO_HUP | G_IO_ERR | G_IO_NVAL))
+ goto error;
+
+ if (cond & G_IO_IN)
+ return ctrl_io_watch_cb(chan, data);
+
+error:
+ bt_hid_notify_state(dev, HAL_HIDHOST_STATE_DISCONNECTED);
+
+ /*
+ * Checking for intr_watch avoids a double g_io_channel_shutdown since
+ * it's likely that intr_watch_cb has been queued for dispatching in
+ * this mainloop iteration
+ */
+ if ((cond & (G_IO_HUP | G_IO_ERR)) && dev->intr_watch)
+ g_io_channel_shutdown(chan, TRUE, NULL);
+
+ if (dev->intr_io && !(cond & G_IO_NVAL))
+ g_io_channel_shutdown(dev->intr_io, TRUE, NULL);
+
+ hid_device_remove(dev);
+
+ return FALSE;
+}
+
+static void bt_hid_set_info(struct hid_device *dev)
+{
+ struct hal_ev_hidhost_info ev;
+
+ DBG("");
+
+ bdaddr2android(&dev->dst, ev.bdaddr);
+ ev.attr = 0; /* TODO: Check what is this field */
+ ev.subclass = dev->subclass;
+ ev.app_id = 0; /* TODO: Check what is this field */
+ ev.vendor = dev->vendor;
+ ev.product = dev->product;
+ ev.version = dev->version;
+ ev.country = dev->country;
+ ev.descr_len = dev->rd_size;
+ memset(ev.descr, 0, sizeof(ev.descr));
+ memcpy(ev.descr, dev->rd_data, ev.descr_len);
+
+ ipc_send_notif(hal_ipc, HAL_SERVICE_ID_HIDHOST, HAL_EV_HIDHOST_INFO,
+ sizeof(ev), &ev);
+}
+
+static int uhid_create(struct hid_device *dev)
+{
+ struct uhid_event ev;
+ int err;
+
+ dev->uhid = bt_uhid_new_default();
+ if (!dev->uhid) {
+ err = -errno;
+ error("hidhost: Failed to create bt_uhid instance");
+ return err;
+ }
+
+ memset(&ev, 0, sizeof(ev));
+ ev.type = UHID_CREATE;
+ strcpy((char *) ev.u.create.name, "bluez-input-device");
+ ev.u.create.bus = BUS_BLUETOOTH;
+ ev.u.create.vendor = dev->vendor;
+ ev.u.create.product = dev->product;
+ ev.u.create.version = dev->version;
+ ev.u.create.country = dev->country;
+ ev.u.create.rd_size = dev->rd_size;
+ ev.u.create.rd_data = dev->rd_data;
+
+ err = bt_uhid_send(dev->uhid, &ev);
+ if (err < 0) {
+ error("hidhost: Failed to create uHID device: %s",
+ strerror(-err));
+ bt_uhid_unref(dev->uhid);
+ dev->uhid = NULL;
+ return err;
+ }
+
+ bt_uhid_register(dev->uhid, UHID_OUTPUT, handle_uhid_output, dev);
+ bt_hid_set_info(dev);
+
+ return 0;
+}
+
+static void interrupt_connect_cb(GIOChannel *chan, GError *conn_err,
+ gpointer user_data)
+{
+ struct hid_device *dev = user_data;
+ uint8_t state;
+
+ DBG("");
+
+ if (conn_err) {
+ error("hidhost: Failed to connect interrupt channel (%s)",
+ conn_err->message);
+ state = HAL_HIDHOST_STATE_FAILED;
+ goto failed;
+ }
+
+ if (uhid_create(dev) < 0) {
+ state = HAL_HIDHOST_STATE_NO_HID;
+ goto failed;
+ }
+
+ dev->intr_watch = g_io_add_watch(dev->intr_io,
+ G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL,
+ intr_watch_cb, dev);
+
+ bt_hid_notify_state(dev, HAL_HIDHOST_STATE_CONNECTED);
+
+ return;
+
+failed:
+ bt_hid_notify_state(dev, state);
+ hid_device_remove(dev);
+}
+
+static void control_connect_cb(GIOChannel *chan, GError *conn_err,
+ gpointer user_data)
+{
+ struct hid_device *dev = user_data;
+ GError *err = NULL;
+
+ DBG("");
+
+ if (conn_err) {
+ bt_hid_notify_state(dev, HAL_HIDHOST_STATE_DISCONNECTED);
+ error("hidhost: Failed to connect control channel (%s)",
+ conn_err->message);
+ goto failed;
+ }
+
+ /* Connect to the HID interrupt channel */
+ dev->intr_io = bt_io_connect(interrupt_connect_cb, dev, NULL, &err,
+ BT_IO_OPT_SOURCE_BDADDR, &adapter_addr,
+ BT_IO_OPT_DEST_BDADDR, &dev->dst,
+ BT_IO_OPT_PSM, L2CAP_PSM_HIDP_INTR,
+ BT_IO_OPT_SEC_LEVEL, dev->sec_level,
+ BT_IO_OPT_INVALID);
+ if (!dev->intr_io) {
+ error("hidhost: Failed to connect interrupt channel (%s)",
+ err->message);
+ g_error_free(err);
+ goto failed;
+ }
+
+ dev->ctrl_watch = g_io_add_watch(dev->ctrl_io,
+ G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL,
+ ctrl_watch_cb, dev);
+
+ return;
+
+failed:
+ hid_device_remove(dev);
+}
+
+static void hid_sdp_search_cb(sdp_list_t *recs, int err, gpointer data)
+{
+ struct hid_device *dev = data;
+ sdp_list_t *list;
+ GError *gerr = NULL;
+
+ DBG("");
+
+ if (err < 0) {
+ error("hidhost: Unable to get SDP record: %s", strerror(-err));
+ goto fail;
+ }
+
+ if (!recs || !recs->data) {
+ error("hidhost: No SDP records found");
+ goto fail;
+ }
+
+ for (list = recs; list != NULL; list = list->next) {
+ sdp_record_t *rec = list->data;
+ sdp_data_t *data;
+
+ data = sdp_data_get(rec, SDP_ATTR_HID_COUNTRY_CODE);
+ if (data)
+ dev->country = data->val.uint8;
+
+ data = sdp_data_get(rec, SDP_ATTR_HID_DEVICE_SUBCLASS);
+ if (data) {
+ dev->subclass = data->val.uint8;
+
+ /* Encryption is mandatory for keyboards */
+ if (dev->subclass & 0x40)
+ dev->sec_level = BT_IO_SEC_MEDIUM;
+ }
+
+ data = sdp_data_get(rec, SDP_ATTR_HID_BOOT_DEVICE);
+ if (data)
+ dev->boot_dev = data->val.uint8;
+
+ data = sdp_data_get(rec, SDP_ATTR_HID_DESCRIPTOR_LIST);
+ if (data) {
+ if (!SDP_IS_SEQ(data->dtd))
+ goto fail;
+
+ /* First HIDDescriptor */
+ data = data->val.dataseq;
+ if (!SDP_IS_SEQ(data->dtd))
+ goto fail;
+
+ /* ClassDescriptorType */
+ data = data->val.dataseq;
+ if (data->dtd != SDP_UINT8)
+ goto fail;
+
+ /* ClassDescriptorData */
+ data = data->next;
+ if (!data || !SDP_IS_TEXT_STR(data->dtd))
+ goto fail;
+
+ dev->rd_size = data->unitSize;
+ dev->rd_data = g_memdup(data->val.str, data->unitSize);
+ }
+ }
+
+ if (dev->ctrl_io) {
+ /* Raise the security level for this device if needed. */
+ if ((dev->sec_level > BT_IO_SEC_LOW) &&
+ !bt_io_set(dev->ctrl_io, &gerr,
+ BT_IO_OPT_SEC_LEVEL, dev->sec_level,
+ BT_IO_OPT_INVALID)) {
+ error("hidhost: Cannot raise security level: %s",
+ gerr->message);
+ g_error_free(gerr);
+
+ goto fail;
+ }
+
+ if (uhid_create(dev) < 0)
+ goto fail;
+ return;
+ }
+
+ dev->ctrl_io = bt_io_connect(control_connect_cb, dev, NULL, &gerr,
+ BT_IO_OPT_SOURCE_BDADDR, &adapter_addr,
+ BT_IO_OPT_DEST_BDADDR, &dev->dst,
+ BT_IO_OPT_PSM, L2CAP_PSM_HIDP_CTRL,
+ BT_IO_OPT_SEC_LEVEL, dev->sec_level,
+ BT_IO_OPT_INVALID);
+ if (gerr) {
+ error("hidhost: Failed to connect control channel (%s)",
+ gerr->message);
+ g_error_free(gerr);
+ goto fail;
+ }
+
+ return;
+
+fail:
+ bt_hid_notify_state(dev, HAL_HIDHOST_STATE_DISCONNECTED);
+ hid_device_remove(dev);
+}
+
+static void hid_sdp_did_search_cb(sdp_list_t *recs, int err, gpointer data)
+{
+ struct hid_device *dev = data;
+ sdp_list_t *list;
+ uuid_t uuid;
+
+ DBG("");
+
+ if (err < 0) {
+ error("hidhost: Unable to get Device ID SDP record: %s",
+ strerror(-err));
+ goto fail;
+ }
+
+ if (!recs || !recs->data) {
+ error("hidhost: No Device ID SDP records found");
+ goto fail;
+ }
+
+ for (list = recs; list; list = list->next) {
+ sdp_record_t *rec = list->data;
+ sdp_data_t *data;
+
+ data = sdp_data_get(rec, SDP_ATTR_VENDOR_ID);
+ if (data)
+ dev->vendor = data->val.uint16;
+
+ data = sdp_data_get(rec, SDP_ATTR_PRODUCT_ID);
+ if (data)
+ dev->product = data->val.uint16;
+
+ data = sdp_data_get(rec, SDP_ATTR_VERSION);
+ if (data)
+ dev->version = data->val.uint16;
+ }
+
+ sdp_uuid16_create(&uuid, HID_SVCLASS_ID);
+ if (bt_search_service(&adapter_addr, &dev->dst, &uuid,
+ hid_sdp_search_cb, dev, NULL, 0) < 0) {
+ error("hidhost: Failed to search SDP details");
+ goto fail;
+ }
+
+ return;
+
+fail:
+ bt_hid_notify_state(dev, HAL_HIDHOST_STATE_DISCONNECTED);
+ hid_device_remove(dev);
+}
+
+static void hog_conn_cb(const bdaddr_t *addr, int err, void *attrib)
+{
+ GSList *l;
+ struct hid_device *dev;
+
+ l = g_slist_find_custom(devices, addr, device_cmp);
+ dev = l ? l->data : NULL;
+
+ if (err < 0) {
+ if (!dev)
+ return;
+ if (dev->hog) {
+ bt_hid_notify_state(dev,
+ HAL_HIDHOST_STATE_DISCONNECTED);
+ bt_hog_detach(dev->hog);
+ return;
+ }
+ goto fail;
+ }
+
+ if (!dev)
+ dev = hid_device_new(addr);
+
+ if (!dev->hog) {
+ /* TODO: Get device details and primary */
+ dev->hog = bt_hog_new_default("bluez-input-device", dev->vendor,
+ dev->product, dev->version, NULL);
+ if (!dev->hog) {
+ error("HoG: unable to create session");
+ goto fail;
+ }
+ }
+
+ if (!bt_hog_attach(dev->hog, attrib)) {
+ error("HoG: unable to attach");
+ goto fail;
+ }
+
+ if (!bt_gatt_set_security(addr, BT_IO_SEC_MEDIUM)) {
+ error("Failed to set security level");
+ goto fail;
+ }
+
+ DBG("");
+
+ bt_hid_notify_state(dev, HAL_HIDHOST_STATE_CONNECTED);
+
+ if (!bt_gatt_add_autoconnect(hog_app, &dev->dst))
+ error("hidhost: Could not add to autoconnect list");
+
+ return;
+
+fail:
+ bt_hid_notify_state(dev, HAL_HIDHOST_STATE_DISCONNECTED);
+ hid_device_remove(dev);
+}
+
+static bool hog_connect(struct hid_device *dev)
+{
+ DBG("");
+
+ if (hog_app)
+ return bt_gatt_connect_app(hog_app, &dev->dst);
+
+ hog_app = bt_gatt_register_app(HOG_UUID, GATT_CLIENT, hog_conn_cb);
+ if (!hog_app) {
+ error("hidhost: bt_gatt_register_app failed");
+ return false;
+ }
+
+ return bt_gatt_connect_app(hog_app, &dev->dst);
+}
+
+static void bt_hid_connect(const void *buf, uint16_t len)
+{
+ const struct hal_cmd_hidhost_connect *cmd = buf;
+ struct hid_device *dev;
+ uint8_t status;
+ char addr[18];
+ bdaddr_t dst;
+ GSList *l;
+ uuid_t uuid;
+
+ DBG("");
+
+ android2bdaddr(&cmd->bdaddr, &dst);
+
+ l = g_slist_find_custom(devices, &dst, device_cmp);
+ if (l)
+ dev = l->data;
+ else
+ dev = hid_device_new(&dst);
+
+ if (dev->state != HAL_HIDHOST_STATE_DISCONNECTED)
+ goto done;
+
+ ba2str(&dev->dst, addr);
+ DBG("connecting to %s", addr);
+
+ if (bt_device_last_seen_bearer(&dev->dst) != BDADDR_BREDR) {
+ if (!hog_connect(dev)) {
+ status = HAL_STATUS_FAILED;
+ hid_device_remove(dev);
+ goto failed;
+ }
+ goto done;
+ }
+
+ sdp_uuid16_create(&uuid, PNP_INFO_SVCLASS_ID);
+ if (bt_search_service(&adapter_addr, &dev->dst, &uuid,
+ hid_sdp_did_search_cb, dev, NULL, 0) < 0) {
+ error("hidhost: Failed to search DeviceID SDP details");
+ hid_device_remove(dev);
+ status = HAL_STATUS_FAILED;
+ goto failed;
+ }
+
+done:
+ if (dev->state == HAL_HIDHOST_STATE_DISCONNECTED)
+ bt_hid_notify_state(dev, HAL_HIDHOST_STATE_CONNECTING);
+
+ status = HAL_STATUS_SUCCESS;
+
+failed:
+ ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_HIDHOST, HAL_OP_HIDHOST_CONNECT,
+ status);
+}
+
+static bool hog_disconnect(struct hid_device *dev)
+{
+ DBG("");
+
+ if (dev->state == HAL_HIDHOST_STATE_DISCONNECTED)
+ return false;
+
+ bt_hid_notify_state(dev, HAL_HIDHOST_STATE_DISCONNECTING);
+
+ if (!bt_gatt_disconnect_app(hog_app, &dev->dst)) {
+ bt_hid_notify_state(dev, HAL_HIDHOST_STATE_DISCONNECTED);
+ hid_device_remove(dev);
+ }
+
+ return true;
+}
+
+static void bt_hid_disconnect(const void *buf, uint16_t len)
+{
+ const struct hal_cmd_hidhost_disconnect *cmd = buf;
+ struct hid_device *dev;
+ uint8_t status;
+ GSList *l;
+ bdaddr_t dst;
+
+ DBG("");
+
+ android2bdaddr(&cmd->bdaddr, &dst);
+
+ l = g_slist_find_custom(devices, &dst, device_cmp);
+ if (!l) {
+ status = HAL_STATUS_FAILED;
+ goto failed;
+ }
+
+ dev = l->data;
+ if (bt_is_device_le(&dst)) {
+ if (!hog_disconnect(dev)) {
+ status = HAL_STATUS_FAILED;
+ goto failed;
+ }
+ goto done;
+ }
+
+ /* Wait either channels to HUP */
+ if (dev->intr_io)
+ g_io_channel_shutdown(dev->intr_io, TRUE, NULL);
+
+ if (dev->ctrl_io)
+ g_io_channel_shutdown(dev->ctrl_io, TRUE, NULL);
+
+ bt_hid_notify_state(dev, HAL_HIDHOST_STATE_DISCONNECTING);
+
+
+done:
+ status = HAL_STATUS_SUCCESS;
+
+failed:
+ ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_HIDHOST, HAL_OP_HIDHOST_DISCONNECT,
+ status);
+}
+
+static bool bt_hid_write_virtual_unplug(GIOChannel *chan)
+{
+ uint8_t hdr = HID_MSG_CONTROL | HID_VIRTUAL_CABLE_UNPLUG;
+ int fd = g_io_channel_unix_get_fd(chan);
+
+ if (write(fd, &hdr, sizeof(hdr)) == sizeof(hdr))
+ return true;
+
+ error("hidhost: Error writing virtual unplug command: %s (%d)",
+ strerror(errno), errno);
+ return false;
+}
+
+static void bt_hid_virtual_unplug(const void *buf, uint16_t len)
+{
+ const struct hal_cmd_hidhost_virtual_unplug *cmd = buf;
+ struct hid_device *dev;
+ GSList *l;
+ uint8_t status;
+ bdaddr_t dst;
+
+ DBG("");
+
+ android2bdaddr(&cmd->bdaddr, &dst);
+
+ l = g_slist_find_custom(devices, &dst, device_cmp);
+ if (!l) {
+ status = HAL_STATUS_FAILED;
+ goto failed;
+ }
+
+ dev = l->data;
+
+ if (!(dev->ctrl_io)) {
+ status = HAL_STATUS_FAILED;
+ goto failed;
+ }
+
+ if (!bt_hid_write_virtual_unplug(dev->ctrl_io)) {
+ status = HAL_STATUS_FAILED;
+ goto failed;
+ }
+
+ /* Wait either channels to HUP */
+ if (dev->intr_io)
+ g_io_channel_shutdown(dev->intr_io, TRUE, NULL);
+
+ if (dev->ctrl_io)
+ g_io_channel_shutdown(dev->ctrl_io, TRUE, NULL);
+
+ bt_hid_notify_state(dev, HAL_HIDHOST_STATE_DISCONNECTING);
+
+ status = HAL_STATUS_SUCCESS;
+
+failed:
+ ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_HIDHOST,
+ HAL_OP_HIDHOST_VIRTUAL_UNPLUG, status);
+}
+
+static void bt_hid_info(const void *buf, uint16_t len)
+{
+ const struct hal_cmd_hidhost_set_info *cmd = buf;
+
+ if (len != sizeof(*cmd) + cmd->descr_len) {
+ error("Invalid hid set info size (%u bytes), terminating", len);
+ raise(SIGTERM);
+ return;
+ }
+
+ /*
+ * Data from hal_cmd_hidhost_set_info is usefull only when we create
+ * UHID device. Once device is created all the transactions will be
+ * done through the fd. There is no way to use this information
+ * once device is created with HID internals.
+ */
+ DBG("Not supported");
+
+ ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_HIDHOST, HAL_OP_HIDHOST_SET_INFO,
+ HAL_STATUS_UNSUPPORTED);
+}
+
+static void bt_hid_get_protocol(const void *buf, uint16_t len)
+{
+ const struct hal_cmd_hidhost_get_protocol *cmd = buf;
+ struct hid_device *dev;
+ GSList *l;
+ bdaddr_t dst;
+ int fd;
+ uint8_t hdr;
+ uint8_t status;
+
+ DBG("");
+
+ switch (cmd->mode) {
+ case HAL_HIDHOST_REPORT_PROTOCOL:
+ case HAL_HIDHOST_BOOT_PROTOCOL:
+ break;
+ default:
+ status = HAL_STATUS_INVALID;
+ goto failed;
+ }
+
+ android2bdaddr(&cmd->bdaddr, &dst);
+
+ l = g_slist_find_custom(devices, &dst, device_cmp);
+ if (!l) {
+ status = HAL_STATUS_FAILED;
+ goto failed;
+ }
+
+ dev = l->data;
+
+ hdr = HID_MSG_GET_PROTOCOL | cmd->mode;
+ fd = g_io_channel_unix_get_fd(dev->ctrl_io);
+
+ if (write(fd, &hdr, sizeof(hdr)) < 0) {
+ error("hidhost: Error writing device_get_protocol: %s (%d)",
+ strerror(errno), errno);
+ status = HAL_STATUS_FAILED;
+ goto failed;
+ }
+
+ dev->last_hid_msg = HID_MSG_GET_PROTOCOL;
+
+ status = HAL_STATUS_SUCCESS;
+
+failed:
+ ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_HIDHOST,
+ HAL_OP_HIDHOST_GET_PROTOCOL, status);
+}
+
+static void bt_hid_set_protocol(const void *buf, uint16_t len)
+{
+ const struct hal_cmd_hidhost_set_protocol *cmd = buf;
+ struct hid_device *dev;
+ GSList *l;
+ bdaddr_t dst;
+ int fd;
+ uint8_t hdr;
+ uint8_t status;
+
+ DBG("");
+
+ switch (cmd->mode) {
+ case HAL_HIDHOST_REPORT_PROTOCOL:
+ case HAL_HIDHOST_BOOT_PROTOCOL:
+ break;
+ default:
+ status = HAL_STATUS_INVALID;
+ goto failed;
+ }
+
+ android2bdaddr(&cmd->bdaddr, &dst);
+
+ l = g_slist_find_custom(devices, &dst, device_cmp);
+ if (!l) {
+ status = HAL_STATUS_FAILED;
+ goto failed;
+ }
+
+ dev = l->data;
+
+ hdr = HID_MSG_SET_PROTOCOL | cmd->mode;
+ fd = g_io_channel_unix_get_fd(dev->ctrl_io);
+
+ if (write(fd, &hdr, sizeof(hdr)) < 0) {
+ error("hidhost: error writing device_set_protocol: %s (%d)",
+ strerror(errno), errno);
+ status = HAL_STATUS_FAILED;
+ goto failed;
+ }
+
+ dev->last_hid_msg = HID_MSG_SET_PROTOCOL;
+
+ status = HAL_STATUS_SUCCESS;
+
+failed:
+ ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_HIDHOST,
+ HAL_OP_HIDHOST_SET_PROTOCOL, status);
+}
+
+static void bt_hid_get_report(const void *buf, uint16_t len)
+{
+ const struct hal_cmd_hidhost_get_report *cmd = buf;
+ struct hid_device *dev;
+ GSList *l;
+ bdaddr_t dst;
+ int fd;
+ uint8_t *req;
+ uint8_t req_size;
+ uint8_t status;
+
+ DBG("");
+
+ switch (cmd->type) {
+ case HAL_HIDHOST_INPUT_REPORT:
+ case HAL_HIDHOST_OUTPUT_REPORT:
+ case HAL_HIDHOST_FEATURE_REPORT:
+ break;
+ default:
+ status = HAL_STATUS_INVALID;
+ goto failed;
+ }
+
+ android2bdaddr(&cmd->bdaddr, &dst);
+
+ l = g_slist_find_custom(devices, &dst, device_cmp);
+ if (!l) {
+ status = HAL_STATUS_FAILED;
+ goto failed;
+ }
+
+ dev = l->data;
+ req_size = (cmd->buf_size > 0) ? 4 : 2;
+ req = g_try_malloc0(req_size);
+ if (!req) {
+ status = HAL_STATUS_NOMEM;
+ goto failed;
+ }
+
+ req[0] = HID_MSG_GET_REPORT | cmd->type;
+ req[1] = cmd->id;
+
+ if (cmd->buf_size > 0) {
+ req[0] = req[0] | HID_GET_REPORT_SIZE_FIELD;
+ put_le16(cmd->buf_size, &req[2]);
+ }
+
+ fd = g_io_channel_unix_get_fd(dev->ctrl_io);
+
+ if (write(fd, req, req_size) < 0) {
+ error("hidhost: error writing hid_get_report: %s (%d)",
+ strerror(errno), errno);
+ g_free(req);
+ status = HAL_STATUS_FAILED;
+ goto failed;
+ }
+
+ dev->last_hid_msg = HID_MSG_GET_REPORT;
+ g_free(req);
+
+ status = HAL_STATUS_SUCCESS;
+
+failed:
+ ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_HIDHOST, HAL_OP_HIDHOST_GET_REPORT,
+ status);
+}
+
+static void bt_hid_set_report(const void *buf, uint16_t len)
+{
+ const struct hal_cmd_hidhost_set_report *cmd = buf;
+ struct hid_device *dev;
+ GSList *l;
+ bdaddr_t dst;
+ int fd;
+ uint8_t *req = NULL;
+ uint8_t req_size;
+ uint8_t status;
+
+ DBG("");
+
+ if (len != sizeof(*cmd) + cmd->len) {
+ error("Invalid hid set report size (%u bytes), terminating",
+ len);
+ raise(SIGTERM);
+ return;
+ }
+
+ switch (cmd->type) {
+ case HAL_HIDHOST_INPUT_REPORT:
+ case HAL_HIDHOST_OUTPUT_REPORT:
+ case HAL_HIDHOST_FEATURE_REPORT:
+ break;
+ default:
+ status = HAL_STATUS_INVALID;
+ goto failed;
+ }
+
+ android2bdaddr(&cmd->bdaddr, &dst);
+
+ l = g_slist_find_custom(devices, &dst, device_cmp);
+ if (!l) {
+ status = HAL_STATUS_FAILED;
+ goto failed;
+ }
+
+ dev = l->data;
+
+ if (!dev->ctrl_io && !dev->hog) {
+ status = HAL_STATUS_FAILED;
+ goto failed;
+ }
+
+ req_size = 1 + (cmd->len / 2);
+ req = g_try_malloc0(req_size);
+ if (!req) {
+ status = HAL_STATUS_NOMEM;
+ goto failed;
+ }
+
+ req[0] = HID_MSG_SET_REPORT | cmd->type;
+ /*
+ * Report data coming to HAL is in ascii format, HAL sends
+ * data in hex to daemon, so convert to binary.
+ */
+ if (!hex2buf(cmd->data, req + 1, req_size - 1)) {
+ status = HAL_STATUS_INVALID;
+ goto failed;
+ }
+
+ if (dev->hog) {
+ if (bt_hog_send_report(dev->hog, req + 1, req_size - 1,
+ cmd->type) < 0) {
+ status = HAL_STATUS_FAILED;
+ goto failed;
+ }
+
+ goto done;
+ }
+
+ fd = g_io_channel_unix_get_fd(dev->ctrl_io);
+
+ if (write(fd, req, req_size) < 0) {
+ error("hidhost: error writing hid_set_report: %s (%d)",
+ strerror(errno), errno);
+ status = HAL_STATUS_FAILED;
+ goto failed;
+ }
+
+ dev->last_hid_msg = HID_MSG_SET_REPORT;
+
+done:
+ status = HAL_STATUS_SUCCESS;
+
+failed:
+ g_free(req);
+
+ ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_HIDHOST, HAL_OP_HIDHOST_SET_REPORT,
+ status);
+}
+
+static void bt_hid_send_data(const void *buf, uint16_t len)
+{
+ const struct hal_cmd_hidhost_send_data *cmd = buf;
+ struct hid_device *dev;
+ GSList *l;
+ bdaddr_t dst;
+ int fd;
+ uint8_t *req = NULL;
+ uint8_t req_size;
+ uint8_t status;
+
+ DBG("");
+
+ if (len != sizeof(*cmd) + cmd->len) {
+ error("Invalid hid send data size (%u bytes), terminating",
+ len);
+ raise(SIGTERM);
+ return;
+ }
+
+ android2bdaddr(&cmd->bdaddr, &dst);
+
+ l = g_slist_find_custom(devices, &dst, device_cmp);
+ if (!l) {
+ status = HAL_STATUS_FAILED;
+ goto failed;
+ }
+
+ dev = l->data;
+
+ if (!(dev->intr_io)) {
+ status = HAL_STATUS_FAILED;
+ goto failed;
+ }
+
+ req_size = 1 + (cmd->len / 2);
+ req = g_try_malloc0(req_size);
+ if (!req) {
+ status = HAL_STATUS_NOMEM;
+ goto failed;
+ }
+
+ req[0] = HID_MSG_DATA | HID_DATA_TYPE_OUTPUT;
+ /*
+ * Report data coming to HAL is in ascii format, HAL sends
+ * data in hex to daemon, so convert to binary.
+ */
+ if (!hex2buf(cmd->data, req + 1, req_size - 1)) {
+ status = HAL_STATUS_INVALID;
+ goto failed;
+ }
+
+ fd = g_io_channel_unix_get_fd(dev->intr_io);
+
+ if (write(fd, req, req_size) < 0) {
+ error("hidhost: error writing data to HID device: %s (%d)",
+ strerror(errno), errno);
+ status = HAL_STATUS_FAILED;
+ goto failed;
+ }
+
+ status = HAL_STATUS_SUCCESS;
+
+failed:
+ g_free(req);
+
+ ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_HIDHOST, HAL_OP_HIDHOST_SEND_DATA,
+ status);
+}
+
+static const struct ipc_handler cmd_handlers[] = {
+ /* HAL_OP_HIDHOST_CONNECT */
+ { bt_hid_connect, false, sizeof(struct hal_cmd_hidhost_connect) },
+ /* HAL_OP_HIDHOST_DISCONNECT */
+ { bt_hid_disconnect, false, sizeof(struct hal_cmd_hidhost_disconnect) },
+ /* HAL_OP_HIDHOST_VIRTUAL_UNPLUG */
+ { bt_hid_virtual_unplug, false,
+ sizeof(struct hal_cmd_hidhost_virtual_unplug) },
+ /* HAL_OP_HIDHOST_SET_INFO */
+ { bt_hid_info, true, sizeof(struct hal_cmd_hidhost_set_info) },
+ /* HAL_OP_HIDHOST_GET_PROTOCOL */
+ { bt_hid_get_protocol, false,
+ sizeof(struct hal_cmd_hidhost_get_protocol) },
+ /* HAL_OP_HIDHOST_SET_PROTOCOL */
+ { bt_hid_set_protocol, false,
+ sizeof(struct hal_cmd_hidhost_get_protocol) },
+ /* HAL_OP_HIDHOST_GET_REPORT */
+ { bt_hid_get_report, false, sizeof(struct hal_cmd_hidhost_get_report) },
+ /* HAL_OP_HIDHOST_SET_REPORT */
+ { bt_hid_set_report, true, sizeof(struct hal_cmd_hidhost_set_report) },
+ /* HAL_OP_HIDHOST_SEND_DATA */
+ { bt_hid_send_data, true, sizeof(struct hal_cmd_hidhost_send_data) },
+};
+
+static void connect_cb(GIOChannel *chan, GError *err, gpointer user_data)
+{
+ struct hid_device *dev;
+ bdaddr_t dst;
+ char address[18];
+ uint16_t psm;
+ GError *gerr = NULL;
+ GSList *l;
+ uuid_t uuid;
+
+ if (err) {
+ error("hidhost: Connect failed (%s)", err->message);
+ return;
+ }
+
+ bt_io_get(chan, &gerr,
+ BT_IO_OPT_DEST_BDADDR, &dst,
+ BT_IO_OPT_PSM, &psm,
+ BT_IO_OPT_INVALID);
+ if (gerr) {
+ error("hidhost: Failed to read remote address (%s)",
+ gerr->message);
+ g_io_channel_shutdown(chan, TRUE, NULL);
+ g_error_free(gerr);
+ return;
+ }
+
+ ba2str(&dst, address);
+ DBG("Incoming connection from %s on PSM %d", address, psm);
+
+ if (!bt_device_is_bonded(&dst)) {
+ warn("hidhost: Rejecting connection from unknown device %s",
+ address);
+ if (psm == L2CAP_PSM_HIDP_CTRL)
+ bt_hid_write_virtual_unplug(chan);
+
+ g_io_channel_shutdown(chan, TRUE, NULL);
+ return;
+ }
+
+ switch (psm) {
+ case L2CAP_PSM_HIDP_CTRL:
+ l = g_slist_find_custom(devices, &dst, device_cmp);
+ if (l)
+ return;
+
+ dev = hid_device_new(&dst);
+ dev->ctrl_io = g_io_channel_ref(chan);
+
+ sdp_uuid16_create(&uuid, PNP_INFO_SVCLASS_ID);
+ if (bt_search_service(&adapter_addr, &dev->dst, &uuid,
+ hid_sdp_did_search_cb, dev, NULL, 0) < 0) {
+ error("hidhost: Failed to search DID SDP details");
+ hid_device_remove(dev);
+ return;
+ }
+
+ dev->ctrl_watch = g_io_add_watch(dev->ctrl_io,
+ G_IO_HUP | G_IO_ERR | G_IO_NVAL,
+ ctrl_watch_cb, dev);
+ bt_hid_notify_state(dev, HAL_HIDHOST_STATE_CONNECTING);
+ break;
+
+ case L2CAP_PSM_HIDP_INTR:
+ l = g_slist_find_custom(devices, &dst, device_cmp);
+ if (!l)
+ return;
+
+ dev = l->data;
+ dev->intr_io = g_io_channel_ref(chan);
+ dev->intr_watch = g_io_add_watch(dev->intr_io,
+ G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL,
+ intr_watch_cb, dev);
+ bt_hid_notify_state(dev, HAL_HIDHOST_STATE_CONNECTED);
+ break;
+ }
+}
+
+static void hid_unpaired_cb(const bdaddr_t *addr)
+{
+ GSList *l;
+ struct hid_device *dev;
+ char address[18];
+
+ l = g_slist_find_custom(devices, addr, device_cmp);
+ if (!l)
+ return;
+
+ dev = l->data;
+
+ ba2str(addr, address);
+ DBG("Unpaired device %s", address);
+
+ if (hog_app)
+ bt_gatt_remove_autoconnect(hog_app, addr);
+
+ hid_device_remove(dev);
+}
+
+bool bt_hid_register(struct ipc *ipc, const bdaddr_t *addr, uint8_t mode)
+{
+ GError *err = NULL;
+
+ DBG("");
+
+ if (!bt_unpaired_register(hid_unpaired_cb)) {
+ error("hidhost: Could not register unpaired callback");
+ return false;
+ }
+
+ bacpy(&adapter_addr, addr);
+
+ ctrl_io = bt_io_listen(connect_cb, NULL, NULL, NULL, &err,
+ BT_IO_OPT_SOURCE_BDADDR, &adapter_addr,
+ BT_IO_OPT_PSM, L2CAP_PSM_HIDP_CTRL,
+ BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_LOW,
+ BT_IO_OPT_INVALID);
+ if (!ctrl_io) {
+ error("hidhost: Failed to listen on control channel: %s",
+ err->message);
+ g_error_free(err);
+ return false;
+ }
+
+ intr_io = bt_io_listen(connect_cb, NULL, NULL, NULL, &err,
+ BT_IO_OPT_SOURCE_BDADDR, &adapter_addr,
+ BT_IO_OPT_PSM, L2CAP_PSM_HIDP_INTR,
+ BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_LOW,
+ BT_IO_OPT_INVALID);
+ if (!intr_io) {
+ error("hidhost: Failed to listen on interrupt channel: %s",
+ err->message);
+ g_error_free(err);
+
+ g_io_channel_shutdown(ctrl_io, TRUE, NULL);
+ g_io_channel_unref(ctrl_io);
+ ctrl_io = NULL;
+
+ return false;
+ }
+
+ hal_ipc = ipc;
+
+ ipc_register(hal_ipc, HAL_SERVICE_ID_HIDHOST, cmd_handlers,
+ G_N_ELEMENTS(cmd_handlers));
+
+ return true;
+}
+
+void bt_hid_unregister(void)
+{
+ DBG("");
+
+ if (hog_app > 0)
+ bt_gatt_unregister_app(hog_app);
+
+ g_slist_free_full(devices, hid_device_free);
+ devices = NULL;
+
+ if (ctrl_io) {
+ g_io_channel_shutdown(ctrl_io, TRUE, NULL);
+ g_io_channel_unref(ctrl_io);
+ ctrl_io = NULL;
+ }
+
+ if (intr_io) {
+ g_io_channel_shutdown(intr_io, TRUE, NULL);
+ g_io_channel_unref(intr_io);
+ intr_io = NULL;
+ }
+
+ ipc_unregister(hal_ipc, HAL_SERVICE_ID_HIDHOST);
+ hal_ipc = NULL;
+
+ bt_unpaired_unregister(hid_unpaired_cb);
+}
diff --git a/repo/android/hidhost.h b/repo/android/hidhost.h
new file mode 100644
index 0000000..e6b87ed
--- /dev/null
+++ b/repo/android/hidhost.h
@@ -0,0 +1,25 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2013-2014 Intel Corporation. All rights reserved.
+ *
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+bool bt_hid_register(struct ipc *ipc, const bdaddr_t *addr, uint8_t mode);
+void bt_hid_unregister(void);
diff --git a/repo/android/init.bluetooth.rc b/repo/android/init.bluetooth.rc
new file mode 100644
index 0000000..2d43f73
--- /dev/null
+++ b/repo/android/init.bluetooth.rc
@@ -0,0 +1,38 @@
+# required permissions
+on boot
+ chown bluetooth bluetooth /data/misc/bluetooth
+ chown bluetooth bluetooth /dev/uhid
+ chown system bluetooth /dev/uinput
+
+# services
+on property:bluetooth.start=daemon
+ setprop bluetooth.start none
+ start bluetoothd
+
+on property:bluetooth.stop=daemon
+ setprop bluetooth.stop none
+ stop bluetoothd
+
+on property:bluetooth.start=snoop
+ setprop bluetooth.start none
+ start bluetoothd-snoop
+
+on property:bluetooth.stop=snoop
+ setprop bluetooth.stop none
+ stop bluetoothd-snoop
+
+service bluetoothd /system/bin/bluetoothd
+ class main
+ # init does not yet support setting capabilities so run as root,
+ # bluetoothd drop uid to bluetooth with the right linux capabilities
+ group bluetooth
+ disabled
+ oneshot
+
+service bluetoothd-snoop /system/bin/bluetoothd-snoop
+ class main
+ # init does not yet support setting capabilities so run as root,
+ # bluetoothd-snoop drops unneeded linux capabilities
+ group nobody
+ disabled
+ oneshot
diff --git a/repo/android/ipc-common.h b/repo/android/ipc-common.h
new file mode 100644
index 0000000..27736e4
--- /dev/null
+++ b/repo/android/ipc-common.h
@@ -0,0 +1,38 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2014 Intel Corporation. All rights reserved.
+ *
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#define IPC_MTU 1024
+
+#define IPC_STATUS_SUCCESS 0x00
+
+struct ipc_hdr {
+ uint8_t service_id;
+ uint8_t opcode;
+ uint16_t len;
+ uint8_t payload[0];
+} __attribute__((packed));
+
+#define IPC_OP_STATUS 0x00
+struct ipc_status {
+ uint8_t code;
+} __attribute__((packed));
diff --git a/repo/android/ipc-tester.c b/repo/android/ipc-tester.c
new file mode 100644
index 0000000..1aa17d2
--- /dev/null
+++ b/repo/android/ipc-tester.c
@@ -0,0 +1,1512 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2013-2014 Intel Corporation. All rights reserved.
+ *
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <errno.h>
+#include <poll.h>
+
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <sys/un.h>
+#include <libgen.h>
+#include <glib.h>
+
+#include "lib/bluetooth.h"
+#include "lib/mgmt.h"
+
+#include "src/shared/tester.h"
+#include "src/shared/mgmt.h"
+#include "emulator/hciemu.h"
+
+#include "hal-msg.h"
+#include "ipc-common.h"
+
+#include <cutils/properties.h>
+
+#define WAIT_FOR_SIGNAL_TIME 2 /* in seconds */
+#define EMULATOR_SIGNAL "emulator_started"
+
+struct test_data {
+ struct mgmt *mgmt;
+ uint16_t mgmt_index;
+ struct hciemu *hciemu;
+ enum hciemu_type hciemu_type;
+ pid_t bluetoothd_pid;
+ bool setup_done;
+};
+
+struct ipc_data {
+ void *buffer;
+ size_t len;
+};
+
+struct generic_data {
+ struct ipc_data ipc_data;
+
+ unsigned int num_services;
+ int init_services[];
+};
+
+struct regmod_msg {
+ struct ipc_hdr header;
+ struct hal_cmd_register_module cmd;
+} __attribute__((packed));
+
+#define CONNECT_TIMEOUT (5 * 1000)
+#define SERVICE_NAME "bluetoothd"
+
+static char exec_dir[PATH_MAX];
+
+static int cmd_sk = -1;
+static int notif_sk = -1;
+
+static void read_info_callback(uint8_t status, uint16_t length,
+ const void *param, void *user_data)
+{
+ struct test_data *data = tester_get_data();
+ const struct mgmt_rp_read_info *rp = param;
+ char addr[18];
+ uint16_t manufacturer;
+ uint32_t supported_settings, current_settings;
+
+ tester_print("Read Info callback");
+ tester_print(" Status: 0x%02x", status);
+
+ if (status || !param) {
+ tester_pre_setup_failed();
+ return;
+ }
+
+ ba2str(&rp->bdaddr, addr);
+ manufacturer = btohs(rp->manufacturer);
+ supported_settings = btohl(rp->supported_settings);
+ current_settings = btohl(rp->current_settings);
+
+ tester_print(" Address: %s", addr);
+ tester_print(" Version: 0x%02x", rp->version);
+ tester_print(" Manufacturer: 0x%04x", manufacturer);
+ tester_print(" Supported settings: 0x%08x", supported_settings);
+ tester_print(" Current settings: 0x%08x", current_settings);
+ tester_print(" Class: 0x%02x%02x%02x",
+ rp->dev_class[2], rp->dev_class[1], rp->dev_class[0]);
+ tester_print(" Name: %s", rp->name);
+ tester_print(" Short name: %s", rp->short_name);
+
+ if (strcmp(hciemu_get_address(data->hciemu), addr)) {
+ tester_pre_setup_failed();
+ return;
+ }
+
+ tester_pre_setup_complete();
+}
+
+static void index_added_callback(uint16_t index, uint16_t length,
+ const void *param, void *user_data)
+{
+ struct test_data *data = tester_get_data();
+
+ tester_print("Index Added callback");
+ tester_print(" Index: 0x%04x", index);
+
+ data->mgmt_index = index;
+
+ mgmt_send(data->mgmt, MGMT_OP_READ_INFO, data->mgmt_index, 0, NULL,
+ read_info_callback, NULL, NULL);
+}
+
+static void index_removed_callback(uint16_t index, uint16_t length,
+ const void *param, void *user_data)
+{
+ struct test_data *data = tester_get_data();
+
+ tester_print("Index Removed callback");
+ tester_print(" Index: 0x%04x", index);
+
+ if (index != data->mgmt_index)
+ return;
+
+ mgmt_unregister_index(data->mgmt, data->mgmt_index);
+
+ mgmt_unref(data->mgmt);
+ data->mgmt = NULL;
+
+ tester_post_teardown_complete();
+}
+
+static void read_index_list_callback(uint8_t status, uint16_t length,
+ const void *param, void *user_data)
+{
+ struct test_data *data = tester_get_data();
+
+ tester_print("Read Index List callback");
+ tester_print(" Status: 0x%02x", status);
+
+ if (status || !param) {
+ tester_pre_setup_failed();
+ return;
+ }
+
+ mgmt_register(data->mgmt, MGMT_EV_INDEX_ADDED, MGMT_INDEX_NONE,
+ index_added_callback, NULL, NULL);
+
+ mgmt_register(data->mgmt, MGMT_EV_INDEX_REMOVED, MGMT_INDEX_NONE,
+ index_removed_callback, NULL, NULL);
+
+ data->hciemu = hciemu_new(data->hciemu_type);
+ if (!data->hciemu) {
+ tester_warn("Failed to setup HCI emulation");
+ tester_pre_setup_failed();
+ return;
+ }
+
+ tester_print("New hciemu instance created");
+}
+
+static void test_pre_setup(const void *data)
+{
+ struct test_data *test_data = tester_get_data();
+
+ if (!tester_use_debug())
+ fclose(stderr);
+
+ test_data->mgmt = mgmt_new_default();
+ if (!test_data->mgmt) {
+ tester_warn("Failed to setup management interface");
+ tester_pre_setup_failed();
+ return;
+ }
+
+ mgmt_send(test_data->mgmt, MGMT_OP_READ_INDEX_LIST, MGMT_INDEX_NONE, 0,
+ NULL, read_index_list_callback, NULL, NULL);
+}
+
+static void test_post_teardown(const void *data)
+{
+ struct test_data *test_data = tester_get_data();
+
+ if (test_data->hciemu) {
+ hciemu_unref(test_data->hciemu);
+ test_data->hciemu = NULL;
+ }
+}
+
+static void bluetoothd_start(int hci_index)
+{
+ char prg_name[PATH_MAX];
+ char index[8];
+ char *prg_argv[4];
+
+ snprintf(prg_name, sizeof(prg_name), "%s/%s", exec_dir, "bluetoothd");
+ snprintf(index, sizeof(index), "%d", hci_index);
+
+ prg_argv[0] = prg_name;
+ prg_argv[1] = "-i";
+ prg_argv[2] = index;
+ prg_argv[3] = NULL;
+
+ if (!tester_use_debug())
+ fclose(stderr);
+
+ execve(prg_argv[0], prg_argv, NULL);
+}
+
+static void emulator(int pipe, int hci_index)
+{
+ static const char SYSTEM_SOCKET_PATH[] = "\0android_system";
+ char buf[1024];
+ struct sockaddr_un addr;
+ struct timeval tv;
+ int fd;
+ ssize_t len;
+
+ fd = socket(PF_LOCAL, SOCK_DGRAM | SOCK_CLOEXEC, 0);
+ if (fd < 0)
+ goto failed;
+
+ tv.tv_sec = WAIT_FOR_SIGNAL_TIME;
+ tv.tv_usec = 0;
+ setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, (char *)&tv, sizeof(tv));
+
+ memset(&addr, 0, sizeof(addr));
+ addr.sun_family = AF_UNIX;
+ memcpy(addr.sun_path, SYSTEM_SOCKET_PATH, sizeof(SYSTEM_SOCKET_PATH));
+
+ if (bind(fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+ perror("Failed to bind system socket");
+ goto failed;
+ }
+
+ len = write(pipe, EMULATOR_SIGNAL, sizeof(EMULATOR_SIGNAL));
+
+ if (len != sizeof(EMULATOR_SIGNAL))
+ goto failed;
+
+ memset(buf, 0, sizeof(buf));
+
+ len = read(fd, buf, sizeof(buf));
+ if (len <= 0 || strcmp(buf, "ctl.start=bluetoothd"))
+ goto failed;
+
+ close(pipe);
+ close(fd);
+ return bluetoothd_start(hci_index);
+
+failed:
+ close(pipe);
+ if (fd >= 0)
+ close(fd);
+}
+
+static int accept_connection(int sk)
+{
+ int err;
+ struct pollfd pfd;
+ int new_sk;
+
+ memset(&pfd, 0 , sizeof(pfd));
+ pfd.fd = sk;
+ pfd.events = POLLIN;
+
+ err = poll(&pfd, 1, CONNECT_TIMEOUT);
+ if (err < 0) {
+ err = errno;
+ tester_warn("Failed to poll: %d (%s)", err, strerror(err));
+ return -errno;
+ }
+
+ if (err == 0) {
+ tester_warn("bluetoothd connect timeout");
+ return -errno;
+ }
+
+ new_sk = accept(sk, NULL, NULL);
+ if (new_sk < 0) {
+ err = errno;
+ tester_warn("Failed to accept socket: %d (%s)",
+ err, strerror(err));
+ return -errno;
+ }
+
+ return new_sk;
+}
+
+static bool init_ipc(void)
+{
+ struct sockaddr_un addr;
+
+ int sk;
+ int err;
+
+ sk = socket(AF_LOCAL, SOCK_SEQPACKET, 0);
+ if (sk < 0) {
+ err = errno;
+ tester_warn("Failed to create socket: %d (%s)", err,
+ strerror(err));
+ return false;
+ }
+
+ memset(&addr, 0, sizeof(addr));
+ addr.sun_family = AF_UNIX;
+
+ memcpy(addr.sun_path, BLUEZ_HAL_SK_PATH, sizeof(BLUEZ_HAL_SK_PATH));
+
+ if (bind(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+ err = errno;
+ tester_warn("Failed to bind socket: %d (%s)", err,
+ strerror(err));
+ close(sk);
+ return false;
+ }
+
+ if (listen(sk, 2) < 0) {
+ err = errno;
+ tester_warn("Failed to listen on socket: %d (%s)", err,
+ strerror(err));
+ close(sk);
+ return false;
+ }
+
+ /* Start Android Bluetooth daemon service */
+ if (property_set("ctl.start", SERVICE_NAME) < 0) {
+ tester_warn("Failed to start service %s", SERVICE_NAME);
+ close(sk);
+ return false;
+ }
+
+ cmd_sk = accept_connection(sk);
+ if (cmd_sk < 0) {
+ close(sk);
+ return false;
+ }
+
+ notif_sk = accept_connection(sk);
+ if (notif_sk < 0) {
+ close(sk);
+ close(cmd_sk);
+ cmd_sk = -1;
+ return false;
+ }
+
+ tester_print("bluetoothd connected");
+
+ close(sk);
+
+ return true;
+}
+
+static void cleanup_ipc(void)
+{
+ if (cmd_sk < 0)
+ return;
+
+ close(cmd_sk);
+ cmd_sk = -1;
+}
+
+static gboolean check_for_daemon(gpointer user_data)
+{
+ int status;
+ struct test_data *data = user_data;
+
+ if ((waitpid(data->bluetoothd_pid, &status, WNOHANG))
+ != data->bluetoothd_pid)
+ return true;
+
+ if (data->setup_done) {
+ if (WIFEXITED(status) &&
+ (WEXITSTATUS(status) == EXIT_SUCCESS)) {
+ tester_test_passed();
+ return false;
+ }
+ tester_test_failed();
+ } else {
+ tester_setup_failed();
+ test_post_teardown(data);
+ }
+
+ tester_warn("Unexpected Daemon shutdown with status %d", status);
+ return false;
+}
+
+static bool setup_module(int service_id)
+{
+ struct ipc_hdr response;
+ struct ipc_hdr expected_response;
+
+ struct regmod_msg btmodule_msg = {
+ .header = {
+ .service_id = HAL_SERVICE_ID_CORE,
+ .opcode = HAL_OP_REGISTER_MODULE,
+ .len = sizeof(struct hal_cmd_register_module),
+ },
+ .cmd = {
+ .service_id = service_id,
+ .mode = HAL_MODE_DEFAULT,
+ .max_clients = 1,
+ },
+ };
+
+ if (write(cmd_sk, &btmodule_msg, sizeof(btmodule_msg)) < 0)
+ goto fail;
+
+ if (read(cmd_sk, &response, sizeof(response)) < 0)
+ goto fail;
+
+ expected_response = btmodule_msg.header;
+ expected_response.len = 0;
+
+ if (memcmp(&response, &expected_response, sizeof(response)) == 0)
+ return true;
+
+fail:
+ tester_warn("Module registration failed.");
+ return false;
+}
+
+static void setup(const void *data)
+{
+ const struct generic_data *generic_data = data;
+ struct test_data *test_data = tester_get_data();
+ int signal_fd[2];
+ char buf[1024];
+ pid_t pid;
+ int len;
+ unsigned int i;
+
+ if (pipe(signal_fd))
+ goto failed;
+
+ pid = fork();
+
+ if (pid < 0) {
+ close(signal_fd[0]);
+ close(signal_fd[1]);
+ goto failed;
+ }
+
+ if (pid == 0) {
+ if (!tester_use_debug())
+ fclose(stderr);
+
+ close(signal_fd[0]);
+ emulator(signal_fd[1], test_data->mgmt_index);
+ exit(0);
+ }
+
+ close(signal_fd[1]);
+ test_data->bluetoothd_pid = pid;
+
+ len = read(signal_fd[0], buf, sizeof(buf));
+ if (len <= 0 || (strcmp(buf, EMULATOR_SIGNAL))) {
+ close(signal_fd[0]);
+ goto failed;
+ }
+
+ g_idle_add_full(G_PRIORITY_DEFAULT_IDLE, check_for_daemon, test_data,
+ NULL);
+
+ if (!init_ipc()) {
+ tester_warn("Cannot initialize IPC mechanism!");
+ goto failed;
+ }
+ tester_print("Will init %d services.", generic_data->num_services);
+
+ for (i = 0; i < generic_data->num_services; i++)
+ if (!setup_module(generic_data->init_services[i])) {
+ cleanup_ipc();
+ goto failed;
+ }
+
+ test_data->setup_done = true;
+
+ tester_setup_complete();
+ return;
+
+failed:
+ g_idle_remove_by_data(test_data);
+ tester_setup_failed();
+ test_post_teardown(data);
+}
+
+static void teardown(const void *data)
+{
+ struct test_data *test_data = tester_get_data();
+
+ g_idle_remove_by_data(test_data);
+ cleanup_ipc();
+
+ if (test_data->bluetoothd_pid)
+ waitpid(test_data->bluetoothd_pid, NULL, 0);
+
+ tester_teardown_complete();
+}
+
+static void ipc_send_tc(const void *data)
+{
+ const struct generic_data *generic_data = data;
+ const struct ipc_data *ipc_data = &generic_data->ipc_data;
+
+ if (ipc_data->len) {
+ if (write(cmd_sk, ipc_data->buffer, ipc_data->len) < 0)
+ tester_test_failed();
+ }
+}
+
+#define service_data(args...) { args }
+
+#define gen_data(writelen, writebuf, servicelist...) \
+ { \
+ .ipc_data = { \
+ .buffer = writebuf, \
+ .len = writelen, \
+ }, \
+ .init_services = service_data(servicelist), \
+ .num_services = sizeof((const int[]) \
+ service_data(servicelist)) / \
+ sizeof(int), \
+ }
+
+#define test_generic(name, test, setup, teardown, buffer, writelen, \
+ services...) \
+ do { \
+ struct test_data *user; \
+ static const struct generic_data data = \
+ gen_data(writelen, buffer, services); \
+ user = g_malloc0(sizeof(struct test_data)); \
+ if (!user) \
+ break; \
+ user->hciemu_type = HCIEMU_TYPE_BREDRLE; \
+ tester_add_full(name, &data, test_pre_setup, setup, \
+ test, teardown, test_post_teardown, \
+ 3, user, g_free); \
+ } while (0)
+
+#define test_opcode_valid(_name, _service, _opcode, _len, _servicelist...) \
+ do { \
+ static struct ipc_hdr hdr = { \
+ .service_id = _service, \
+ .opcode = _opcode, \
+ .len = _len, \
+ }; \
+ \
+ test_generic("Opcode out of range: "_name, \
+ ipc_send_tc, setup, teardown, \
+ &hdr, \
+ sizeof(hdr), \
+ _servicelist); \
+ } while (0)
+
+struct vardata {
+ struct ipc_hdr hdr;
+ uint8_t buf[IPC_MTU];
+} __attribute__((packed));
+
+#define test_datasize_valid(_name, _service, _opcode, _hlen, _addatasize, \
+ _servicelist...) \
+ do { \
+ static struct vardata vdata = { \
+ .hdr.service_id = _service, \
+ .hdr.opcode = _opcode, \
+ .hdr.len = (_hlen) + (_addatasize), \
+ .buf = {}, \
+ }; \
+ test_generic("Data size "_name, \
+ ipc_send_tc, setup, teardown, \
+ &vdata, \
+ sizeof(vdata.hdr) + (_hlen) + (_addatasize),\
+ _servicelist); \
+ } while (0)
+
+static struct regmod_msg register_bt_msg = {
+ .header = {
+ .service_id = HAL_SERVICE_ID_CORE,
+ .opcode = HAL_OP_REGISTER_MODULE,
+ .len = sizeof(struct hal_cmd_register_module),
+ },
+ .cmd = {
+ .service_id = HAL_SERVICE_ID_BLUETOOTH,
+ },
+};
+
+static struct regmod_msg register_bt_malformed_size_msg = {
+ .header = {
+ .service_id = HAL_SERVICE_ID_CORE,
+ .opcode = HAL_OP_REGISTER_MODULE,
+ /* wrong payload size declared */
+ .len = sizeof(struct hal_cmd_register_module) - 1,
+ },
+ .cmd = {
+ .service_id = HAL_SERVICE_ID_CORE,
+ },
+};
+
+struct malformed_data3_struct {
+ struct regmod_msg valid_msg;
+ int redundant_data;
+} __attribute__((packed));
+
+static struct malformed_data3_struct malformed_data3_msg = {
+ /* valid register service message */
+ .valid_msg = {
+ .header = {
+ .service_id = HAL_SERVICE_ID_CORE,
+ .opcode = HAL_OP_REGISTER_MODULE,
+ .len = sizeof(struct hal_cmd_register_module),
+ },
+ .cmd = {
+ .service_id = HAL_SERVICE_ID_CORE,
+ },
+ },
+ /* plus redundant data */
+ . redundant_data = 666,
+};
+
+static struct ipc_hdr enable_unknown_service_hdr = {
+ .service_id = HAL_SERVICE_ID_MAX + 1,
+ .opcode = HAL_OP_REGISTER_MODULE,
+ .len = 0,
+};
+
+static struct ipc_hdr enable_bt_service_hdr = {
+ .service_id = HAL_SERVICE_ID_BLUETOOTH,
+ .opcode = HAL_OP_ENABLE,
+ .len = 0,
+};
+
+struct bt_set_adapter_prop_data {
+ struct ipc_hdr hdr;
+ struct hal_cmd_set_adapter_prop prop;
+
+ /* data placeholder for hal_cmd_set_adapter_prop.val[0] */
+ uint8_t buf[IPC_MTU - sizeof(struct ipc_hdr) -
+ sizeof(struct hal_cmd_set_adapter_prop)];
+} __attribute__((packed));
+
+#define set_name "new name"
+
+static struct bt_set_adapter_prop_data bt_set_adapter_prop_data_overs = {
+ .hdr.service_id = HAL_SERVICE_ID_BLUETOOTH,
+ .hdr.opcode = HAL_OP_SET_ADAPTER_PROP,
+ .hdr.len = sizeof(struct hal_cmd_set_adapter_prop) + sizeof(set_name),
+
+ .prop.type = HAL_PROP_ADAPTER_NAME,
+ /* declare wrong descriptor length */
+ .prop.len = sizeof(set_name) + 1,
+ /* init prop.val[0] */
+ .buf = set_name,
+};
+
+static struct bt_set_adapter_prop_data bt_set_adapter_prop_data_unders = {
+ .hdr.service_id = HAL_SERVICE_ID_BLUETOOTH,
+ .hdr.opcode = HAL_OP_SET_ADAPTER_PROP,
+ .hdr.len = sizeof(struct hal_cmd_set_adapter_prop) + sizeof(set_name),
+
+ .prop.type = HAL_PROP_ADAPTER_NAME,
+ /* declare wrong descriptor length */
+ .prop.len = sizeof(set_name) - 1,
+ /* init prop.val[0] */
+ .buf = set_name,
+};
+
+struct bt_set_remote_prop_data {
+ struct ipc_hdr hdr;
+ struct hal_cmd_set_remote_device_prop prop;
+
+ /* data placeholder for hal_cmd_set_remote_device_prop.val[0] */
+ uint8_t buf[IPC_MTU - sizeof(struct ipc_hdr) -
+ sizeof(struct hal_cmd_set_remote_device_prop)];
+} __attribute__((packed));
+
+static struct bt_set_remote_prop_data bt_set_remote_prop_data_overs = {
+ .hdr.service_id = HAL_SERVICE_ID_BLUETOOTH,
+ .hdr.opcode = HAL_OP_SET_REMOTE_DEVICE_PROP,
+ .hdr.len = sizeof(struct hal_cmd_set_remote_device_prop) +
+ sizeof(set_name),
+
+ .prop.bdaddr = {},
+ .prop.type = HAL_PROP_DEVICE_NAME,
+ /* declare wrong descriptor length */
+ .prop.len = sizeof(set_name) + 1,
+ .buf = set_name,
+};
+
+static struct bt_set_remote_prop_data bt_set_remote_prop_data_unders = {
+ .hdr.service_id = HAL_SERVICE_ID_BLUETOOTH,
+ .hdr.opcode = HAL_OP_SET_REMOTE_DEVICE_PROP,
+ .hdr.len = sizeof(struct hal_cmd_set_remote_device_prop) +
+ sizeof(set_name),
+
+ .prop.bdaddr = {},
+ .prop.type = HAL_PROP_DEVICE_NAME,
+ /* declare wrong descriptor length */
+ .prop.len = sizeof(set_name) - 1,
+ .buf = set_name,
+};
+
+struct hidhost_set_info_data {
+ struct ipc_hdr hdr;
+ struct hal_cmd_hidhost_set_info info;
+
+ /* data placeholder for hal_cmd_hidhost_set_info.descr[0] field */
+ uint8_t buf[IPC_MTU - sizeof(struct ipc_hdr) -
+ sizeof(struct hal_cmd_hidhost_set_info)];
+} __attribute__((packed));
+
+#define set_info_data "some descriptor"
+
+static struct hidhost_set_info_data hidhost_set_info_data_overs = {
+ .hdr.service_id = HAL_SERVICE_ID_HIDHOST,
+ .hdr.opcode = HAL_OP_HIDHOST_SET_INFO,
+ .hdr.len = sizeof(struct hal_cmd_hidhost_set_info) +
+ sizeof(set_info_data),
+
+ /* declare wrong descriptor length */
+ .info.descr_len = sizeof(set_info_data) + 1,
+ /* init .info.descr[0] */
+ .buf = set_info_data,
+};
+
+static struct hidhost_set_info_data hidhost_set_info_data_unders = {
+ .hdr.service_id = HAL_SERVICE_ID_HIDHOST,
+ .hdr.opcode = HAL_OP_HIDHOST_SET_INFO,
+ .hdr.len = sizeof(struct hal_cmd_hidhost_set_info) +
+ sizeof(set_info_data),
+
+ /* declare wrong descriptor length */
+ .info.descr_len = sizeof(set_info_data) - 1,
+ /* init .info.descr[0] */
+ .buf = set_info_data,
+};
+
+struct hidhost_set_report_data {
+ struct ipc_hdr hdr;
+ struct hal_cmd_hidhost_set_report report;
+
+ /* data placeholder for hal_cmd_hidhost_set_report.data[0] field */
+ uint8_t buf[IPC_MTU - sizeof(struct ipc_hdr) -
+ sizeof(struct hal_cmd_hidhost_set_report)];
+} __attribute__((packed));
+
+#define set_rep_data "1234567890"
+
+static struct hidhost_set_report_data hidhost_set_report_data_overs = {
+ .hdr.service_id = HAL_SERVICE_ID_HIDHOST,
+ .hdr.opcode = HAL_OP_HIDHOST_SET_REPORT,
+ .hdr.len = sizeof(struct hal_cmd_hidhost_set_report) +
+ sizeof(set_rep_data),
+
+ /* declare wrong descriptor length */
+ .report.len = sizeof(set_rep_data) + 1,
+ /* init report.data[0] */
+ .buf = set_rep_data,
+};
+
+static struct hidhost_set_report_data hidhost_set_report_data_unders = {
+ .hdr.service_id = HAL_SERVICE_ID_HIDHOST,
+ .hdr.opcode = HAL_OP_HIDHOST_SET_REPORT,
+ .hdr.len = sizeof(struct hal_cmd_hidhost_set_report) +
+ sizeof(set_rep_data),
+
+ /* declare wrong descriptor length */
+ .report.len = sizeof(set_rep_data) - 1,
+ /* init report.data[0] */
+ .buf = set_rep_data,
+};
+
+struct hidhost_send_data_data {
+ struct ipc_hdr hdr;
+ struct hal_cmd_hidhost_send_data hiddata;
+
+ /* data placeholder for hal_cmd_hidhost_send_data.data[0] field */
+ uint8_t buf[IPC_MTU - sizeof(struct ipc_hdr) -
+ sizeof(struct hal_cmd_hidhost_send_data)];
+} __attribute__((packed));
+
+#define send_data_data "1234567890"
+
+static struct hidhost_send_data_data hidhost_send_data_overs = {
+ .hdr.service_id = HAL_SERVICE_ID_HIDHOST,
+ .hdr.opcode = HAL_OP_HIDHOST_SEND_DATA,
+ .hdr.len = sizeof(struct hal_cmd_hidhost_send_data) +
+ sizeof(send_data_data),
+
+ /* declare wrong descriptor length */
+ .hiddata.len = sizeof(send_data_data) + 1,
+ /* init .hiddata.data[0] */
+ .buf = send_data_data,
+};
+
+static struct hidhost_send_data_data hidhost_send_data_unders = {
+ .hdr.service_id = HAL_SERVICE_ID_HIDHOST,
+ .hdr.opcode = HAL_OP_HIDHOST_SEND_DATA,
+ .hdr.len = sizeof(struct hal_cmd_hidhost_send_data) +
+ sizeof(send_data_data),
+
+ /* declare wrong descriptor length */
+ .hiddata.len = sizeof(send_data_data) - 1,
+ /* init .hiddata.data[0] */
+ .buf = send_data_data,
+};
+
+#define hfp_number "#1234567890"
+
+struct hfp_dial_data {
+ struct ipc_hdr hdr;
+ struct hal_cmd_hf_client_dial data;
+
+ uint8_t buf[IPC_MTU - sizeof(struct ipc_hdr) -
+ sizeof(struct hal_cmd_hf_client_dial)];
+} __attribute__((packed));
+
+static struct hfp_dial_data hfp_dial_overs = {
+ .hdr.service_id = HAL_SERVICE_ID_HANDSFREE_CLIENT,
+ .hdr.opcode = HAL_OP_HF_CLIENT_DIAL,
+ .hdr.len = sizeof(struct hal_cmd_hf_client_dial) + sizeof(hfp_number),
+
+ .data.number_len = sizeof(hfp_number) + 1,
+ .buf = hfp_number,
+};
+
+static struct hfp_dial_data hfp_dial_unders = {
+ .hdr.service_id = HAL_SERVICE_ID_HANDSFREE_CLIENT,
+ .hdr.opcode = HAL_OP_HF_CLIENT_DIAL,
+ .hdr.len = sizeof(struct hal_cmd_hf_client_dial) + sizeof(hfp_number),
+
+ .data.number_len = sizeof(hfp_number) - 1,
+ .buf = hfp_number,
+};
+
+int main(int argc, char *argv[])
+{
+ snprintf(exec_dir, sizeof(exec_dir), "%s", dirname(argv[0]));
+
+ tester_init(&argc, &argv);
+
+ /* check general IPC errors */
+ test_generic("Too small data",
+ ipc_send_tc, setup, teardown,
+ ®ister_bt_msg, 1);
+
+ test_generic("Malformed data (wrong payload declared)",
+ ipc_send_tc, setup, teardown,
+ ®ister_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,
+ ®ister_bt_msg,
+ sizeof(register_bt_msg) - 1,
+ HAL_SERVICE_ID_BLUETOOTH);
+
+ test_generic("Malformed data3 (oversized msg)",
+ ipc_send_tc, setup, teardown,
+ &malformed_data3_msg,
+ sizeof(malformed_data3_msg),
+ HAL_SERVICE_ID_BLUETOOTH);
+
+ test_generic("Invalid service",
+ ipc_send_tc, setup, teardown,
+ &enable_unknown_service_hdr,
+ sizeof(enable_unknown_service_hdr),
+ HAL_SERVICE_ID_BLUETOOTH);
+
+ test_generic("Enable unregistered service",
+ ipc_send_tc, setup, teardown,
+ &enable_bt_service_hdr,
+ sizeof(enable_bt_service_hdr));
+
+ /* check service handler's max opcode value */
+ test_opcode_valid("CORE", HAL_SERVICE_ID_CORE, 0x03, 0);
+
+ test_opcode_valid("BLUETOOTH", HAL_SERVICE_ID_BLUETOOTH, 0x15, 0,
+ HAL_SERVICE_ID_BLUETOOTH);
+
+ test_opcode_valid("SOCK", HAL_SERVICE_ID_SOCKET, 0x03, 0,
+ HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_SOCKET);
+
+ test_opcode_valid("HIDHOST", HAL_SERVICE_ID_HIDHOST, 0x10, 0,
+ HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_HIDHOST);
+
+ test_opcode_valid("PAN", HAL_SERVICE_ID_PAN, 0x05, 0,
+ HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_PAN);
+
+ test_opcode_valid("HANDSFREE", HAL_SERVICE_ID_HANDSFREE, 0x10, 0,
+ HAL_SERVICE_ID_BLUETOOTH,
+ HAL_SERVICE_ID_HANDSFREE);
+
+ test_opcode_valid("A2DP", HAL_SERVICE_ID_A2DP, 0x03, 0,
+ HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_A2DP);
+
+ test_opcode_valid("HEALTH", HAL_SERVICE_ID_HEALTH, 0x06, 0,
+ HAL_SERVICE_ID_BLUETOOTH,
+ HAL_SERVICE_ID_HEALTH);
+
+ test_opcode_valid("AVRCP", HAL_SERVICE_ID_AVRCP, 0x0b, 0,
+ HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_AVRCP);
+
+ test_opcode_valid("GATT", HAL_SERVICE_ID_GATT, 0x24, 0,
+ HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_GATT);
+
+ test_opcode_valid("HF_CLIENT", HAL_SERVICE_ID_HANDSFREE_CLIENT, 0x10, 0,
+ HAL_SERVICE_ID_BLUETOOTH,
+ HAL_SERVICE_ID_HANDSFREE_CLIENT);
+
+ test_opcode_valid("MAP_CLIENT", HAL_SERVICE_ID_MAP_CLIENT, 0x01, 0,
+ HAL_SERVICE_ID_BLUETOOTH,
+ HAL_SERVICE_ID_MAP_CLIENT);
+
+ /* check for valid data size */
+ test_datasize_valid("CORE Register+", HAL_SERVICE_ID_CORE,
+ HAL_OP_REGISTER_MODULE,
+ sizeof(struct hal_cmd_register_module), 1);
+ test_datasize_valid("CORE Register-", HAL_SERVICE_ID_CORE,
+ HAL_OP_REGISTER_MODULE,
+ sizeof(struct hal_cmd_register_module), -1);
+ test_datasize_valid("CORE Unregister+", HAL_SERVICE_ID_CORE,
+ HAL_OP_UNREGISTER_MODULE,
+ sizeof(struct hal_cmd_unregister_module), 1);
+ test_datasize_valid("CORE Unregister-", HAL_SERVICE_ID_CORE,
+ HAL_OP_UNREGISTER_MODULE,
+ sizeof(struct hal_cmd_unregister_module), -1);
+
+ /* check for valid data size for BLUETOOTH */
+ test_datasize_valid("BT Enable+", HAL_SERVICE_ID_BLUETOOTH,
+ HAL_OP_ENABLE,
+ 0, 1,
+ HAL_SERVICE_ID_BLUETOOTH);
+ test_datasize_valid("BT Disable+", HAL_SERVICE_ID_BLUETOOTH,
+ HAL_OP_DISABLE,
+ 0, 1,
+ HAL_SERVICE_ID_BLUETOOTH);
+ test_datasize_valid("BT Get Adapter Props+", HAL_SERVICE_ID_BLUETOOTH,
+ HAL_OP_GET_ADAPTER_PROPS,
+ 0, 1,
+ HAL_SERVICE_ID_BLUETOOTH);
+ test_datasize_valid("BT Get Adapter Prop+", HAL_SERVICE_ID_BLUETOOTH,
+ HAL_OP_GET_ADAPTER_PROP,
+ sizeof(struct hal_cmd_get_adapter_prop), 1,
+ HAL_SERVICE_ID_BLUETOOTH);
+ test_datasize_valid("BT Get Adapter Prop-", HAL_SERVICE_ID_BLUETOOTH,
+ HAL_OP_GET_ADAPTER_PROP,
+ sizeof(struct hal_cmd_get_adapter_prop), -1,
+ HAL_SERVICE_ID_BLUETOOTH);
+ test_datasize_valid("BT Set Adapter Prop+", HAL_SERVICE_ID_BLUETOOTH,
+ HAL_OP_SET_ADAPTER_PROP,
+ sizeof(struct hal_cmd_set_adapter_prop), 1,
+ HAL_SERVICE_ID_BLUETOOTH);
+ test_datasize_valid("BT Set Adapter Prop-", HAL_SERVICE_ID_BLUETOOTH,
+ HAL_OP_SET_ADAPTER_PROP,
+ sizeof(struct hal_cmd_set_adapter_prop), -1,
+ HAL_SERVICE_ID_BLUETOOTH);
+ test_generic("Data size BT Set Adapter Prop Vardata+",
+ ipc_send_tc, setup, teardown,
+ &bt_set_adapter_prop_data_overs,
+ (sizeof(struct ipc_hdr) +
+ sizeof(struct hal_cmd_set_adapter_prop) +
+ sizeof(set_name)),
+ HAL_SERVICE_ID_BLUETOOTH);
+ test_generic("Data size BT Set Adapter Prop Vardata+",
+ ipc_send_tc, setup, teardown,
+ &bt_set_adapter_prop_data_unders,
+ (sizeof(struct ipc_hdr) +
+ sizeof(struct hal_cmd_set_adapter_prop) +
+ sizeof(set_name)),
+ HAL_SERVICE_ID_BLUETOOTH);
+ test_datasize_valid("BT Get Remote Props+", HAL_SERVICE_ID_BLUETOOTH,
+ HAL_OP_GET_REMOTE_DEVICE_PROPS,
+ sizeof(struct hal_cmd_get_remote_device_props), 1,
+ HAL_SERVICE_ID_BLUETOOTH);
+ test_datasize_valid("BT Get Remote Props-", HAL_SERVICE_ID_BLUETOOTH,
+ HAL_OP_GET_REMOTE_DEVICE_PROPS,
+ sizeof(struct hal_cmd_get_remote_device_props), -1,
+ HAL_SERVICE_ID_BLUETOOTH);
+ test_datasize_valid("BT Get Remote Prop+", HAL_SERVICE_ID_BLUETOOTH,
+ HAL_OP_GET_REMOTE_DEVICE_PROP,
+ sizeof(struct hal_cmd_get_remote_device_prop), 1,
+ HAL_SERVICE_ID_BLUETOOTH);
+ test_datasize_valid("BT Get Remote Prop-", HAL_SERVICE_ID_BLUETOOTH,
+ HAL_OP_GET_REMOTE_DEVICE_PROP,
+ sizeof(struct hal_cmd_get_remote_device_prop), -1,
+ HAL_SERVICE_ID_BLUETOOTH);
+ test_datasize_valid("BT Set Remote Prop+", HAL_SERVICE_ID_BLUETOOTH,
+ HAL_OP_SET_REMOTE_DEVICE_PROP,
+ sizeof(struct hal_cmd_set_remote_device_prop), 1,
+ HAL_SERVICE_ID_BLUETOOTH);
+ test_datasize_valid("BT Set Remote Prop-", HAL_SERVICE_ID_BLUETOOTH,
+ HAL_OP_SET_REMOTE_DEVICE_PROP,
+ sizeof(struct hal_cmd_set_remote_device_prop), -1,
+ HAL_SERVICE_ID_BLUETOOTH);
+ test_generic("Data size BT Set Remote Prop Vardata+",
+ ipc_send_tc, setup, teardown,
+ &bt_set_remote_prop_data_overs,
+ (sizeof(struct ipc_hdr) +
+ sizeof(struct hal_cmd_set_remote_device_prop) +
+ sizeof(set_name)),
+ HAL_SERVICE_ID_BLUETOOTH);
+ test_generic("Data size BT Set Remote Prop Vardata-",
+ ipc_send_tc, setup, teardown,
+ &bt_set_remote_prop_data_unders,
+ (sizeof(struct ipc_hdr) +
+ sizeof(struct hal_cmd_set_remote_device_prop) +
+ sizeof(set_name)),
+ HAL_SERVICE_ID_BLUETOOTH);
+ test_datasize_valid("BT Get Remote SV Rec+", HAL_SERVICE_ID_BLUETOOTH,
+ HAL_OP_GET_REMOTE_SERVICE_REC,
+ sizeof(struct hal_cmd_get_remote_service_rec), 1,
+ HAL_SERVICE_ID_BLUETOOTH);
+ test_datasize_valid("BT Get Remote SV Rec-", HAL_SERVICE_ID_BLUETOOTH,
+ HAL_OP_GET_REMOTE_SERVICE_REC,
+ sizeof(struct hal_cmd_get_remote_service_rec), -1,
+ HAL_SERVICE_ID_BLUETOOTH);
+ test_datasize_valid("BT Get Remote Services+", HAL_SERVICE_ID_BLUETOOTH,
+ HAL_OP_GET_REMOTE_SERVICES,
+ sizeof(struct hal_cmd_get_remote_services), 1,
+ HAL_SERVICE_ID_BLUETOOTH);
+ test_datasize_valid("BT Get Remote Services-", HAL_SERVICE_ID_BLUETOOTH,
+ HAL_OP_GET_REMOTE_SERVICES,
+ sizeof(struct hal_cmd_get_remote_services), -1,
+ HAL_SERVICE_ID_BLUETOOTH);
+ test_datasize_valid("BT Start Discovery+", HAL_SERVICE_ID_BLUETOOTH,
+ HAL_OP_START_DISCOVERY,
+ 0, 1,
+ HAL_SERVICE_ID_BLUETOOTH);
+ test_datasize_valid("BT Cancel Discovery+", HAL_SERVICE_ID_BLUETOOTH,
+ HAL_OP_CANCEL_DISCOVERY,
+ 0, 1,
+ HAL_SERVICE_ID_BLUETOOTH);
+ test_datasize_valid("BT Create Bond+", HAL_SERVICE_ID_BLUETOOTH,
+ HAL_OP_CREATE_BOND,
+ sizeof(struct hal_cmd_create_bond), 1,
+ HAL_SERVICE_ID_BLUETOOTH);
+ test_datasize_valid("BT Create Bond-", HAL_SERVICE_ID_BLUETOOTH,
+ HAL_OP_CREATE_BOND,
+ sizeof(struct hal_cmd_create_bond), -1,
+ HAL_SERVICE_ID_BLUETOOTH);
+ test_datasize_valid("BT Remove Bond+", HAL_SERVICE_ID_BLUETOOTH,
+ HAL_OP_REMOVE_BOND,
+ sizeof(struct hal_cmd_remove_bond), 1,
+ HAL_SERVICE_ID_BLUETOOTH);
+ test_datasize_valid("BT Remove Bond-", HAL_SERVICE_ID_BLUETOOTH,
+ HAL_OP_REMOVE_BOND,
+ sizeof(struct hal_cmd_remove_bond), -1,
+ HAL_SERVICE_ID_BLUETOOTH);
+ test_datasize_valid("BT Cancel Bond+", HAL_SERVICE_ID_BLUETOOTH,
+ HAL_OP_CANCEL_BOND,
+ sizeof(struct hal_cmd_cancel_bond), 1,
+ HAL_SERVICE_ID_BLUETOOTH);
+ test_datasize_valid("BT Cancel Bond-", HAL_SERVICE_ID_BLUETOOTH,
+ HAL_OP_CANCEL_BOND,
+ sizeof(struct hal_cmd_cancel_bond), -1,
+ HAL_SERVICE_ID_BLUETOOTH);
+ test_datasize_valid("BT Pin Reply+", HAL_SERVICE_ID_BLUETOOTH,
+ HAL_OP_PIN_REPLY,
+ sizeof(struct hal_cmd_pin_reply), 1,
+ HAL_SERVICE_ID_BLUETOOTH);
+ test_datasize_valid("BT Pin Reply-", HAL_SERVICE_ID_BLUETOOTH,
+ HAL_OP_PIN_REPLY,
+ sizeof(struct hal_cmd_pin_reply), -1,
+ HAL_SERVICE_ID_BLUETOOTH);
+ test_datasize_valid("BT SSP Reply+", HAL_SERVICE_ID_BLUETOOTH,
+ HAL_OP_SSP_REPLY,
+ sizeof(struct hal_cmd_ssp_reply), 1,
+ HAL_SERVICE_ID_BLUETOOTH);
+ test_datasize_valid("BT SSP Reply-", HAL_SERVICE_ID_BLUETOOTH,
+ HAL_OP_SSP_REPLY,
+ sizeof(struct hal_cmd_ssp_reply), -1,
+ HAL_SERVICE_ID_BLUETOOTH);
+ test_datasize_valid("BT DUT Mode Conf+", HAL_SERVICE_ID_BLUETOOTH,
+ HAL_OP_DUT_MODE_CONF,
+ sizeof(struct hal_cmd_dut_mode_conf), 1,
+ HAL_SERVICE_ID_BLUETOOTH);
+ test_datasize_valid("BT DUT Mode Conf-", HAL_SERVICE_ID_BLUETOOTH,
+ HAL_OP_DUT_MODE_CONF,
+ sizeof(struct hal_cmd_dut_mode_conf), -1,
+ HAL_SERVICE_ID_BLUETOOTH);
+ test_datasize_valid("BT DUT Mode Send+", HAL_SERVICE_ID_BLUETOOTH,
+ HAL_OP_DUT_MODE_SEND,
+ sizeof(struct hal_cmd_dut_mode_send), 1,
+ HAL_SERVICE_ID_BLUETOOTH);
+ test_datasize_valid("BT DUT Mode Send-", HAL_SERVICE_ID_BLUETOOTH,
+ HAL_OP_DUT_MODE_SEND,
+ sizeof(struct hal_cmd_dut_mode_send), -1,
+ HAL_SERVICE_ID_BLUETOOTH);
+ test_datasize_valid("BT LE Test+", HAL_SERVICE_ID_BLUETOOTH,
+ HAL_OP_LE_TEST_MODE,
+ sizeof(struct hal_cmd_le_test_mode), 1,
+ HAL_SERVICE_ID_BLUETOOTH);
+ test_datasize_valid("BT LE Test-", HAL_SERVICE_ID_BLUETOOTH,
+ HAL_OP_LE_TEST_MODE,
+ sizeof(struct hal_cmd_le_test_mode), -1,
+ HAL_SERVICE_ID_BLUETOOTH);
+
+ /* check for valid data size for SOCK */
+ test_datasize_valid("SOCKET Listen+", HAL_SERVICE_ID_SOCKET,
+ HAL_OP_SOCKET_LISTEN,
+ sizeof(struct hal_cmd_socket_listen), 1,
+ HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_SOCKET);
+ test_datasize_valid("SOCKET Listen-", HAL_SERVICE_ID_SOCKET,
+ HAL_OP_SOCKET_LISTEN,
+ sizeof(struct hal_cmd_socket_listen), -1,
+ HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_SOCKET);
+ test_datasize_valid("SOCKET Connect+", HAL_SERVICE_ID_SOCKET,
+ HAL_OP_SOCKET_CONNECT,
+ sizeof(struct hal_cmd_socket_connect), 1,
+ HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_SOCKET);
+ test_datasize_valid("SOCKET Connect-", HAL_SERVICE_ID_SOCKET,
+ HAL_OP_SOCKET_CONNECT,
+ sizeof(struct hal_cmd_socket_connect), -1,
+ HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_SOCKET);
+
+ /* check for valid data size for HID Host */
+ test_datasize_valid("HIDHOST Connect+", HAL_SERVICE_ID_HIDHOST,
+ HAL_OP_HIDHOST_CONNECT,
+ sizeof(struct hal_cmd_hidhost_connect), 1,
+ HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_HIDHOST);
+ test_datasize_valid("HIDHOST Connect-", HAL_SERVICE_ID_HIDHOST,
+ HAL_OP_HIDHOST_CONNECT,
+ sizeof(struct hal_cmd_hidhost_connect), -1,
+ HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_HIDHOST);
+ test_datasize_valid("HIDHOST Disconnect+", HAL_SERVICE_ID_HIDHOST,
+ HAL_OP_HIDHOST_DISCONNECT,
+ sizeof(struct hal_cmd_hidhost_disconnect), 1,
+ HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_HIDHOST);
+ test_datasize_valid("HIDHOST Disconnect-", HAL_SERVICE_ID_HIDHOST,
+ HAL_OP_HIDHOST_DISCONNECT,
+ sizeof(struct hal_cmd_hidhost_disconnect), -1,
+ HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_HIDHOST);
+ test_datasize_valid("HIDHOST Virt. Unplug+", HAL_SERVICE_ID_HIDHOST,
+ HAL_OP_HIDHOST_VIRTUAL_UNPLUG,
+ sizeof(struct hal_cmd_hidhost_virtual_unplug), 1,
+ HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_HIDHOST);
+ test_datasize_valid("HIDHOST Virt. Unplug-", HAL_SERVICE_ID_HIDHOST,
+ HAL_OP_HIDHOST_VIRTUAL_UNPLUG,
+ sizeof(struct hal_cmd_hidhost_virtual_unplug), -1,
+ HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_HIDHOST);
+ test_datasize_valid("HIDHOST Set Info+", HAL_SERVICE_ID_HIDHOST,
+ HAL_OP_HIDHOST_SET_INFO,
+ sizeof(struct hal_cmd_hidhost_set_info), 1,
+ HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_HIDHOST);
+ test_datasize_valid("HIDHOST Set Info-", HAL_SERVICE_ID_HIDHOST,
+ HAL_OP_HIDHOST_SET_INFO,
+ sizeof(struct hal_cmd_hidhost_set_info), -1,
+ HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_HIDHOST);
+ test_generic("Data size HIDHOST Set Info Vardata+",
+ ipc_send_tc, setup, teardown,
+ &hidhost_set_info_data_overs,
+ (sizeof(struct ipc_hdr) +
+ sizeof(struct hal_cmd_hidhost_set_info) +
+ sizeof(set_info_data)),
+ HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_HIDHOST);
+ test_generic("Data size HIDHOST Set Info Vardata-",
+ ipc_send_tc, setup, teardown,
+ &hidhost_set_info_data_unders,
+ (sizeof(struct ipc_hdr) +
+ sizeof(struct hal_cmd_hidhost_set_info) +
+ sizeof(set_info_data)),
+ HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_HIDHOST);
+ test_datasize_valid("HIDHOST Get Protocol+", HAL_SERVICE_ID_HIDHOST,
+ HAL_OP_HIDHOST_GET_PROTOCOL,
+ sizeof(struct hal_cmd_hidhost_get_protocol), 1,
+ HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_HIDHOST);
+ test_datasize_valid("HIDHOST Get Protocol-", HAL_SERVICE_ID_HIDHOST,
+ HAL_OP_HIDHOST_GET_PROTOCOL,
+ sizeof(struct hal_cmd_hidhost_get_protocol), -1,
+ HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_HIDHOST);
+ test_datasize_valid("HIDHOST Set Protocol+", HAL_SERVICE_ID_HIDHOST,
+ HAL_OP_HIDHOST_SET_PROTOCOL,
+ sizeof(struct hal_cmd_hidhost_set_protocol), 1,
+ HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_HIDHOST);
+ test_datasize_valid("HIDHOST Set Protocol-", HAL_SERVICE_ID_HIDHOST,
+ HAL_OP_HIDHOST_SET_PROTOCOL,
+ sizeof(struct hal_cmd_hidhost_set_protocol), -1,
+ HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_HIDHOST);
+ test_datasize_valid("HIDHOST Get Report+", HAL_SERVICE_ID_HIDHOST,
+ HAL_OP_HIDHOST_GET_REPORT,
+ sizeof(struct hal_cmd_hidhost_get_report), 1,
+ HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_HIDHOST);
+ test_datasize_valid("HIDHOST Get Report-", HAL_SERVICE_ID_HIDHOST,
+ HAL_OP_HIDHOST_GET_REPORT,
+ sizeof(struct hal_cmd_hidhost_get_report), -1,
+ HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_HIDHOST);
+ test_datasize_valid("HIDHOST Set Report+", HAL_SERVICE_ID_HIDHOST,
+ HAL_OP_HIDHOST_SET_REPORT,
+ sizeof(struct hal_cmd_hidhost_set_report), 1,
+ HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_HIDHOST);
+ test_datasize_valid("HIDHOST Set Report-", HAL_SERVICE_ID_HIDHOST,
+ HAL_OP_HIDHOST_SET_REPORT,
+ sizeof(struct hal_cmd_hidhost_set_report), -1,
+ HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_HIDHOST);
+ test_generic("Data size HIDHOST Set Report Vardata+",
+ ipc_send_tc, setup, teardown,
+ &hidhost_set_report_data_overs,
+ (sizeof(struct ipc_hdr) +
+ sizeof(struct hal_cmd_hidhost_set_report) +
+ sizeof(set_rep_data)),
+ HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_HIDHOST);
+ test_generic("Data size HIDHOST Set Report Vardata-",
+ ipc_send_tc, setup, teardown,
+ &hidhost_set_report_data_unders,
+ (sizeof(struct ipc_hdr) +
+ sizeof(struct hal_cmd_hidhost_set_report) +
+ sizeof(set_rep_data)),
+ HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_HIDHOST);
+ test_datasize_valid("HIDHOST Send Data+", HAL_SERVICE_ID_HIDHOST,
+ HAL_OP_HIDHOST_SEND_DATA,
+ sizeof(struct hal_cmd_hidhost_send_data), 1,
+ HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_HIDHOST);
+ test_datasize_valid("HIDHOST Send Data-", HAL_SERVICE_ID_HIDHOST,
+ HAL_OP_HIDHOST_SEND_DATA,
+ sizeof(struct hal_cmd_hidhost_send_data), -1,
+ HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_HIDHOST);
+ test_generic("Data size HIDHOST Send Vardata+",
+ ipc_send_tc, setup, teardown,
+ &hidhost_send_data_overs,
+ (sizeof(struct ipc_hdr) +
+ sizeof(struct hal_cmd_hidhost_send_data) +
+ sizeof(send_data_data)),
+ HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_HIDHOST);
+ test_generic("Data size HIDHOST Send Vardata-",
+ ipc_send_tc, setup, teardown,
+ &hidhost_send_data_unders,
+ (sizeof(struct ipc_hdr) +
+ sizeof(struct hal_cmd_hidhost_send_data) +
+ sizeof(send_data_data)),
+ HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_HIDHOST);
+
+ /* check for valid data size for PAN */
+ test_datasize_valid("PAN Enable+", HAL_SERVICE_ID_PAN,
+ HAL_OP_PAN_ENABLE,
+ sizeof(struct hal_cmd_pan_enable), 1,
+ HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_PAN);
+ test_datasize_valid("PAN Enable-", HAL_SERVICE_ID_PAN,
+ HAL_OP_PAN_ENABLE,
+ sizeof(struct hal_cmd_pan_enable), -1,
+ HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_PAN);
+ test_datasize_valid("PAN Get Role+", HAL_SERVICE_ID_PAN,
+ HAL_OP_PAN_GET_ROLE,
+ 0, 1,
+ HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_PAN);
+ test_datasize_valid("PAN Connect+", HAL_SERVICE_ID_PAN,
+ HAL_OP_PAN_CONNECT,
+ sizeof(struct hal_cmd_pan_connect), 1,
+ HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_PAN);
+ test_datasize_valid("PAN Connect-", HAL_SERVICE_ID_PAN,
+ HAL_OP_PAN_CONNECT,
+ sizeof(struct hal_cmd_pan_connect), -1,
+ HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_PAN);
+ test_datasize_valid("PAN Disconnect+", HAL_SERVICE_ID_PAN,
+ HAL_OP_PAN_DISCONNECT,
+ sizeof(struct hal_cmd_pan_disconnect), 1,
+ HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_PAN);
+ test_datasize_valid("PAN Disconnect-", HAL_SERVICE_ID_PAN,
+ HAL_OP_PAN_DISCONNECT,
+ sizeof(struct hal_cmd_pan_disconnect), -1,
+ HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_PAN);
+
+ /* check for valid data size for A2DP */
+ test_datasize_valid("A2DP Connect+", HAL_SERVICE_ID_A2DP,
+ HAL_OP_A2DP_CONNECT,
+ sizeof(struct hal_cmd_a2dp_connect), 1,
+ HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_A2DP);
+ test_datasize_valid("A2DP Connect-", HAL_SERVICE_ID_A2DP,
+ HAL_OP_A2DP_CONNECT,
+ sizeof(struct hal_cmd_a2dp_connect), -1,
+ HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_A2DP);
+ test_datasize_valid("A2DP Disconnect+", HAL_SERVICE_ID_A2DP,
+ HAL_OP_A2DP_DISCONNECT,
+ sizeof(struct hal_cmd_a2dp_disconnect), 1,
+ HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_A2DP);
+ test_datasize_valid("A2DP Disconnect-", HAL_SERVICE_ID_A2DP,
+ HAL_OP_A2DP_DISCONNECT,
+ sizeof(struct hal_cmd_a2dp_disconnect), -1,
+ HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_A2DP);
+
+ /* Check for valid data size for Handsfree Client */
+ test_datasize_valid("HF_CLIENT Connect+",
+ HAL_SERVICE_ID_HANDSFREE_CLIENT,
+ HAL_OP_HF_CLIENT_CONNECT,
+ sizeof(struct hal_cmd_hf_client_connect), 1,
+ HAL_SERVICE_ID_BLUETOOTH,
+ HAL_SERVICE_ID_HANDSFREE_CLIENT);
+ test_datasize_valid("HF_CLIENT Connect-",
+ HAL_SERVICE_ID_HANDSFREE_CLIENT,
+ HAL_OP_HF_CLIENT_CONNECT,
+ sizeof(struct hal_cmd_hf_client_connect), -1,
+ HAL_SERVICE_ID_BLUETOOTH,
+ HAL_SERVICE_ID_HANDSFREE_CLIENT);
+ test_datasize_valid("HF_CLIENT Disconnect+",
+ HAL_SERVICE_ID_HANDSFREE_CLIENT,
+ HAL_OP_HF_CLIENT_DISCONNECT,
+ sizeof(struct hal_cmd_hf_client_disconnect), 1,
+ HAL_SERVICE_ID_BLUETOOTH,
+ HAL_SERVICE_ID_HANDSFREE_CLIENT);
+ test_datasize_valid("HF_CLIENT Disconnect-",
+ HAL_SERVICE_ID_HANDSFREE_CLIENT,
+ HAL_OP_HF_CLIENT_DISCONNECT,
+ sizeof(struct hal_cmd_hf_client_disconnect), -1,
+ HAL_SERVICE_ID_BLUETOOTH,
+ HAL_SERVICE_ID_HANDSFREE_CLIENT);
+ test_datasize_valid("HF_CLIENT Connect Audio+",
+ HAL_SERVICE_ID_HANDSFREE_CLIENT,
+ HAL_OP_HF_CLIENT_CONNECT_AUDIO,
+ sizeof(struct hal_cmd_hf_client_connect_audio), 1,
+ HAL_SERVICE_ID_BLUETOOTH,
+ HAL_SERVICE_ID_HANDSFREE_CLIENT);
+ test_datasize_valid("HF_CLIENT Connect Audio-",
+ HAL_SERVICE_ID_HANDSFREE_CLIENT,
+ HAL_OP_HF_CLIENT_CONNECT_AUDIO,
+ sizeof(struct hal_cmd_hf_client_connect_audio), -1,
+ HAL_SERVICE_ID_BLUETOOTH,
+ HAL_SERVICE_ID_HANDSFREE_CLIENT);
+ test_datasize_valid("HF_CLIENT Disconnect Audio+",
+ HAL_SERVICE_ID_HANDSFREE_CLIENT,
+ HAL_OP_HF_CLIENT_DISCONNECT_AUDIO,
+ sizeof(struct hal_cmd_hf_client_disconnect_audio), 1,
+ HAL_SERVICE_ID_BLUETOOTH,
+ HAL_SERVICE_ID_HANDSFREE_CLIENT);
+ test_datasize_valid("HF_CLIENT Disconnect Audio-",
+ HAL_SERVICE_ID_HANDSFREE_CLIENT,
+ HAL_OP_HF_CLIENT_DISCONNECT_AUDIO,
+ sizeof(struct hal_cmd_hf_client_disconnect_audio), -1,
+ HAL_SERVICE_ID_BLUETOOTH,
+ HAL_SERVICE_ID_HANDSFREE_CLIENT);
+ test_datasize_valid("HF_CLIENT Start VR+",
+ HAL_SERVICE_ID_HANDSFREE_CLIENT,
+ HAL_OP_HF_CLIENT_START_VR,
+ 0, 1,
+ HAL_SERVICE_ID_BLUETOOTH,
+ HAL_SERVICE_ID_HANDSFREE_CLIENT);
+ test_datasize_valid("HF_CLIENT Start VR-",
+ HAL_SERVICE_ID_HANDSFREE_CLIENT,
+ HAL_OP_HF_CLIENT_START_VR,
+ 0, -1,
+ HAL_SERVICE_ID_BLUETOOTH,
+ HAL_SERVICE_ID_HANDSFREE_CLIENT);
+ test_datasize_valid("HF_CLIENT Stop VR+",
+ HAL_SERVICE_ID_HANDSFREE_CLIENT,
+ HAL_OP_HF_CLIENT_STOP_VR,
+ 0, 1,
+ HAL_SERVICE_ID_BLUETOOTH,
+ HAL_SERVICE_ID_HANDSFREE_CLIENT);
+ test_datasize_valid("HF_CLIENT Stop VR-",
+ HAL_SERVICE_ID_HANDSFREE_CLIENT,
+ HAL_OP_HF_CLIENT_STOP_VR,
+ 0, -1,
+ HAL_SERVICE_ID_BLUETOOTH,
+ HAL_SERVICE_ID_HANDSFREE_CLIENT);
+ test_datasize_valid("HF_CLIENT Vol Contr.+",
+ HAL_SERVICE_ID_HANDSFREE_CLIENT,
+ HAL_OP_HF_CLIENT_VOLUME_CONTROL,
+ sizeof(struct hal_cmd_hf_client_volume_control), 1,
+ HAL_SERVICE_ID_BLUETOOTH,
+ HAL_SERVICE_ID_HANDSFREE_CLIENT);
+ test_datasize_valid("HF_CLIENT Vol Contr.-",
+ HAL_SERVICE_ID_HANDSFREE_CLIENT,
+ HAL_OP_HF_CLIENT_VOLUME_CONTROL,
+ sizeof(struct hal_cmd_hf_client_volume_control), -1,
+ HAL_SERVICE_ID_BLUETOOTH,
+ HAL_SERVICE_ID_HANDSFREE_CLIENT);
+ test_generic("Data size HF_CLIENT Dial Vardata+",
+ ipc_send_tc, setup, teardown,
+ &hfp_dial_overs,
+ (sizeof(struct ipc_hdr) +
+ sizeof(struct hal_cmd_hf_client_dial) +
+ sizeof(hfp_number)),
+ HAL_SERVICE_ID_BLUETOOTH,
+ HAL_SERVICE_ID_HANDSFREE_CLIENT);
+ test_generic("Data size HF_CLIENT Dial Vardata-",
+ ipc_send_tc, setup, teardown,
+ &hfp_dial_unders,
+ (sizeof(struct ipc_hdr) +
+ sizeof(struct hal_cmd_hf_client_dial) +
+ sizeof(hfp_number)),
+ HAL_SERVICE_ID_BLUETOOTH,
+ HAL_SERVICE_ID_HANDSFREE_CLIENT);
+ test_datasize_valid("HF_CLIENT Dial Memory+",
+ HAL_SERVICE_ID_HANDSFREE_CLIENT,
+ HAL_OP_HF_CLIENT_DIAL_MEMORY,
+ sizeof(struct hal_cmd_hf_client_dial_memory), 1,
+ HAL_SERVICE_ID_BLUETOOTH,
+ HAL_SERVICE_ID_HANDSFREE_CLIENT);
+ test_datasize_valid("HF_CLIENT Dial Memory-",
+ HAL_SERVICE_ID_HANDSFREE_CLIENT,
+ HAL_OP_HF_CLIENT_DIAL_MEMORY,
+ sizeof(struct hal_cmd_hf_client_dial_memory), -1,
+ HAL_SERVICE_ID_BLUETOOTH,
+ HAL_SERVICE_ID_HANDSFREE_CLIENT);
+ test_datasize_valid("HF_CLIENT Call Action+",
+ HAL_SERVICE_ID_HANDSFREE_CLIENT,
+ HAL_OP_HF_CLIENT_CALL_ACTION,
+ sizeof(struct hal_cmd_hf_client_call_action), 1,
+ HAL_SERVICE_ID_BLUETOOTH,
+ HAL_SERVICE_ID_HANDSFREE_CLIENT);
+ test_datasize_valid("HF_CLIENT Call Action-",
+ HAL_SERVICE_ID_HANDSFREE_CLIENT,
+ HAL_OP_HF_CLIENT_CALL_ACTION,
+ sizeof(struct hal_cmd_hf_client_call_action), -1,
+ HAL_SERVICE_ID_BLUETOOTH,
+ HAL_SERVICE_ID_HANDSFREE_CLIENT);
+ test_datasize_valid("HF_CLIENT Query Current Calls+",
+ HAL_SERVICE_ID_BLUETOOTH,
+ HAL_OP_HF_CLIENT_QUERY_CURRENT_CALLS,
+ 0, 1,
+ HAL_SERVICE_ID_BLUETOOTH,
+ HAL_SERVICE_ID_HANDSFREE_CLIENT);
+ test_datasize_valid("HF_CLIENT Query Current Calls-",
+ HAL_SERVICE_ID_HANDSFREE_CLIENT,
+ HAL_OP_HF_CLIENT_QUERY_CURRENT_CALLS,
+ 0, -1,
+ HAL_SERVICE_ID_BLUETOOTH,
+ HAL_SERVICE_ID_HANDSFREE_CLIENT);
+ test_datasize_valid("HF_CLIENT Query Operator Name+",
+ HAL_SERVICE_ID_HANDSFREE_CLIENT,
+ HAL_OP_HF_CLIENT_QUERY_OPERATOR_NAME,
+ 0, 1,
+ HAL_SERVICE_ID_BLUETOOTH,
+ HAL_SERVICE_ID_HANDSFREE_CLIENT);
+ test_datasize_valid("HF_CLIENT Query Operator Name-",
+ HAL_SERVICE_ID_HANDSFREE_CLIENT,
+ HAL_OP_HF_CLIENT_QUERY_OPERATOR_NAME,
+ 0, -1,
+ HAL_SERVICE_ID_BLUETOOTH,
+ HAL_SERVICE_ID_HANDSFREE_CLIENT);
+ test_datasize_valid("HF_CLIENT Retrieve Subscrb. Info+",
+ HAL_SERVICE_ID_HANDSFREE_CLIENT,
+ HAL_OP_HF_CLIENT_RETRIEVE_SUBSCR_INFO,
+ 0, 1,
+ HAL_SERVICE_ID_BLUETOOTH,
+ HAL_SERVICE_ID_HANDSFREE_CLIENT);
+ test_datasize_valid("HF_CLIENT Retrieve Subscrb. Info-",
+ HAL_SERVICE_ID_HANDSFREE_CLIENT,
+ HAL_OP_HF_CLIENT_RETRIEVE_SUBSCR_INFO,
+ 0, -1,
+ HAL_SERVICE_ID_BLUETOOTH,
+ HAL_SERVICE_ID_HANDSFREE_CLIENT);
+ test_datasize_valid("HF_CLIENT Send DTMF+",
+ HAL_SERVICE_ID_HANDSFREE_CLIENT,
+ HAL_OP_HF_CLIENT_SEND_DTMF,
+ sizeof(struct hal_cmd_hf_client_send_dtmf), 1,
+ HAL_SERVICE_ID_BLUETOOTH,
+ HAL_SERVICE_ID_HANDSFREE_CLIENT);
+ test_datasize_valid("HF_CLIENT Send DTMF-",
+ HAL_SERVICE_ID_HANDSFREE_CLIENT,
+ HAL_OP_HF_CLIENT_SEND_DTMF,
+ sizeof(struct hal_cmd_hf_client_send_dtmf), -1,
+ HAL_SERVICE_ID_BLUETOOTH,
+ HAL_SERVICE_ID_HANDSFREE_CLIENT);
+ test_datasize_valid("HF_CLIENT Get Last Voice Tag+",
+ HAL_SERVICE_ID_HANDSFREE_CLIENT,
+ HAL_OP_HF_CLIENT_GET_LAST_VOICE_TAG_NUM,
+ 0, 1,
+ HAL_SERVICE_ID_BLUETOOTH,
+ HAL_SERVICE_ID_HANDSFREE_CLIENT);
+ test_datasize_valid("HF_CLIENT Get Last Voice Tag-",
+ HAL_SERVICE_ID_HANDSFREE_CLIENT,
+ HAL_OP_HF_CLIENT_GET_LAST_VOICE_TAG_NUM,
+ 0, -1,
+ HAL_SERVICE_ID_BLUETOOTH,
+ HAL_SERVICE_ID_HANDSFREE_CLIENT);
+
+ /* check for valid data size for MAP CLIENT */
+ test_datasize_valid("MAP CLIENT Get instances+",
+ HAL_SERVICE_ID_MAP_CLIENT,
+ HAL_OP_MAP_CLIENT_GET_INSTANCES,
+ sizeof(struct hal_cmd_map_client_get_instances),
+ 1, HAL_SERVICE_ID_BLUETOOTH,
+ HAL_SERVICE_ID_MAP_CLIENT);
+ test_datasize_valid("MAP CLIENT Get instances-",
+ HAL_SERVICE_ID_MAP_CLIENT,
+ HAL_OP_MAP_CLIENT_GET_INSTANCES,
+ sizeof(struct hal_cmd_map_client_get_instances),
+ -1, HAL_SERVICE_ID_BLUETOOTH,
+ HAL_SERVICE_ID_MAP_CLIENT);
+
+ return tester_run();
+}
diff --git a/repo/android/ipc.c b/repo/android/ipc.c
new file mode 100644
index 0000000..2e67428
--- /dev/null
+++ b/repo/android/ipc.c
@@ -0,0 +1,437 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2013-2014 Intel Corporation. All rights reserved.
+ *
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stddef.h>
+#include <errno.h>
+#include <stdint.h>
+#include <string.h>
+#include <signal.h>
+#include <stdbool.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <unistd.h>
+#include <glib.h>
+
+#include "ipc-common.h"
+#include "ipc.h"
+#include "src/log.h"
+
+struct service_handler {
+ const struct ipc_handler *handler;
+ uint8_t size;
+};
+
+struct ipc {
+ struct service_handler *services;
+ int service_max;
+
+ const char *path;
+ size_t size;
+
+ GIOChannel *cmd_io;
+ guint cmd_watch;
+
+ bool notifications;
+ GIOChannel *notif_io;
+ guint notif_watch;
+
+ ipc_disconnect_cb disconnect_cb;
+ void *disconnect_cb_data;
+};
+
+static void ipc_disconnect(struct ipc *ipc, bool in_cleanup)
+{
+ if (ipc->cmd_watch) {
+ g_source_remove(ipc->cmd_watch);
+ ipc->cmd_watch = 0;
+ }
+
+ if (ipc->cmd_io) {
+ g_io_channel_shutdown(ipc->cmd_io, TRUE, NULL);
+ g_io_channel_unref(ipc->cmd_io);
+ ipc->cmd_io = NULL;
+ }
+
+ if (ipc->notif_watch) {
+ g_source_remove(ipc->notif_watch);
+ ipc->notif_watch = 0;
+ }
+
+ if (ipc->notif_io) {
+ g_io_channel_shutdown(ipc->notif_io, TRUE, NULL);
+ g_io_channel_unref(ipc->notif_io);
+ ipc->notif_io = NULL;
+ }
+
+ if (in_cleanup)
+ return;
+
+ if (ipc->disconnect_cb)
+ ipc->disconnect_cb(ipc->disconnect_cb_data);
+}
+
+static int ipc_handle_msg(struct service_handler *handlers, size_t max_index,
+ const void *buf, ssize_t len)
+{
+ const struct ipc_hdr *msg = buf;
+ const struct ipc_handler *handler;
+
+ if (len < (ssize_t) sizeof(*msg)) {
+ DBG("message too small (%zd bytes)", len);
+ return -EBADMSG;
+ }
+
+ if (len != (ssize_t) (sizeof(*msg) + msg->len)) {
+ DBG("message malformed (%zd bytes)", len);
+ return -EBADMSG;
+ }
+
+ /* if service is valid */
+ if (msg->service_id > max_index) {
+ DBG("unknown service (0x%x)", msg->service_id);
+ return -EOPNOTSUPP;
+ }
+
+ /* if service is registered */
+ if (!handlers[msg->service_id].handler) {
+ DBG("service not registered (0x%x)", msg->service_id);
+ return -EOPNOTSUPP;
+ }
+
+ /* if opcode is valid */
+ if (msg->opcode == IPC_OP_STATUS ||
+ msg->opcode > handlers[msg->service_id].size) {
+ DBG("invalid opcode 0x%x for service 0x%x", msg->opcode,
+ msg->service_id);
+ return -EOPNOTSUPP;
+ }
+
+ /* opcode is table offset + 1 */
+ handler = &handlers[msg->service_id].handler[msg->opcode - 1];
+
+ /* if payload size is valid */
+ if ((handler->var_len && handler->data_len > msg->len) ||
+ (!handler->var_len && handler->data_len != msg->len)) {
+ DBG("invalid size for opcode 0x%x service 0x%x",
+ msg->opcode, msg->service_id);
+ return -EMSGSIZE;
+ }
+
+ handler->handler(msg->payload, msg->len);
+
+ return 0;
+}
+
+static gboolean cmd_watch_cb(GIOChannel *io, GIOCondition cond,
+ gpointer user_data)
+{
+ struct ipc *ipc = user_data;
+
+ char buf[IPC_MTU];
+ ssize_t ret;
+ int fd, err;
+
+ if (cond & (G_IO_NVAL | G_IO_ERR | G_IO_HUP)) {
+ info("IPC: command socket closed");
+
+ ipc->cmd_watch = 0;
+ goto fail;
+ }
+
+ fd = g_io_channel_unix_get_fd(io);
+
+ ret = read(fd, buf, sizeof(buf));
+ if (ret < 0) {
+ error("IPC: command read failed (%s)", strerror(errno));
+ goto fail;
+ }
+
+ err = ipc_handle_msg(ipc->services, ipc->service_max, buf, ret);
+ if (err < 0) {
+ error("IPC: failed to handle message (%s)", strerror(-err));
+ goto fail;
+ }
+
+ return TRUE;
+
+fail:
+ ipc_disconnect(ipc, false);
+
+ return FALSE;
+}
+
+static gboolean notif_watch_cb(GIOChannel *io, GIOCondition cond,
+ gpointer user_data)
+{
+ struct ipc *ipc = user_data;
+
+ info("IPC: notification socket closed");
+
+ ipc->notif_watch = 0;
+
+ ipc_disconnect(ipc, false);
+
+ return FALSE;
+}
+
+static GIOChannel *ipc_connect(const char *path, size_t size,
+ GIOFunc connect_cb, void *user_data)
+{
+ struct sockaddr_un addr;
+ GIOCondition cond;
+ GIOChannel *io;
+ int sk;
+
+ sk = socket(PF_LOCAL, SOCK_SEQPACKET, 0);
+ if (sk < 0) {
+ error("IPC: failed to create socket: %d (%s)", errno,
+ strerror(errno));
+ return NULL;
+ }
+
+ io = g_io_channel_unix_new(sk);
+
+ g_io_channel_set_close_on_unref(io, TRUE);
+ g_io_channel_set_flags(io, G_IO_FLAG_NONBLOCK, NULL);
+
+ memset(&addr, 0, sizeof(addr));
+ addr.sun_family = AF_UNIX;
+
+ memcpy(addr.sun_path, path, size);
+
+ connect(sk, (struct sockaddr *) &addr, sizeof(addr));
+
+ cond = G_IO_OUT | G_IO_ERR | G_IO_HUP | G_IO_NVAL;
+
+ g_io_add_watch(io, cond, connect_cb, user_data);
+
+ return io;
+}
+
+static gboolean notif_connect_cb(GIOChannel *io, GIOCondition cond,
+ gpointer user_data)
+{
+ struct ipc *ipc = user_data;
+
+ DBG("");
+
+ if (cond & (G_IO_NVAL | G_IO_ERR | G_IO_HUP)) {
+ error("IPC: notification socket connect failed");
+
+ ipc_disconnect(ipc, false);
+
+ return FALSE;
+ }
+
+ cond = G_IO_ERR | G_IO_HUP | G_IO_NVAL;
+
+ ipc->notif_watch = g_io_add_watch(io, cond, notif_watch_cb, ipc);
+
+ cond = G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL;
+
+ ipc->cmd_watch = g_io_add_watch(ipc->cmd_io, cond, cmd_watch_cb, ipc);
+
+ info("IPC: successfully connected (with notifications)");
+
+ return FALSE;
+}
+
+static gboolean cmd_connect_cb(GIOChannel *io, GIOCondition cond,
+ gpointer user_data)
+{
+ struct ipc *ipc = user_data;
+
+ DBG("");
+
+ if (cond & (G_IO_NVAL | G_IO_ERR | G_IO_HUP)) {
+ error("IPC: command socket connect failed");
+ ipc_disconnect(ipc, false);
+
+ return FALSE;
+ }
+
+ if (ipc->notifications) {
+ ipc->notif_io = ipc_connect(ipc->path, ipc->size,
+ notif_connect_cb, ipc);
+ if (!ipc->notif_io)
+ ipc_disconnect(ipc, false);
+
+ return FALSE;
+ }
+
+ cond = G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL;
+
+ ipc->cmd_watch = g_io_add_watch(ipc->cmd_io, cond, cmd_watch_cb, ipc);
+
+ info("IPC: successfully connected (without notifications)");
+
+ return FALSE;
+}
+
+struct ipc *ipc_init(const char *path, size_t size, int max_service_id,
+ bool notifications,
+ ipc_disconnect_cb cb, void *cb_data)
+{
+ struct ipc *ipc;
+
+ ipc = g_new0(struct ipc, 1);
+
+ ipc->services = g_new0(struct service_handler, max_service_id + 1);
+ ipc->service_max = max_service_id;
+
+ ipc->path = path;
+ ipc->size = size;
+
+ ipc->notifications = notifications;
+
+ ipc->cmd_io = ipc_connect(path, size, cmd_connect_cb, ipc);
+ if (!ipc->cmd_io) {
+ g_free(ipc->services);
+ g_free(ipc);
+ return NULL;
+ }
+
+ ipc->disconnect_cb = cb;
+ ipc->disconnect_cb_data = cb_data;
+
+ return ipc;
+}
+
+void ipc_cleanup(struct ipc *ipc)
+{
+ ipc_disconnect(ipc, true);
+
+ g_free(ipc->services);
+ g_free(ipc);
+}
+
+static void ipc_send(int sk, uint8_t service_id, uint8_t opcode, uint16_t len,
+ void *param, int fd)
+{
+ struct msghdr msg;
+ struct iovec iv[2];
+ struct ipc_hdr m;
+ char cmsgbuf[CMSG_SPACE(sizeof(int))];
+ struct cmsghdr *cmsg;
+
+ memset(&msg, 0, sizeof(msg));
+ memset(&m, 0, sizeof(m));
+ memset(cmsgbuf, 0, sizeof(cmsgbuf));
+
+ m.service_id = service_id;
+ m.opcode = opcode;
+ m.len = len;
+
+ iv[0].iov_base = &m;
+ iv[0].iov_len = sizeof(m);
+
+ iv[1].iov_base = param;
+ iv[1].iov_len = len;
+
+ msg.msg_iov = iv;
+ msg.msg_iovlen = 2;
+
+ if (fd >= 0) {
+ msg.msg_control = cmsgbuf;
+ msg.msg_controllen = sizeof(cmsgbuf);
+
+ cmsg = CMSG_FIRSTHDR(&msg);
+ cmsg->cmsg_level = SOL_SOCKET;
+ cmsg->cmsg_type = SCM_RIGHTS;
+ cmsg->cmsg_len = CMSG_LEN(sizeof(int));
+
+ /* Initialize the payload */
+ memcpy(CMSG_DATA(cmsg), &fd, sizeof(int));
+ }
+
+ if (sendmsg(sk, &msg, 0) < 0) {
+ error("IPC send failed :%s", strerror(errno));
+
+ /* TODO disconnect IPC here when this function becomes static */
+ raise(SIGTERM);
+ }
+}
+
+void ipc_send_rsp(struct ipc *ipc, uint8_t service_id, uint8_t opcode,
+ uint8_t status)
+{
+ struct ipc_status s;
+ int sk;
+
+ sk = g_io_channel_unix_get_fd(ipc->cmd_io);
+
+ if (status == IPC_STATUS_SUCCESS) {
+ ipc_send(sk, service_id, opcode, 0, NULL, -1);
+ return;
+ }
+
+ s.code = status;
+
+ ipc_send(sk, service_id, IPC_OP_STATUS, sizeof(s), &s, -1);
+}
+
+void ipc_send_rsp_full(struct ipc *ipc, uint8_t service_id, uint8_t opcode,
+ uint16_t len, void *param, int fd)
+{
+ ipc_send(g_io_channel_unix_get_fd(ipc->cmd_io), service_id, opcode, len,
+ param, fd);
+}
+
+void ipc_send_notif(struct ipc *ipc, uint8_t service_id, uint8_t opcode,
+ uint16_t len, void *param)
+{
+ return ipc_send_notif_with_fd(ipc, service_id, opcode, len, param, -1);
+}
+
+void ipc_send_notif_with_fd(struct ipc *ipc, uint8_t service_id, uint8_t opcode,
+ uint16_t len, void *param, int fd)
+{
+ if (!ipc || !ipc->notif_io)
+ return;
+
+ ipc_send(g_io_channel_unix_get_fd(ipc->notif_io), service_id, opcode,
+ len, param, fd);
+}
+
+void ipc_register(struct ipc *ipc, uint8_t service,
+ const struct ipc_handler *handlers, uint8_t size)
+{
+ if (service > ipc->service_max)
+ return;
+
+ ipc->services[service].handler = handlers;
+ ipc->services[service].size = size;
+}
+
+void ipc_unregister(struct ipc *ipc, uint8_t service)
+{
+ if (service > ipc->service_max)
+ return;
+
+ ipc->services[service].handler = NULL;
+ ipc->services[service].size = 0;
+}
diff --git a/repo/android/ipc.h b/repo/android/ipc.h
new file mode 100644
index 0000000..fd2b985
--- /dev/null
+++ b/repo/android/ipc.h
@@ -0,0 +1,50 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2013-2014 Intel Corporation. All rights reserved.
+ *
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+struct ipc_handler {
+ void (*handler) (const void *buf, uint16_t len);
+ bool var_len;
+ size_t data_len;
+};
+
+struct ipc;
+
+typedef void (*ipc_disconnect_cb) (void *data);
+
+struct ipc *ipc_init(const char *path, size_t size, int max_service_id,
+ bool notifications,
+ ipc_disconnect_cb cb, void *cb_data);
+void ipc_cleanup(struct ipc *ipc);
+
+void ipc_send_rsp(struct ipc *ipc, uint8_t service_id, uint8_t opcode,
+ uint8_t status);
+void ipc_send_rsp_full(struct ipc *ipc, uint8_t service_id, uint8_t opcode,
+ uint16_t len, void *param, int fd);
+void ipc_send_notif(struct ipc *ipc, uint8_t service_id, uint8_t opcode,
+ uint16_t len, void *param);
+void ipc_send_notif_with_fd(struct ipc *ipc, uint8_t service_id, uint8_t opcode,
+ uint16_t len, void *param, int fd);
+
+void ipc_register(struct ipc *ipc, uint8_t service,
+ const struct ipc_handler *handlers, uint8_t size);
+void ipc_unregister(struct ipc *ipc, uint8_t service);
diff --git a/repo/android/log.c b/repo/android/log.c
new file mode 100644
index 0000000..35917c6
--- /dev/null
+++ b/repo/android/log.c
@@ -0,0 +1,216 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2014 Intel Corporation. All rights reserved.
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <fcntl.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <stdbool.h>
+#include <time.h>
+#include <sys/uio.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+
+#include "src/log.h"
+
+#define LOG_TAG "bluetoothd"
+
+#define LOG_DEBUG 3
+#define LOG_INFO 4
+#define LOG_WARN 5
+#define LOG_ERR 6
+
+#define LOG_ID_SYSTEM 3
+
+struct logd_header {
+ uint8_t id;
+ uint16_t pid; /* Android logd expects only 2 bytes for PID */
+ uint32_t sec;
+ uint32_t nsec;
+} __attribute__ ((packed));
+
+static int log_fd = -1;
+static bool legacy_log = false;
+
+static void android_log(unsigned char level, const char *fmt, va_list ap)
+{
+ struct logd_header header;
+ struct iovec vec[4];
+ int cnt = 0;
+ char *msg;
+ static pid_t pid = 0;
+
+ if (log_fd < 0)
+ return;
+
+ /* no need to call getpid all the time since we don't fork */
+ if (!pid)
+ pid = getpid();
+
+ if (vasprintf(&msg, fmt, ap) < 0)
+ return;
+
+ if (!legacy_log) {
+ struct timespec ts;
+
+ clock_gettime(CLOCK_REALTIME, &ts);
+
+ header.id = LOG_ID_SYSTEM;
+ header.pid = pid;
+ header.sec = ts.tv_sec;
+ header.nsec = ts.tv_nsec;
+
+ vec[0].iov_base = &header;
+ vec[0].iov_len = sizeof(header);
+
+ cnt += 1;
+ }
+
+ vec[cnt + 0].iov_base = &level;
+ vec[cnt + 0].iov_len = sizeof(level);
+ vec[cnt + 1].iov_base = LOG_TAG;
+ vec[cnt + 1].iov_len = sizeof(LOG_TAG);
+ vec[cnt + 2].iov_base = msg;
+ vec[cnt + 2].iov_len = strlen(msg) + 1;
+
+ cnt += 3;
+
+ writev(log_fd, vec, cnt);
+
+ free(msg);
+}
+
+void info(const char *format, ...)
+{
+ va_list ap;
+
+ va_start(ap, format);
+
+ android_log(LOG_INFO, format, ap);
+
+ va_end(ap);
+}
+
+void warn(const char *format, ...)
+{
+ va_list ap;
+
+ va_start(ap, format);
+
+ android_log(LOG_WARN, format, ap);
+
+ va_end(ap);
+}
+
+void error(const char *format, ...)
+{
+ va_list ap;
+
+ va_start(ap, format);
+
+ android_log(LOG_ERR, format, ap);
+
+ va_end(ap);
+}
+
+void btd_debug(uint16_t index, const char *format, ...)
+{
+ va_list ap;
+
+ va_start(ap, format);
+
+ android_log(LOG_DEBUG, format, ap);
+
+ va_end(ap);
+}
+
+static bool init_legacy_log(void)
+{
+ log_fd = open("/dev/log/system", O_WRONLY);
+ if (log_fd < 0)
+ return false;
+
+ legacy_log = true;
+
+ return true;
+}
+
+static bool init_logd(void)
+{
+ struct sockaddr_un addr;
+
+ log_fd = socket(PF_UNIX, SOCK_DGRAM | SOCK_CLOEXEC, 0);
+ if (log_fd < 0)
+ return false;
+
+ if (fcntl(log_fd, F_SETFL, O_NONBLOCK) < 0)
+ goto failed;
+
+ memset(&addr, 0, sizeof(addr));
+ addr.sun_family = AF_UNIX;
+ strcpy(addr.sun_path, "/dev/socket/logdw");
+
+ if (connect(log_fd, (struct sockaddr *)&addr, sizeof(addr)) < 0)
+ goto failed;
+
+ return true;
+
+failed:
+ close(log_fd);
+ log_fd = -1;
+
+ return false;
+}
+
+extern struct btd_debug_desc __start___debug[];
+extern struct btd_debug_desc __stop___debug[];
+
+void __btd_log_init(const char *debug, int detach)
+{
+ if (!init_logd() && !init_legacy_log())
+ return;
+
+ if (debug) {
+ struct btd_debug_desc *desc;
+
+ for (desc = __start___debug; desc < __stop___debug; desc++)
+ desc->flags |= BTD_DEBUG_FLAG_PRINT;
+ }
+
+ info("Bluetooth daemon %s", VERSION);
+}
+
+void __btd_log_cleanup(void)
+{
+ if (log_fd < 0)
+ return;
+
+ close(log_fd);
+ log_fd = -1;
+}
diff --git a/repo/android/main.c b/repo/android/main.c
new file mode 100644
index 0000000..03c8760
--- /dev/null
+++ b/repo/android/main.c
@@ -0,0 +1,805 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2013-2014 Intel Corporation. All rights reserved.
+ *
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdbool.h>
+#include <signal.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+
+#include <sys/signalfd.h>
+#if defined(ANDROID)
+#include <sys/prctl.h>
+#include <sys/capability.h>
+#endif
+
+#include <glib.h>
+
+#include "lib/bluetooth.h"
+#include "lib/sdp.h"
+
+#include "src/log.h"
+#include "src/sdpd.h"
+#include "src/shared/util.h"
+
+#include "ipc-common.h"
+#include "ipc.h"
+#include "bluetooth.h"
+#include "socket.h"
+#include "hidhost.h"
+#include "hal-msg.h"
+#include "a2dp.h"
+#include "pan.h"
+#include "avrcp.h"
+#include "handsfree.h"
+#include "gatt.h"
+#include "health.h"
+#include "handsfree-client.h"
+#include "map-client.h"
+#include "utils.h"
+
+#define DEFAULT_VENDOR "BlueZ"
+#define DEFAULT_MODEL "BlueZ for Android"
+#define DEFAULT_NAME "BlueZ for Android"
+
+#define STARTUP_GRACE_SECONDS 5
+#define SHUTDOWN_GRACE_SECONDS 5
+
+static char *config_vendor = NULL;
+static char *config_model = NULL;
+static char *config_name = NULL;
+static char *config_serial = NULL;
+static char *config_fw_rev = NULL;
+static char *config_hw_rev = NULL;
+static uint64_t config_system_id = 0;
+static uint16_t config_pnp_source = 0x0002; /* USB */
+static uint16_t config_pnp_vendor = 0x1d6b; /* Linux Foundation */
+static uint16_t config_pnp_product = 0x0247; /* BlueZ for Android */
+static uint16_t config_pnp_version = 0x0000;
+
+static guint quit_timeout = 0;
+
+static bdaddr_t adapter_bdaddr;
+
+static GMainLoop *event_loop;
+
+static struct ipc *hal_ipc = NULL;
+
+static bool services[HAL_SERVICE_ID_MAX + 1] = { false };
+
+const char *bt_config_get_vendor(void)
+{
+ if (config_vendor)
+ return config_vendor;
+
+ return DEFAULT_VENDOR;
+}
+
+const char *bt_config_get_name(void)
+{
+ if (config_name)
+ return config_name;
+
+ return DEFAULT_NAME;
+}
+
+const char *bt_config_get_model(void)
+{
+ if (config_model)
+ return config_model;
+
+ return DEFAULT_MODEL;
+}
+
+const char *bt_config_get_serial(void)
+{
+ return config_serial;
+}
+
+const char *bt_config_get_fw_rev(void)
+{
+ return config_fw_rev;
+}
+
+const char *bt_config_get_hw_rev(void)
+{
+ return config_hw_rev;
+}
+
+uint64_t bt_config_get_system_id(void)
+{
+ return config_system_id;
+}
+
+uint16_t bt_config_get_pnp_source(void)
+{
+ return config_pnp_source;
+}
+
+uint16_t bt_config_get_pnp_vendor(void)
+{
+ return config_pnp_vendor;
+}
+
+uint16_t bt_config_get_pnp_product(void)
+{
+ return config_pnp_product;
+}
+
+uint16_t bt_config_get_pnp_version(void)
+{
+ return config_pnp_version;
+}
+
+static void service_register(const void *buf, uint16_t len)
+{
+ const struct hal_cmd_register_module *m = buf;
+ uint8_t status;
+
+ if (m->service_id > HAL_SERVICE_ID_MAX || services[m->service_id]) {
+ status = HAL_STATUS_FAILED;
+ goto failed;
+ }
+
+ switch (m->service_id) {
+ case HAL_SERVICE_ID_BLUETOOTH:
+ if (!bt_bluetooth_register(hal_ipc, m->mode)) {
+ status = HAL_STATUS_FAILED;
+ goto failed;
+ }
+
+ break;
+ case HAL_SERVICE_ID_SOCKET:
+ bt_socket_register(hal_ipc, &adapter_bdaddr, m->mode);
+
+ break;
+ case HAL_SERVICE_ID_HIDHOST:
+ if (!bt_hid_register(hal_ipc, &adapter_bdaddr, m->mode)) {
+ status = HAL_STATUS_FAILED;
+ goto failed;
+ }
+
+ break;
+ case HAL_SERVICE_ID_A2DP:
+ if (!bt_a2dp_register(hal_ipc, &adapter_bdaddr, m->mode)) {
+ status = HAL_STATUS_FAILED;
+ goto failed;
+ }
+
+ break;
+ case HAL_SERVICE_ID_PAN:
+ if (!bt_pan_register(hal_ipc, &adapter_bdaddr, m->mode)) {
+ status = HAL_STATUS_FAILED;
+ goto failed;
+ }
+
+ break;
+ case HAL_SERVICE_ID_AVRCP:
+ if (!bt_avrcp_register(hal_ipc, &adapter_bdaddr, m->mode)) {
+ status = HAL_STATUS_FAILED;
+ goto failed;
+ }
+
+ break;
+ case HAL_SERVICE_ID_HANDSFREE:
+ if (!bt_handsfree_register(hal_ipc, &adapter_bdaddr, m->mode,
+ m->max_clients)) {
+ status = HAL_STATUS_FAILED;
+ goto failed;
+ }
+
+ break;
+ case HAL_SERVICE_ID_GATT:
+ if (!bt_gatt_register(hal_ipc, &adapter_bdaddr)) {
+ status = HAL_STATUS_FAILED;
+ goto failed;
+ }
+
+ break;
+ case HAL_SERVICE_ID_HEALTH:
+ if (!bt_health_register(hal_ipc, &adapter_bdaddr, m->mode)) {
+ status = HAL_STATUS_FAILED;
+ goto failed;
+ }
+
+ break;
+ case HAL_SERVICE_ID_HANDSFREE_CLIENT:
+ if (!bt_hf_client_register(hal_ipc, &adapter_bdaddr)) {
+ status = HAL_STATUS_FAILED;
+ goto failed;
+ }
+
+ break;
+ case HAL_SERVICE_ID_MAP_CLIENT:
+ if (!bt_map_client_register(hal_ipc, &adapter_bdaddr,
+ m->mode)) {
+ status = HAL_STATUS_FAILED;
+ goto failed;
+ }
+
+ break;
+ default:
+ DBG("service %u not supported", m->service_id);
+ status = HAL_STATUS_FAILED;
+ goto failed;
+ }
+
+ services[m->service_id] = true;
+
+ status = HAL_STATUS_SUCCESS;
+
+ info("Service ID=%u registered", m->service_id);
+
+failed:
+ ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_CORE, HAL_OP_REGISTER_MODULE,
+ status);
+}
+
+static bool unregister_service(uint8_t id)
+{
+ if (id > HAL_SERVICE_ID_MAX || !services[id])
+ return false;
+
+ switch (id) {
+ case HAL_SERVICE_ID_BLUETOOTH:
+ bt_bluetooth_unregister();
+ break;
+ case HAL_SERVICE_ID_SOCKET:
+ bt_socket_unregister();
+ break;
+ case HAL_SERVICE_ID_HIDHOST:
+ bt_hid_unregister();
+ break;
+ case HAL_SERVICE_ID_A2DP:
+ bt_a2dp_unregister();
+ break;
+ case HAL_SERVICE_ID_PAN:
+ bt_pan_unregister();
+ break;
+ case HAL_SERVICE_ID_AVRCP:
+ bt_avrcp_unregister();
+ break;
+ case HAL_SERVICE_ID_HANDSFREE:
+ bt_handsfree_unregister();
+ break;
+ case HAL_SERVICE_ID_GATT:
+ bt_gatt_unregister();
+ break;
+ case HAL_SERVICE_ID_HEALTH:
+ bt_health_unregister();
+ break;
+ case HAL_SERVICE_ID_HANDSFREE_CLIENT:
+ bt_hf_client_unregister();
+ break;
+ case HAL_SERVICE_ID_MAP_CLIENT:
+ bt_map_client_unregister();
+ break;
+ default:
+ DBG("service %u not supported", id);
+ return false;
+ }
+
+ services[id] = false;
+
+ return true;
+}
+
+static void service_unregister(const void *buf, uint16_t len)
+{
+ const struct hal_cmd_unregister_module *m = buf;
+ uint8_t status;
+
+ if (!unregister_service(m->service_id)) {
+ status = HAL_STATUS_FAILED;
+ goto failed;
+ }
+
+ status = HAL_STATUS_SUCCESS;
+
+ info("Service ID=%u unregistered", m->service_id);
+
+failed:
+ ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_CORE, HAL_OP_UNREGISTER_MODULE,
+ status);
+}
+
+static char *get_prop(char *prop, uint16_t len, const uint8_t *val)
+{
+ /* TODO should fail if set more than once ? */
+ free(prop);
+
+ prop = malloc0(len);
+ if (!prop)
+ return NULL;
+
+ memcpy(prop, val, len);
+ prop[len - 1] = '\0';
+
+ return prop;
+}
+
+static void parse_pnp_id(uint16_t len, const uint8_t *val)
+{
+ int result;
+ uint16_t vendor, product, version , source;
+ char *pnp;
+
+ /* version is optional */
+ version = config_pnp_version;
+
+ pnp = get_prop(NULL, len, val);
+ if (!pnp)
+ return;
+
+ DBG("pnp_id %s", pnp);
+
+ result = sscanf(pnp, "bluetooth:%4hx:%4hx:%4hx",
+ &vendor, &product, &version);
+ if (result != EOF && result >= 2) {
+ source = 0x0001;
+ goto done;
+ }
+
+ result = sscanf(pnp, "usb:%4hx:%4hx:%4hx", &vendor, &product, &version);
+ if (result != EOF && result >= 2) {
+ source = 0x0002;
+ goto done;
+ }
+
+ free(pnp);
+ return;
+done:
+ free(pnp);
+
+ config_pnp_source = source;
+ config_pnp_vendor = vendor;
+ config_pnp_product = product;
+ config_pnp_version = version;
+}
+
+static void parse_system_id(uint16_t len, const uint8_t *val)
+{
+ uint64_t res;
+ char *id;
+
+ id = get_prop(NULL, len, val);
+ if (!id)
+ return;
+
+ res = strtoull(id, NULL, 16);
+ if (res == ULLONG_MAX && errno == ERANGE)
+ goto done;
+
+ config_system_id = res;
+done:
+ free(id);
+}
+
+static void configuration(const void *buf, uint16_t len)
+{
+ const struct hal_cmd_configuration *cmd = buf;
+ const struct hal_config_prop *prop;
+ unsigned int i;
+
+ buf += sizeof(*cmd);
+ len -= sizeof(*cmd);
+
+ for (i = 0; i < cmd->num; i++) {
+ prop = buf;
+
+ if (len < sizeof(*prop) || len < sizeof(*prop) + prop->len) {
+ error("Invalid configuration command, terminating");
+ raise(SIGTERM);
+ return;
+ }
+
+ switch (prop->type) {
+ case HAL_CONFIG_VENDOR:
+ config_vendor = get_prop(config_vendor, prop->len,
+ prop->val);
+ DBG("vendor %s", config_vendor);
+ break;
+ case HAL_CONFIG_NAME:
+ config_name = get_prop(config_name, prop->len,
+ prop->val);
+ DBG("name %s", config_name);
+ break;
+ case HAL_CONFIG_MODEL:
+ config_model = get_prop(config_model, prop->len,
+ prop->val);
+ DBG("model %s", config_model);
+ break;
+ case HAL_CONFIG_SERIAL_NUMBER:
+ config_serial = get_prop(config_serial, prop->len,
+ prop->val);
+ DBG("serial %s", config_serial);
+ break;
+ case HAL_CONFIG_SYSTEM_ID:
+ parse_system_id(prop->len, prop->val);
+ break;
+ case HAL_CONFIG_PNP_ID:
+ parse_pnp_id(prop->len, prop->val);
+ break;
+ case HAL_CONFIG_FW_REV:
+ config_fw_rev = get_prop(config_fw_rev, prop->len,
+ prop->val);
+ DBG("fw_rev %s", config_fw_rev);
+ break;
+ case HAL_CONFIG_HW_REV:
+ config_hw_rev = get_prop(config_hw_rev, prop->len,
+ prop->val);
+ DBG("hw_rev %s", config_hw_rev);
+ break;
+ default:
+ error("Invalid configuration option (%u), terminating",
+ prop->type);
+ raise(SIGTERM);
+ return;
+ }
+
+ buf += sizeof(*prop) + prop->len;
+ len -= sizeof(*prop) + prop->len;
+ }
+
+ ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_CORE, HAL_OP_CONFIGURATION,
+ HAL_STATUS_SUCCESS);
+}
+
+static const struct ipc_handler cmd_handlers[] = {
+ /* HAL_OP_REGISTER_MODULE */
+ { service_register, false, sizeof(struct hal_cmd_register_module) },
+ /* HAL_OP_UNREGISTER_MODULE */
+ { service_unregister, false, sizeof(struct hal_cmd_unregister_module) },
+ /* HAL_OP_CONFIGURATION */
+ { configuration, true, sizeof(struct hal_cmd_configuration) },
+};
+
+static void bluetooth_stopped(void)
+{
+ g_main_loop_quit(event_loop);
+}
+
+static gboolean quit_eventloop(gpointer user_data)
+{
+ g_main_loop_quit(event_loop);
+
+ quit_timeout = 0;
+
+ return FALSE;
+}
+
+static void stop_bluetooth(void)
+{
+ static bool __stop = false;
+
+ if (__stop)
+ return;
+
+ __stop = true;
+
+ if (!bt_bluetooth_stop(bluetooth_stopped)) {
+ g_main_loop_quit(event_loop);
+ return;
+ }
+
+ quit_timeout = g_timeout_add_seconds(SHUTDOWN_GRACE_SECONDS,
+ quit_eventloop, NULL);
+}
+
+static void ipc_disconnected(void *data)
+{
+ stop_bluetooth();
+}
+
+static void adapter_ready(int err, const bdaddr_t *addr)
+{
+ if (err < 0) {
+ error("Adapter initialization failed: %s", strerror(-err));
+ exit(EXIT_FAILURE);
+ }
+
+ bacpy(&adapter_bdaddr, addr);
+
+ if (quit_timeout > 0) {
+ g_source_remove(quit_timeout);
+ quit_timeout = 0;
+ }
+
+ info("Adapter initialized");
+
+ hal_ipc = ipc_init(BLUEZ_HAL_SK_PATH, sizeof(BLUEZ_HAL_SK_PATH),
+ HAL_SERVICE_ID_MAX, true,
+ ipc_disconnected, NULL);
+ if (!hal_ipc) {
+ error("Failed to initialize IPC");
+ exit(EXIT_FAILURE);
+ }
+
+ ipc_register(hal_ipc, HAL_SERVICE_ID_CORE, cmd_handlers,
+ G_N_ELEMENTS(cmd_handlers));
+}
+
+static gboolean signal_handler(GIOChannel *channel, GIOCondition cond,
+ gpointer user_data)
+{
+ static bool __terminated = false;
+ struct signalfd_siginfo si;
+ ssize_t result;
+ int fd;
+
+ if (cond & (G_IO_NVAL | G_IO_ERR | G_IO_HUP))
+ return FALSE;
+
+ fd = g_io_channel_unix_get_fd(channel);
+
+ result = read(fd, &si, sizeof(si));
+ if (result != sizeof(si))
+ return FALSE;
+
+ switch (si.ssi_signo) {
+ case SIGINT:
+ case SIGTERM:
+ if (!__terminated) {
+ info("Terminating");
+ stop_bluetooth();
+ }
+
+ __terminated = true;
+ break;
+ }
+
+ return TRUE;
+}
+
+static guint setup_signalfd(void)
+{
+ GIOChannel *channel;
+ guint source;
+ sigset_t mask;
+ int fd;
+
+ sigemptyset(&mask);
+ sigaddset(&mask, SIGINT);
+ sigaddset(&mask, SIGTERM);
+
+ if (sigprocmask(SIG_BLOCK, &mask, NULL) < 0) {
+ perror("Failed to set signal mask");
+ return 0;
+ }
+
+ fd = signalfd(-1, &mask, 0);
+ if (fd < 0) {
+ perror("Failed to create signal descriptor");
+ return 0;
+ }
+
+ channel = g_io_channel_unix_new(fd);
+
+ g_io_channel_set_close_on_unref(channel, TRUE);
+ g_io_channel_set_encoding(channel, NULL, NULL);
+ g_io_channel_set_buffered(channel, FALSE);
+
+ source = g_io_add_watch(channel,
+ G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL,
+ signal_handler, NULL);
+
+ g_io_channel_unref(channel);
+
+ return source;
+}
+
+static gboolean option_version = FALSE;
+static gint option_index = -1;
+static gboolean option_dbg = FALSE;
+static gboolean option_mgmt_dbg = FALSE;
+
+static GOptionEntry options[] = {
+ { "version", 'v', 0, G_OPTION_ARG_NONE, &option_version,
+ "Show version information and exit", NULL },
+ { "index", 'i', 0, G_OPTION_ARG_INT, &option_index,
+ "Use specified controller", "INDEX"},
+ { "debug", 'd', 0, G_OPTION_ARG_NONE, &option_dbg,
+ "Enable debug logs", NULL},
+ { "mgmt-debug", 0, 0, G_OPTION_ARG_NONE, &option_mgmt_dbg,
+ "Enable mgmt debug logs", NULL},
+
+ { NULL }
+};
+
+static void cleanup_services(void)
+{
+ int i;
+
+ DBG("");
+
+ for (i = HAL_SERVICE_ID_MAX; i > HAL_SERVICE_ID_CORE; i--)
+ unregister_service(i);
+}
+
+static bool set_capabilities(void)
+{
+#if defined(ANDROID)
+ struct __user_cap_header_struct header;
+ struct __user_cap_data_struct cap;
+
+ header.version = _LINUX_CAPABILITY_VERSION;
+ header.pid = 0;
+
+ /*
+ * CAP_NET_ADMIN: Allow use of MGMT interface
+ * CAP_NET_BIND_SERVICE: Allow use of privileged PSM
+ * CAP_NET_RAW: Allow use of bnep ioctl calls
+ */
+ cap.effective = cap.permitted =
+ CAP_TO_MASK(CAP_NET_RAW) |
+ CAP_TO_MASK(CAP_NET_ADMIN) |
+ CAP_TO_MASK(CAP_NET_BIND_SERVICE);
+ cap.inheritable = 0;
+
+ /* don't clear capabilities when dropping root */
+ if (prctl(PR_SET_KEEPCAPS, 1) < 0) {
+ error("%s: prctl(): %s", __func__, strerror(errno));
+ return false;
+ }
+
+ /* Android bluetooth user UID=1002 */
+ if (setuid(1002) < 0) {
+ error("%s: setuid(): %s", __func__, strerror(errno));
+ return false;
+ }
+
+ /* TODO: Move to cap_set_proc once bionic support it */
+ if (capset(&header, &cap) < 0) {
+ error("%s: capset(): %s", __func__, strerror(errno));
+ return false;
+ }
+
+ /* TODO: Move to cap_get_proc once bionic support it */
+ if (capget(&header, &cap) < 0) {
+ error("%s: capget(): %s", __func__, strerror(errno));
+ return false;
+ }
+
+ DBG("Caps: eff: 0x%x, perm: 0x%x, inh: 0x%x", cap.effective,
+ cap.permitted, cap.inheritable);
+
+#endif
+ return true;
+}
+
+static void set_version(void)
+{
+ uint8_t major, minor;
+
+ if (sscanf(VERSION, "%hhu.%hhu", &major, &minor) != 2)
+ return;
+
+ config_pnp_version = major << 8 | minor;
+}
+
+int main(int argc, char *argv[])
+{
+ GOptionContext *context;
+ GError *err = NULL;
+ guint signal;
+
+ set_version();
+
+ context = g_option_context_new(NULL);
+ g_option_context_add_main_entries(context, options, NULL);
+
+ if (g_option_context_parse(context, &argc, &argv, &err) == FALSE) {
+ if (err != NULL) {
+ g_printerr("%s\n", err->message);
+ g_error_free(err);
+ } else
+ g_printerr("An unknown error occurred\n");
+
+ exit(EXIT_FAILURE);
+ }
+
+ g_option_context_free(context);
+
+ if (option_version == TRUE) {
+ printf("%s\n", VERSION);
+ exit(EXIT_SUCCESS);
+ }
+
+ signal = setup_signalfd();
+ if (!signal)
+ return EXIT_FAILURE;
+
+ if (option_dbg || option_mgmt_dbg)
+ __btd_log_init("*", 0);
+ else
+ __btd_log_init(NULL, 0);
+
+ if (!set_capabilities()) {
+ __btd_log_cleanup();
+ g_source_remove(signal);
+ return EXIT_FAILURE;
+ }
+
+ quit_timeout = g_timeout_add_seconds(STARTUP_GRACE_SECONDS,
+ quit_eventloop, NULL);
+ if (quit_timeout == 0) {
+ error("Failed to init startup timeout");
+ __btd_log_cleanup();
+ g_source_remove(signal);
+ return EXIT_FAILURE;
+ }
+
+ if (!bt_bluetooth_start(option_index, option_mgmt_dbg, adapter_ready)) {
+ __btd_log_cleanup();
+ g_source_remove(quit_timeout);
+ g_source_remove(signal);
+ return EXIT_FAILURE;
+ }
+
+ /* Use params: mtu = 0, flags = 0 */
+ start_sdp_server(0, 0);
+
+ DBG("Entering main loop");
+
+ event_loop = g_main_loop_new(NULL, FALSE);
+
+ g_main_loop_run(event_loop);
+
+ g_source_remove(signal);
+
+ if (quit_timeout > 0)
+ g_source_remove(quit_timeout);
+
+ cleanup_services();
+
+ stop_sdp_server();
+ bt_bluetooth_cleanup();
+ g_main_loop_unref(event_loop);
+
+ /* If no adapter was initialized, hal_ipc is NULL */
+ if (hal_ipc) {
+ ipc_unregister(hal_ipc, HAL_SERVICE_ID_CORE);
+ ipc_cleanup(hal_ipc);
+ }
+
+ info("Exit");
+
+ __btd_log_cleanup();
+
+ free(config_vendor);
+ free(config_model);
+ free(config_name);
+ free(config_serial);
+ free(config_fw_rev);
+ free(config_hw_rev);
+
+ return EXIT_SUCCESS;
+}
diff --git a/repo/android/map-client.c b/repo/android/map-client.c
new file mode 100644
index 0000000..e3ad148
--- /dev/null
+++ b/repo/android/map-client.c
@@ -0,0 +1,203 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2014 Intel Corporation. All rights reserved.
+ *
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdbool.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <glib.h>
+
+#include "lib/sdp.h"
+#include "lib/sdp_lib.h"
+#include "src/sdp-client.h"
+
+#include "ipc.h"
+#include "lib/bluetooth.h"
+#include "map-client.h"
+#include "src/log.h"
+#include "hal-msg.h"
+#include "ipc-common.h"
+#include "utils.h"
+#include "src/shared/util.h"
+
+static struct ipc *hal_ipc = NULL;
+static bdaddr_t adapter_addr;
+
+static int fill_mce_inst(void *buf, int32_t id, int32_t scn, int32_t msg_type,
+ const void *name, uint8_t name_len)
+{
+ struct hal_map_client_mas_instance *inst = buf;
+
+ inst->id = id;
+ inst->scn = scn;
+ inst->msg_types = msg_type;
+ inst->name_len = name_len;
+
+ if (name_len)
+ memcpy(inst->name, name, name_len);
+
+ return sizeof(*inst) + name_len;
+}
+
+static void map_client_sdp_search_cb(sdp_list_t *recs, int err, gpointer data)
+{
+ uint8_t buf[IPC_MTU];
+ struct hal_ev_map_client_remote_mas_instances *ev = (void *) buf;
+ bdaddr_t *dst = data;
+ sdp_list_t *list, *protos;
+ uint8_t status;
+ int32_t id, scn, msg_type, name_len, num_instances = 0;
+ char *name;
+ size_t size;
+
+ size = sizeof(*ev);
+ bdaddr2android(dst, &ev->bdaddr);
+
+ if (err < 0) {
+ error("mce: Unable to get SDP record: %s", strerror(-err));
+ status = HAL_STATUS_FAILED;
+ goto fail;
+ }
+
+ for (list = recs; list != NULL; list = list->next) {
+ sdp_record_t *rec = list->data;
+ sdp_data_t *data;
+
+ data = sdp_data_get(rec, SDP_ATTR_MAS_INSTANCE_ID);
+ if (!data) {
+ error("mce: cannot get mas instance id");
+ continue;
+ }
+
+ id = data->val.uint8;
+
+ data = sdp_data_get(rec, SDP_ATTR_SVCNAME_PRIMARY);
+ if (!data) {
+ error("mce: cannot get mas instance name");
+ continue;
+ }
+
+ name = data->val.str;
+ name_len = data->unitSize;
+
+ data = sdp_data_get(rec, SDP_ATTR_SUPPORTED_MESSAGE_TYPES);
+ if (!data) {
+ error("mce: cannot get mas instance msg type");
+ continue;
+ }
+
+ msg_type = data->val.uint8;
+
+ if (sdp_get_access_protos(rec, &protos) < 0) {
+ error("mce: cannot get mas instance sdp protocol list");
+ continue;
+ }
+
+ scn = sdp_get_proto_port(protos, RFCOMM_UUID);
+
+ sdp_list_foreach(protos, (sdp_list_func_t) sdp_list_free, NULL);
+ sdp_list_free(protos, NULL);
+
+ if (!scn) {
+ error("mce: cannot get mas instance rfcomm channel");
+ continue;
+ }
+
+ size += fill_mce_inst(buf + size, id, scn, msg_type, name,
+ name_len);
+ num_instances++;
+ }
+
+ status = HAL_STATUS_SUCCESS;
+
+fail:
+ ev->num_instances = num_instances;
+ ev->status = status;
+
+ ipc_send_notif(hal_ipc, HAL_SERVICE_ID_MAP_CLIENT,
+ HAL_EV_MAP_CLIENT_REMOTE_MAS_INSTANCES, size, buf);
+}
+
+static void handle_get_instances(const void *buf, uint16_t len)
+{
+ const struct hal_cmd_map_client_get_instances *cmd = buf;
+ uint8_t status;
+ bdaddr_t *dst;
+ uuid_t uuid;
+
+ DBG("");
+
+ dst = new0(bdaddr_t, 1);
+ if (!dst) {
+ error("mce: Fail to allocate cb data");
+ status = HAL_STATUS_FAILED;
+ goto failed;
+ }
+
+ android2bdaddr(&cmd->bdaddr, dst);
+ sdp_uuid16_create(&uuid, MAP_MSE_SVCLASS_ID);
+
+ if (bt_search_service(&adapter_addr, dst, &uuid,
+ map_client_sdp_search_cb, dst, free, 0)) {
+ error("mce: Failed to search SDP details");
+ status = HAL_STATUS_FAILED;
+ goto failed;
+ }
+
+ status = HAL_STATUS_SUCCESS;
+
+failed:
+ ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_MAP_CLIENT,
+ HAL_OP_MAP_CLIENT_GET_INSTANCES, status);
+}
+
+static const struct ipc_handler cmd_handlers[] = {
+ /* HAL_OP_MAP_CLIENT_GET_INSTANCES */
+ { handle_get_instances, false,
+ sizeof(struct hal_cmd_map_client_get_instances) },
+};
+
+bool bt_map_client_register(struct ipc *ipc, const bdaddr_t *addr, uint8_t mode)
+{
+ DBG("");
+
+ bacpy(&adapter_addr, addr);
+
+ hal_ipc = ipc;
+
+ ipc_register(hal_ipc, HAL_SERVICE_ID_MAP_CLIENT, cmd_handlers,
+ G_N_ELEMENTS(cmd_handlers));
+
+ return true;
+}
+
+void bt_map_client_unregister(void)
+{
+ DBG("");
+
+ ipc_unregister(hal_ipc, HAL_SERVICE_ID_MAP_CLIENT);
+ hal_ipc = NULL;
+}
diff --git a/repo/android/map-client.h b/repo/android/map-client.h
new file mode 100644
index 0000000..0e63072
--- /dev/null
+++ b/repo/android/map-client.h
@@ -0,0 +1,26 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2014 Intel Corporation. All rights reserved.
+ *
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+bool bt_map_client_register(struct ipc *ipc, const bdaddr_t *addr,
+ uint8_t mode);
+void bt_map_client_unregister(void);
diff --git a/repo/android/pan.c b/repo/android/pan.c
new file mode 100644
index 0000000..c40a6d3
--- /dev/null
+++ b/repo/android/pan.c
@@ -0,0 +1,903 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2013-2014 Intel Corporation. All rights reserved.
+ *
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdint.h>
+#include <stdbool.h>
+#include <errno.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <glib.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <sys/wait.h>
+#include <sys/types.h>
+#include <net/if.h>
+#include <linux/sockios.h>
+#include <netinet/in.h>
+#include <netinet/ip6.h>
+#include <linux/if_bridge.h>
+
+#include "btio/btio.h"
+#include "lib/bluetooth.h"
+#include "lib/bnep.h"
+#include "lib/sdp.h"
+#include "lib/sdp_lib.h"
+#include "src/uuid-helper.h"
+#include "profiles/network/bnep.h"
+#include "src/log.h"
+
+#include "hal-msg.h"
+#include "ipc-common.h"
+#include "ipc.h"
+#include "utils.h"
+#include "bluetooth.h"
+#include "pan.h"
+
+#define SVC_HINT_NETWORKING 0x02
+
+#define BNEP_BRIDGE "bt-pan"
+#define BNEP_PANU_INTERFACE "bt-pan"
+#define BNEP_NAP_INTERFACE "bt-pan%d"
+
+struct pan_device {
+ char iface[16];
+ bdaddr_t dst;
+ uint8_t conn_state;
+ uint8_t role;
+ GIOChannel *io;
+ struct bnep *session;
+ guint watch;
+};
+
+static bdaddr_t adapter_addr;
+static GSList *devices = NULL;
+static uint8_t local_role = HAL_PAN_ROLE_NONE;
+static uint32_t nap_rec_id = 0;
+static uint32_t panu_rec_id = 0;
+static GIOChannel *nap_io = NULL;
+static bool nap_bridge_mode = false;
+static struct ipc *hal_ipc = NULL;
+
+static int set_forward_delay(int sk)
+{
+ unsigned long args[4] = { BRCTL_SET_BRIDGE_FORWARD_DELAY, 0 , 0, 0 };
+ struct ifreq ifr;
+
+ memset(&ifr, 0, sizeof(ifr));
+ strncpy(ifr.ifr_name, BNEP_BRIDGE, IFNAMSIZ - 1);
+ ifr.ifr_data = (char *) args;
+
+ if (ioctl(sk, SIOCDEVPRIVATE, &ifr) < 0) {
+ error("pan: setting forward delay failed: %d (%s)",
+ errno, strerror(errno));
+ return -1;
+ }
+
+ return 0;
+}
+
+static int nap_create_bridge(void)
+{
+ int sk, err;
+
+ DBG("%s", BNEP_BRIDGE);
+
+ if (nap_bridge_mode)
+ return 0;
+
+ sk = socket(AF_INET, SOCK_STREAM | SOCK_CLOEXEC, 0);
+ if (sk < 0)
+ return -EOPNOTSUPP;
+
+ if (ioctl(sk, SIOCBRADDBR, BNEP_BRIDGE) < 0) {
+ err = -errno;
+ if (err != -EEXIST) {
+ close(sk);
+ return -EOPNOTSUPP;
+ }
+ }
+
+ err = set_forward_delay(sk);
+ if (err < 0)
+ ioctl(sk, SIOCBRDELBR, BNEP_BRIDGE);
+
+ close(sk);
+
+ nap_bridge_mode = err == 0;
+
+ return err;
+}
+
+static int bridge_if_down(void)
+{
+ struct ifreq ifr;
+ int sk, err;
+
+ sk = socket(AF_INET, SOCK_DGRAM, 0);
+
+ memset(&ifr, 0, sizeof(ifr));
+ strncpy(ifr.ifr_name, BNEP_BRIDGE, IF_NAMESIZE - 1);
+
+ ifr.ifr_flags &= ~IFF_UP;
+
+ /* Bring down the interface */
+ err = ioctl(sk, SIOCSIFFLAGS, (caddr_t) &ifr);
+
+ close(sk);
+
+ if (err < 0) {
+ error("pan: Could not bring down %s", BNEP_BRIDGE);
+ return err;
+ }
+
+ return 0;
+}
+
+static int nap_remove_bridge(void)
+{
+ int sk, err;
+
+ DBG("%s", BNEP_BRIDGE);
+
+ if (!nap_bridge_mode)
+ return 0;
+
+ bridge_if_down();
+
+ sk = socket(AF_INET, SOCK_STREAM | SOCK_CLOEXEC, 0);
+ if (sk < 0)
+ return -EOPNOTSUPP;
+
+ err = ioctl(sk, SIOCBRDELBR, BNEP_BRIDGE);
+ if (err < 0)
+ err = -errno;
+
+ close(sk);
+
+ if (err < 0)
+ return err;
+
+ nap_bridge_mode = false;
+
+ return 0;
+}
+
+static int device_cmp(gconstpointer s, gconstpointer user_data)
+{
+ const struct pan_device *dev = s;
+ const bdaddr_t *dst = user_data;
+
+ return bacmp(&dev->dst, dst);
+}
+
+static void pan_device_free(void *data)
+{
+ struct pan_device *dev = data;
+
+ if (dev->watch > 0) {
+ bnep_server_delete(BNEP_BRIDGE, dev->iface, &dev->dst);
+ g_source_remove(dev->watch);
+ }
+
+ if (dev->io) {
+ g_io_channel_shutdown(dev->io, FALSE, NULL);
+ g_io_channel_unref(dev->io);
+ }
+
+ if (dev->session)
+ bnep_free(dev->session);
+
+ g_free(dev);
+}
+
+static void pan_device_remove(struct pan_device *dev)
+{
+ devices = g_slist_remove(devices, dev);
+
+ if (g_slist_length(devices) == 0) {
+ local_role = HAL_PAN_ROLE_NONE;
+ nap_remove_bridge();
+ }
+
+ pan_device_free(dev);
+}
+
+static void bt_pan_notify_conn_state(struct pan_device *dev, uint8_t state)
+{
+ struct hal_ev_pan_conn_state ev;
+ char addr[18];
+
+ if (dev->conn_state == state)
+ return;
+
+ dev->conn_state = state;
+ ba2str(&dev->dst, addr);
+ DBG("device %s state %u", addr, state);
+
+ bdaddr2android(&dev->dst, ev.bdaddr);
+ ev.state = state;
+ ev.local_role = local_role;
+ ev.remote_role = dev->role;
+ ev.status = HAL_STATUS_SUCCESS;
+
+ ipc_send_notif(hal_ipc, HAL_SERVICE_ID_PAN, HAL_EV_PAN_CONN_STATE,
+ sizeof(ev), &ev);
+ if (dev->conn_state == HAL_PAN_STATE_DISCONNECTED)
+ pan_device_remove(dev);
+}
+
+static void bt_pan_notify_ctrl_state(struct pan_device *dev, uint8_t state,
+ uint8_t status)
+{
+ struct hal_ev_pan_ctrl_state ev;
+
+ DBG("");
+
+ ev.state = state;
+ ev.local_role = local_role;
+ ev.status = status;
+
+ memset(ev.name, 0, sizeof(ev.name));
+
+ if (local_role == HAL_PAN_ROLE_NAP)
+ memcpy(ev.name, BNEP_BRIDGE, sizeof(BNEP_BRIDGE));
+ else if (local_role == HAL_PAN_ROLE_PANU)
+ memcpy(ev.name, dev->iface, sizeof(dev->iface));
+
+ ipc_send_notif(hal_ipc, HAL_SERVICE_ID_PAN, HAL_EV_PAN_CTRL_STATE,
+ sizeof(ev), &ev);
+}
+
+static void bnep_disconn_cb(void *data)
+{
+ struct pan_device *dev = data;
+
+ DBG("%s disconnected", dev->iface);
+
+ bt_pan_notify_conn_state(dev, HAL_PAN_STATE_DISCONNECTED);
+}
+
+static void bnep_conn_cb(char *iface, int err, void *data)
+{
+ struct pan_device *dev = data;
+
+ DBG("");
+
+ if (err < 0) {
+ error("bnep connect req failed: %s", strerror(-err));
+ bt_pan_notify_conn_state(dev, HAL_PAN_STATE_DISCONNECTED);
+ return;
+ }
+
+ memcpy(dev->iface, iface, sizeof(dev->iface));
+
+ DBG("%s connected", dev->iface);
+
+ bt_pan_notify_ctrl_state(dev, HAL_PAN_CTRL_ENABLED, HAL_STATUS_SUCCESS);
+ bt_pan_notify_conn_state(dev, HAL_PAN_STATE_CONNECTED);
+}
+
+static void connect_cb(GIOChannel *chan, GError *err, gpointer data)
+{
+ struct pan_device *dev = data;
+ uint16_t l_role, r_role;
+ int perr, sk;
+
+ DBG("");
+
+ if (err) {
+ error("%s", err->message);
+ goto fail;
+ }
+
+ l_role = (local_role == HAL_PAN_ROLE_NAP) ? BNEP_SVC_NAP :
+ BNEP_SVC_PANU;
+ r_role = (dev->role == HAL_PAN_ROLE_NAP) ? BNEP_SVC_NAP : BNEP_SVC_PANU;
+
+ sk = g_io_channel_unix_get_fd(dev->io);
+
+ dev->session = bnep_new(sk, l_role, r_role, BNEP_PANU_INTERFACE);
+ if (!dev->session)
+ goto fail;
+
+ perr = bnep_connect(dev->session, bnep_conn_cb, bnep_disconn_cb, dev,
+ dev);
+ if (perr < 0) {
+ error("bnep connect req failed: %s", strerror(-perr));
+ goto fail;
+ }
+
+ if (dev->io) {
+ g_io_channel_unref(dev->io);
+ dev->io = NULL;
+ }
+
+ return;
+
+fail:
+ bt_pan_notify_conn_state(dev, HAL_PAN_STATE_DISCONNECTED);
+}
+
+static void bt_pan_connect(const void *buf, uint16_t len)
+{
+ const struct hal_cmd_pan_connect *cmd = buf;
+ struct pan_device *dev;
+ uint8_t status;
+ bdaddr_t dst;
+ char addr[18];
+ GSList *l;
+ GError *gerr = NULL;
+
+ DBG("");
+
+ switch (cmd->local_role) {
+ case HAL_PAN_ROLE_NAP:
+ if (cmd->remote_role != HAL_PAN_ROLE_PANU) {
+ status = HAL_STATUS_UNSUPPORTED;
+ goto failed;
+ }
+ break;
+ case HAL_PAN_ROLE_PANU:
+ if (cmd->remote_role != HAL_PAN_ROLE_NAP &&
+ cmd->remote_role != HAL_PAN_ROLE_PANU) {
+ status = HAL_STATUS_UNSUPPORTED;
+ goto failed;
+ }
+ break;
+ default:
+ status = HAL_STATUS_UNSUPPORTED;
+ goto failed;
+ }
+
+ android2bdaddr(&cmd->bdaddr, &dst);
+
+ l = g_slist_find_custom(devices, &dst, device_cmp);
+ if (l) {
+ status = HAL_STATUS_FAILED;
+ goto failed;
+ }
+
+ dev = g_new0(struct pan_device, 1);
+ bacpy(&dev->dst, &dst);
+ local_role = cmd->local_role;
+ dev->role = cmd->remote_role;
+
+ ba2str(&dev->dst, addr);
+ DBG("connecting to %s %s", addr, dev->iface);
+
+ dev->io = bt_io_connect(connect_cb, dev, NULL, &gerr,
+ BT_IO_OPT_SOURCE_BDADDR, &adapter_addr,
+ BT_IO_OPT_DEST_BDADDR, &dev->dst,
+ BT_IO_OPT_PSM, BNEP_PSM,
+ BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_MEDIUM,
+ BT_IO_OPT_OMTU, BNEP_MTU,
+ BT_IO_OPT_IMTU, BNEP_MTU,
+ BT_IO_OPT_INVALID);
+ if (!dev->io) {
+ error("%s", gerr->message);
+ g_error_free(gerr);
+ g_free(dev);
+ status = HAL_STATUS_FAILED;
+ goto failed;
+ }
+
+ devices = g_slist_append(devices, dev);
+ bt_pan_notify_conn_state(dev, HAL_PAN_STATE_CONNECTING);
+
+ status = HAL_STATUS_SUCCESS;
+
+failed:
+ ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_PAN, HAL_OP_PAN_CONNECT, status);
+}
+
+static void bt_pan_disconnect(const void *buf, uint16_t len)
+{
+ const struct hal_cmd_pan_disconnect *cmd = buf;
+ struct pan_device *dev;
+ uint8_t status;
+ GSList *l;
+ bdaddr_t dst;
+
+ DBG("");
+
+ android2bdaddr(&cmd->bdaddr, &dst);
+
+ l = g_slist_find_custom(devices, &dst, device_cmp);
+ if (!l) {
+ status = HAL_STATUS_FAILED;
+ goto failed;
+ }
+
+ dev = l->data;
+
+ if (dev->conn_state == HAL_PAN_STATE_CONNECTED && dev->session)
+ bnep_disconnect(dev->session);
+
+ bt_pan_notify_conn_state(dev, HAL_PAN_STATE_DISCONNECTED);
+ status = HAL_STATUS_SUCCESS;
+
+failed:
+ ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_PAN, HAL_OP_PAN_DISCONNECT,
+ status);
+}
+
+static gboolean nap_watchdog_cb(GIOChannel *chan, GIOCondition cond,
+ gpointer user_data)
+{
+ struct pan_device *dev = user_data;
+
+ DBG("disconnected");
+
+ bt_pan_notify_conn_state(dev, HAL_PAN_STATE_DISCONNECTED);
+
+ return FALSE;
+}
+
+static gboolean nap_setup_cb(GIOChannel *chan, GIOCondition cond,
+ gpointer user_data)
+{
+ struct pan_device *dev = user_data;
+ uint8_t packet[BNEP_MTU];
+ int sk, n, err;
+
+ if (cond & (G_IO_ERR | G_IO_HUP | G_IO_NVAL)) {
+ error("Hangup or error or inval on BNEP socket");
+ return FALSE;
+ }
+
+ sk = g_io_channel_unix_get_fd(chan);
+
+ /*
+ * BNEP_SETUP_CONNECTION_REQUEST_MSG should be read and left in case
+ * of kernel setup connection msg handling.
+ */
+ n = recv(sk, packet, sizeof(packet), MSG_PEEK);
+ if (n < 0) {
+ error("read(): %s(%d)", strerror(errno), errno);
+ goto failed;
+ }
+
+ if (n < 3) {
+ error("pan: to few setup connection request data received");
+ goto failed;
+ }
+
+ err = nap_create_bridge();
+ if (err < 0)
+ error("pan: Failed to create bridge: %s (%d)", strerror(-err),
+ -err);
+
+ if (bnep_server_add(sk, (err < 0) ? NULL : BNEP_BRIDGE, dev->iface,
+ &dev->dst, packet, n) < 0) {
+ error("pan: server_connadd failed");
+ goto failed;
+ }
+
+ dev->watch = g_io_add_watch(chan, G_IO_HUP | G_IO_ERR | G_IO_NVAL,
+ nap_watchdog_cb, dev);
+ g_io_channel_unref(dev->io);
+ dev->io = NULL;
+
+ bt_pan_notify_ctrl_state(dev, HAL_PAN_CTRL_ENABLED, HAL_STATUS_SUCCESS);
+ bt_pan_notify_conn_state(dev, HAL_PAN_STATE_CONNECTED);
+
+ return FALSE;
+
+failed:
+ pan_device_remove(dev);
+
+ return FALSE;
+}
+
+static void nap_connect_cb(GIOChannel *chan, GError *err, gpointer user_data)
+{
+ struct pan_device *dev = user_data;
+
+ DBG("");
+
+ if (err) {
+ error("%s", err->message);
+ bt_pan_notify_conn_state(dev, HAL_PAN_STATE_DISCONNECTED);
+ return;
+ }
+
+ g_io_channel_set_close_on_unref(chan, TRUE);
+ dev->watch = g_io_add_watch(chan,
+ G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL,
+ nap_setup_cb, dev);
+}
+
+static void nap_confirm_cb(GIOChannel *chan, gpointer data)
+{
+ struct pan_device *dev;
+ bdaddr_t dst;
+ char address[18];
+ GError *err = NULL;
+
+ DBG("");
+
+ bt_io_get(chan, &err, BT_IO_OPT_DEST_BDADDR, &dst,
+ BT_IO_OPT_DEST, address, BT_IO_OPT_INVALID);
+ if (err) {
+ error("%s", err->message);
+ g_error_free(err);
+ return;
+ }
+
+ DBG("incoming connect request from %s", address);
+ dev = g_new0(struct pan_device, 1);
+ bacpy(&dev->dst, &dst);
+ local_role = HAL_PAN_ROLE_NAP;
+ dev->role = HAL_PAN_ROLE_PANU;
+
+ strncpy(dev->iface, BNEP_NAP_INTERFACE, 16);
+ dev->iface[15] = '\0';
+
+ dev->io = g_io_channel_ref(chan);
+ g_io_channel_set_close_on_unref(dev->io, TRUE);
+
+ if (!bt_io_accept(dev->io, nap_connect_cb, dev, NULL, &err)) {
+ error("bt_io_accept: %s", err->message);
+ g_error_free(err);
+ goto failed;
+ }
+
+ devices = g_slist_append(devices, dev);
+ bt_pan_notify_conn_state(dev, HAL_PAN_STATE_CONNECTING);
+
+ return;
+
+failed:
+ bt_pan_notify_conn_state(dev, HAL_PAN_STATE_DISCONNECTED);
+}
+
+static void destroy_nap_device(void)
+{
+ DBG("");
+
+ nap_remove_bridge();
+
+ if (nap_io) {
+ g_io_channel_shutdown(nap_io, FALSE, NULL);
+ g_io_channel_unref(nap_io);
+ nap_io = NULL;
+ }
+}
+
+static int register_nap_server(void)
+{
+ GError *gerr = NULL;
+
+ DBG("");
+
+ nap_io = bt_io_listen(NULL, nap_confirm_cb, NULL, NULL, &gerr,
+ BT_IO_OPT_SOURCE_BDADDR, &adapter_addr,
+ BT_IO_OPT_PSM, BNEP_PSM,
+ BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_MEDIUM,
+ BT_IO_OPT_OMTU, BNEP_MTU,
+ BT_IO_OPT_IMTU, BNEP_MTU,
+ BT_IO_OPT_INVALID);
+
+ if (!nap_io) {
+ destroy_nap_device();
+ error("%s", gerr->message);
+ g_error_free(gerr);
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static void bt_pan_enable(const void *buf, uint16_t len)
+{
+ const struct hal_cmd_pan_enable *cmd = buf;
+ uint8_t status, state;
+ int err;
+
+ DBG("");
+
+ if (local_role == cmd->local_role) {
+ status = HAL_STATUS_SUCCESS;
+ goto reply;
+ }
+
+ /* destroy existing server */
+ destroy_nap_device();
+
+ switch (cmd->local_role) {
+ case HAL_PAN_ROLE_NAP:
+ break;
+ case HAL_PAN_ROLE_NONE:
+ local_role = HAL_PAN_ROLE_NONE;
+ status = HAL_STATUS_SUCCESS;
+ state = HAL_PAN_CTRL_DISABLED;
+ goto notify;
+ default:
+ status = HAL_STATUS_UNSUPPORTED;
+ goto reply;
+ }
+
+ local_role = cmd->local_role;
+ err = register_nap_server();
+ if (err < 0) {
+ status = HAL_STATUS_FAILED;
+ destroy_nap_device();
+ goto reply;
+ }
+
+ status = HAL_STATUS_SUCCESS;
+ state = HAL_PAN_CTRL_ENABLED;
+
+notify:
+ bt_pan_notify_ctrl_state(NULL, state, status);
+
+reply:
+ ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_PAN, HAL_OP_PAN_ENABLE, status);
+}
+
+static void bt_pan_get_role(const void *buf, uint16_t len)
+{
+ struct hal_rsp_pan_get_role rsp;
+
+ DBG("");
+
+ rsp.local_role = local_role;
+ ipc_send_rsp_full(hal_ipc, HAL_SERVICE_ID_PAN, HAL_OP_PAN_GET_ROLE,
+ sizeof(rsp), &rsp, -1);
+}
+
+static const struct ipc_handler cmd_handlers[] = {
+ /* HAL_OP_PAN_ENABLE */
+ { bt_pan_enable, false, sizeof(struct hal_cmd_pan_enable) },
+ /* HAL_OP_PAN_GET_ROLE */
+ { bt_pan_get_role, false, 0 },
+ /* HAL_OP_PAN_CONNECT */
+ { bt_pan_connect, false, sizeof(struct hal_cmd_pan_connect) },
+ /* HAL_OP_PAN_DISCONNECT */
+ { bt_pan_disconnect, false, sizeof(struct hal_cmd_pan_disconnect) },
+};
+
+static sdp_record_t *nap_record(void)
+{
+ sdp_list_t *svclass, *pfseq, *apseq, *root, *aproto;
+ uuid_t root_uuid, nap, l2cap, bnep;
+ sdp_profile_desc_t profile[1];
+ sdp_list_t *proto[2];
+ sdp_data_t *v, *p;
+ uint16_t psm = BNEP_PSM, version = 0x0100;
+ uint16_t security = 0x0001, type = 0xfffe;
+ uint32_t rate = 0;
+ const char *desc = "Network Access Point", *name = "Network Service";
+ sdp_record_t *record;
+ uint16_t ptype[] = { 0x0800, /* IPv4 */ 0x0806, /* ARP */ };
+ sdp_data_t *head, *pseq, *data;
+
+ record = sdp_record_alloc();
+ if (!record)
+ return NULL;
+
+ record->attrlist = NULL;
+ record->pattern = NULL;
+
+ sdp_uuid16_create(&nap, NAP_SVCLASS_ID);
+ svclass = sdp_list_append(NULL, &nap);
+ sdp_set_service_classes(record, svclass);
+
+ sdp_uuid16_create(&profile[0].uuid, NAP_PROFILE_ID);
+ profile[0].version = 0x0100;
+ pfseq = sdp_list_append(NULL, &profile[0]);
+ sdp_set_profile_descs(record, pfseq);
+ sdp_set_info_attr(record, name, NULL, desc);
+ sdp_attr_add_new(record, SDP_ATTR_NET_ACCESS_TYPE, SDP_UINT16, &type);
+ sdp_attr_add_new(record, SDP_ATTR_MAX_NET_ACCESSRATE,
+ SDP_UINT32, &rate);
+
+ sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
+ root = sdp_list_append(NULL, &root_uuid);
+ sdp_set_browse_groups(record, root);
+
+ sdp_uuid16_create(&l2cap, L2CAP_UUID);
+ proto[0] = sdp_list_append(NULL, &l2cap);
+ p = sdp_data_alloc(SDP_UINT16, &psm);
+ proto[0] = sdp_list_append(proto[0], p);
+ apseq = sdp_list_append(NULL, proto[0]);
+
+ sdp_uuid16_create(&bnep, BNEP_UUID);
+ proto[1] = sdp_list_append(NULL, &bnep);
+ v = sdp_data_alloc(SDP_UINT16, &version);
+ proto[1] = sdp_list_append(proto[1], v);
+
+ head = sdp_data_alloc(SDP_UINT16, &ptype[0]);
+ data = sdp_data_alloc(SDP_UINT16, &ptype[1]);
+ sdp_seq_append(head, data);
+
+ pseq = sdp_data_alloc(SDP_SEQ16, head);
+ proto[1] = sdp_list_append(proto[1], pseq);
+ apseq = sdp_list_append(apseq, proto[1]);
+ aproto = sdp_list_append(NULL, apseq);
+ sdp_set_access_protos(record, aproto);
+ sdp_add_lang_attr(record);
+ sdp_attr_add_new(record, SDP_ATTR_SECURITY_DESC, SDP_UINT16, &security);
+
+ sdp_data_free(p);
+ sdp_data_free(v);
+ sdp_list_free(apseq, NULL);
+ sdp_list_free(root, NULL);
+ sdp_list_free(aproto, NULL);
+ sdp_list_free(proto[0], NULL);
+ sdp_list_free(proto[1], NULL);
+ sdp_list_free(svclass, NULL);
+ sdp_list_free(pfseq, NULL);
+
+ return record;
+}
+
+static sdp_record_t *panu_record(void)
+{
+ sdp_list_t *svclass, *pfseq, *apseq, *root, *aproto;
+ uuid_t root_uuid, panu, l2cap, bnep;
+ sdp_profile_desc_t profile[1];
+ sdp_list_t *proto[2];
+ sdp_data_t *v, *p;
+ uint16_t psm = BNEP_PSM, version = 0x0100;
+ uint16_t security = 0x0001, type = 0xfffe;
+ uint32_t rate = 0;
+ const char *desc = "PAN User", *name = "Network Service";
+ sdp_record_t *record;
+ uint16_t ptype[] = { 0x0800, /* IPv4 */ 0x0806, /* ARP */ };
+ sdp_data_t *head, *pseq, *data;
+
+ record = sdp_record_alloc();
+ if (!record)
+ return NULL;
+
+ record->attrlist = NULL;
+ record->pattern = NULL;
+
+ sdp_uuid16_create(&panu, PANU_SVCLASS_ID);
+ svclass = sdp_list_append(NULL, &panu);
+ sdp_set_service_classes(record, svclass);
+
+ sdp_uuid16_create(&profile[0].uuid, PANU_PROFILE_ID);
+ profile[0].version = 0x0100;
+ pfseq = sdp_list_append(NULL, &profile[0]);
+ sdp_set_profile_descs(record, pfseq);
+ sdp_set_info_attr(record, name, NULL, desc);
+ sdp_attr_add_new(record, SDP_ATTR_NET_ACCESS_TYPE, SDP_UINT16, &type);
+ sdp_attr_add_new(record, SDP_ATTR_MAX_NET_ACCESSRATE,
+ SDP_UINT32, &rate);
+
+ sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
+ root = sdp_list_append(NULL, &root_uuid);
+ sdp_set_browse_groups(record, root);
+
+ sdp_uuid16_create(&l2cap, L2CAP_UUID);
+ proto[0] = sdp_list_append(NULL, &l2cap);
+ p = sdp_data_alloc(SDP_UINT16, &psm);
+ proto[0] = sdp_list_append(proto[0], p);
+ apseq = sdp_list_append(NULL, proto[0]);
+
+ sdp_uuid16_create(&bnep, BNEP_UUID);
+ proto[1] = sdp_list_append(NULL, &bnep);
+ v = sdp_data_alloc(SDP_UINT16, &version);
+ proto[1] = sdp_list_append(proto[1], v);
+
+ head = sdp_data_alloc(SDP_UINT16, &ptype[0]);
+ data = sdp_data_alloc(SDP_UINT16, &ptype[1]);
+ sdp_seq_append(head, data);
+
+ pseq = sdp_data_alloc(SDP_SEQ16, head);
+ proto[1] = sdp_list_append(proto[1], pseq);
+ apseq = sdp_list_append(apseq, proto[1]);
+ aproto = sdp_list_append(NULL, apseq);
+ sdp_set_access_protos(record, aproto);
+ sdp_add_lang_attr(record);
+ sdp_attr_add_new(record, SDP_ATTR_SECURITY_DESC, SDP_UINT16, &security);
+
+ sdp_data_free(p);
+ sdp_data_free(v);
+ sdp_list_free(apseq, NULL);
+ sdp_list_free(root, NULL);
+ sdp_list_free(aproto, NULL);
+ sdp_list_free(proto[0], NULL);
+ sdp_list_free(proto[1], NULL);
+ sdp_list_free(svclass, NULL);
+ sdp_list_free(pfseq, NULL);
+
+ return record;
+}
+
+bool bt_pan_register(struct ipc *ipc, const bdaddr_t *addr, uint8_t mode)
+{
+ sdp_record_t *nap_rec, *panu_rec;
+ int err;
+
+ DBG("");
+
+ bacpy(&adapter_addr, addr);
+
+ nap_rec = nap_record();
+ if (bt_adapter_add_record(nap_rec, SVC_HINT_NETWORKING) < 0) {
+ sdp_record_free(nap_rec);
+ error("Failed to allocate PAN-NAP sdp record");
+ return false;
+ }
+
+ panu_rec = panu_record();
+ if (bt_adapter_add_record(panu_rec, SVC_HINT_NETWORKING) < 0) {
+ sdp_record_free(nap_rec);
+ sdp_record_free(panu_rec);
+ error("Failed to allocate PAN-PANU sdp record");
+ return false;
+ }
+
+ err = bnep_init();
+ if (err < 0) {
+ error("Failed to init BNEP");
+ bt_adapter_remove_record(nap_rec->handle);
+ bt_adapter_remove_record(panu_rec->handle);
+ return false;
+ }
+
+ err = register_nap_server();
+ if (err < 0) {
+ error("Failed to register NAP server");
+ bt_adapter_remove_record(nap_rec->handle);
+ bt_adapter_remove_record(panu_rec->handle);
+ bnep_cleanup();
+ return false;
+ }
+
+ nap_rec_id = nap_rec->handle;
+ panu_rec_id = panu_rec->handle;
+
+ hal_ipc = ipc;
+ ipc_register(hal_ipc, HAL_SERVICE_ID_PAN, cmd_handlers,
+ G_N_ELEMENTS(cmd_handlers));
+
+ return true;
+}
+
+void bt_pan_unregister(void)
+{
+ DBG("");
+
+ g_slist_free_full(devices, pan_device_free);
+ devices = NULL;
+ local_role = HAL_PAN_ROLE_NONE;
+
+ bnep_cleanup();
+
+ ipc_unregister(hal_ipc, HAL_SERVICE_ID_PAN);
+ hal_ipc = NULL;
+
+ bt_adapter_remove_record(nap_rec_id);
+ nap_rec_id = 0;
+ bt_adapter_remove_record(panu_rec_id);
+ panu_rec_id = 0;
+ destroy_nap_device();
+}
diff --git a/repo/android/pan.h b/repo/android/pan.h
new file mode 100644
index 0000000..cfbea96
--- /dev/null
+++ b/repo/android/pan.h
@@ -0,0 +1,25 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2013-2014 Intel Corporation. All rights reserved.
+ *
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+bool bt_pan_register(struct ipc *ipc, const bdaddr_t *addr, uint8_t mode);
+void bt_pan_unregister(void);
diff --git a/repo/android/pics-a2dp.txt b/repo/android/pics-a2dp.txt
new file mode 100644
index 0000000..2e87104
--- /dev/null
+++ b/repo/android/pics-a2dp.txt
@@ -0,0 +1,162 @@
+A2DP PICS for the PTS tool.
+
+PTS version: 6.1
+
+* - different than PTS defaults
+# - not yet implemented/supported
+
+M - mandatory if such role selected
+O - optional
+
+ Profile Version
+-------------------------------------------------------------------------------
+Parameter Name Selected Description
+-------------------------------------------------------------------------------
+TSPC_A2DP_0_1 False A2DP 1.0 (C.1)
+TSPC_A2DP_0_2 False A2DP 1.2 (C.1)
+TSPC_A2DP_0_3 True (*) A2DP 1.3 (C.1)
+-------------------------------------------------------------------------------
+C.1: It is mandatory to select one of the profile versions.
+-------------------------------------------------------------------------------
+
+
+ Roles
+-------------------------------------------------------------------------------
+Parameter Name Selected Description
+-------------------------------------------------------------------------------
+TSPC_A2DP_1_1 True (*) Role: Source (C.1)
+TSPC_A2DP_1_2 False Role: Sink (C.1)
+-------------------------------------------------------------------------------
+C.1: It is mandatory to support at least one of the defined roles.
+-------------------------------------------------------------------------------
+
+
+ A2DP SRC Features
+-------------------------------------------------------------------------------
+Parameter Name Selected Description
+-------------------------------------------------------------------------------
+TSPC_A2DP_2_1 True SRC: Initiate connection establishment (M)
+TSPC_A2DP_2_2 True SRC: Accept connection establishment (M)
+TSPC_A2DP_2_3 True SRC: Initiate start streaming (M)
+TSPC_A2DP_2_4 True SRC: Accept start streaming (M)
+TSPC_A2DP_2_5 True SRC: Send audio stream (M)
+TSPC_A2DP_2_6 True SRC: Initiate connection release (M)
+TSPC_A2DP_2_7 True SRC: Accept connection release (M)
+TSPC_A2DP_2_8 True (*) SRC: Initiate suspend (O)
+TSPC_A2DP_2_9 True (*) SRC: Accept suspend (O)
+TSPC_A2DP_2_10 True SRC: SBC encoder (M)
+TSPC_A2DP_2_10a False SRC: Encode and Forward Audio Stream (O)
+TSPC_A2DP_2_11 False SRC: SBC Configurations in 16 KHz sampling (O)
+TSPC_A2DP_2_12 False SRC: SBC Configurations in 32 KHz sampling (O)
+TSPC_A2DP_2_13 True (*) SRC: SBC Configurations in 44.1 KHz sampling
+ (C.1)
+TSPC_A2DP_2_14 True (*) SRC: SBC Configurations in 48 KHz sampling (C.1)
+TSPC_A2DP_2_15 False SRC: Delay Reporting (C.2)
+TSPC_A2DP_2_16 False SRC: SRC video playback via Bluetooth VDP (C.3)
+TSPC_A2DP_2_17 False SRC: SRC video playback on a local video
+ display (C.3)
+-------------------------------------------------------------------------------
+C.1: At least one of the values shall be supported.
+C.2: Mandatory if A2DP 0/3 AND (2/16 OR 2/17) is supported, otherwise excluded.
+C.3: Optional to support if A2DP 0/3 is supported, otherwise excluded.
+-------------------------------------------------------------------------------
+
+
+ Supported Codecs in SRC
+-------------------------------------------------------------------------------
+Parameter Name Selected Description
+-------------------------------------------------------------------------------
+TSPC_A2DP_3_1 True SRC: SBC encoder (M)
+TSPC_A2DP_3_1a False SRC: Encode and Forward SBC Audio Stream (O)
+TSPC_A2DP_3_2 False SRC: Optional codec (O)
+TSPC_A2DP_3_3 False SRC: MPEG-1,2 Audio decoder (C.1)
+TSPC_A2DP_3_4 False SRC: MPEG-1,2 Audio encoder (C.1)
+TSPC_A2DP_3_5 False SRC: MPEG-2,4 AAC decoder (C.1)
+TSPC_A2DP_3_6 False SRC: MPEG-2,4 AAC encoder (C.1)
+TSPC_A2DP_3_7 False SRC: ATRAC family decoder (C.1)
+TSPC_A2DP_3_8 False SRC: ATRAC family encoder (C.1)
+-------------------------------------------------------------------------------
+C.1: At least one of the implementations shall be supported if 3/2
+ is supported, else excluded.
+-------------------------------------------------------------------------------
+
+
+ Supported Codec Features in SRC
+-------------------------------------------------------------------------------
+Parameter Name Selected Description
+-------------------------------------------------------------------------------
+TSPC_A2DP_3a_1 True SRC: Channel Mode - Mono (M)
+TSPC_A2DP_3a_2 True (*) SRC: Channel Mode - Dual Channel (C.1)
+TSPC_A2DP_3a_3 True (*) SRC: Channel Mode - Stereo (C.1)
+TSPC_A2DP_3a_4 True (*) SRC: Channel Mode - Joint Stereo (C.1)
+TSPC_A2DP_3a_5 True SRC: Block Length 4 (M)
+TSPC_A2DP_3a_6 True SRC: Block Length 8 (M)
+TSPC_A2DP_3a_7 True SRC: Block Length 12 (M)
+TSPC_A2DP_3a_8 True SRC: Block Length 16 (M)
+TSPC_A2DP_3a_9 True (*) SRC: Subbands - 4 (O)
+TSPC_A2DP_3a_10 True SRC: Subbands - 8 (M)
+TSPC_A2DP_3a_11 True (*) SRC: Allocation - SNR (O)
+TSPC_A2DP_3a_12 True SRC: Allocation - Loudness (M)
+-------------------------------------------------------------------------------
+C.1: At least one of the values shall be supported.
+-------------------------------------------------------------------------------
+
+
+ A2DP Sink Features
+-------------------------------------------------------------------------------
+Parameter Name Selected Description
+-------------------------------------------------------------------------------
+TSPC_A2DP_4_1 False SNK: Initiate connection establishment (O)
+TSPC_A2DP_4_2 False (*) SNK: Accept connection establishment (M)
+TSPC_A2DP_4_3 False SNK: Initiate start streaming (O)
+TSPC_A2DP_4_4 False (*) SNK: Accept start streaming (M)
+TSPC_A2DP_4_5 False (*) SNK: Receive audio stream (M)
+TSPC_A2DP_4_6 False SNK: Initiate connection release (O)
+TSPC_A2DP_4_7 False (*) SNK: Accept connection release (M)
+TSPC_A2DP_4_8 False SNK: Initiate suspend (O)
+TSPC_A2DP_4_9 False SNK: Accept suspend (O)
+TSPC_A2DP_4_10 False (*) SNK: SBC decoder (M)
+TSPC_A2DP_4_10a False SNK: Decode and Forward Audio Stream (O)
+TSPC_A2DP_4_11 False SNK: SBC Configurations in 16 KHz sampling (O)
+TSPC_A2DP_4_12 False SNK: SBC Configurations in 32 KHz sampling (O)
+TSPC_A2DP_4_13 False (*) SNK: SBC Configurations in 44.1 KHz sampling (M)
+TSPC_A2DP_4_14 False (*) SNK: SBC Configurations in 48 KHz sampling (M)
+TSPC_A2DP_4_15 False SNK: Delay Reporting (C.1)
+-------------------------------------------------------------------------------
+C.1: Mandatory to support if A2DP 0/3 is supported, otherwise excluded.
+-------------------------------------------------------------------------------
+
+
+ Supported codecs in SNK
+-------------------------------------------------------------------------------
+Parameter Name Selected Description
+-------------------------------------------------------------------------------
+TSPC_A2DP_5_1 False (*) SNK: SBC decoder (M)
+TSPC_A2DP_5_1a False SNK: Decode and Forward SBC Audio Stream (O)
+TSPC_A2DP_5_2 False SNK: Optional codec decoder (O)
+TSPC_A2DP_5_3 False SNK: MPEG-1,2 Audio (C.1)
+TSPC_A2DP_5_4 False SNK: MPEG-2,4 AAC (C.1)
+TSPC_A2DP_5_5 False SNK: ATRAC family (C.1)
+-------------------------------------------------------------------------------
+C.1: At least one codec shall be supported if Table 5/2 is supported, otherwise
+ excluded.
+-------------------------------------------------------------------------------
+
+
+ Supported Codec Features in SNK
+-------------------------------------------------------------------------------
+Parameter Name Selected Description
+-------------------------------------------------------------------------------
+TSPC_A2DP_5a_1 False (*) SNK: Channel Mode - Mono (M)
+TSPC_A2DP_5a_2 False (*) SNK: Channel Mode - Dual Channel (M)
+TSPC_A2DP_5a_3 False (*) SNK: Channel Mode - Stereo (M)
+TSPC_A2DP_5a_4 False (*) SNK: Channel Mode - Joint Stereo (M)
+TSPC_A2DP_5a_5 False (*) SNK: Block Length 4 (M)
+TSPC_A2DP_5a_6 False (*) SNK: Block Length 8 (M)
+TSPC_A2DP_5a_7 False (*) SNK: Block Length 12 (M)
+TSPC_A2DP_5a_8 False (*) SNK: Block Length 16 (M)
+TSPC_A2DP_5a_9 False (*) SNK: Subbands - 4 (M)
+TSPC_A2DP_5a_10 False (*) SNK: Subbands - 8 (M)
+TSPC_A2DP_5a_11 False (*) SNK: Allocation - SNR (M)
+TSPC_A2DP_5a_12 False (*) SNK: Allocation - Loudness (M)
+-------------------------------------------------------------------------------
diff --git a/repo/android/pics-avctp.txt b/repo/android/pics-avctp.txt
new file mode 100644
index 0000000..3447962
--- /dev/null
+++ b/repo/android/pics-avctp.txt
@@ -0,0 +1,75 @@
+AVCTP PICS for the PTS tool.
+
+PTS version: 6.1
+
+* - different than PTS defaults
+# - not yet implemented/supported
+
+M - mandatory if such role selected
+O - optional
+
+ Protocol Version
+-------------------------------------------------------------------------------
+Parameter Name Selected Description
+-------------------------------------------------------------------------------
+TSPC_AVCTP_0_1 False AVCTP 1.0 (C.1)
+TSPC_AVCTP_0_2 False AVCTP 1.2 (C.1)
+TSPC_AVCTP_0_3 False AVCTP 1.3 (C.1)
+TSPC_AVCTP_0_4 True (*) AVCTP 1.4 (C.1)
+-------------------------------------------------------------------------------
+C.1: Mandatory to support only one Protocol Version.
+-------------------------------------------------------------------------------
+
+
+ Roles
+-------------------------------------------------------------------------------
+Parameter Name Selected Description
+-------------------------------------------------------------------------------
+TSPC_AVCTP_1_1 True (*) Controller (C.1)
+TSPC_AVCTP_1_2 True (*) Target (C.1)
+-------------------------------------------------------------------------------
+C.1: Mandatory to support at least one of the defined roles.
+-------------------------------------------------------------------------------
+
+
+ Controller Features
+-------------------------------------------------------------------------------
+Parameter Name Selected Description
+-------------------------------------------------------------------------------
+TSPC_AVCTP_2_1 False Message fragmentation (O)
+TSPC_AVCTP_2_2 True Transaction label management (M)
+TSPC_AVCTP_2_3 True Packet type field management (M)
+TSPC_AVCTP_2_4 True Message type field management (M)
+TSPC_AVCTP_2_5 True PID field management (M)
+TSPC_AVCTP_2_6 True IPID field mangement (M)
+TSPC_AVCTP_2_7 True Message information management (M)
+TSPC_AVCTP_2_8 False Event registration for message reception (O)
+TSPC_AVCTP_2_9 False Event registration for connection request (O)
+TSPC_AVCTP_2_10 False Event registration for disconnection (O)
+TSPC_AVCTP_2_11 False Connect request (O)
+TSPC_AVCTP_2_12 False Disconnect request (O)
+TSPC_AVCTP_2_13 False Send message (O)
+TSPC_AVCTP_2_14 False Support for multiple AVCTP channel establishment
+ (O)
+-------------------------------------------------------------------------------
+
+
+ Target Features
+-------------------------------------------------------------------------------
+Parameter Name Selected Description
+-------------------------------------------------------------------------------
+TSPC_AVCTP_3_1 False Message fragmentation (O)
+TSPC_AVCTP_3_2 True Transaction label management (M)
+TSPC_AVCTP_3_3 True Packet type field management (M)
+TSPC_AVCTP_3_4 True Message type field management (M)
+TSPC_AVCTP_3_5 True PID field management (M)
+TSPC_AVCTP_3_6 True IPID field management (M)
+TSPC_AVCTP_3_7 True Message information management (M)
+TSPC_AVCTP_3_8 True (*) Event registration for message reception (O)
+TSPC_AVCTP_3_9 True (*) Event registration for connection request (O)
+TSPC_AVCTP_3_10 True (*) Event registration for disconnection request (O)
+TSPC_AVCTP_3_11 True (*) Connect request (O)
+TSPC_AVCTP_3_12 True (*) Disconnect request (O)
+TSPC_AVCTP_3_13 True (*) Send message (O)
+TSPC_AVCTP_ALL False Enables all test cases when set to TRUE
+-------------------------------------------------------------------------------
diff --git a/repo/android/pics-avdtp.txt b/repo/android/pics-avdtp.txt
new file mode 100644
index 0000000..c0c5529
--- /dev/null
+++ b/repo/android/pics-avdtp.txt
@@ -0,0 +1,236 @@
+AVDTP PICS for the PTS tool.
+
+PTS version: 6.1
+
+* - different than PTS defaults
+# - not yet implemented/supported
+
+M - mandatory if such role selected
+O - optional
+
+ Versions
+-------------------------------------------------------------------------------
+Parameter Name Selected Description
+-------------------------------------------------------------------------------
+TSPC_AVDTP_0_1 False AVDTP 1.0 (C.1)
+TSPC_AVDTP_0_2 False AVDTP 1.2 (C.1)
+TSPC_AVDTP_0_3 True (*) AVDTP 1.3 (C.1)
+-------------------------------------------------------------------------------
+C.1: It is mandatory to select only one of the protocol versions.
+-------------------------------------------------------------------------------
+
+
+ Roles
+-------------------------------------------------------------------------------
+Parameter Name Selected Description
+-------------------------------------------------------------------------------
+TSPC_AVDTP_1_1 True (*) Source (C.1)
+TSPC_AVDTP_1_2 True (*) Sink (C.1)
+TSPC_AVDTP_1_3 True (*) Initiator (C.2)
+TSPC_AVDTP_1_4 True (*) Acceptor (C.2)
+-------------------------------------------------------------------------------
+C.1: It is mandatory to support at least one of the defined roles.
+C.2: It is within the scope of profiles using the AVDTP specification to
+ mandate Initiator/Acceptor capabilities. It is mandatory to support at
+ least one of the defined roles.
+-------------------------------------------------------------------------------
+
+
+ Signaling Message Format (Initiator)
+-------------------------------------------------------------------------------
+Parameter Name Selected Description
+-------------------------------------------------------------------------------
+TSPC_AVDTP_2_1 True Transaction label (M)
+TSPC_AVDTP_2_2 True Packet type (M)
+TSPC_AVDTP_2_3 True Message type (M)
+TSPC_AVDTP_2_4 True Signal identifier (M)
+-------------------------------------------------------------------------------
+
+
+ Signaling Channel Establishment/Disconnection (Initiator)
+-------------------------------------------------------------------------------
+Parameter Name Selected Description
+-------------------------------------------------------------------------------
+TSPC_AVDTP_3_1 True (*) Establish signaling channel (O)
+TSPC_AVDTP_3_2 True (*) Disconnect signaling channel (O)
+-------------------------------------------------------------------------------
+
+
+ Stream Discovery and Configuration (Initiator)
+-------------------------------------------------------------------------------
+Parameter Name Selected Description
+-------------------------------------------------------------------------------
+TSPC_AVDTP_4_1 True (*) Stream discover command (O)
+TSPC_AVDTP_4_2 True (*) Stream get capabilities command (C.2)
+TSPC_AVDTP_4_3 True (*) Set configuration command (O)
+TSPC_AVDTP_4_4 True (*) Get configuration command (O)
+TSPC_AVDTP_4_5 False Reconfigure command (O)
+TSPC_AVDTP_4_6 True (*) Stream get all capabilities command (C.1)
+-------------------------------------------------------------------------------
+C.1: It is optional to support if TSPC_AVDTP_0_3 is supported, otherwise
+ excluded.
+C.2: Mandatory to support if TSPC_AVDTP_4_6 is supported, otherwise Optional.
+-------------------------------------------------------------------------------
+
+
+ Stream Establishment, Suspension and Release (Initiator)
+-------------------------------------------------------------------------------
+Parameter Name Selected Description
+-------------------------------------------------------------------------------
+TSPC_AVDTP_5_1 True (*) Open stream command (O)
+TSPC_AVDTP_5_2 True (*) Start stream command (O)
+TSPC_AVDTP_5_3 True (*) Close stream command (O)
+TSPC_AVDTP_5_4 True (*) Suspend command (O)
+TSPC_AVDTP_5_5 True (*) Abort stream command (O)
+-------------------------------------------------------------------------------
+
+
+ Security Signaling (Initiator)
+-------------------------------------------------------------------------------
+Parameter Name Selected Description
+-------------------------------------------------------------------------------
+TSPC_AVDTP_6_1 False Content security control command (O)
+-------------------------------------------------------------------------------
+
+
+ Message Fragmentation (Initiator)
+-------------------------------------------------------------------------------
+Parameter Name Selected Description
+-------------------------------------------------------------------------------
+TSPC_AVDTP_7_1 True Signaling message fragmentation (M)
+-------------------------------------------------------------------------------
+
+
+ Signaling Message Format (Acceptor)
+-------------------------------------------------------------------------------
+Parameter Name Selected Description
+-------------------------------------------------------------------------------
+TSPC_AVDTP_8_1 True Transaction label (M)
+TSPC_AVDTP_8_2 True Packet type (M)
+TSPC_AVDTP_8_3 True Message type (M)
+TSPC_AVDTP_8_4 True Signal identifier (M)
+-------------------------------------------------------------------------------
+
+
+ Signaling Channel Establishment/Disconnection (Acceptor)
+-------------------------------------------------------------------------------
+Parameter Name Selected Description
+-------------------------------------------------------------------------------
+TSPC_AVDTP_9_1 True (*) Establish signaling channel (O)
+TSPC_AVDTP_9_2 True (*) Disconnect signaling channel (O)
+-------------------------------------------------------------------------------
+
+
+ Stream Discovery and Configuration (Acceptor)
+-------------------------------------------------------------------------------
+Parameter Name Selected Description
+-------------------------------------------------------------------------------
+TSPC_AVDTP_10_1 True (*) Stream discover response (O)
+TSPC_AVDTP_10_2 True (*) Stream get capabilities response (C.2)
+TSPC_AVDTP_10_3 True (*) Set configuration response (O)
+TSPC_AVDTP_10_4 True (*) Get configuration response (O)
+TSPC_AVDTP_10_5 False Reconfigure response (O)
+TSPC_AVDTP_10_6 True (*) Stream get all capabilities response (C.1)
+-------------------------------------------------------------------------------
+C.1: It is optional to support if TSPC_AVDTP_0_3 is supported, otherwise
+ excluded.
+C.2: It is Mandatory to support if TSPC_AVDTP_10_6 is supported, otherwise
+ Optional.
+-------------------------------------------------------------------------------
+
+
+ Stream Establishment, Suspension and Release (Acceptor)
+-------------------------------------------------------------------------------
+Parameter Name Selected Description
+-------------------------------------------------------------------------------
+TSPC_AVDTP_11_1 True (*) Open stream response (O)
+TSPC_AVDTP_11_2 True (*) Start stream response (O)
+TSPC_AVDTP_11_3 True (*) Close stream response (O)
+TSPC_AVDTP_11_4 True (*) Suspend response (O)
+TSPC_AVDTP_11_5 True (*) Abort stream response (O)
+TSPC_AVDTP_11_6 True (*) General reject message (O)
+-------------------------------------------------------------------------------
+
+
+ Security Signaling (Acceptor)
+-------------------------------------------------------------------------------
+Parameter Name Selected Description
+-------------------------------------------------------------------------------
+TSPC_AVDTP_12_1 False Content security control response (O)
+-------------------------------------------------------------------------------
+
+
+ Message Fragmentation (Acceptor)
+-------------------------------------------------------------------------------
+Parameter Name Selected Description
+-------------------------------------------------------------------------------
+TSPC_AVDTP_13_1 True Signaling message fragmentation (M)
+-------------------------------------------------------------------------------
+
+
+ Source Capabilities
+-------------------------------------------------------------------------------
+Parameter Name Selected Description
+-------------------------------------------------------------------------------
+TSPC_AVDTP_14_1 True Basic transport service support (M)
+TSPC_AVDTP_14_2 False Reporting service support (O)
+TSPC_AVDTP_14_3 False Recovery service support (O)
+TSPC_AVDTP_14_4 False Multiplexing service support (O)
+TSPC_AVDTP_14_5 False Robust header compression service support (O)
+TSPC_AVDTP_14_6 True (*) Delay Reporting (C.1)
+-------------------------------------------------------------------------------
+C.1: It is optional to support if TSPC_AVDTP_0_3 is supported, else excluded.
+-------------------------------------------------------------------------------
+
+
+ Sink Capabilities
+-------------------------------------------------------------------------------
+Parameter Name Selected Description
+-------------------------------------------------------------------------------
+TSPC_AVDTP_15_1 True Basic transport service support (M)
+TSPC_AVDTP_15_2 False Reporting service support (O)
+TSPC_AVDTP_15_3 False Recovery service support (O)
+TSPC_AVDTP_15_4 False Multiplexing service support (O)
+TSPC_AVDTP_15_5 False Robust header compression service support (O)
+TSPC_AVDTP_15_6 True (*) Delay Reporting (C.1)
+-------------------------------------------------------------------------------
+C.1: It is optional to support if TSPC_AVDTP_0_3 is supported, else excluded.
+-------------------------------------------------------------------------------
+
+
+ Message Error Handling Capabilities
+-------------------------------------------------------------------------------
+Parameter Name Selected Description
+-------------------------------------------------------------------------------
+TSPC_AVDTP_16_1 False Reporting Capability Error (C.1)
+TSPC_AVDTP_16_2 False Reject Corrupted Messages (C.2)
+TSPC_AVDTP_16_3 True (*) General Reject Response Includes Signal ID (C.3)
+-------------------------------------------------------------------------------
+C.1: Optional if TSPC_AVDTP_0_2 or TSPC_AVDTP_0_3 supported, excluded
+ otherwise.
+C.2: Optional, excluded if TSPC_AVDTP_16_3 (General Reject Response Includes
+ Signal ID) is supported.
+C.3: Mandatory if TSPC_AVDTP_0_3 supported, otherwise Optional.
+-------------------------------------------------------------------------------
+
+
+ Upper Test Interface
+-------------------------------------------------------------------------------
+Parameter Name Selected Description
+-------------------------------------------------------------------------------
+TSPC_AVDTP_17_1 False Upper Test Interface provided (O)
+-------------------------------------------------------------------------------
+
+
+ L2CAP Capabilities
+-------------------------------------------------------------------------------
+Parameter Name Selected Description
+-------------------------------------------------------------------------------
+TSPC_AVDTP_18_1 False Enhanced Retransmission Mode preferred for
+ signaling channel (O)
+TSPC_AVDTP_18_2 False Streaming Mode preferred for Media Transport
+ channel (O)
+TSPC_AVDTP_18_3 False FCS Option (C.1)
+-------------------------------------------------------------------------------
+C.1: Mandatory if TSPC_AVDTP_18_1 is supported, otherwise Optional.
+-------------------------------------------------------------------------------
diff --git a/repo/android/pics-avrcp.txt b/repo/android/pics-avrcp.txt
new file mode 100644
index 0000000..7bd68fa
--- /dev/null
+++ b/repo/android/pics-avrcp.txt
@@ -0,0 +1,644 @@
+AVRCP PICS for the PTS tool.
+
+PTS version: 6.1
+
+* - different than PTS defaults
+# - not yet implemented/supported
+
+M - mandatory if such role selected
+O - optional
+
+ Roles
+-------------------------------------------------------------------------------
+Parameter Name Selected Description
+-------------------------------------------------------------------------------
+TSPC_AVRCP_1_1 True (*) Role: Controller (CT) (C.1)
+TSPC_AVRCP_1_2 True (*) Role: Target (TG) (C.1)
+-------------------------------------------------------------------------------
+C.1: Mandatory to support at least one of the defined roles.
+-------------------------------------------------------------------------------
+
+
+ Controller Features
+-------------------------------------------------------------------------------
+Parameter Name Selected Description
+-------------------------------------------------------------------------------
+TSPC_AVRCP_2_1 False (*) CT: Initiating connection establishment (M)
+TSPC_AVRCP_2_2 False (*) CT: Accepting connection establishment for
+ control initiated by TG (M)
+TSPC_AVRCP_2_3 False (*) CT: Initiating connection release (M)
+TSPC_AVRCP_2_4 False (*) CT: Accepting connection release for control
+ initiated by TG (M)
+TSPC_AVRCP_2_5 False CT: Sending UNIT INFO (O)
+TSPC_AVRCP_2_6 False CT: Sending SUBUNIT INFO (O)
+TSPC_AVRCP_2_7 False CT: Sending PASS THROUGH command category 1
+ (C.1)
+TSPC_AVRCP_2_8 False CT: Sending PASS THROUGH command category 2
+ (C.1)
+TSPC_AVRCP_2_9 False CT: Sending PASS THROUGH command category 3
+ (C.1)
+TSPC_AVRCP_2_10 False CT: Sending PASS THROUGH command category 4
+ (C.1)
+TSPC_AVRCP_2_11 False CT: Get Capabilities (O)
+TSPC_AVRCP_2_12 False CT: List Player Application Setting
+ Attributes (C.9)
+TSPC_AVRCP_2_13 False CT: List Player Application Setting Values (O)
+TSPC_AVRCP_2_14 False CT: Get Current Player Application Setting
+ (C.10)
+TSPC_AVRCP_2_15 False CT: Set Player Application Setting Value (C.10)
+TSPC_AVRCP_2_16 False CT: Get Player Application Setting
+ Attribute Text (O)
+TSPC_AVRCP_2_17 False CT: Get Player Application Setting Value Text
+ (O)
+TSPC_AVRCP_2_18 False CT: Inform Displayable Character Set (O)
+TSPC_AVRCP_2_19 False CT: Inform Battery Status of CT (O)
+TSPC_AVRCP_2_20 False CT: Get Element Attributes (O)
+TSPC_AVRCP_2_21 False CT: Get Play Status (O)
+TSPC_AVRCP_2_22 False CT: Register Notification (C.11)
+TSPC_AVRCP_2_23 False CT: Request Continuing Response (C.2)
+TSPC_AVRCP_2_24 False CT: Abort Continuing Response (C.2)
+TSPC_AVRCP_2_25 False CT: Next Group (C.12)
+TSPC_AVRCP_2_26 False CT: Previous Group (C.12)
+TSPC_AVRCP_2_27 False CT: Media Player Selection (O)
+TSPC_AVRCP_2_28 False CT: SetAddressedPlayer (O)
+TSPC_AVRCP_2_29 False CT: GetFolderItems(MediaPlayerList) (C.5)
+TSPC_AVRCP_2_29b False CT: GetTotalNumberOfItems(MediaPlayerList) (C.5)
+TSPC_AVRCP_2_30 False CT: EVENT_AVAILABLE_PLAYERS_CHANGED (O)
+TSPC_AVRCP_2_31 False CT: EVENT_ADDRESSED_PLAYER_CHANGED (O)
+TSPC_AVRCP_2_32 False CT: Browsing (O)
+TSPC_AVRCP_2_33 False CT: SetBrowsedPlayer (C.4)
+TSPC_AVRCP_2_34 False CT: ChangePath (C.4)
+TSPC_AVRCP_2_35 False CT: GetFolderItems(Filesystem) (C.4)
+TSPC_AVRCP_2_35b False CT: GetTotalNumberOfItems(Filesystem) (C.4)
+TSPC_AVRCP_2_36 False CT: GetItemAttributes (O)
+TSPC_AVRCP_2_37 False CT: PlayItem(Filesystem) (C.4)
+TSPC_AVRCP_2_38 False CT: EVENT_UIDS_CHANGED (O)
+TSPC_AVRCP_2_39 False CT: Searching (O)
+TSPC_AVRCP_2_40 False CT: Search (C.7)
+TSPC_AVRCP_2_41 False CT: GetFolderItems(Search Results) (C.7)
+TSPC_AVRCP_2_41b False CT: GetTotalNumberOfItems(Search Results) (C.7)
+TSPC_AVRCP_2_42 False CT: PlayItem(SearchResultList) (C.7)
+TSPC_AVRCP_2_43 False CT: NowPlaying (C.8)
+TSPC_AVRCP_2_44 False CT: GetFolderItems(NowPlayingList) (C.8)
+TSPC_AVRCP_2_44b False CT: GetTotalNumberOfItems(NowPlayingList) (C.8)
+TSPC_AVRCP_2_45 False CT: PlayItem(NowPlayingList) (C.8)
+TSPC_AVRCP_2_46 False CT: AddToNowPlaying (O)
+TSPC_AVRCP_2_47 False CT: EVENT_NOW_PLAYING_CONTENT_CHANGED (O)
+TSPC_AVRCP_2_48 False CT: Playable Folders (O)
+TSPC_AVRCP_2_49 True (*) CT: Absolute Volume (C.3)
+TSPC_AVRCP_2_50 True (*) CT: SetAbsoluteVolume (C.3)
+TSPC_AVRCP_2_51 True (*) CT: NotifyVolumeChange (C.3)
+TSPC_AVRCP_2_52 False (*) CT: Discoverable Mode (M)
+TSPC_AVRCP_2_53 False CT: PASSTHROUGH operation supporting press
+ and hold (O)
+TSPC_AVRCP_2_54 False CT: Cover Art (O)
+TSPC_AVRCP_2_55 False CT: GetImageProperties (C.14)
+TSPC_AVRCP_2_56 False CT: GetImage (C.13)
+TSPC_AVRCP_2_57 False CT: GetLinkedThumbnail (C.13)
+-------------------------------------------------------------------------------
+C.1: Mandatory to support at least one of the defined categories
+ (TSPC_AVRCP_2_7 through TSPC_AVRCP_2_10).
+C.2: Mandatory to support at least one of TSPC_AVRCP_2_23 or TSPC_AVRC_2_24
+ if TSPC_AVRCP_2_20 is supported, otherwise Optional.
+C.3: Mandatory if TSPC_AVRCP_2_8 is supported, otherwise Excluded.
+C.4: Mandatory if TSPC_AVRCP_2_32 is supported, otherwise Excluded.
+C.5: Mandatory if TSPC_AVRCP_2_27 is supported, otherwise Excluded.
+C.7: Mandatory if item TSPC_AVRCP_2_39 is supported, Excluded otherwise.
+C.8: Mandatory if TSPC_AVRCP_2_32 is supported, otherwise Excluded.
+C.9: Mandatory to support if Player Application Settings feature is supported.
+ If any item TSPC_AVRCP_2_13 through TSPC_AVRCP_2_15 is supported it is
+ required to claim support for this feature in accordance with Player
+ Application Settings support requirements, otherwise Optional.
+C.10: Mandatory to support either Get or Set Player Application Settings
+ (TSPC_AVRCP_2_14 or TSPC_AVRCP_2_15) if List Player Application Setting
+ Attributes (TSPC_AVRCP_2_12) is supported. Either TSPC_AVRCP_2_14
+ or TSPC_AVRCP_2_15 must be supported if Player Application Settings
+ feature is supported, in accordance with Player Application Settings
+ support requirements.
+C.11: Mandatory if TSPC_AVRCP_2_20 or TSPC_AVRCP_2_49 is supported, otherwise
+ Optional.
+C.12: Mandatory if Basic Group Navigation Feature supported. If any item
+ TSPC_AVRCP_2_25 or TSPC_AVRCP_2_26 is supported it is mandatory to
+ support both, in accordance with Basic Group Navigation support
+ requirements, otherwise Excluded.
+C.13: Mandatory to support at least one of the functions if TSPC_AVRCP_2_54
+ (Cover Art) is support, otherwise Excluded.
+C.14: Optional if TSPC_AVRCP_2_54 (Cover Art) is supported, otherwise Excluded.
+-------------------------------------------------------------------------------
+
+
+ Controller Profile Version
+-------------------------------------------------------------------------------
+Parameter Name Selected Description
+-------------------------------------------------------------------------------
+TSPC_AVRCP_2b_1 False CT: AVRCP v1.0 (C.1)
+TSPC_AVRCP_2b_2 False CT: AVRCP v1.3 (C.1)
+TSPC_AVRCP_2b_3 False CT: AVRCP v1.4 (C.1)
+TSPC_AVRCP_2b_4 False CT: AVRCP v1.5 (C.1)
+TSPC_AVRCP_2b_5 False CT: AVRCP v1.6 (C.1)
+-------------------------------------------------------------------------------
+C.1: It is mandatory to support at least one of the profile versions if
+ Controller role supported (SPC_AVRCP_1_1).
+-------------------------------------------------------------------------------
+
+
+ Operation_id of Category 1 for CT
+-------------------------------------------------------------------------------
+Parameter Name Selected Description
+-------------------------------------------------------------------------------
+TSPC_AVRCP_3_1 False CT: category 1 - Operation id: 0 (C.1)
+TSPC_AVRCP_3_2 False CT: category 1 - Operation id: 1 (C.1)
+TSPC_AVRCP_3_3 False CT: category 1 - Operation id: 2 (C.1)
+TSPC_AVRCP_3_4 False CT: category 1 - Operation id: 3 (C.1)
+TSPC_AVRCP_3_5 False CT: category 1 - Operation id: 4 (C.1)
+TSPC_AVRCP_3_6 False CT: category 1 - Operation id: 5 (C.1)
+TSPC_AVRCP_3_7 False CT: category 1 - Operation id: 6 (C.1)
+TSPC_AVRCP_3_8 False CT: category 1 - Operation id: 7 (C.1)
+TSPC_AVRCP_3_9 False CT: category 1 - Operation id: 8 (C.1)
+TSPC_AVRCP_3_10 False CT: category 1 - Operation id: 9 (C.1)
+TSPC_AVRCP_3_11 False CT: category 1 - Operation id: dot (C.1)
+TSPC_AVRCP_3_12 False CT: category 1 - Operation id: enter (C.1)
+TSPC_AVRCP_3_13 False CT: category 1 - Operation id: clear (C.1)
+TSPC_AVRCP_3_14 False CT: category 1 - Operation id: sound_select
+ (C.1)
+TSPC_AVRCP_3_15 False CT: category 1 - Operation id: input_select
+ (C.1)
+TSPC_AVRCP_3_16 False CT: category 1 - Operation id:
+ display_information (C.1)
+TSPC_AVRCP_3_17 False CT: category 1 - Operation id: help (C.1)
+TSPC_AVRCP_3_18 False CT: category 1 - Operation id: power (C.1)
+TSPC_AVRCP_3_19 False CT: category 1 - Operation id: play (C.1)
+TSPC_AVRCP_3_20 False CT: category 1 - Operation id: stop (C.1)
+TSPC_AVRCP_3_21 False CT: category 1 - Operation id: pause (C.1)
+TSPC_AVRCP_3_22 False CT: category 1 - Operation id: record (C.1)
+TSPC_AVRCP_3_23 False CT: category 1 - Operation id: rewind (C.1)
+TSPC_AVRCP_3_24 False CT: category 1 - Operation id: fast_forward
+ (C.1)
+TSPC_AVRCP_3_25 False CT: category 1 - Operation id: eject (C.1)
+TSPC_AVRCP_3_26 False CT: category 1 - Operation id: forward (C.1)
+TSPC_AVRCP_3_27 False CT: category 1 - Operation id: backward (C.1)
+TSPC_AVRCP_3_28 False CT: category 1 - Operation id: angle (C.1)
+TSPC_AVRCP_3_29 False CT: category 1 - Operation id: subpicture (C.1)
+TSPC_AVRCP_3_30 False CT: category 1 - Operation id: F1 (C.1)
+TSPC_AVRCP_3_31 False CT: category 1 - Operation id: F2 (C.1)
+TSPC_AVRCP_3_32 False CT: category 1 - Operation id: F3 (C.1)
+TSPC_AVRCP_3_33 False CT: category 1 - Operation id: F4 (C.1)
+TSPC_AVRCP_3_34 False CT: category 1 - Operation id: vendor_unique
+ (C.1)
+-------------------------------------------------------------------------------
+C.1: Mandatory to support at least one of these operation_ids if the device
+ supports category 1 (TSPC_AVRCP_2_7).
+-------------------------------------------------------------------------------
+
+
+ Operation_id of category 2 for CT
+-------------------------------------------------------------------------------
+Parameter Name Selected Description
+-------------------------------------------------------------------------------
+TSPC_AVRCP_4_1 False CT: category 2 - Operation id: 0 (C.1)
+TSPC_AVRCP_4_2 False CT: category 2 - Operation id: 1 (C.1)
+TSPC_AVRCP_4_3 False CT: category 2 - Operation id: 2 (C.1)
+TSPC_AVRCP_4_4 False CT: category 2 - Operation id: 3 (C.1)
+TSPC_AVRCP_4_5 False CT: category 2 - Operation id: 4 (C.1)
+TSPC_AVRCP_4_6 False CT: category 2 - Operation id: 5 (C.1)
+TSPC_AVRCP_4_7 False CT: category 2 - Operation id: 6 (C.1)
+TSPC_AVRCP_4_8 False CT: category 2 - Operation id: 7 (C.1)
+TSPC_AVRCP_4_9 False CT: category 2 - Operation id: 8 (C.1)
+TSPC_AVRCP_4_10 False CT: category 2 - Operation id: 9 (C.1)
+TSPC_AVRCP_4_11 False CT: category 2 - Operation id: dot (C.1)
+TSPC_AVRCP_4_12 False CT: category 2 - Operation id: enter (C.1)
+TSPC_AVRCP_4_13 False CT: category 2 - Operation id: clear (C.1)
+TSPC_AVRCP_4_14 False CT: category 2 - Operation id: sound_select
+ (C.1)
+TSPC_AVRCP_4_15 False CT: category 2 - Operation id: input_select
+ (C.1)
+TSPC_AVRCP_4_16 False CT: category 2 - Operation id:
+ display_information (C.1)
+TSPC_AVRCP_4_17 False CT: category 2 - Operation id: help (C.1)
+TSPC_AVRCP_4_18 False CT: category 2 - Operation id: power (C.1)
+TSPC_AVRCP_4_19 False CT: category 2 - Operation id: volume_up (C.1)
+TSPC_AVRCP_4_20 False CT: category 2 - Operation id: volume_down (C.1)
+TSPC_AVRCP_4_21 False CT: category 2 - Operation id: mute (C.1)
+TSPC_AVRCP_4_22 False CT: category 2 - Operation id: F1 (C.1)
+TSPC_AVRCP_4_23 False CT: category 2 - Operation id: F2 (C.1)
+TSPC_AVRCP_4_24 False CT: category 2 - Operation id: F3 (C.1)
+TSPC_AVRCP_4_25 False CT: category 2 - Operation id: F4 (C.1)
+TSPC_AVRCP_4_26 False CT: category 2 - Operation id: vendor_unique
+ (C.1)
+-------------------------------------------------------------------------------
+C.1: Mandatory to support at least one of these operation_ids if the device
+ supports category 2 (TSPC_AVRCP_2_8).
+-------------------------------------------------------------------------------
+
+
+ Operation_id of category 3 for CT
+-------------------------------------------------------------------------------
+Parameter Name Selected Description
+-------------------------------------------------------------------------------
+TSPC_AVRCP_5_1 False CT: category 3 - Operation id: 0 (C.1)
+TSPC_AVRCP_5_2 False CT: category 3 - Operation id: 1 (C.1)
+TSPC_AVRCP_5_3 False CT: category 3 - Operation id: 2 (C.1)
+TSPC_AVRCP_5_4 False CT: category 3 - Operation id: 3 (C.1)
+TSPC_AVRCP_5_5 False CT: category 3 - Operation id: 4 (C.1)
+TSPC_AVRCP_5_6 False CT: category 3 - Operation id: 5 (C.1)
+TSPC_AVRCP_5_7 False CT: category 3 - Operation id: 6 (C.1)
+TSPC_AVRCP_5_8 False CT: category 3 - Operation id: 7 (C.1)
+TSPC_AVRCP_5_9 False CT: category 3 - Operation id: 8 (C.1)
+TSPC_AVRCP_5_10 False CT: category 3 - Operation id: 9 (C.1)
+TSPC_AVRCP_5_11 False CT: category 3 - Operation id: dot (C.1)
+TSPC_AVRCP_5_12 False CT: category 3 - Operation id: enter (C.1)
+TSPC_AVRCP_5_13 False CT: category 3 - Operation id: clear (C.1)
+TSPC_AVRCP_5_14 False CT: category 3 - Operation id: channel up (C.1)
+TSPC_AVRCP_5_15 False CT: category 3 - Operation id: channel down
+ (C.1)
+TSPC_AVRCP_5_16 False CT: category 3 - Operation id: previous channel
+ (C.1)
+TSPC_AVRCP_5_17 False CT: category 3 - Operation id: sound_select
+ (C.1)
+TSPC_AVRCP_5_18 False CT: category 3 - Operation id: input_select
+ (C.1)
+TSPC_AVRCP_5_19 False CT: category 3 - Operation id:
+ display_information (C.1)
+TSPC_AVRCP_5_20 False CT: category 3 - Operation id: help (C.1)
+TSPC_AVRCP_5_21 False CT: category 3 - Operation id: power (C.1)
+TSPC_AVRCP_5_22 False CT: category 3 - Operation id: angle (C.1)
+TSPC_AVRCP_5_23 False CT: category 3 - Operation id: subpicture(C.1)
+TSPC_AVRCP_5_24 False CT: category 3 - Operation id: F1 (C.1)
+TSPC_AVRCP_5_25 False CT: category 3 - Operation id: F2 (C.1)
+TSPC_AVRCP_5_26 False CT: category 3 - Operation id: F3 (C.1)
+TSPC_AVRCP_5_27 False CT: category 3 - Operation id: F4 (C.1)
+TSPC_AVRCP_5_28 False CT: category 3 - Operation id: vendor_unique
+ (C.1)
+-------------------------------------------------------------------------------
+C.1: Mandatory to support at least one of these operation_ids if the device
+ supports category 3 (TSPC_AVRCP_2_9).
+-------------------------------------------------------------------------------
+
+
+ Operation_id of category 4 for CT
+-------------------------------------------------------------------------------
+Parameter Name Selected Description
+-------------------------------------------------------------------------------
+TSPC_AVRCP_6_1 False CT: category 4 - Operation id: select (C.1)
+TSPC_AVRCP_6_2 False CT: category 4 - Operation id: up (C.1)
+TSPC_AVRCP_6_3 False CT: category 4 - Operation id: down (C.1)
+TSPC_AVRCP_6_4 False CT: category 4 - Operation id: left (C.1)
+TSPC_AVRCP_6_5 False CT: category 4 - Operation id: right (C.1)
+TSPC_AVRCP_6_6 False CT: category 4 - Operation id: right up (C.1)
+TSPC_AVRCP_6_7 False CT: category 4 - Operation id: right down (C.1)
+TSPC_AVRCP_6_8 False CT: category 4 - Operation id: left up (C.1)
+TSPC_AVRCP_6_9 False CT: category 4 - Operation id: left down (C.1)
+TSPC_AVRCP_6_10 False CT: category 4 - Operation id: root menu (C.1)
+TSPC_AVRCP_6_11 False CT: category 4 - Operation id: setup menu (C.1)
+TSPC_AVRCP_6_12 False CT: category 4 - Operation id: contents menu
+ (C.1)
+TSPC_AVRCP_6_13 False CT: category 4 - Operation id: favorite menu
+ (C.1)
+TSPC_AVRCP_6_14 False CT: category 4 - Operation id: exit (C.1)
+TSPC_AVRCP_6_15 False CT: category 4 - Operation id: 0 (C.1)
+TSPC_AVRCP_6_16 False CT: category 4 - Operation id: 1 (C.1)
+TSPC_AVRCP_6_17 False CT: category 4 - Operation id: 2 (C.1)
+TSPC_AVRCP_6_18 False CT: category 4 - Operation id: 3 (C.1)
+TSPC_AVRCP_6_19 False CT: category 4 - Operation id: 4 (C.1)
+TSPC_AVRCP_6_20 False CT: category 4 - Operation id: 5 (C.1)
+TSPC_AVRCP_6_21 False CT: category 4 - Operation id: 6 (C.1)
+TSPC_AVRCP_6_22 False CT: category 4 - Operation id: 7 (C.1)
+TSPC_AVRCP_6_23 False CT: category 4 - Operation id: 8 (C.1)
+TSPC_AVRCP_6_24 False CT: category 4 - Operation id: 9 (C.1)
+TSPC_AVRCP_6_25 False CT: category 4 - Operation id: dot (C.1)
+TSPC_AVRCP_6_26 False CT: category 4 - Operation id: enter (C.1)
+TSPC_AVRCP_6_27 False CT: category 4 - Operation id: clear (C.1)
+TSPC_AVRCP_6_28 False CT: category 4 - Operation id:
+ display_information (C.1)
+TSPC_AVRCP_6_29 False CT: category 4 - Operation id: help (C.1)
+TSPC_AVRCP_6_30 False CT: category 4 - Operation id: page up (C.1)
+TSPC_AVRCP_6_31 False CT: category 4 - Operation id: page down (C.1)
+TSPC_AVRCP_6_32 False CT: category 4 - Operation id: power (C.1)
+TSPC_AVRCP_6_33 False CT: category 4 - Operation id: F1 (C.1)
+TSPC_AVRCP_6_34 False CT: category 4 - Operation id: F2 (C.1)
+TSPC_AVRCP_6_35 False CT: category 4 - Operation id: F3 (C.1)
+TSPC_AVRCP_6_36 False CT: category 4 - Operation id: F4 (C.1)
+TSPC_AVRCP_6_37 False CT: category 4 - Operation id: vendor_unique
+ (C.1)
+-------------------------------------------------------------------------------
+C.1: Mandatory to support at least one of these operation_ids if the device
+ supports category 4 (TSPC_AVRCP_2_10).
+-------------------------------------------------------------------------------
+
+
+ Target Features
+-------------------------------------------------------------------------------
+Parameter Name Selected Description
+-------------------------------------------------------------------------------
+TSPC_AVRCP_7_1 True (*) TG: Initiating connection establishment for
+ Control (O)
+TSPC_AVRCP_7_2 True TG: Accept connection establishment for Control
+ initiated by CT (M)
+TSPC_AVRCP_7_3 True (*) TG: Initiating connection release (M)
+TSPC_AVRCP_7_4 True TG: Accepting connection release (M)
+TSPC_AVRCP_7_5 True TG: Receiving UNIT INFO (M)
+TSPC_AVRCP_7_6 True TG: Receiving SUBUNIT INFO (M)
+TSPC_AVRCP_7_7 True (*) TG: Receiving PASS THROUGH command category 1
+ (C.1)
+TSPC_AVRCP_7_8 True (*) TG: Receiving PASS THROUGH command category 2
+ (C.1)
+TSPC_AVRCP_7_9 False TG: Receiving PASS THROUGH command category 3
+ (C.1)
+TSPC_AVRCP_7_10 False TG: Receiving PASS THROUGH command category 4
+ (C.1)
+TSPC_AVRCP_7_11 True (*) TG: Get Capabilities Response (C.3)
+TSPC_AVRCP_7_12 False TG: List Player Application Settings Attributes
+ Response (C.14)
+TSPC_AVRCP_7_13 False TG: List Player Application Setting Values
+ Response (C.14)
+TSPC_AVRCP_7_14 False TG: Get Current Player Application Settings
+ Value Response (C.14)
+TSPC_AVRCP_7_15 False TG: Set Player Application Setting Value
+ Response (C.14)
+TSPC_AVRCP_7_16 False TG: Get Player Application Setting Attribute
+ Text Response (O)
+TSPC_AVRCP_7_17 False TG: Get Player Application Setting Value Text
+ Response (O)
+TSPC_AVRCP_7_18 False TG: Inform Displayable Character Set Response
+ (O)
+TSPC_AVRCP_7_19 False TG: Inform Battery Status Of CT Response (O)
+TSPC_AVRCP_7_20 True (*) TG: Get Element Attributes Response (C.3)
+TSPC_AVRCP_7_21 True (*) TG: Get Play Status Response (C.2)
+TSPC_AVRCP_7_22 True (*) TG: Register Notification Response (C.12)
+TSPC_AVRCP_7_23 True (*) TG: Notify Event Response:
+ PLAYBACK_STATUS_CHANGED (C.4)
+TSPC_AVRCP_7_24 True (*) TG: Notify Event Response: TRACK_CHANGED (C.4)
+TSPC_AVRCP_7_25 False TG: Notify Event Response: TRACK_REACHED_END (O)
+TSPC_AVRCP_7_26 False TG: Notify Event Response: TRACK_REACHED_START
+ (O)
+TSPC_AVRCP_7_27 False TG: Notify Event Response: PLAYBACK_POS_CHANGED
+ (O)
+TSPC_AVRCP_7_28 False TG: Notify Event Response: BATT_STATUS_CHANGED
+ (O)
+TSPC_AVRCP_7_29 False TG: Notify Event Response: SYSTEM_STATUS_CHANGED
+ (O)
+TSPC_AVRCP_7_30 False TG: Notify Event Response:
+ PLAYER_APPLICATION_SETTING_CHANGED (O)
+TSPC_AVRCP_7_31 True (*) TG: Request Continuing Response (C.2)
+TSPC_AVRCP_7_32 True (*) TG: Abort ContinuingResponse Response (C.2)
+TSPC_AVRCP_7_34 False TG: Next Group (C.15)
+TSPC_AVRCP_7_35 False TG: Previous Group (C.15)
+TSPC_AVRCP_7_36 False TG: Media Player Selection (C.8)
+TSPC_AVRCP_7_37 False TG: SetAddressedPlayer (C.8)
+TSPC_AVRCP_7_38 False TG: GetFolderItems(MediaPlayerList) (C.8)
+TSPC_AVRCP_7_38b False TG: GetTotalNumberOfItems(MediaPlayerList) (C.8)
+TSPC_AVRCP_7_39 False TG: EVENT_AVAILABLE_PLAYERS_CHANGED (C.8)
+TSPC_AVRCP_7_40 False TG: EVENT_ADDRESSED_PLAYER_CHANGED (C.8)
+TSPC_AVRCP_7_41 False TG: Supports Multiple Players (O)
+TSPC_AVRCP_7_42 False TG: Browsing (O)
+TSPC_AVRCP_7_42a False TG: Initiating connection establishment for
+ browsing channel (O)
+TSPC_AVRCP_7_43 False TG: SetBrowsedPlayer (C.6)
+TSPC_AVRCP_7_44 False TG: ChangePath (C.6)
+TSPC_AVRCP_7_45 False TG: GetFolderItems(Filesystem) (C.6)
+TSPC_AVRCP_7_45b False TG: GetTotalNumberOfItems(Filesystem) (C.6)
+TSPC_AVRCP_7_46 False TG: GetItemAttributes (C.6)
+TSPC_AVRCP_7_47 False TG: PlayItem(Filesystem) (C.6)
+TSPC_AVRCP_7_48 False TG: EVENT_UIDS_CHANGED (C.9)
+TSPC_AVRCP_7_49 False TG: Database Aware Players (O)
+TSPC_AVRCP_7_50 False TG: Searching (O)
+TSPC_AVRCP_7_51 False TG: Search (C.10)
+TSPC_AVRCP_7_52 False TG: GetFolderItems(Search Results) (C.10)
+TSPC_AVRCP_7_52b False TG: GetTotalNumberOfItems(Search Results) (C.10)
+TSPC_AVRCP_7_53 False TG: PlayItem(SearchResultList) (C.10)
+TSPC_AVRCP_7_54 False TG: NowPlaying (C.11)
+TSPC_AVRCP_7_55 False TG: GetFolderItems(NowPlayingList) (C.11)
+TSPC_AVRCP_7_55b False TG: GetTotalNumberOfItems(NowPlayingList) (C.11)
+TSPC_AVRCP_7_56 False TG: PlayItem(NowPlayingList) (C.11)
+TSPC_AVRCP_7_57 False TG: AddToNowPlaying (O)
+TSPC_AVRCP_7_58 False TG: EVENT_NOW_PLAYING_CONTENT_CHANGED (C.11)
+TSPC_AVRCP_7_59 False TG: Playable Folders (O)
+TSPC_AVRCP_7_60 False TG: Absolute Volume (C.5)
+TSPC_AVRCP_7_61 False TG: SetAbsoluteVolume (C.5)
+TSPC_AVRCP_7_62 False TG: NotifyVolumeChange (C.5)
+TSPC_AVRCP_7_63 False TG: Error Response (O)
+TSPC_AVRCP_7_64 False TG: General Reject (C.13)
+TSPC_AVRCP_7_65 True TG: Discoverable Mode (M)
+TSPC_AVRCP_7_66 False TG: PASSTHROUGH operation supporting press
+ and hold (O)
+TSPC_AVRCP_7_67 False TG: Cover Art (O)
+TSPC_AVRCP_7_68 False TG: GetImageProperties (C.16)
+TSPC_AVRCP_7_69 False TG: GetImage (C.16)
+TSPC_AVRCP_7_70 False TG: GetLinkedThumbnail (C.16)
+-------------------------------------------------------------------------------
+C.1: Mandatory to support at least one of the categories. Supported
+ operation_id's are shown in Table 8 to Table 11.
+C.2: Mandatory if 7/20 is supported, otherwise Optional.
+C.3: Mandatory if 7/7 is supported, otherwise Optional.
+C.4: Mandatory if 7/22 and 7/20 is supported, otherwise Optional.
+C.5: Mandatory if 7/8 is supported, otherwise Excluded.
+C.6: Mandatory if 7/42 is supported, otherwise Excluded.
+C.7: Mandatory if 7/36 is supported, otherwise Excluded.
+C.8: Mandatory if (7/7 or 7/9) is supported, otherwise Excluded.
+C.9: Mandatory if 7/49 is supported, otherwise Optional.
+C.10: Mandatory if 7/50 is supported, otherwise Excluded.
+C.11: Mandatory if 7/42 is supported, otherwise Optional.
+C.12: Mandatory if 7/7 or (7/8 AND 7/60) or 7/9 is supported, otherwise Optional
+C.13: Mandatory if 7/7 or 7/9 or 7/42 is supported, otherwise Optional.
+C.14: Mandatory if Player Application Settings Feature supported. If any item
+ 7/12 – 7/15 is supported, all items 7/12 – 7/15 shall be supported,
+ in accordance with Player Application Settings Feature support
+ requirements, otherwise Excluded.
+C.15: Mandatory if Basic Group Navigation Feature supported. If any item
+ 7/34 or 7/35 is supported it is mandatory to support both,
+ in accordance with Basic Group Navigation support requirements,
+ otherwise Excluded.
+C.16: Mandatory if 7/67 (Cover Art) is supported, otherwise Excluded.
+-------------------------------------------------------------------------------
+
+ Target Profile Version
+-------------------------------------------------------------------------------
+Parameter Name Selected Description
+-------------------------------------------------------------------------------
+TSPC_AVRCP_7b_1 False TG: AVRCP v1.0 (C.1)
+TSPC_AVRCP_7b_2 False TG: AVRCP v1.3 (C.1)
+TSPC_AVRCP_7b_3 False TG: AVRCP v1.4 (C.1)
+TSPC_AVRCP_7b_4 True (*) TG: AVRCP v1.5 (C.1)
+TSPC_AVRCP_7b_5 False TG: AVRCP v1.6 (C.1)
+-------------------------------------------------------------------------------
+C.1: It is mandatory to support at least one of the profile versions.
+-------------------------------------------------------------------------------
+
+
+ operation_id of category 1 for TG
+-------------------------------------------------------------------------------
+Parameter Name Selected Description
+-------------------------------------------------------------------------------
+TSPC_AVRCP_8_1 False TG: category 1 - Operation id: 0 (O)
+TSPC_AVRCP_8_2 False TG: category 1 - Operation id: 1 (O)
+TSPC_AVRCP_8_3 False TG: category 1 - Operation id: 2 (O)
+TSPC_AVRCP_8_4 False TG: category 1 - Operation id: 3 (O)
+TSPC_AVRCP_8_5 False TG: category 1 - Operation id: 4 (O)
+TSPC_AVRCP_8_6 False TG: category 1 - Operation id: 5 (O)
+TSPC_AVRCP_8_7 False TG: category 1 - Operation id: 6 (O)
+TSPC_AVRCP_8_8 False TG: category 1 - Operation id: 7 (O)
+TSPC_AVRCP_8_9 False TG: category 1 - Operation id: 8 (O)
+TSPC_AVRCP_8_10 False TG: category 1 - Operation id: 9 (O)
+TSPC_AVRCP_8_11 False TG: category 1 - Operation id: dot (O)
+TSPC_AVRCP_8_12 False TG: category 1 - Operation id: enter (O)
+TSPC_AVRCP_8_13 False TG: category 1 - Operation id: clear (O)
+TSPC_AVRCP_8_14 False TG: category 1 - Operation id: sound select (O)
+TSPC_AVRCP_8_15 False TG: category 1 - Operation id: input select (O)
+TSPC_AVRCP_8_16 False TG: category 1 - Operation id: display
+ information (O)
+TSPC_AVRCP_8_17 False TG: category 1 - Operation id: help (O)
+TSPC_AVRCP_8_18 False TG: category 1 - Operation id: power (O)
+TSPC_AVRCP_8_19 True TG: category 1 - Operation id: play (M)
+TSPC_AVRCP_8_20 True TG: category 1 - Operation id: stop (M)
+TSPC_AVRCP_8_21 True (*) TG: category 1 - Operation id: pause (O)
+TSPC_AVRCP_8_22 False TG: category 1 - Operation id: record (O)
+TSPC_AVRCP_8_23 True (*) TG: category 1 - Operation id: rewind (O)
+TSPC_AVRCP_8_24 True (*) TG: category 1 - Operation id: fast forward (O)
+TSPC_AVRCP_8_25 False TG: category 1 - Operation id: eject (O)
+TSPC_AVRCP_8_26 True (*) TG: category 1 - Operation id: forward (O)
+TSPC_AVRCP_8_27 True (*) TG: category 1 - Operation id: backward (O)
+TSPC_AVRCP_8_28 False TG: category 1 - Operation id: angle (O)
+TSPC_AVRCP_8_29 False TG: category 1 - Operation id: subpicture (O)
+TSPC_AVRCP_8_30 False TG: category 1 - Operation id: F1 (O)
+TSPC_AVRCP_8_31 False TG: category 1 - Operation id: F2 (O)
+TSPC_AVRCP_8_32 False TG: category 1 - Operation id: F3 (O)
+TSPC_AVRCP_8_33 False TG: category 1 - Operation id: F4 (O)
+TSPC_AVRCP_8_33a False TG: category 1 - Operation id: F5 (O)
+TSPC_AVRCP_8_34 False TG: category 1 - Operation id: vendor unique (O)
+-------------------------------------------------------------------------------
+
+
+ operation_id of category 2 for TG
+-------------------------------------------------------------------------------
+Parameter Name Selected Description
+-------------------------------------------------------------------------------
+TSPC_AVRCP_9_1 False TG: category 2 - Operation id: 0 (O)
+TSPC_AVRCP_9_2 False TG: category 2 - Operation id: 1 (O)
+TSPC_AVRCP_9_3 False TG: category 2 - Operation id: 2 (O)
+TSPC_AVRCP_9_4 False TG: category 2 - Operation id: 3 (O)
+TSPC_AVRCP_9_5 False TG: category 2 - Operation id: 4 (O)
+TSPC_AVRCP_9_6 False TG: category 2 - Operation id: 5 (O)
+TSPC_AVRCP_9_7 False TG: category 2 - Operation id: 6 (O)
+TSPC_AVRCP_9_8 False TG: category 2 - Operation id: 7 (O)
+TSPC_AVRCP_9_9 False TG: category 2 - Operation id: 8 (O)
+TSPC_AVRCP_9_10 False TG: category 2 - Operation id: 9 (O)
+TSPC_AVRCP_9_11 False TG: category 2 - Operation id: dot (O)
+TSPC_AVRCP_9_12 False TG: category 2 - Operation id: enter (O)
+TSPC_AVRCP_9_13 False TG: category 2 - Operation id: clear (O)
+TSPC_AVRCP_9_14 False TG: category 2 - Operation id: sound select (O)
+TSPC_AVRCP_9_15 False TG: category 2 - Operation id: input select (O)
+TSPC_AVRCP_9_16 False TG: category 2 - Operation id: display
+ information (O)
+TSPC_AVRCP_9_17 False TG: category 2 - Operation id: help (O)
+TSPC_AVRCP_9_18 False TG: category 2 - Operation id: power (O)
+TSPC_AVRCP_9_19 True TG: category 2 - Operation id: volume up (C.2)
+TSPC_AVRCP_9_20 True TG: category 2 - Operation id: volume down (C.2)
+TSPC_AVRCP_9_21 False TG: category 2 - Operation id: mute (O)
+TSPC_AVRCP_9_24 False TG: category 2 - Operation id: F1 (O)
+TSPC_AVRCP_9_25 False TG: category 2 - Operation id: F2 (O)
+TSPC_AVRCP_9_26 False TG: category 2 - Operation id: F3 (O)
+TSPC_AVRCP_9_27 False TG: category 2 - Operation id: F4 (O)
+TSPC_AVRCP_9_27a False TG: category 2 - Operation id: F5 (O)
+TSPC_AVRCP_9_28 False TG: category 2 - Operation id: vendor unique (O)
+-------------------------------------------------------------------------------
+C.2: Mandatory to support if the device supports category 2 (TSPC_AVRCP_7_8).
+-------------------------------------------------------------------------------
+
+
+ operation_id of category 3 for TG
+-------------------------------------------------------------------------------
+Parameter Name Selected Description
+-------------------------------------------------------------------------------
+TSPC_AVRCP_10_1 False TG: category 3 - Operation id: 0 (O)
+TSPC_AVRCP_10_2 False TG: category 3 - Operation id: 1 (O)
+TSPC_AVRCP_10_3 False TG: category 3 - Operation id: 2 (O)
+TSPC_AVRCP_10_4 False TG: category 3 - Operation id: 3 (O)
+TSPC_AVRCP_10_5 False TG: category 3 - Operation id: 4 (O)
+TSPC_AVRCP_10_6 False TG: category 3 - Operation id: 5 (O)
+TSPC_AVRCP_10_7 False TG: category 3 - Operation id: 6 (O)
+TSPC_AVRCP_10_8 False TG: category 3 - Operation id: 7 (O)
+TSPC_AVRCP_10_9 False TG: category 3 - Operation id: 8 (O)
+TSPC_AVRCP_10_10 False TG: category 3 - Operation id: 9 (O)
+TSPC_AVRCP_10_11 False TG: category 3 - Operation id: dot (O)
+TSPC_AVRCP_10_12 False TG: category 3 - Operation id: enter (O)
+TSPC_AVRCP_10_13 False TG: category 3 - Operation id: clear (O)
+TSPC_AVRCP_10_14 False (*) TG: category 3 - Operation id: channel up (C.3)
+TSPC_AVRCP_10_15 False (*) TG: category 3 - Operation id: channel down
+ (C.3)
+TSPC_AVRCP_10_16 False TG: category 3 - Operation id: previous channel
+ (O)
+TSPC_AVRCP_10_17 False TG: category 3 - Operation id: sound select (O)
+TSPC_AVRCP_10_18 False TG: category 3 - Operation id: input select (O)
+TSPC_AVRCP_10_19 False TG: category 3 - Operation id: display
+ information (O)
+TSPC_AVRCP_10_20 False TG: category 3 - Operation id: help (O)
+TSPC_AVRCP_10_21 False TG: category 3 - Operation id: power (O)
+TSPC_AVRCP_10_21a False TG: category 3 - Operation id: angle (O)
+TSPC_AVRCP_10_21b False TG: category 3 - Operation id: subpicture (O)
+TSPC_AVRCP_10_22 False TG: category 3 - Operation id: F1 (O)
+TSPC_AVRCP_10_23 False TG: category 3 - Operation id: F2 (O)
+TSPC_AVRCP_10_24 False TG: category 3 - Operation id: F3 (O)
+TSPC_AVRCP_10_25 False TG: category 3 - Operation id: F4 (O)
+TSPC_AVRCP_10_25a False TG: category 3 - Operation id: F5 (O)
+TSPC_AVRCP_10_26 False TG: category 3 - Operation id: vendor unique (O)
+-------------------------------------------------------------------------------
+C.3: Mandatory to support if the device supports category 3 (TSPC_AVRCP_7_9).
+-------------------------------------------------------------------------------
+
+
+ operation_id of category 4 for TG
+-------------------------------------------------------------------------------
+Parameter Name Selected Description
+-------------------------------------------------------------------------------
+TSPC_AVRCP_11_1 False (*) TG: category 4 - Operation id: select (C.4)
+TSPC_AVRCP_11_2 False (*) TG: category 4 - Operation id: up (C.4)
+TSPC_AVRCP_11_3 False (*) TG: category 4 - Operation id: down (C.4)
+TSPC_AVRCP_11_4 False (*) TG: category 4 - Operation id: left (C.4)
+TSPC_AVRCP_11_5 False (*) TG: category 4 - Operation id: right (C.4)
+TSPC_AVRCP_11_6 False TG: category 4 - Operation id: right up (O)
+TSPC_AVRCP_11_7 False TG: category 4 - Operation id: right down (O)
+TSPC_AVRCP_11_8 False TG: category 4 - Operation id: left up (O)
+TSPC_AVRCP_11_9 False TG: category 4 - Operation id: left down (O)
+TSPC_AVRCP_11_10 False (*) TG: category 4 - Operation id: root menu (C.4)
+TSPC_AVRCP_11_11 False TG: category 4 - Operation id: setup menu (O)
+TSPC_AVRCP_11_12 False TG: category 4 - Operation id: contents menu (O)
+TSPC_AVRCP_11_13 False TG: category 4 - Operation id: favorite menu (O)
+TSPC_AVRCP_11_14 False TG: category 4 - Operation id: exit (O)
+TSPC_AVRCP_11_15 False TG: category 4 - Operation id: 0 (O)
+TSPC_AVRCP_11_16 False TG: category 4 - Operation id: 1 (O)
+TSPC_AVRCP_11_17 False TG: category 4 - Operation id: 2 (O)
+TSPC_AVRCP_11_18 False TG: category 4 - Operation id: 3 (O)
+TSPC_AVRCP_11_19 False TG: category 4 - Operation id: 4 (O)
+TSPC_AVRCP_11_20 False TG: category 4 - Operation id: 5 (O)
+TSPC_AVRCP_11_21 False TG: category 4 - Operation id: 6 (O)
+TSPC_AVRCP_11_22 False TG: category 4 - Operation id: 7 (O)
+TSPC_AVRCP_11_23 False TG: category 4 - Operation id: 8 (O)
+TSPC_AVRCP_11_24 False TG: category 4 - Operation id: 9 (O)
+TSPC_AVRCP_11_25 False TG: category 4 - Operation id: dot (O)
+TSPC_AVRCP_11_26 False TG: category 4 - Operation id: enter (O)
+TSPC_AVRCP_11_27 False TG: category 4 - Operation id: clear (O)
+TSPC_AVRCP_11_28 False TG: category 4 - Operation id: disply (O)
+TSPC_AVRCP_11_29 False TG: category 4 - Operation id: help (O)
+TSPC_AVRCP_11_30 False TG: category 4 - Operation id: page up (O)
+TSPC_AVRCP_11_31 False TG: category 4 - Operation id: page down (O)
+TSPC_AVRCP_11_32 False TG: category 4 - Operation id: power (O)
+TSPC_AVRCP_11_33 False TG: category 4 - Operation id: F1 (O)
+TSPC_AVRCP_11_34 False TG: category 4 - Operation id: F2 (O)
+TSPC_AVRCP_11_35 False TG: category 4 - Operation id: F3 (O)
+TSPC_AVRCP_11_36 False TG: category 4 - Operation id: F4 (O)
+TSPC_AVRCP_11_36a False TG: category 4 - Operation id: F5 (O)
+TSPC_AVRCP_11_37 False TG: category 4 - Operation id: vendor unique (O)
+
+TSPC_AVRCP_12_1 True General discoverable mode (M)
+TSPC_AVRCP_13_1 True General discoverable mode (M)
+TSPC_AVRCP_14_1 False OBEX Connect operation (C.1)
+TSPC_AVRCP_14_2 False OBEX Get operation (C.1)
+TSPC_AVRCP_14_3 False OBEX Disconnect operation (C.1)
+TSPC_AVRCP_15_1 False OBEX Connect operation (C.1)
+TSPC_AVRCP_15_2 False OBEX Get operation (C.1)
+TSPC_AVRCP_15_3 False OBEX Disconnect operation (C.1)
+
+TSPC_ALL False Enables all test cases when set to TRUE.
+-------------------------------------------------------------------------------
+C.4: Mandatory to support if the device supports category 4 (TSPC_AVRCP_7_10).
+-------------------------------------------------------------------------------
diff --git a/repo/android/pics-bnep.txt b/repo/android/pics-bnep.txt
new file mode 100644
index 0000000..289e470
--- /dev/null
+++ b/repo/android/pics-bnep.txt
@@ -0,0 +1,26 @@
+BNEP PICS for the PTS tool.
+
+PTS version: 6.1
+
+* - different than PTS defaults
+# - not yet implemented/supported
+
+M - mandatory if such role selected
+O - optional
+
+ Profile Version
+-------------------------------------------------------------------------------
+Parameter Name Selected Description
+-------------------------------------------------------------------------------
+TSPC_BNEP_1_1 True BNEP Connection Setup (M)
+TSPC_BNEP_1_2 True (*) BNEP Data Packet Reception (M)
+TSPC_BNEP_1_3 True (*) BNEP Data Packet Transmission (M)
+TSPC_BNEP_1_3a True (*) BNEP Compressed Packet Transmission (O)
+TSPC_BNEP_1_3b True (*) BNEP Compressed Packet Transmission Source Only
+ (O)
+TSPC_BNEP_1_4 True BNEP Control Message Processing (M)
+TSPC_BNEP_1_5 True BNEP Extension Header Processing (M)
+TSPC_BNEP_1_6 True Network Protocol Filter Message Transmission (O)
+TSPC_BNEP_1_7 True Multicast Address Filter Message Transmission
+ (O)
+-------------------------------------------------------------------------------
diff --git a/repo/android/pics-did.txt b/repo/android/pics-did.txt
new file mode 100644
index 0000000..9ecb86d
--- /dev/null
+++ b/repo/android/pics-did.txt
@@ -0,0 +1,23 @@
+DID PICS for the PTS tool.
+
+PTS version: 6.1
+
+* - different than PTS defaults
+# - not yet implemented/supported
+
+M - mandatory
+O - optional
+
+
+ SDP Requirements
+-------------------------------------------------------------------------------
+Parameter Name Selected Description
+-------------------------------------------------------------------------------
+TSPC_DID_1_1 True Specification ID (M)
+TSPC_DID_1_2 True Vendor ID (M)
+TSPC_DID_1_3 True Product ID (M)
+TSPC_DID_1_4 True Version (M)
+TSPC_DID_1_5 True Primary Record (M)
+TSPC_DID_1_6 True Vendor ID Source (M)
+TSPC_ALL False Turns on all the test cases
+-------------------------------------------------------------------------------
diff --git a/repo/android/pics-dis.txt b/repo/android/pics-dis.txt
new file mode 100644
index 0000000..83f25ce
--- /dev/null
+++ b/repo/android/pics-dis.txt
@@ -0,0 +1,59 @@
+DIS PICS for the PTS tool.
+
+PTS version: 6.1
+
+* - different than PTS defaults
+
+M - mandatory
+O - optional
+
+ Transport Requirements
+-------------------------------------------------------------------------------
+Parameter Name Selected Description
+-------------------------------------------------------------------------------
+TSPC_DIS_1_1 True Service supported over BR/EDR (C.1)
+TSPC_DIS_1_2 True Service supported over LE (C.1)
+-------------------------------------------------------------------------------
+C.1: Mandatory to support at least one of TSPC_DIS_1_1 or TSPC_DIS_1_2.
+-------------------------------------------------------------------------------
+
+
+ Service Requirements
+-------------------------------------------------------------------------------
+Parameter Name Selected Description
+-------------------------------------------------------------------------------
+TSPC_DIS_2_1 True Device Information Service (M)
+TSPC_DIS_2_2 True Manufacturer Name String Characteristic (O)
+TSPC_DIS_2_3 True Model Number String Characteristic (O)
+TSPC_DIS_2_4 True Serial Number String Characteristic (O)
+TSPC_DIS_2_5 True Hardware Revision String Characteristic (O)
+TSPC_DIS_2_6 True Firmware Revision String Characteristic (O)
+TSPC_DIS_2_7 True Software Revision String Characteristic (O)
+TSPC_DIS_2_8 True System ID Characteristic (O)
+TSPC_DIS_2_9 False (*) IEEE 11073-20601 Regulatory Certification
+ Data List Characteristic (O)
+TSPC_DIS_2_10 True SDP Interoperability (C.1)
+TSPC_DIS_2_11 True PnP ID (O)
+-------------------------------------------------------------------------------
+C.1: Mandatory if TSPC_DIS_1_1 (BR/EDR) is supported, otherwise excluded.
+-------------------------------------------------------------------------------
+
+
+ GATT Requirements
+-------------------------------------------------------------------------------
+Parameter Name Selected Description
+-------------------------------------------------------------------------------
+TSPC_DIS_3_1 True Generic Attribute Profile Server (M)
+-------------------------------------------------------------------------------
+
+
+ SDP Requirements
+-------------------------------------------------------------------------------
+Parameter Name Selected Description
+-------------------------------------------------------------------------------
+TSPC_DIS_4_1 True Support for server role (M)
+TSPC_DIS_4_2 True ProtocolDescriptorList (M)
+TSPC_DIS_4_3 True BrowseGroupList (M)
+-------------------------------------------------------------------------------
+Note: Marked as False as TSPC_DIS_1_1 is set to False
+-------------------------------------------------------------------------------
diff --git a/repo/android/pics-gap.txt b/repo/android/pics-gap.txt
new file mode 100644
index 0000000..3775995
--- /dev/null
+++ b/repo/android/pics-gap.txt
@@ -0,0 +1,788 @@
+GAP PICS for the PTS tool.
+
+PTS version: 6.1
+
+* - different than PTS defaults
+
+M - mandatory
+O - optional
+
+ Device Configuration
+-------------------------------------------------------------------------------
+Parameter Name Selected Description
+-------------------------------------------------------------------------------
+TSPC_GAP_0_1 False (*) BR/EDR (C.1)
+TSPC_GAP_0_2 False (*) LE (C.2)
+TSPC_GAP_0_3 True BR/EDR/LE (C.3)
+-------------------------------------------------------------------------------
+C.1: Mandatory if ('End Product' or 'Host Subsystem') and ('BR Host' or
+ 'BR/HS Host') are Supported ('End Product' or 'Host Subsystem' with 'BR'
+ or 'BR/HS Host' CC), otherwise excluded. Optional for
+ 'Component (Tested)' or 'Component (Non-Tested)'.
+C.2: Mandatory if ('End Product' or 'Host Subsystem') and ('LE Host') are
+ Supported (End Product or Host Subsystem with LE Host CC),
+ otherwise excluded. Optional for 'Component (Tested)' or
+ 'Component (Non-Tested)'.
+C.3: Mandatory if ('End Product' or 'Host Subsystem') and ('BR/LE Host' or
+ 'BR/HS/LE Host') are Supported (End Product or Host Subsystem with
+ BR/LE or BR/HS/LE Host CC), otherwise excluded.
+ Optional for 'Component (Tested)' or 'Component (Non-tested)'.
+Note - Only one transport shall be supported.
+-------------------------------------------------------------------------------
+
+
+ Version Configuration
+-------------------------------------------------------------------------------
+Parameter Name Selected Description
+-------------------------------------------------------------------------------
+TSPC_GAP_0A_1 True Core Specification Addendum 3 (CSA3), GAP
+ Connection Parameters Changes,
+ Authentication and Lost Bond Changes,
+ Private Addressing Changes, Dual Mode
+ Addressing Changes,
+ Adopted 24 July 2012 (C.1)
+TSPC_GAP_0A_2 True Core Specification Addendum 4 (CSA4)
+TSPC_GAP_0A_3 True Core Spec version 4.1 (Core v4.1) GAP Connection
+ Parameters Changes, Authentication and
+ Lost Bond Changes, Private Addressing
+ Changes, Dual Mode Addressing Changes,
+ Adopted 03 December 2013
+-------------------------------------------------------------------------------
+C.1: Mandatory if 'CSA3 Adopted 24 July 2012' is supported, otherwise Excluded.
+-------------------------------------------------------------------------------
+
+
+ Modes
+-------------------------------------------------------------------------------
+Parameter Name Selected Description
+-------------------------------------------------------------------------------
+TSPC_GAP_1_1 True Non-discoverable mode (C.1)
+TSPC_GAP_1_2 True Limited-discoverable Mode (O)
+TSPC_GAP_1_3 True General-discoverable mode (O)
+TSPC_GAP_1_4 True Non-connectable mode (O)
+TSPC_GAP_1_5 True Connectable mode (M)
+TSPC_GAP_1_6 True Non-bondable mode (O)
+TSPC_GAP_1_7 True Bondable mode (C.2)
+TSPC_GAP_1_8 False (*) Non-Synchronizable Mode (C.3)
+TSPC_GAP_1_9 False (*) Synchronizable Mode (C.4)
+-------------------------------------------------------------------------------
+C.1: Mandatory if TSPC_GAP_0_2 is supported, otherwise Optional.
+C.2: Mandatory if TSPC_GAP_3_5 is supported, otherwise Optional.
+C.3: Mandatory if TSPC_GAP_0A_2 or TSPC_GAP_0A_3 and is supported, otherwise
+ Excluded.
+C.4: Optional if TSPC_GAP_0A_2 or later is supported; Mandatory if TSPC_GAP_0A_2
+ or later and BB 3a/1 (Connectionless Slave Broadcast Transmitter) are
+ supported, otherwise Excluded.
+-------------------------------------------------------------------------------
+
+
+ Security Aspects
+-------------------------------------------------------------------------------
+Parameter Name Selected Description
+-------------------------------------------------------------------------------
+TSPC_GAP_2_1 True Authentication procedure (C.1)
+TSPC_GAP_2_2 True Support of LMP-Authentication (M)
+TSPC_GAP_2_3 True Initiate LMP-Authentication (C.5)
+TSPC_GAP_2_4 False (*) Security mode 1 (C.2)
+TSPC_GAP_2_5 True Security mode 2 (O)
+TSPC_GAP_2_6 False (*) Security mode 3 (C.7)
+TSPC_GAP_2_7 True Security mode 4 (C.4)
+TSPC_GAP_2_8 True Support of Authenticated link key (C.6)
+TSPC_GAP_2_9 True Support of Unauthenticated link key (C.6)
+TSPC_GAP_2_10 True No security (C.6)
+TSPC_GAP_2_11 False (*) Secure Connections Only Mode (O)
+-------------------------------------------------------------------------------
+C.1: Mandatory If (TSPC_GAP_2_5 or TSPC_GAP_2_6) is supported, otherwise
+ Optional.
+Note 1: The Authentication Procedure in item GAP, TSPC_GAP_2_1 is the one
+ described in Fig. 5.1 on page 198 in the GAP Profile Specification and
+ not the LMP-Authenticaion.
+C.2: Excluded if TSPC_GAP_2_7 is supported, otherwise Optional.
+C.5: Mandatory If (TSPC_GAP_2_5 or TSPC_GAP_2_6 or TSPC_GAP_2_7) is supported,
+ otherwise Optional.
+C.4: Mandatory if (Core Spec 2.1 or later) is supported, otherwise Excluded.
+Note 2. If a Core 2.0 and earlier design claims to support secure communcation
+ it should support either Security mode 2 or 3.
+Note 3. A Core 2.1 or later device shall always support secure communication
+ in Security Mode 4, and shall use that mode to connect with another
+ Core 2.1 or later device. It shall use Security Mode 2 only for
+ backward compatibility purposes with Core 2.0 and earlier devices.
+ Security Mode 1 is excluded for Core 2.1 or later devices based on
+ condition C.2.
+C.6: If TSPC_GAP_2_7 is supported then at least one of (TSPC_GAP_2_8 or
+ TSPC_GAP_2_9 or TSPC_GAP_2_10) is Mandatory, otherwise Excluded.
+C.7: Excluded if TSPC_GAP_2_7 is supported, otherwise Optional.
+-------------------------------------------------------------------------------
+
+
+ Idle Mode Procedures
+-------------------------------------------------------------------------------
+Parameter Name Selected Description
+-------------------------------------------------------------------------------
+TSPC_GAP_3_1 True Initiation of general inquiry (C.1)
+TSPC_GAP_3_2 True Initiation of limited inquiry (C.1)
+TSPC_GAP_3_3 True Initiation of name discover (O)
+TSPC_GAP_3_4 True Initiation of device discovery (O)
+TSPC_GAP_3_5 True Initiation of general bonding (O)
+TSPC_GAP_3_6 True Initiation of dedicated bonding (O)
+-------------------------------------------------------------------------------
+C.1: Mandatory to support at least one of TSPC_GAP_3_1 or TSPC_GAP_3_2 if
+ TSPC_GAP_3_5 is supported, otherwise Optional.
+-------------------------------------------------------------------------------
+
+
+ Establishment Procedures
+-------------------------------------------------------------------------------
+Parameter Name Selected Description
+-------------------------------------------------------------------------------
+TSPC_GAP_4_1 True Support link establishment as initiator (M)
+TSPC_GAP_4_2 True Support link establishment as acceptor (M)
+TSPC_GAP_4_3 True Support channel establishment as initiator (O)
+TSPC_GAP_4_4 True Support channel establishment as acceptor (M)
+TSPC_GAP_4_5 True Support connection establishment as initiator
+ (O)
+TSPC_GAP_4_6 True Support connection establishment as acceptor
+ (O)
+TSPC_GAP_4_7 True Support synchronization establishment
+ as receiver (O)
+-------------------------------------------------------------------------------
+
+
+ LE Roles
+-------------------------------------------------------------------------------
+Parameter Name Selected Description
+-------------------------------------------------------------------------------
+TSPC_GAP_5_1 False (*) Broadcaster (C.1)
+TSPC_GAP_5_2 False (*) Observer (C.1)
+TSPC_GAP_5_3 True Peripheral (C.1)
+TSPC_GAP_5_4 True Central (C.1)
+-------------------------------------------------------------------------------
+C.1: It is mandatory to support at least one of the defined roles.
+Note: 'LE Roles' is applicable for LE-only configurations, but it appears that
+ PTS is checking this precondition also in some BR/EDR/LE tests.
+-------------------------------------------------------------------------------
+
+
+ Broadcaster Physical Layer
+-------------------------------------------------------------------------------
+Parameter Name Selected Description
+-------------------------------------------------------------------------------
+TSPC_GAP_6_1 False (*) Broadcaster: Transmitter (M)
+TSPC_GAP_6_2 False (*) Broadcaster: Receiver (O)
+-------------------------------------------------------------------------------
+
+
+ Broadcaster Link Layer States
+-------------------------------------------------------------------------------
+Parameter Name Selected Description
+-------------------------------------------------------------------------------
+TSPC_GAP_7_1 False (*) Broadcaster: Standby (M)
+TSPC_GAP_7_2 False (*) Broadcaster: Advertising (M)
+-------------------------------------------------------------------------------
+
+
+ Broadcaster Link Layer Advertising Event Types
+-------------------------------------------------------------------------------
+Parameter Name Selected Description
+-------------------------------------------------------------------------------
+TSPC_GAP_8_1 False (*) Broadcaster: Non-Connectable Undirected Event
+ (M)
+TSPC_GAP_8_2 False (*) Broadcaster: Scannable Undirected Event (O)
+-------------------------------------------------------------------------------
+
+
+ Broadcaster Link Layer Advertising Data Types
+-------------------------------------------------------------------------------
+Parameter Name Selected Description
+-------------------------------------------------------------------------------
+TSPC_GAP_8A_1 False (*) AD Type-Service UUID (O)
+TSPC_GAP_8A_2 False (*) AD Type-Local Name (O)
+TSPC_GAP_8A_3 False (*) AD Type-Flags (O)
+TSPC_GAP_8A_4 False (*) AD Type-Manufacturer Specific Data (O)
+TSPC_GAP_8A_5 False (*) AD Type-TX Power Level (O)
+TSPC_GAP_8A_6 False (*) AD Type-Security Manager Out of Band (OOB) (C.1)
+TSPC_GAP_8A_7 False (*) AD Type-Security manager TK Value (O)
+TSPC_GAP_8A_8 False (*) AD Type-Slave Connection Interval Range (O)
+TSPC_GAP_8A_9 False (*) AD Type-Service Solicitation (O)
+TSPC_GAP_8A_10 False (*) AD Type-Service Data (O)
+TSPC_GAP_8A_11 False (*) AD Type-Appearance (O)
+TSPC_GAP_8A_12 False (*) AD Type-Public Target Address (O)
+TSPC_GAP_8A_13 False (*) AD Type-Random Target Address (O)
+TSPC_GAP_8A_14 False (*) AD Type-Advertising Interval (O)
+TSPC_GAP_8A_15 False (*) AD Type-LE Bluetooth Device Address (O)
+TSPC_GAP_8A_16 False (*) AD Type –LE Role (O)
+-------------------------------------------------------------------------------
+C.1: Optional if TSPC_SM_2_4 (OOB supported) is supported, otherwise Excluded.
+-------------------------------------------------------------------------------
+
+
+ Broadcaster Connection Modes and Procedures
+-------------------------------------------------------------------------------
+Parameter Name Selected Description
+-------------------------------------------------------------------------------
+TSPC_GAP_9_1 False (*) Broadcaster: Non-Connectable Mode
+-------------------------------------------------------------------------------
+
+
+ Broadcaster Broadcasting and Observing Features
+-------------------------------------------------------------------------------
+Parameter Name Selected Description
+-------------------------------------------------------------------------------
+TSPC_GAP_10_1 False (*) Broadcaster: Broadcast Mode
+TSPC_GAP_11_1 False (*) Broadcaster: Privacy Feature v.1.0
+TSPC_GAP_11_1A False (*) Broadcaster: Privacy Feature v1.1 (O)
+TSPC_GAP_11_2 False (*) Broadcaster: Resolvable Private Address
+ Generation Procedure
+TSPC_GAP_11_3 False (*) Broadcaster: Non-Resolvable Private Address
+ Generation Procedure (O)
+-------------------------------------------------------------------------------
+
+
+ Observer Physical Layer
+-------------------------------------------------------------------------------
+Parameter Name Selected Description
+-------------------------------------------------------------------------------
+TSPC_GAP_12_1 False (*) Observer: Receiver
+TSPC_GAP_12_2 False (*) Observer: Transmitter
+-------------------------------------------------------------------------------
+
+
+ Observer Link Layer States
+-------------------------------------------------------------------------------
+Parameter Name Selected Description
+-------------------------------------------------------------------------------
+TSPC_GAP_13_1 False (*) Observer: Standby
+TSPC_GAP_13_2 False (*) Observer: Scanning
+-------------------------------------------------------------------------------
+
+
+ Observer Link Layer Scanning Types
+-------------------------------------------------------------------------------
+Parameter Name Selected Description
+-------------------------------------------------------------------------------
+TSPC_GAP_14_1 False (*) Observer: Passive Scanning
+TSPC_GAP_14_2 False (*) Observer: Active Scanning
+-------------------------------------------------------------------------------
+
+
+ Observer Connection Modes and Procedures
+-------------------------------------------------------------------------------
+Parameter Name Selected Description
+-------------------------------------------------------------------------------
+TSPC_GAP_15_1 False (*) Observer: Non-Connectable Mode
+-------------------------------------------------------------------------------
+
+
+ Observer Broadcasting and Observing Features
+-------------------------------------------------------------------------------
+Parameter Name Selected Description
+-------------------------------------------------------------------------------
+TSPC_GAP_16_1 False (*) Observer: Observation Procedure
+-------------------------------------------------------------------------------
+
+
+ Observer Privacy Feature
+-------------------------------------------------------------------------------
+Parameter Name Selected Description
+-------------------------------------------------------------------------------
+TSPC_GAP_17_1 False (*) Observer: Privacy Feature v1.0 (O)
+TSPC_GAP_17_1A False (*) Observer: Privacy Feature v1.1 (O)
+TSPC_GAP_17_2 False (*) Observer: Non-Resolvable Private Address
+ Generation Procedure (C.1)
+TSPC_GAP_17_3 False (*) Observer: Resolvable Private Address Resolution
+ Procedure (C.2)
+TSPC_GAP_17_4 False (*) Observer: Resolvable Private Address Generation
+ Procedure (C.3)
+-------------------------------------------------------------------------------
+C.1: Mandatory if TSPC_GAP_17_1 and TSPC_GAP_14_2 (Active Scanning) are
+ supported and TSPC_GAP_17_4 (Resolvable Private Address Generation
+ Procedure) is Not Supported; Optional if CSA3 or later and
+ TSPC_GAP_17_4 are supported, otherwise Excluded.
+C.2: Optional if TSPC_GAP_17_1 is supported, otherwise Excluded.
+C.3: Mandatory if CSA3 or later and TSPC_GAP_17_1 and TSPC_GAP_14_2
+ (Active Scanning) are supported and TSPC_GAP_17_2 (Non-Resolvable
+ Private Address Generation Procedure) is not supported; Optional if
+ CSA3 or later and TSPC_GAP_17_2 (Non-Resolvable Private Address
+ Generation Procedure) are supported, otherwise Excluded.
+-------------------------------------------------------------------------------
+
+
+ Peripheral Physical Layer
+-------------------------------------------------------------------------------
+Parameter Name Selected Description
+-------------------------------------------------------------------------------
+TSPC_GAP_18_1 True Peripheral: Transmitter
+TSPC_GAP_18_2 True Peripheral: Receiver
+-------------------------------------------------------------------------------
+
+
+ Peripheral Link Layer States
+-------------------------------------------------------------------------------
+Parameter Name Selected Description
+-------------------------------------------------------------------------------
+TSPC_GAP_19_1 True Peripheral: Standby
+TSPC_GAP_19_2 True Peripheral: Advertising
+TSPC_GAP_19_3 True Peripheral: Connection, Slave Role
+-------------------------------------------------------------------------------
+
+
+ Peripheral Link Layer Advertising Event Types
+-------------------------------------------------------------------------------
+Parameter Name Selected Description
+-------------------------------------------------------------------------------
+TSPC_GAP_20_1 True Peripheral: Connectable Undirected Event (C.1)
+TSPC_GAP_20_2 True Peripheral: Connectable Directed Event (C.2)
+TSPC_GAP_20_2A True Peripheral: Low Duty Directed Advertising (C.3)
+TSPC_GAP_20_3 True Peripheral: Non-Connectable Undirected Event
+TSPC_GAP_20_4 True Peripheral: Scannable Undirected Event
+-------------------------------------------------------------------------------
+
+
+ Peripheral Link Layer Advertising Data Types
+-------------------------------------------------------------------------------
+Parameter Name Selected Description
+-------------------------------------------------------------------------------
+TSPC_GAP_20A_1 False (*) AD Type-Service UUID (C.1)
+TSPC_GAP_20A_2 True AD Type-Local Name (C.1)
+TSPC_GAP_20A_3 True AD Type-Flags (C.2)
+TSPC_GAP_20A_4 False (*) AD Type-Manufacturer Specific Data (C.1)
+TSPC_GAP_20A_5 True AD Type-TX Power Level (C.1)
+TSPC_GAP_20A_6 False (*) AD Type-Security Manager Out of Band (OOB) (C.3)
+TSPC_GAP_20A_7 False (*) AD Type-Security manager TK Value (C.1)
+TSPC_GAP_20A_8 False (*) AD Type-Slave Connection Interval Range (C.1)
+TSPC_GAP_20A_9 False (*) AD Type-Service Solicitation (C.1)
+TSPC_GAP_20A_10 False (*) AD Type-Service Data (C.1)
+TSPC_GAP_20A_11 False (*) AD Type –Appearance (C.1)
+TSPC_GAP_20A_12 False (*) AD Type-Public Target Address (C.1)
+TSPC_GAP_20A_13 False (*) AD Type-Random Target Address (C.1)
+TSPC_GAP_20A_14 False (*) AD Type-Advertising Interval (C.1)
+TSPC_GAP_20A_15 False (*) AD Type-LE Bluetooth Device Address (C.1)
+TSPC_GAP_20A_16 False (*) AD Type – LE Role (C.1)
+-------------------------------------------------------------------------------
+C.1: Optional if (TSPC_GAP_20_1 or TSPC_GAP_20_3 or TSPC_GAP_20_4) is
+ supported, otherwise Excluded.
+C.2: Mandatory if TSPC_GAP_22_2 (Limited Discoverable Mode) or TSPC_GAP_22_3
+ (General Discoverable Mode) is supported, otherwise Optional.
+C.3: Optional if (TSPC_GAP_20_1 (Connectable Undirected Event) or TSPC_GAP_20_3
+ (Non-Connectable Undirected Event) or TSPC_GAP_20_4
+ (Scannable Undirected Event)) and TSPC_SM_2_4 (OOB supported) are
+ supported, otherwise Excluded.
+-------------------------------------------------------------------------------
+
+
+ Peripheral Link Layer Control Procedures
+-------------------------------------------------------------------------------
+Parameter Name Selected Description
+-------------------------------------------------------------------------------
+TSPC_GAP_21_1 True Peripheral: Connection Update Procedure (M)
+TSPC_GAP_21_2 True Peripheral: Channel Map Update Procedure (M)
+TSPC_GAP_21_3 True Peripheral: Encryption Procedure (O)
+TSPC_GAP_21_4 True Peripheral: Feature Exchange Procedure (M)
+TSPC_GAP_21_5 True Peripheral: Version Exchange Procedure (M)
+TSPC_GAP_21_6 True Peripheral: Termination Procedure (M)
+TSPC_GAP_21_7 True Peripheral: LE Ping Procedure (C.3)
+TSPC_GAP_21_8 True Peripheral: Slave Initiated Feature Exchange
+ Procedure (C.4)
+TSPC_GAP_21_9 True Peripheral: Connection Parameter Request
+ Procedure (C.5)
+-------------------------------------------------------------------------------
+
+
+ Peripheral Discovery Modes and Procedures
+-------------------------------------------------------------------------------
+Parameter Name Selected Description
+-------------------------------------------------------------------------------
+TSPC_GAP_22_1 True Peripheral: Non-Discoverable Mode (C.2)
+TSPC_GAP_22_2 True Peripheral: Limited Discoverable Mode (C.1)
+TSPC_GAP_22_3 True Peripheral: General Discoverable Mode (C.1)
+TSPC_GAP_22_4 True Peripheral: Name Discovery Procedure (C.3)
+-------------------------------------------------------------------------------
+C.1: Optional if (TSPC_GAP_5_3 OR TSPC_GAP_42_2), otherwise Excluded.
+C.2: Mandatory if (TSPC_GAP_5_3 or TSPC_GAP_42_1) is supported,
+ otherwise Excluded.
+C.3: Optional if TSPC_GAP_5_3 is supported, otherwise Excluded.
+-------------------------------------------------------------------------------
+
+
+ Peripheral Connection Modes and Procedures
+-------------------------------------------------------------------------------
+Parameter Name Selected Description
+-------------------------------------------------------------------------------
+TSPC_GAP_23_1 True Peripheral: Non-Connectable Mode (C.1)
+TSPC_GAP_23_2 True Peripheral: Directed Connectable Mode (O)
+TSPC_GAP_23_3 True Peripheral: Undirected Connectable Mode (M)
+TSPC_GAP_23_4 True Peripheral: Connection Parameter Update
+ Procedure (O)
+TSPC_GAP_23_5 True Peripheral: Terminate Connection Procedure (M)
+-------------------------------------------------------------------------------
+C.1: Mandatory if TSPC_GAP_5_3 (LE Only – Peripheral role) OR TSPC_GAP_42_3
+ (BR/EDR/LE – Non-Connectable Mode) OR TSPC_GAP_42_4
+ (BR/EDR/LE – Connectable Mode) is supported, otherwise Excluded.
+-------------------------------------------------------------------------------
+
+
+ Peripheral Bonding Modes and Procedures
+-------------------------------------------------------------------------------
+Parameter Name Selected Description
+-------------------------------------------------------------------------------
+TSPC_GAP_24_1 True Peripheral: Non-Bondable Mode (M)
+TSPC_GAP_24_2 True Peripheral: Bondable Mode (C.1)
+TSPC_GAP_24_3 True Peripheral: Bonding Procedure (C.2)
+TSPC_GAP_24_4 True Peripheral: Multiple Bonds (C.3)
+-------------------------------------------------------------------------------
+C.1: Optional if TSPC_GAP_5_3 (LE Only – Peripheral role) OR (TSPC_GAP_38_3
+ (BR/EDR/LE – Peripheral role) AND NOT TSPC_GAP_42_6 (BR.EDR/LE -
+ Bondable Mode)) is supported, Mandatory if TSPC_GAP_42_6
+ (BR/EDR/LE – Bondable Mode) is supported, otherwise Excluded.
+C.2: Optional if TSPC_GAP_24_2 (Bondable Mode) is supported, otherwise Excluded
+-------------------------------------------------------------------------------
+
+
+ Peripheral Security Aspects Features
+-------------------------------------------------------------------------------
+Parameter Name Selected Description
+-------------------------------------------------------------------------------
+TSPC_GAP_25_1 True Peripheral: Security Mode (O)
+TSPC_GAP_25_2 True Peripheral: Security Mode 2 (O)
+TSPC_GAP_25_3 True Peripheral: Authentication Procedure (C.2)
+TSPC_GAP_25_4 True Peripheral: Authorization Procedure (O)
+TSPC_GAP_25_5 True Peripheral: Connection Data Signing Procedure
+ (O)
+TSPC_GAP_25_6 True Peripheral: Authenticate Signed Data Procedure
+ (O)
+TSPC_GAP_25_7 True Peripheral: Authenticated Pairing
+ (LE security mode 1 level 3) (C.1)
+TSPC_GAP_25_8 True Peripheral: Unauthenticated Pairing
+ (LE security mode 1 level 2) (C.1)
+-------------------------------------------------------------------------------
+C.1: Optional if TSPC_GAP_25_1 is supported, otherwise Excluded.
+C.2: Mandatory if TSPC_GAP_0A_1 and TSPC_GAP_27_4 are supported,
+ otherwise Optional.
+-------------------------------------------------------------------------------
+
+
+ Peripheral Privacy Feature
+-------------------------------------------------------------------------------
+Parameter Name Selected Description
+-------------------------------------------------------------------------------
+TSPC_GAP_26_1 False (*) Peripheral: Privacy Feature v1.0 (O)
+TSPC_GAP_26_1A True Peripheral: Privacy Feature v1.1 (O)
+TSPC_GAP_26_2 True Peripheral: Non-Resolvable Private Address
+ Generation Procedure (C.1)
+TSPC_GAP_26_3 True Peripheral: Resolvable Private Address
+ Generation Procedure (C.2)
+TSPC_GAP_26_4 True Peripheral: Resolvable Private Address
+ Generation Procedure (C.4)
+-------------------------------------------------------------------------------
+C.1: Optional if TSPC_GAP_26_1 is supported, otherwise Excluded.
+C.2: Mandatory if TSPC_GAP_26_1 is supported, otherwise Excluded.
+-------------------------------------------------------------------------------
+
+
+ Peripheral GAP Characteristics
+-------------------------------------------------------------------------------
+Parameter Name Selected Description
+-------------------------------------------------------------------------------
+TSPC_GAP_27_1 True Peripheral: Device Name (M)
+TSPC_GAP_27_2 True Peripheral: Appearance (M)
+TSPC_GAP_27_3 False (*) Peripheral: Peripheral Privacy Flag (C.1)
+TSPC_GAP_27_4 False (*) Peripheral: Reconnection Address (C.2)
+TSPC_GAP_27_5 False (*) Peripheral: Peripheral Preferred Connection
+ Parameters (O)
+TSPC_GAP_27_6 False (*) Peripheral: Writeable Device Name (O)
+TSPC_GAP_27_7 False (*) Peripheral: Writeable Appearance (O)
+TSPC_GAP_27_8 False (*) Peripheral: Writeable Peripheral Privacy Flag
+ (O)
+-------------------------------------------------------------------------------
+C.1: Mandatory if TSPC_GAP_26_1 is supported, otherwise Excluded.
+C.2: Optional if TSPC_GAP_26_1 and TSPC_GAP_27_3 are supported,
+ otherwise Excluded.
+-------------------------------------------------------------------------------
+
+
+ Central Physical Layer
+-------------------------------------------------------------------------------
+Parameter Name Selected Description
+-------------------------------------------------------------------------------
+TSPC_GAP_28_1 True Central: Transmitter (M)
+TSPC_GAP_28_2 True Central: Receiver (M)
+-------------------------------------------------------------------------------
+
+
+ Central Link Layer States
+-------------------------------------------------------------------------------
+Parameter Name Selected Description
+-------------------------------------------------------------------------------
+TSPC_GAP_29_1 True Central: Standby (M)
+TSPC_GAP_29_2 True Central: Scanning (M)
+TSPC_GAP_29_3 True Central: Initiating (M)
+TSPC_GAP_29_4 True Central: Connection, Master Role (M)
+-------------------------------------------------------------------------------
+
+
+ Central Link Layer Scanning Types
+-------------------------------------------------------------------------------
+Parameter Name Selected Description
+-------------------------------------------------------------------------------
+TSPC_GAP_30_1 True Central: Passive Scanning (O)
+TSPC_GAP_30_2 True Central: Active Scanning (C.1)
+-------------------------------------------------------------------------------
+C.1: Mandatory if (TSPC_GAP_5_4 or TSPC_GAP_38_4) is supported.
+ Optional if TSPC_GAP_30_1 and (TSPC_GAP_5_4 OR TSPC_GAP_38_4)
+ is supported, otherwise Excluded.
+-------------------------------------------------------------------------------
+
+
+ Central Link Layer Control Procedures
+-------------------------------------------------------------------------------
+Parameter Name Selected Description
+-------------------------------------------------------------------------------
+TSPC_GAP_31_1 True Central: Connection Update Procedure (M)
+TSPC_GAP_31_2 True Central: Channel Map Update Procedure (M)
+TSPC_GAP_31_3 True Central: Encryption Procedure (O)
+TSPC_GAP_31_4 True Central: Feature Exchange Procedure (M)
+TSPC_GAP_31_5 True Central: Version Exchange Procedure (M)
+TSPC_GAP_31_6 True Central: Termination Procedure (M)
+TSPC_GAP_31_7 True Central: LE Ping Procedure (C.1)
+TSPC_GAP_31_8 True Central: Slave Initiated Feature Exchange
+ Procedure (C.2)
+TSPC_GAP_31_9 True Central: Connection Parameter Request Procedure
+ (C.3)
+-------------------------------------------------------------------------------
+
+
+ Central Discovery Modes and Procedures
+-------------------------------------------------------------------------------
+Parameter Name Selected Description
+-------------------------------------------------------------------------------
+TSPC_GAP_32_1 True Central: Limited Discovery Procedure (C.2)
+TSPC_GAP_32_2 True Central: General Discovery Procedure (C.1)
+TSPC_GAP_32_3 True Central: Name Discovery Procedure (C.3)
+-------------------------------------------------------------------------------
+C.1: Mandatory if (TSPC_GAP_5_4 or TSPC_GAP_40_1) is supported, else Excluded.
+C.2: Optional if (TSPC_GAP_5_4 or TSPC_GAP_40_2) is supported,
+ otherwise Excluded.
+C.3: Optional if (TSPC_GAP_5_4 or TSPC_GAP_40_4) is supported,
+ otherwise Excluded.
+-------------------------------------------------------------------------------
+
+
+ Central Connection Modes and Procedures
+-------------------------------------------------------------------------------
+Parameter Name Selected Description
+-------------------------------------------------------------------------------
+TSPC_GAP_33_1 True Central: Auto Connection Establishment
+ Procedure (C.3)
+TSPC_GAP_33_2 True Central: General Connection Establishment
+ Procedure (C.1)
+TSPC_GAP_33_3 True Central: Selective Connection Establishment
+ Procedure (C.3)
+TSPC_GAP_33_4 True Central: Direct Connection Establishment
+ Procedure (C.2)
+TSPC_GAP_33_5 True Central: Connection Parameter Update Procedure
+ (C.2)
+TSPC_GAP_33_6 True Central: Terminate Connection Procedure
+ (C.2)
+-------------------------------------------------------------------------------
+C.1: Mandatory if (TSPC_GAP_5_4 or TSPC_GAP_40_5) and TSPC_GAP_36_1 is
+ supported, otherwise Optional.
+C.2: Mandatory if (TSPC_GAP_5_4 or TSPC_GAP_40_5) is supported,
+ otherwise Excluded.
+C.3: Optional if (TSPC_GAP_5_4 or TSPC_GAP_40_5) is supported,
+ otherwise Excluded.
+-------------------------------------------------------------------------------
+
+
+ Central Bonding Modes and Procedures
+-------------------------------------------------------------------------------
+Parameter Name Selected Description
+-------------------------------------------------------------------------------
+TSPC_GAP_34_1 True Central: Non-Bondable Mode (C.1)
+TSPC_GAP_34_2 True Central: Bondable Mode (C.2)
+TSPC_GAP_34_3 True Central: Bonding Procedure (O)
+-------------------------------------------------------------------------------
+C.1: Mandatory if (TSPC_GAP_5_4 or 39/5) is supported, otherwise Excluded.
+C.2: Optional if (TSPC_GAP_5_4 or 39/6) is supported, otherwise Excluded.
+-------------------------------------------------------------------------------
+
+
+ Central Security Features
+-------------------------------------------------------------------------------
+Parameter Name Selected Description
+-------------------------------------------------------------------------------
+TSPC_GAP_35_1 True Central: Security Mode 1 (O)
+TSPC_GAP_35_2 True Central: Security Mode 2 (O)
+TSPC_GAP_35_3 True Central: Authentication Procedure (O)
+TSPC_GAP_35_4 True Central: Authorization Procedure (O)
+TSPC_GAP_35_5 True Central: Connection Data Signing Procedure (O)
+TSPC_GAP_35_6 True Central: Authenticate Signed Data Procedure (O)
+TSPC_GAP_35_7 True Central: Authenticated Pairing
+ (LE security mode 1 level 3) (C.1)
+TSPC_GAP_35_8 True Central: Unauthenticated Pairing
+ (LE security mode 1 level 2) (C.1)
+-------------------------------------------------------------------------------
+C.1: Optional if TSPC_GAP_35_1 is supported, otherwise Excluded.
+-------------------------------------------------------------------------------
+
+
+ Central Privacy Feature
+-------------------------------------------------------------------------------
+Parameter Name Selected Description
+-------------------------------------------------------------------------------
+TSPC_GAP_36_1 False (*) Central: Privacy Feature v1.0 (C.2)
+TSPC_GAP_36_1A True Central: Privacy Feature v1.1 (C.4)
+TSPC_GAP_36_2 True Central: Non-Resolvable Private Address
+ Generation Procedure (C.1)
+TSPC_GAP_36_3 True Central: Resolvable Private Address Resolution
+ Procedure (C.2)
+TSPC_GAP_36_4 False (*) Central: Write to Privacy Characteristic
+ (Enable/Disable Privacy) (O)
+TSPC_GAP_36_5 True Central: Resolvable Private Address Generation
+ Procedure (C.6)
+-------------------------------------------------------------------------------
+C.1: Mandatory if TSPC_GAP_36_1 and TSPC_GAP_30_2 are supported,
+ otherwise Excluded.
+C.2: Mandatory if TSPC_GAP_36_1 is supported, otherwise Excluded.
+-------------------------------------------------------------------------------
+
+
+ Central GAP Characteristics
+-------------------------------------------------------------------------------
+Parameter Name Selected Description
+-------------------------------------------------------------------------------
+TSPC_GAP_37_1 True Central: Device Name (M)
+TSPC_GAP_37_2 True Central: Appearance (M)
+-------------------------------------------------------------------------------
+
+
+ BR/EDR/LE Roles
+-------------------------------------------------------------------------------
+Parameter Name Selected Description
+-------------------------------------------------------------------------------
+TSPC_GAP_38_1 False (*) BR/EDR/LE: Broadcaster (C.1)
+TSPC_GAP_38_2 False (*) BR/EDR/LE: Observer (C.1)
+TSPC_GAP_38_3 True BR/EDR/LE: Peripheral (C.1)
+TSPC_GAP_38_4 True BR/EDR/LE: Central (C.1)
+-------------------------------------------------------------------------------
+C.1: It is mandatory to support at least one of the defined roles.
+This table is applicable for BR/EDR/LE configurations. For LE-only
+configurations, see 'LE Roles' table for role declarations.
+-------------------------------------------------------------------------------
+
+
+ Central BR/EDR/LE Modes
+-------------------------------------------------------------------------------
+Parameter Name Selected Description
+-------------------------------------------------------------------------------
+TSPC_GAP_39_1 True Central BR/EDR/LE: Non-Discoverable Mode (C.1)
+TSPC_GAP_39_2 True Central BR/EDR/LE: Discoverable Mode (C.2)
+TSPC_GAP_39_3 True Central BR/EDR/LE: Non-Connectable Mode (C.3)
+TSPC_GAP_39_4 True Central BR/EDR/LE: Connectable Mode (M)
+TSPC_GAP_39_5 True Central BR/EDR/LE: Non-Bondable Mode (C.4)
+TSPC_GAP_39_6 True Central BR/EDR/LE: Bondable Mode (C.5)
+-------------------------------------------------------------------------------
+C.1: Mandatory if TSPC_GAP_1_1 is supported over BR/EDR, otherwise Excluded.
+C.2: Mandatory if (TSPC_GAP_1_2 or TSPC_GAP_1_3) is supported over BR/EDR,
+ otherwise Excluded.
+C.3: Mandatory if TSPC_GAP_1_4 is supported over BR/EDR, otherwise Excluded.
+C.4: Mandatory if TSPC_GAP_1_6 is supported over BR/EDR, otherwise Excluded.
+C.5: Mandatory if TSPC_GAP_1_7 is supported over BR/EDR, otherwise Excluded.
+-------------------------------------------------------------------------------
+
+
+ Central BR/EDR/LE Idle Mode Procedures
+-------------------------------------------------------------------------------
+Parameter Name Selected Description
+-------------------------------------------------------------------------------
+TSPC_GAP_40_1 True Central BR/EDR/LE: General Discovery (C.1)
+TSPC_GAP_40_2 True Central BR/EDR/LE: Limited Discovery (C.2)
+TSPC_GAP_40_3 True Central BR/EDR/LE: Device Type Discovery (C.3)
+TSPC_GAP_40_4 True Central BR/EDR/LE: Name Discovery (C.4)
+TSPC_GAP_40_5 True Central BR/EDR/LE: Link Establishment (C.5)
+-------------------------------------------------------------------------------
+C.1: Mandatory if TSPC_GAP_3_1 is supported over BR/EDR, otherwise Excluded.
+C.2: Mandatory if TSPC_GAP_3_2 is supported over BR/EDR, otherwise Excluded.
+C.3: Mandatory if (TSPC_GAP_3_1 or TSPC_GAP_3_2) is supported over BR/EDR,
+ otherwise Excluded.
+C.4: Mandatory if TSPC_GAP_3_3 is supported over BR/EDR, otherwise Excluded.
+C.5: Mandatory if (TSPC_GAP_4_1 or TSPC_GAP_4_2) is supported over BR/EDR,
+ otherwise Excluded.
+-------------------------------------------------------------------------------
+
+
+ Central BR/EDR/LE Security Aspects
+-------------------------------------------------------------------------------
+Parameter Name Selected Description
+-------------------------------------------------------------------------------
+TSPC_GAP_41_1 True Central BR/EDR/LE: Security Aspects (M)
+-------------------------------------------------------------------------------
+
+
+ Peripheral BR/EDR/LE Modes
+-------------------------------------------------------------------------------
+Parameter Name Selected Description
+-------------------------------------------------------------------------------
+TSPC_GAP_42_1 True Peripheral BR/EDR/LE: Non-Discoverable Mode
+ (See Spec)
+TSPC_GAP_42_2 True Peripheral BR/EDR/LE: Discoverable Mode
+ (See Spec)
+TSPC_GAP_42_3 True Peripheral BR/EDR/LE: Non-Connectable Mode
+ (See Spec)
+TSPC_GAP_42_4 True Peripheral BR/EDR/LE: Connectable Mode (M)
+TSPC_GAP_42_5 True Peripheral BR/EDR/LE: Non-Bondable Mode
+ (See Spec)
+TSPC_GAP_42_6 True Peripheral BR/EDR/LE: Bondable Mode (See Spec)
+-------------------------------------------------------------------------------
+C.1: Mandatory if TSPC_GAP_1_1 is supported over BR/EDR, otherwise Excluded.
+C.2: Mandatory if (TSPC_GAP_1_2 or TSPC_GAP_1_3) is supported over BR/EDR,
+ otherwise Excluded.
+C.3: Mandatory if TSPC_GAP_1_4 is supported over BR/EDR, otherwise Excluded.
+C.4: Mandatory if TSPC_GAP_1_6 is supported over BR/EDR, otherwise Excluded.
+C.5: Mandatory if TSPC_GAP_1_7 is supported over BR/EDR, otherwise Excluded.
+-------------------------------------------------------------------------------
+
+
+ Peripheral BR/EDR/LE Security Aspects
+-------------------------------------------------------------------------------
+Parameter Name Selected Description
+-------------------------------------------------------------------------------
+TSPC_GAP_43_1 True Peripheral BR/EDR/LE: Non-Discoverable Mode
+-------------------------------------------------------------------------------
+
+
+ Central Simultaneous BR/EDR and LE Transports
+-------------------------------------------------------------------------------
+Parameter Name Selected Description
+-------------------------------------------------------------------------------
+TSPC_GAP_44_1 True Central BR/EDR/LE: Simultaneous BR/EDR and LE
+ Transports – BR/EDR Slave to the same
+ device (O)
+TSPC_GAP_44_2 True Central BR/EDR/LE: Simultaneous BR/EDR and LE
+ Transports – BR/EDR Master to the same
+ device (O)
+-------------------------------------------------------------------------------
+
+
+ Peripheral Simultaneous BR/EDR and LE Transports
+-------------------------------------------------------------------------------
+Parameter Name Selected Description
+-------------------------------------------------------------------------------
+TSPC_GAP_45_1 True Simultaneous BR/EDR and LE Transports – BR/EDR
+ Slave to the same device (C.1)
+TSPC_GAP_45_2 True Simultaneous BR/EDR and LE Transports – BR/EDR
+ Master to the same device (C.1)
+-------------------------------------------------------------------------------
+C.1: Optional if ((SUM ICS 31/14 (Core Spec Version 4.1) or SUM ICS 31/15
+(Core Spec Version 4.1+HS)) is supported, otherwise Excluded.
+-------------------------------------------------------------------------------
+
+
+-------------------------------------------------------------------------------
+Parameter Name Selected Description
+-------------------------------------------------------------------------------
+TSPC_GATT_1_1 True GATT Client Role (O)
+TSPC_GATT_1_2 True GATT Server Role (O)
+TSPC_SM_1_1 True Master Role (Initiator)
+TSPC_SM_1_2 True Slave Role (Responder)
+TSPC_SM_2_4 True OOB supported (O)
+-------------------------------------------------------------------------------
diff --git a/repo/android/pics-gatt.txt b/repo/android/pics-gatt.txt
new file mode 100644
index 0000000..90585f5
--- /dev/null
+++ b/repo/android/pics-gatt.txt
@@ -0,0 +1,326 @@
+GATT PICS for the PTS tool.
+
+PTS version: 6.1
+
+* - different than PTS defaults
+
+M - mandatory
+O - optional
+
+ Generic Attribute Profile Role
+-------------------------------------------------------------------------------
+Parameter Name Selected Description
+-------------------------------------------------------------------------------
+TSPC_GATT_1_1 True Generic Attribute Profile Client (C.1)
+TSPC_GATT_1_2 True Generic Attribute Profile Server (C.2)
+TSPC_GATT_1A_1 True Complete GATT client (C.3)
+TSPC_GATT_1A_2 True Complete GATT server (C.4)
+-------------------------------------------------------------------------------
+C.1: Optional to support IF TSPC_GATT_2_2; else IF TSPC_GATT_2_1 it is mandatory
+ to support at least one of TSPC_GATT_1_1 OR TSPC_GATT_1_2
+C.2: Mandatory to support IF TSPC_GATT_2_2; else IF TSPC_GATT_2_1 it is
+ mandatory to support at least one of TSPC_GATT_1_1 OR TSPC_GATT_1_2
+C.3: Optional IF TSPC_GATT_1_1 is supported, otherwise Excluded
+C.4: Optional IF TSPC_GATT_1_2 is supported, otherwise Excluded
+-------------------------------------------------------------------------------
+
+
+ ATT Bearer Transport
+-------------------------------------------------------------------------------
+Parameter Name Selected Description
+-------------------------------------------------------------------------------
+TSPC_GATT_2_1 True Attribute Protocol Supported over BR/EDR
+ (L2CAP fixed channel support) (C.1)
+TSPC_GATT_2_2 True Attribute Protocol Supported over LE (C.2)
+-------------------------------------------------------------------------------
+C.1: Mandatory IF (SUM ICS 12/2 OR SUM ICS 12/9) is supported, otherwise
+ Excluded
+C.2: Mandatory IF (SUM ICS 12/7 OR SUM ICS 12/9) is supported, otherwise
+ Excluded
+-------------------------------------------------------------------------------
+
+
+
+ Generic Attribute Profile Support
+-------------------------------------------------------------------------------
+Parameter Name Selected Description
+-------------------------------------------------------------------------------
+TSPC_GATT_3_1 True Client: Exchange MTU (C.2)
+TSPC_GATT_3_2 True Client: Discover All Primary Services (C.1)
+TSPC_GATT_3_3 True Client: Discover Primary Services Service
+ UUID (C.1)
+TSPC_GATT_3_4 True Client: Find Included Services (C.1)
+TSPC_GATT_3_5 True Client: Discover All characteristics of a
+ Service (C.1)
+TSPC_GATT_3_6 True Client: Discover Characteristics by UUID (C.1)
+TSPC_GATT_3_7 True Client: Discover All Characteristic Descriptors
+ (C.1)
+TSPC_GATT_3_8 True Client: Read Characteristic Value (C.1)
+TSPC_GATT_3_9 True Client: Read using Characteristic UUID (C.1)
+TSPC_GATT_3_10 True Client: Read Long Characteristic Values (C.1)
+TSPC_GATT_3_11 False (*) Client: Read Multiple Characteristic
+ Values (C.1)
+TSPC_GATT_3_12 True Client: Write without Response (C.1)
+TSPC_GATT_3_13 True Client: Signed Write Without Response (C.1)
+TSPC_GATT_3_14 True Client: Write Characteristic Value (C.1)
+TSPC_GATT_3_15 True Client: Write Long Characteristic Values (C.1)
+TSPC_GATT_3_16 True Client: Characteristic Value Reliable
+ Writes (C.1)
+TSPC_GATT_3_17 True Client: Notifications (C.1)
+TSPC_GATT_3_18 True Client: Indications (M)
+TSPC_GATT_3_19 True Client: Read Characteristic Descriptors (C.1)
+TSPC_GATT_3_20 True Client: Read long Characteristic Descriptors
+ (C.1)
+TSPC_GATT_3_21 True Client: Write Characteristic Descriptors (C.1)
+TSPC_GATT_3_22 True Client: Write Long Characteristic Descriptors
+ (C.1)
+TSPC_GATT_3_23 True Client: Service Changed Characteristic (M)
+TSPC_GATT_3B_1 True Client: Primary Service Declaration (M)
+TSPC_GATT_3B_2 True Client: Secondary Service Declaration (M)
+TSPC_GATT_3B_3 True Client: Include Declaration (M)
+TSPC_GATT_3B_4 True Client: Characteristic Declaration (M)
+TSPC_GATT_3B_5 True Client: Characteristic Value Declaration (M)
+TSPC_GATT_3B_6 True Client: Characteristic Extended Properties (M)
+TSPC_GATT_3B_7 True Client: Characteristic User Description
+ Descriptor (M)
+TSPC_GATT_3B_8 True Client: Client Characteristic Configuration
+ Descriptor (M)
+TSPC_GATT_3B_9 True Client: Server Characteristic Configuration
+ Descriptor (M)
+TSPC_GATT_3B_10 True Client: Characteristic Format Descriptor (M)
+TSPC_GATT_3B_11 True Client: Characteristic Aggregate Format
+ Descriptor (M)
+TSPC_GATT_3B_12 True Client: Characteristic Format: Boolean (M)
+TSPC_GATT_3B_13 True Client: Characteristic Format: 2Bit (M)
+TSPC_GATT_3B_14 True Client: Characteristic Format: nibble (M)
+TSPC_GATT_3B_15 True Client: Characteristic Format: Uint8 (M)
+TSPC_GATT_3B_16 True Client: Characteristic Format: Uint12 (M)
+TSPC_GATT_3B_17 True Client: Characteristic Format: Uint16 (M)
+TSPC_GATT_3B_18 True Client: Characteristic Format: Uint24 (M)
+TSPC_GATT_3B_19 True Client: Characteristic Format: Uint32 (M)
+TSPC_GATT_3B_20 True Client: Characteristic Format: Uint48 (M)
+TSPC_GATT_3B_21 True Client: Characteristic Format: Uint64 (M)
+TSPC_GATT_3B_22 True Client: Characteristic Format: Uint128 (M)
+TSPC_GATT_3B_23 True Client: Characteristic Format: Sint8 (M)
+TSPC_GATT_3B_24 True Client: Characteristic Format: Sint12 (M)
+TSPC_GATT_3B_25 True Client: Characteristic Format: Sint16 (M)
+TSPC_GATT_3B_26 True Client: Characteristic Format: Sint24 (M)
+TSPC_GATT_3B_27 True Client: Characteristic Format: Sint32 (M)
+TSPC_GATT_3B_28 True Client: Characteristic Format: Sint48 (M)
+TSPC_GATT_3B_29 True Client: Characteristic Format: Sint64 (M)
+TSPC_GATT_3B_30 True Client: Characteristic Format: Sint128 (M)
+TSPC_GATT_3B_31 True Client: Characteristic Format: Float32 (M)
+TSPC_GATT_3B_32 True Client: Characteristic Format: Float64 (M)
+TSPC_GATT_3B_33 True Client: Characteristic Format: SFLOAT (M)
+TSPC_GATT_3B_34 True Client: Characteristic Format: FLOAT (M)
+TSPC_GATT_3B_35 True Client: Characteristic Format: Duint16 (M)
+TSPC_GATT_3B_36 True Client: Characteristic Format: utf8s (M)
+TSPC_GATT_3B_37 True Client: Characteristic Format: utf16s (M)
+TSPC_GATT_3B_38 True Client: Characteristic Format: struct (M)
+-------------------------------------------------------------------------------
+C.1: Mandatory IF TSPC_GATT_1_3 is supported, otherwise Optional
+C.2: Mandatory IF TSPC_GATT_1_3 AND TSPC_GATT_2_2 is supported, otherwise
+ Excluded
+-------------------------------------------------------------------------------
+
+
+
+ Generic Attribute Profile Support, by Server
+-------------------------------------------------------------------------------
+Parameter Name Selected Description
+-------------------------------------------------------------------------------
+TSPC_GATT_4_1 True Server: Exchange MTU (C.4)
+TSPC_GATT_4_2 True Server: Discover All Primary Services (M)
+TSPC_GATT_4_3 True Server: Discover Primary Services Service
+ UUID (M)
+TSPC_GATT_4_4 True Server: Find Included Services (M)
+TSPC_GATT_4_5 True Server: Discover All characteristics of
+ a Service (M)
+TSPC_GATT_4_6 True Server: Discover Characteristics by UUID (M)
+TSPC_GATT_4_7 True Server: Discover All Characteristic
+ Descriptors (M)
+TSPC_GATT_4_8 True Server: Read Characteristic Value (M)
+TSPC_GATT_4_9 True Server: Read using Characteristic UUID (M)
+TSPC_GATT_4_10 True Server: Read Long Characteristic Values (C.4)
+TSPC_GATT_4_11 False (*) Server: Read Multiple Characteristic
+ Values (C.4)
+TSPC_GATT_4_12 True Server: Write without Response (C.2)
+TSPC_GATT_4_13 True Server: Signed Write Without Response (C.4)
+TSPC_GATT_4_14 True Server: Write Characteristic Value (C.3)
+TSPC_GATT_4_15 True Server: Write Long Characteristic Values (C.4)
+TSPC_GATT_4_16 True Server: Characteristic Value Reliable
+ Writes (C.4)
+TSPC_GATT_4_17 True Server: Notifications (C.4)
+TSPC_GATT_4_18 True Server: Indications (C.1)
+TSPC_GATT_4_19 True Server: Read Characteristic Descriptors (C.4)
+TSPC_GATT_4_20 True Server: Read long Characteristic
+ Descriptors (C.4)
+TSPC_GATT_4_21 True Server: Write Characteristic Descriptors (C.4)
+TSPC_GATT_4_22 True Server: Write Long Characteristic
+ Descriptors (C.4)
+TSPC_GATT_4_23 True Server: Service Changed Characteristic (C.1)
+-------------------------------------------------------------------------------
+C.1: Mandatory IF service definitions on the server can be added, changed, or
+ removed, otherwise Optional
+C.2: Mandatory IF GATT TSPC_GATT_4_13 is supported, otherwise Optional
+C.3: Mandatory IF GATT TSPC_GATT_4_15 is supported, otherwise Optional
+C.4: Mandatory IF GATT TSPC_GATT_1_4 is supported, otherwise Optional
+-------------------------------------------------------------------------------
+
+
+ Profile Attribute Types and Characteristic Formats
+-------------------------------------------------------------------------------
+Parameter Name Selected Description
+-------------------------------------------------------------------------------
+TSPC_GATT_4B_1 True Server: Primary Service Declaration (M)
+TSPC_GATT_4B_2 True Server: Secondary Service Declaration (M)
+TSPC_GATT_4B_3 True Server: Include Declaration (M)
+TSPC_GATT_4B_4 True Server: Characteristic Declaration (M)
+TSPC_GATT_4B_5 True Server: Characteristic Value Declaration (M)
+TSPC_GATT_4B_6 True Server: Characteristic Extended Properties (M)
+TSPC_GATT_4B_7 True Server: Characteristic User Description
+ Descriptor (M)
+TSPC_GATT_4B_8 True Server: Client Characteristic Configuration
+ Descriptor (M)
+TSPC_GATT_4B_9 True Server: Server Characteristic Configuration
+ Descriptor (M)
+TSPC_GATT_4B_10 True Server: Characteristic Format Descriptor (M)
+TSPC_GATT_4B_11 True Server: Characteristic Aggregate Format
+ Descriptor (M)
+TSPC_GATT_4B_12 True Server: Characteristic Format: Boolean (M)
+TSPC_GATT_4B_13 True Server: Characteristic Format: 2Bit (M)
+TSPC_GATT_4B_14 True Server: Characteristic Format: nibble (M)
+TSPC_GATT_4B_15 True Server: Characteristic Format: Uint8 (M)
+TSPC_GATT_4B_16 True Server: Characteristic Format: Uint12 (M)
+TSPC_GATT_4B_17 True Server: Characteristic Format: Uint16 (M)
+TSPC_GATT_4B_18 True Server: Characteristic Format: Uint24 (M)
+TSPC_GATT_4B_19 True Server: Characteristic Format: Uint32 (M)
+TSPC_GATT_4B_20 True Server: Characteristic Format: Uint48 (M)
+TSPC_GATT_4B_21 True Server: Characteristic Format: Uint64 (M)
+TSPC_GATT_4B_22 True Server: Characteristic Format: Uint128 (M)
+TSPC_GATT_4B_23 True Server: Characteristic Format: Sint8 (M)
+TSPC_GATT_4B_24 True Server: Characteristic Format: Sint12 (M)
+TSPC_GATT_4B_25 True Server: Characteristic Format: Sint16 (M)
+TSPC_GATT_4B_26 True Server: Characteristic Format: Sint24 (M)
+TSPC_GATT_4B_27 True Server: Characteristic Format: Sint32 (M)
+TSPC_GATT_4B_28 True Server: Characteristic Format: Sint48 (M)
+TSPC_GATT_4B_29 True Server: Characteristic Format: Sint64 (M)
+TSPC_GATT_4B_30 True Server: Characteristic Format: Sint128 (M)
+TSPC_GATT_4B_31 True Server: Characteristic Format: Float32 (M)
+TSPC_GATT_4B_32 True Server: Characteristic Format: Float64 (M)
+TSPC_GATT_4B_33 True Server: Characteristic Format: SFLOAT (M)
+TSPC_GATT_4B_34 True Server: Characteristic Format: FLOAT (M)
+TSPC_GATT_4B_35 True Server: Characteristic Format: Duint16 (M)
+TSPC_GATT_4B_36 True Server: Characteristic Format: utf8s (M)
+TSPC_GATT_4B_37 True Server: Characteristic Format: utf16s (M)
+TSPC_GATT_4B_38 True Server: Characteristic Format: struct (M)
+-------------------------------------------------------------------------------
+
+
+ Generic Attribute Profile Service
+-------------------------------------------------------------------------------
+Parameter Name Selected Description
+-------------------------------------------------------------------------------
+TSPC_GATT_6_2 True Discover GATT Services using Service Discovery
+ Profile (C.1)
+TSPC_GATT_6_3 True Publish SDP record for GATT services support
+ via BR/EDR (C.2)
+-------------------------------------------------------------------------------
+C.1: Mandatory IF TSPC_GATT_1_1 is supported, otherwise Excluded
+C.2: Mandatory IF TSPC_GATT_1_2 is supported, otherwise Excluded
+-------------------------------------------------------------------------------
+
+
+ Attribute Protocol Transport Security
+-------------------------------------------------------------------------------
+Parameter Name Selected Description
+-------------------------------------------------------------------------------
+TSPC_GATT_7_1 True Security Mode 4 (C.1)
+TSPC_GATT_7_2 True LE Security Mode 1 (C.2)
+TSPC_GATT_7_3 True LE Security Mode 2 (C.2)
+TSPC_GATT_7_4 True LE Authentication Procedure (C.2)
+TSPC_GATT_7_5 True LE connection data signing procedure (C.2)
+TSPC_GATT_7_6 True LE Authenticate signed data procedure (C.2)
+TSPC_GATT_7_7 True LE Authorization Procedure (C.2)
+-------------------------------------------------------------------------------
+C.1: Mandatory IF TSPC_GATT_2_1 is supported, otherwise Excluded
+C.2: Optional IF TSPC_GATT_2_2 is supported, otherwise Excluded
+-------------------------------------------------------------------------------
+
+
+ Attribute Protocol Client Messages
+-------------------------------------------------------------------------------
+Parameter Name Selected Description
+-------------------------------------------------------------------------------
+TSPC_ATT_3_1 True Attribute Error Response (M)
+TSPC_ATT_3_2 True Exchange MTU Request (O)
+TSPC_ATT_3_4 True Find Information Request (O)
+TSPC_ATT_3_6 True Find by Type Value Request (O)
+TSPC_ATT_3_8 True Read by Type Request (O)
+TSPC_ATT_3_10 True Read Request (O)
+TSPC_ATT_3_12 True Read Blob Request (O)
+TSPC_ATT_3_14 False (*) Read Multiple Request (O)
+TSPC_ATT_3_16 True Read by Group Type Request (O)
+TSPC_ATT_3_17 True Read by Group Type Response (C.6)
+TSPC_ATT_3_18 True Write Request (O)
+TSPC_ATT_3_20 True Write Command (O)
+TSPC_ATT_3_21 True Signed Write Command (O)
+TSPC_ATT_3_22 True Prepare Write Request (O)
+TSPC_ATT_3_24 True Execute Write Request (C.8)
+TSPC_ATT_3_26 True Handle Value Notification (M)
+TSPC_ATT_3_28 True Handle Value Confirmation (M)
+-------------------------------------------------------------------------------
+C.6: Mandatory IF TSPC_ATT_3_16 is supported, otherwise Excluded
+C.8: Mandatory IF TSPC_ATT_3_22 is supported, otherwise Excluded
+-------------------------------------------------------------------------------
+
+ Attribute Protocol Server Messages
+-------------------------------------------------------------------------------
+Parameter Name Selected Description
+-------------------------------------------------------------------------------
+TSPC_ATT_4_1 True Attribute Error Response (M)
+TSPC_ATT_4_3 True Exchange MTU Response (M)
+TSPC_ATT_4_5 True Find Information Response (M)
+TSPC_ATT_4_7 True Find by Type Value Response (M)
+TSPC_ATT_4_8 True Read by Type Request (M)
+TSPC_ATT_4_9 True Read by Type Response (M)
+TSPC_ATT_4_11 True Read Response (M)
+TSPC_ATT_4_15 False (*) Read Multiple Response (C.2)
+TSPC_ATT_4_17 True Read by Group Type Response (M)
+TSPC_ATT_4_19 True Write Response (C.3)
+TSPC_ATT_4_20 True Write Command (O)
+TSPC_ATT_4_21 True Signed Write Command (O)
+TSPC_ATT_4_23 True Prepare Write Response (C.4)
+TSPC_ATT_4_25 True Execute Write Response (C.4)
+TSPC_ATT_4_26 True Handle Value Notification (O)
+TSPC_ATT_4_27 True Handle Value Indication (O)
+-------------------------------------------------------------------------------
+C.2: Mandatory IF TSPC_ATT_4_14 is supported, otherwise Excluded
+C.3: Mandatory IF TSPC_ATT_4_18 is supported, otherwise Excluded
+C.4: Mandatory IF TSPC_ATT_4_22 is supported, otherwise Excluded
+C.5: Mandatory IF TSPC_ATT_4_27 is supported, otherwise Excluded
+-------------------------------------------------------------------------------
+
+
+ Attribute Protocol Transport
+-------------------------------------------------------------------------------
+Parameter Name Selected Description
+-------------------------------------------------------------------------------
+TSPC_ATT_5_2 True LE Security Mode 1 (C.2)
+TSPC_ATT_5_4 True LE Authentication Procedure (C.2)
+TSPC_ATT_5_7 True LE Authorization Procedure (C.2)