initial commit

This commit is contained in:
2021-07-08 23:07:49 +03:00
commit 528c093ae4
1704 changed files with 873462 additions and 0 deletions
+1
View File
@@ -0,0 +1 @@
/build
+71
View File
@@ -0,0 +1,71 @@
plugins {
id 'com.android.application'
id 'kotlin-android'
}
android {
compileSdkVersion 30
buildToolsVersion "30.0.2"
android.ndkVersion = "21.3.6528147"
defaultConfig {
applicationId "com.illusionware.shockpair"
minSdkVersion 29
targetSdkVersion 30
versionCode 1
versionName "1.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
repositories {
maven { url 'https://jitpack.io' }
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
kotlinOptions {
jvmTarget = '1.8'
useIR = true
}
externalNativeBuild {
cmake {
path file('src/main/c/CMakeLists.txt')
version "3.10.2"
}
}
}
dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
implementation 'androidx.core:core-ktx:1.2.0'
implementation 'androidx.appcompat:appcompat:1.1.0'
implementation 'com.google.android.material:material:1.1.0'
implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
implementation "com.github.topjohnwu.libsu:core:3.1.2"
implementation "com.github.topjohnwu.libsu:service:3.1.2"
testImplementation 'junit:junit:4.+'
androidTestImplementation 'androidx.test.ext:junit:1.1.1'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
}
// function to run before the build starts.
// this function runs the ndk-build binary on the bluez bluetooth lib to build the shared lib
task runNdkBuild(type: Exec) {
def ndkDir = android.ndkDirectory
executable = "$ndkDir/ndk-build"
args = ['NDK_PROJECT_PATH=build/intermediates/ndk',
'NDK_LIBS_OUT=src/main/btLibs',
'APP_BUILD_SCRIPT=src/main/c/bluez-android-3.35/libs/Android.mk']
}
// tell gradle to run the function before the build
preBuild.dependsOn runNdkBuild
+21
View File
@@ -0,0 +1,21 @@
# Add project specific ProGuard rules here.
# You can control the set of applied configuration files using the
# proguardFiles setting in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}
# Uncomment this to preserve the line number information for
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable
# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile
@@ -0,0 +1,24 @@
package com.illusionware.shockpair
import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.ext.junit.runners.AndroidJUnit4
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.Assert.*
/**
* Instrumented test, which will execute on an Android device.
*
* See [testing documentation](http://d.android.com/tools/testing).
*/
@RunWith(AndroidJUnit4::class)
class ExampleInstrumentedTest {
@Test
fun useAppContext() {
// Context of the app under test.
val appContext = InstrumentationRegistry.getInstrumentation().targetContext
assertEquals("com.illusionware.shockpair", appContext.packageName)
}
}
+28
View File
@@ -0,0 +1,28 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.illusionware.shockpair">
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
<uses-permission android:name="android.permission.BLUETOOTH_PRIVILEGED" />
<uses-permission android:name="android.permission.BIND_DEVICE_ADMIN" />
<uses-feature android:name="android.hardware.bluetooth" />
<application
android:debuggable="true"
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.ShockPair">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
@@ -0,0 +1,10 @@
// IRootService.aidl
package com.illusionware.shockpair;
// Declare any non-default types here with import statements
interface IRootService {
int getPid();
int getUid();
int connectToDS3();
}
+49
View File
@@ -0,0 +1,49 @@
# For more information about using CMake with Android Studio, read the
# documentation: https://d.android.com/studio/projects/add-native-code.html
# Sets the minimum version of CMake required to build the native library.
cmake_minimum_required(VERSION 3.10.2)
set(CMAKE_BUILD_TYPE Debug)
# Creates and names a library, sets it as either STATIC
# or SHARED, and provides the relative paths to its source code.
# You can define multiple libraries, and CMake builds them for you.
# Gradle automatically packages shared libraries with your APK.
add_library( # Sets the name of the library.
shockpairdriver-lib
# Sets the library as a shared library.
SHARED
# Provides a relative path to your source file(s).
shockpairdriver.c)
# Searches for a specified prebuilt library and stores the path as a
# variable. Because CMake includes system libraries in the search path by
# default, you only need to specify the name of the public NDK library
# you want to add. CMake verifies that the library exists before
# completing its build.
find_library( # Sets the name of the path variable.
log-lib
# Specifies the name of the NDK library that
# you want CMake to locate.
log )
# include the headers directory for the bluez lib
include_directories(bluez-android-3.35/libs/include)
# Specifies libraries CMake should link to your target library. You
# can link multiple libraries, such as libraries you define in this
# build script, prebuilt third-party libraries, or system libraries.
target_link_libraries( # Specifies the target library.
shockpairdriver-lib
# link the bluez shared lib to our driver for each ABI
${PROJECT_SOURCE_DIR}/../btLibs/${ANDROID_ABI}/libbluetooth.so
# Links the target library to the log library
# included in the NDK.
${log-lib})
+109
View File
@@ -0,0 +1,109 @@
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 <brian.gix@intel.com>
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@codecoup.pl>
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>
Michał Narajowski <michal.narajowski@codecoup.pl>
Inga Stotland <inga.stotland@intel.com>
Alain Michaud <alainm@chromium.org>
Robert Lubaś <robert.lubas@silvair.com>
Michał Lowas-Rzechonek <michal.lowas-rzechonek@silvair.com>
Jakub Witowski <jakub.witowski@silvair.com>
Rafał Gajda <rafal.gajda@silvair.com>
Szymon Czapracki <szymon.czapracki@codecoup.pl>
+340
View File
@@ -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.
+504
View File
@@ -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!
File diff suppressed because it is too large Load Diff
+236
View File
@@ -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.
+699
View File
@@ -0,0 +1,699 @@
# SPDX-License-Identifier: GPL-2.0
AM_MAKEFLAGS = --no-print-directory
AM_CPPFLAGS =
lib_LTLIBRARIES =
noinst_LIBRARIES =
noinst_LTLIBRARIES =
bin_PROGRAMS =
noinst_PROGRAMS =
CLEANFILES =
EXTRA_DIST =
pkglibexecdir = $(libexecdir)/bluetooth
pkglibexec_PROGRAMS =
pkgincludedir = $(includedir)/bluetooth
pkginclude_HEADERS =
AM_CFLAGS = $(WARNING_CFLAGS) $(MISC_CFLAGS) $(UDEV_CFLAGS) $(ell_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
if MANPAGES
man_MANS =
endif
manual_pages =
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) $(ell_built_sources) src/builtin.h
if LIBRARY
pkginclude_HEADERS += $(lib_headers)
lib_LTLIBRARIES += lib/libbluetooth.la
lib_libbluetooth_la_SOURCES = $(lib_headers) $(lib_sources)
lib_libbluetooth_la_LDFLAGS = $(AM_LDFLAGS) -version-info 22:5:19
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
if EXTERNAL_ELL
ell_cflags = @ELL_CFLAGS@
ell_ldadd = @ELL_LIBS@
ell_dependencies =
ell_built_sources = ell/shared
else
ell_cflags =
ell_ldadd = ell/libell-internal.la
ell_dependencies = $(ell_ldadd)
ell_built_sources = ell/shared ell/internal ell/ell.h
noinst_LTLIBRARIES += ell/libell-internal.la
ell_headers = ell/util.h \
ell/log.h \
ell/queue.h \
ell/hashmap.h \
ell/random.h \
ell/signal.h \
ell/time.h \
ell/time-private.h \
ell/timeout.h \
ell/cipher.h \
ell/checksum.h \
ell/io.h \
ell/idle.h \
ell/main.h \
ell/settings.h \
ell/strv.h \
ell/string.h \
ell/utf8.h \
ell/dbus.h \
ell/dbus-service.h \
ell/dbus-client.h \
ell/key.h \
ell/cert.h \
ell/pem.h \
ell/base64.h \
ell/asn1-private.h \
ell/cert-private.h \
ell/pem-private.h \
ell/uuid.h \
ell/useful.h \
ell/main-private.h \
ell/tester.h \
ell/tls.h \
ell/tls-private.h \
ell/ecc.h \
ell/ecc-private.h \
ell/ecdh.h
ell_sources = ell/private.h ell/missing.h \
ell/util.c \
ell/log.c \
ell/queue.c \
ell/hashmap.c \
ell/random.c \
ell/signal.c \
ell/time.c \
ell/timeout.c \
ell/io.c \
ell/idle.c \
ell/main.c \
ell/settings.c \
ell/strv.c \
ell/string.c \
ell/cipher.c \
ell/checksum.c \
ell/pem.c \
ell/cert.c \
ell/cert-crypto.c \
ell/key.c \
ell/base64.c \
ell/utf8.c \
ell/dbus-private.h \
ell/dbus.c \
ell/dbus-message.c \
ell/dbus-util.c \
ell/dbus-service.c \
ell/dbus-client.c \
ell/dbus-name-cache.c \
ell/dbus-filter.c \
ell/gvariant-private.h \
ell/gvariant-util.c \
ell/siphash-private.h \
ell/siphash.c \
ell/uuid.c \
ell/tester.c \
ell/tls.c \
ell/tls-extensions.c \
ell/tls-suites.c \
ell/tls-record.c \
ell/ecc.c \
ell/ecc-external.c \
ell/ecdh.c
ell_shared = ell/useful.h
ell_libell_internal_la_SOURCES = $(ell_headers) $(ell_sources) $(ell_shared)
endif
CLEANFILES += $(ell_built_sources)
noinst_LTLIBRARIES += src/libshared-glib.la src/libshared-mainloop.la
if LIBSHARED_ELL
noinst_LTLIBRARIES += src/libshared-ell.la
endif
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/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/shared/log.h src/shared/log.c \
src/shared/tty.h
if READLINE
shared_sources += src/shared/shell.c src/shared/shell.h
endif
src_libshared_glib_la_SOURCES = $(shared_sources) \
src/shared/io-glib.c \
src/shared/timeout-glib.c \
src/shared/mainloop-glib.c \
src/shared/mainloop-notify.h \
src/shared/mainloop-notify.c \
src/shared/tester.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 \
src/shared/mainloop-notify.h \
src/shared/mainloop-notify.c
if LIBSHARED_ELL
src_libshared_ell_la_SOURCES = $(shared_sources) \
src/shared/io-ell.c \
src/shared/timeout-ell.c \
src/shared/mainloop.h \
src/shared/mainloop-ell.c
endif
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_cppflags =
builtin_nodist =
builtin_ldadd =
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
pkglibexec_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/rfkill.c src/btd.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/dbus-common.c src/dbus-common.h \
src/eir.h src/eir.c \
src/adv_monitor.h src/adv_monitor.c \
src/battery.h src/battery.c
src_bluetoothd_LDADD = lib/libbluetooth-internal.la \
gdbus/libgdbus-internal.la \
src/libshared-glib.la \
$(BACKTRACE_LIBS) $(GLIB_LIBS) $(DBUS_LIBS) -ldl -lrt \
$(builtin_ldadd)
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_CPPFLAGS = $(AM_CPPFLAGS) -DBLUETOOTH_PLUGIN_BUILTIN \
-DPLUGINDIR=\""$(build_plugindir)"\" \
$(BACKTRACE_CFLAGS) $(builtin_cppflags)
src_bluetoothd_SHORTNAME = bluetoothd
builtin_files = src/builtin.h $(builtin_nodist)
nodist_src_bluetoothd_SOURCES = $(builtin_files)
CLEANFILES += $(builtin_files) src/bluetooth.service
if MANPAGES
man_MANS += src/bluetoothd.8
endif
manual_pages += src/bluetoothd.8
EXTRA_DIST += src/genbuiltin src/bluetooth.conf \
src/main.conf profiles/network/network.conf \
profiles/input/input.conf
test_scripts =
unit_tests =
include Makefile.tools
include Makefile.obexd
include android/Makefile.am
include Makefile.mesh
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/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 += doc/btsnoop.txt
EXTRA_DIST += tools/magic.btsnoop
AM_CPPFLAGS += $(DBUS_CFLAGS) $(GLIB_CFLAGS) -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)
if OBEX
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)
endif
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 MIDI
unit_tests += unit/test-midi
unit_test_midi_CPPFLAGS = $(AM_CPPFLAGS) $(ALSA_CFLAGS) -DMIDI_TEST
unit_test_midi_SOURCES = unit/test-midi.c \
profiles/midi/libmidi.h \
profiles/midi/libmidi.c
unit_test_midi_LDADD = src/libshared-glib.la \
$(GLIB_LIBS) $(ALSA_LIBS)
endif
if MESH
unit_tests += unit/test-mesh-crypto
unit_test_mesh_crypto_CPPFLAGS = $(ell_cflags)
unit_test_mesh_crypto_SOURCES = unit/test-mesh-crypto.c \
mesh/crypto.h ell/internal ell/ell.h
unit_test_mesh_crypto_LDADD = $(ell_ldadd)
endif
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
EXTRA_DIST += $(manual_pages) $(patsubst %.1,%.rst, \
$(patsubst %.8,%.rst,$(manual_pages)))
DISTCHECK_CONFIGURE_FLAGS = --disable-datafiles --enable-library \
--enable-health \
--enable-midi \
--enable-manpages \
--enable-android \
--enable-mesh \
--enable-btpclient \
--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,@pkglibexecdir\@,$(pkglibexecdir),g' \
< $< > $@
if RUN_RST2MAN
RST2MAN_PROCESS = $(AM_V_GEN)$(MKDIR_P) $(dir $@) && \
$(RST2MAN) --strict --no-raw \
--no-generator --no-datestamp $< $@
else
RST2MAN_PROCESS = $(AM_V_GEN)test -f $@ || \
{ echo "Generated manual page $@ does not exist"; false; }
endif
%.service: %.service.in Makefile
$(SED_PROCESS)
%.1: %.rst Makefile
$(RST2MAN_PROCESS)
%.8: %.rst Makefile
$(RST2MAN_PROCESS)
src/builtin.h: src/genbuiltin $(builtin_sources)
$(AM_V_GEN)$(srcdir)/src/genbuiltin $(builtin_modules) > $@
tools/%.rules:
$(AM_V_at)$(MKDIR_P) tools
$(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 $(abspath $<) $@
ell/shared: Makefile
$(AM_V_at)$(MKDIR_P) ell
$(AM_V_GEN)for f in $(ell_shared) ; do \
if [ ! -f $$f ] ; then \
$(LN_S) -t ell -f $(abs_srcdir)/../ell/$$f ; \
fi \
done > $@
ell/internal: Makefile
$(AM_V_at)$(MKDIR_P) ell
$(AM_V_GEN)for f in $(ell_headers) $(ell_sources) ; do \
if [ ! -f $$f ] ; then \
$(LN_S) -t ell -f $(abs_srcdir)/../ell/$$f ; \
fi \
done > $@
ell/ell.h: Makefile
$(AM_V_at)echo -n > $@
$(AM_V_GEN)for f in $(ell_headers) ; do \
echo "#include <$$f>" >> $@ ; \
done
maintainer-clean-local:
-rm -rf ell
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
File diff suppressed because it is too large Load Diff
+60
View File
@@ -0,0 +1,60 @@
# SPDX-License-Identifier: GPL-2.0
if MESH
if DATAFILES
dbus_DATA += mesh/bluetooth-mesh.conf
endif
if SYSTEMD
systemdsystemunit_DATA += mesh/bluetooth-mesh.service
dbussystembus_DATA += mesh/org.bluez.mesh.service
endif
mesh_sources = mesh/mesh.h mesh/mesh.c \
mesh/net-keys.h mesh/net-keys.c \
mesh/mesh-io.h mesh/mesh-io.c \
mesh/mesh-mgmt.c mesh/mesh-mgmt.h \
mesh/error.h mesh/mesh-io-api.h \
mesh/mesh-io-generic.h \
mesh/mesh-io-generic.c \
mesh/mesh-io-unit.h \
mesh/mesh-io-unit.c \
mesh/net.h mesh/net.c \
mesh/crypto.h mesh/crypto.c \
mesh/friend.h mesh/friend.c \
mesh/appkey.h mesh/appkey.c \
mesh/node.h mesh/node.c \
mesh/provision.h mesh/prov.h \
mesh/model.h mesh/model.c \
mesh/cfgmod.h mesh/cfgmod-server.c \
mesh/mesh-config.h mesh/mesh-config-json.c \
mesh/util.h mesh/util.c \
mesh/dbus.h mesh/dbus.c \
mesh/agent.h mesh/agent.c \
mesh/prov-acceptor.c mesh/prov-initiator.c \
mesh/manager.h mesh/manager.c \
mesh/pb-adv.h mesh/pb-adv.c \
mesh/keyring.h mesh/keyring.c \
mesh/rpl.h mesh/rpl.c \
mesh/mesh-defs.h
pkglibexec_PROGRAMS += mesh/bluetooth-meshd
mesh/mesh.$(OBJEXT): ell/internal
mesh/main.$(OBJEXT): src/builtin.h lib/bluetooth/bluetooth.h
mesh_bluetooth_meshd_SOURCES = $(mesh_sources) mesh/main.c
mesh_bluetooth_meshd_LDADD = src/libshared-ell.la $(ell_ldadd) -ljson-c
mesh_bluetooth_meshd_DEPENDENCIES = $(ell_dependencies) src/libshared-ell.la \
mesh/bluetooth-mesh.service
if MANPAGES
man_MANS += mesh/bluetooth-meshd.8
endif
manual_pages += mesh/bluetooth-meshd.8
CLEANFILES += mesh/bluetooth-mesh.service
endif
EXTRA_DIST += mesh/bluetooth-mesh.conf mesh/bluetooth-mesh.service.in \
mesh/org.bluez.mesh.service mesh/mesh-main.conf
+113
View File
@@ -0,0 +1,113 @@
# SPDX-License-Identifier: GPL-2.0
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
if OBEX
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
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
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
pkglibexec_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/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 = $(AM_LDFLAGS) -Wl,--export-dynamic
obexd_src_obexd_CPPFLAGS = $(AM_CPPFLAGS) $(GLIB_CFLAGS) $(DBUS_CFLAGS) \
$(ICAL_CFLAGS) -DOBEX_PLUGIN_BUILTIN \
-DPLUGINDIR=\""$(obex_plugindir)"\" \
-D_FILE_OFFSET_BITS=64 \
-I$(builddir)/lib -I$(builddir)/obexd/src
obexd_src_obexd_CFLAGS = $(AM_CFLAGS) -fPIC
endif
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_at)$(MKDIR_P) $(dir $@)
$(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
+113
View File
@@ -0,0 +1,113 @@
# SPDX-License-Identifier: GPL-2.0
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 NFC
builtin_modules += neard
builtin_sources += plugins/neard.c
endif
if SAP
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
endif
if A2DP
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
endif
if AVRCP
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
endif
if NETWORK
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
endif
if HID
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 profiles/input/sixaxis.h
endif
if HOG
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
endif
if HEALTH
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 MIDI
builtin_modules += midi
builtin_sources += profiles/midi/midi.c \
profiles/midi/libmidi.h \
profiles/midi/libmidi.c
builtin_cppflags += $(ALSA_CFLAGS)
builtin_ldadd += $(ALSA_LIBS)
endif
builtin_modules += battery
builtin_sources += profiles/battery/battery.c
if SIXAXIS
plugin_LTLIBRARIES += plugins/sixaxis.la
plugins_sixaxis_la_SOURCES = plugins/sixaxis.c
plugins_sixaxis_la_LDFLAGS = $(AM_LDFLAGS) -module -avoid-version
plugins_sixaxis_la_LIBADD = $(UDEV_LIBS)
plugins_sixaxis_la_CFLAGS = $(AM_CFLAGS) -fvisibility=hidden
endif
+515
View File
@@ -0,0 +1,515 @@
# SPDX-License-Identifier: GPL-2.0
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/advertising.h \
client/advertising.c \
client/adv_monitor.h \
client/adv_monitor.c \
client/gatt.h client/gatt.c
client_bluetoothctl_LDADD = gdbus/libgdbus-internal.la src/libshared-glib.la \
$(GLIB_LIBS) $(DBUS_LIBS) -lreadline
endif
if ZSH_COMPLETIONS
zshcompletiondir=$(ZSH_COMPLETIONDIR)
dist_zshcompletion_DATA = completion/zsh/_bluetoothctl
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/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/msft.h monitor/msft.c \
monitor/jlink.h monitor/jlink.c \
monitor/tty.h
monitor_btmon_LDADD = lib/libbluetooth-internal.la \
src/libshared-mainloop.la $(UDEV_LIBS) -ldl
if MANPAGES
man_MANS += monitor/btmon.1
endif
endif
manual_pages += monitor/btmon.1
if LOGGER
pkglibexec_PROGRAMS += tools/btmon-logger
tools_btmon_logger_SOURCES = tools/btmon-logger.c
tools_btmon_logger_LDADD = src/libshared-mainloop.la
tools_btmon_logger_DEPENDENCIES = src/libshared-mainloop.la \
tools/bluetooth-logger.service
if SYSTEMD
systemdsystemunit_DATA += tools/bluetooth-logger.service
endif
endif
CLEANFILES += tools/bluetooth-logger.service
EXTRA_DIST += tools/bluetooth-logger.service.in
if TESTING
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/rctest tools/l2test tools/l2ping tools/bluemoon \
tools/hex2hcd tools/mpris-proxy tools/btattach
noinst_PROGRAMS += tools/bdaddr tools/avinfo tools/avtest \
tools/scotest tools/amptest tools/hwdb \
tools/hcieventmask tools/hcisecfilter \
tools/btinfo tools/btconfig \
tools/btsnoop tools/btproxy \
tools/btiotest tools/bneptest tools/mcaptest \
tools/cltest tools/oobtest tools/advtest \
tools/seq2bseq tools/nokfw tools/rtlfw \
tools/bcmfw tools/create-image \
tools/eddystone tools/ibeacon \
tools/btgatt-client tools/btgatt-server \
tools/test-runner tools/check-selftest \
tools/gatt-service profiles/iap/iapd
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_btconfig_SOURCES = tools/btconfig.c
tools_btconfig_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) \
src/libshared-mainloop.la -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) \
src/libshared-mainloop.la
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_advtest_SOURCES = tools/advtest.c
tools_advtest_LDADD = lib/libbluetooth-internal.la src/libshared-mainloop.la
tools_seq2bseq_SOURCES = tools/seq2bseq.c
tools_nokfw_SOURCES = tools/nokfw.c
tools_rtlfw_SOURCES = tools/rtlfw.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
tools_rctest_LDADD = lib/libbluetooth-internal.la
tools_l2test_LDADD = lib/libbluetooth-internal.la
tools_l2ping_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)
tools_gatt_service_SOURCES = tools/gatt-service.c
tools_gatt_service_LDADD = $(GLIB_LIBS) $(DBUS_LIBS) gdbus/libgdbus-internal.la
profiles_iap_iapd_SOURCES = profiles/iap/main.c
profiles_iap_iapd_LDADD = gdbus/libgdbus-internal.la $(GLIB_LIBS) $(DBUS_LIBS)
if MANPAGES
man_MANS += tools/rctest.1 tools/l2ping.1 tools/btattach.1
endif
if MESH
bin_PROGRAMS += tools/meshctl
tools_meshctl_SOURCES = tools/meshctl.c \
tools/mesh/agent.h tools/mesh/agent.c \
tools/mesh/config-model.h\
tools/mesh-gatt/mesh-net.h \
tools/mesh-gatt/node.h tools/mesh-gatt/node.c \
tools/mesh-gatt/gatt.h tools/mesh-gatt/gatt.c \
tools/mesh-gatt/crypto.h\
tools/mesh-gatt/crypto.c \
tools/mesh-gatt/keys.h \
tools/mesh-gatt/net.h tools/mesh-gatt/net.c \
tools/mesh-gatt/prov.h tools/mesh-gatt/prov.c \
tools/mesh-gatt/util.h tools/mesh-gatt/util.c \
tools/mesh-gatt/prov-db.h \
tools/mesh-gatt/prov-db.c \
tools/mesh-gatt/config-client.c \
tools/mesh-gatt/config-server.c \
tools/mesh-gatt/onoff-model.h \
tools/mesh-gatt/onoff-model.c
tools_meshctl_LDADD = gdbus/libgdbus-internal.la src/libshared-glib.la \
lib/libbluetooth-internal.la \
$(GLIB_LIBS) $(DBUS_LIBS) -ljson-c -lreadline
bin_PROGRAMS += tools/mesh-cfgclient
tools_mesh_cfgclient_SOURCES = tools/mesh-cfgclient.c \
tools/mesh/model.h tools/mesh/config-model.h \
tools/mesh/cfgcli.h tools/mesh/cfgcli.c \
tools/mesh/keys.h tools/mesh/keys.c \
tools/mesh/util.h tools/mesh/util.c \
tools/mesh/remote.h tools/mesh/remote.c \
tools/mesh/agent.h tools/mesh/agent.c \
tools/mesh/mesh-db.h tools/mesh/mesh-db.c \
mesh/util.h mesh/util.c \
mesh/crypto.h mesh/crypto.c
tools_mesh_cfgclient_LDADD = lib/libbluetooth-internal.la src/libshared-ell.la \
$(ell_ldadd) -ljson-c -lreadline
bin_PROGRAMS += tools/mesh-cfgtest
tools_mesh_cfgtest_SOURCES = tools/mesh-cfgtest.c
tools_mesh_cfgtest_LDADD = lib/libbluetooth-internal.la src/libshared-ell.la \
$(ell_ldadd)
endif
EXTRA_DIST += tools/mesh-gatt/local_node.json tools/mesh-gatt/prov_db.json
if DEPRECATED
bin_PROGRAMS += tools/hciattach tools/hciconfig tools/hcitool tools/hcidump \
tools/rfcomm tools/sdptool tools/ciptool
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_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_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_hcidump_LDADD = lib/libbluetooth-internal.la
tools_rfcomm_LDADD = lib/libbluetooth-internal.la
if MANPAGES
man_MANS += tools/hciattach.1 tools/hciconfig.1 \
tools/hcitool.1 tools/hcidump.1 \
tools/rfcomm.1 tools/sdptool.1 tools/ciptool.1
endif
endif
endif
manual_pages += tools/hciattach.1 tools/hciconfig.1 \
tools/hcitool.1 tools/hcidump.1 \
tools/rfcomm.1 tools/sdptool.1 tools/ciptool.1 \
tools/rctest.1 tools/l2ping.1 tools/btattach.1 \
tools/bdaddr.1
if HID2HCI
udevdir = $(UDEV_DIR)
udev_PROGRAMS = tools/hid2hci
tools_hid2hci_LDADD = $(UDEV_LIBS)
if MANPAGES
man_MANS += tools/hid2hci.1
endif
endif
manual_pages += tools/hid2hci.1
if READLINE
noinst_PROGRAMS += tools/btmgmt tools/obex-client-tool tools/obex-server-tool \
tools/bluetooth-player tools/obexctl
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
tools_bluetooth_player_LDADD = gdbus/libgdbus-internal.la \
src/libshared-glib.la \
$(GLIB_LIBS) $(DBUS_LIBS) -lreadline
tools_obexctl_SOURCES = tools/obexctl.c
tools_obexctl_LDADD = gdbus/libgdbus-internal.la src/libshared-glib.la \
$(GLIB_LIBS) $(DBUS_LIBS) -lreadline
tools_btmgmt_SOURCES = tools/btmgmt.c src/uuid-helper.c client/display.c
tools_btmgmt_LDADD = lib/libbluetooth-internal.la src/libshared-mainloop.la \
-lreadline
if DEPRECATED
noinst_PROGRAMS += attrib/gatttool
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
endif
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-network 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-hfp 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 test/test-mesh test/agent.py
if BTPCLIENT
noinst_PROGRAMS += tools/btpclient tools/btpclientctl
tools_btpclient_SOURCES = tools/btpclient.c src/shared/btp.c src/shared/btp.h
tools_btpclient_LDADD = lib/libbluetooth-internal.la \
src/libshared-ell.la $(ell_ldadd)
tools_btpclient_DEPENDENCIES = lib/libbluetooth-internal.la $(ell_dependencies)
tools/btpclient.$(OBJEXT): src/libshared-ell.la ell/internal
tools_btpclientctl_SOURCES = tools/btpclientctl.c client/display.c
tools_btpclientctl_LDADD = src/libshared-mainloop.la src/libshared-glib.la \
lib/libbluetooth-internal.la -lreadline
endif
View File
+300
View File
@@ -0,0 +1,300 @@
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)
On a debian based system, this can be done by running the following command:
sudo apt-get build-dep bluez
./bootstrap
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
Embedded Linux library
======================
In order to compile mesh support and test client utility the development
version of Embedded Linux library is required to be present. The development
repositories can be found here:
git://git.kernel.org/pub/scm/libs/ell/ell.git
https://kernel.googlesource.com/pub/scm/libs/ell/ell.git
The build systems requires that the Embedded Linux library source code
is available on the same top level directory as the source code:
.
|--- ell
| |--- ell
| `--- unit
`--- bluez
|--- src
`--- tools
It is not required to build or install Embedded Linux library. The build
will happen when building the binaries and it will then be linked internally.
When using --enable-external-ell build option, it is not required that the
Embedded Linux library source code is available in the top level directory.
When neither --enable-mesh nor --enable-btpclient is specified, then this
part is irrelevant and Embedded Linux library is not required.
Kernel Build Options (for Mesh)
===============================
The Mesh daemon uses kernel provided crypto utilities to perform security
functions required of Bluetooth Mesh. Many standard distributions currently
enable all required crypto features, but a few notable distributions do
not.
If Mesh Cryptography is not working, the following configuration options
may need to be enabled, and the kernel rebuilt.
1. A minimum of kernel version 4.9 or later is required
2. The kernel must at a minimum have the following .config options turned on:
CONFIG_CRYPTO_USER
CONFIG_CRYPTO_USER_API
CONFIG_CRYPTO_USER_API_AEAD
CONFIG_CRYPTO_USER_API_HASH
CONFIG_CRYPTO_AES
CONFIG_CRYPTO_CCM
CONFIG_CRYPTO_AEAD
CONFIG_CRYPTO_CMAC
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.
--disable-a2dp
Disable A2DP profile
By default bluetoothd supports A2DP profile using a built-in
plugin, this option disables it.
This option is provided for distributions that do not have any
audio capabilities.
--disable-avrcp
Disable AVRCP profile
By default bluetoothd supports AVRCP profile using a built-in
plugin, this option disables it.
This option is provided for distributions that do not have any
audio capabilities.
--disable-network
Disable PANU, NAP, GN profiles
By default bluetoothd supports PANU, NAP and GN profile using a
built-in plugin, this option disables it.
This option is provided for distributions that do not have any
network capabilities.
--disable-hid
Disable HID profile
By default bluetoothd supports HID profile using a built-in
plugin, this option disables it.
This option is provided for distributions that do not have any
input capabilities.
--disable-hog
Disable HoG profile
By default bluetoothd supports HoG profile using a built-in
plugin, this option disables it.
This option is provided for distributions that do not have any
input capabilities.
--enable-testing
Enable testing tools
By default tools used only for testing emulation are disabled.
This option can be used to enable them.
It is not recommended to enable this option for production
systems. These tools may contain tests that depend on specific
environment or kernel features in development.
--enable-experimental
Enable experimental tools
By default all tools 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 behavior of the experimental tools is unstable
and might still change.
--enable-deprecated
Enable deprecated tools
By defauld all tools that are no longer maintained are
disabled. This option can be used to enable them.
It is not recommended to enable this option for production
systems. The behavior of the deprecated tools may be unstable
or simply don't work anymore.
--enable-nfc
This option enable NFC pairing support.
By default the integration with neard is disabled, this gives
the option to enable it in system where neard is supported.
The plugin is built into bluetoothd therefore it does not need
to be package separately.
--enable-sap
This option enable SAP profile using sap plugin.
By default sap plugin is disabled since it requires tight
integration with systems and is very rarely required.
The plugin is built into bluetoothd therefore it does not need
to be package separately.
--enable-health
This option enable health profiles.
By default health plugin is disabled since its profiles are
target for the health industry.
The plugin is built into bluetoothd therefore it does not need
to be package separately.
--enable-midi
This option enable MIDI support via ALSA Sequencer.
By default midi plugin is disabled since it still considered
experimental. When bluetoothd will create a new ALSA Sequencer
client and port for each device connected that supports the
MIDI GATT primary service.
The plugin is built into bluetoothd therefore it does not need
to be package separately.
Information
===========
Mailing lists:
linux-bluetooth@vger.kernel.org
For additional information about the project visit BlueZ web site:
http://www.bluez.org
+224
View File
@@ -0,0 +1,224 @@
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
- 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
- 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
Management Interface
====================
Mesh
====
- Add unit tests
Priority: High
Complexity: C1
- Implement unified feature management mechanism for mesh nodes that are hosted
on the same device
Priority: Medium
Complexity: C2
- Implement mandatory Health Server model. Most likely, this will involve
additions to mesh D-Bus API
Priority: Medium
Complexity: C2
- Add support for GATT proxy server
Priority: Low
Complexity: C4
- Merge common functionality from tools/mesh. Ideally, source code from the
tools/mesh directory should completely dissapear.
Priority: Low
Complexity: C2
- Support for Low Power Node (LPN)
Priority: Low
Complexity: C4
+62
View File
@@ -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_32"
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 -Wl,-z,now"
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)
])
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,857 @@
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/shared/ad.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/shared/log.c \
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)
@@ -0,0 +1,322 @@
if ANDROID
AM_CPPFLAGS += -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_CPPFLAGS = $(AM_CPPFLAGS) -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 \
src/shared/log.h src/shared/log.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_CPPFLAGS = $(AM_CPPFLAGS) -I$(srcdir)/android \
-DPLUGINDIR=\""$(android_plugindir)"\"
android_haltest_LDFLAGS = $(AM_LDFLAGS) -pthread
android_haltest_LDADD = -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_CPPFLAGS = $(AM_CPPFLAGS) -I$(srcdir)/android \
-DPLUGINDIR=\""$(android_plugindir)"\"
android_android_tester_LDADD = lib/libbluetooth-internal.la \
src/libshared-glib.la $(GLIB_LIBS) -ldl
android_android_tester_LDFLAGS = $(AM_LDFLAGS) -pthread
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_CPPFLAGS = $(AM_CPPFLAGS) -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_CPPFLAGS = $(AM_CPPFLAGS) -I$(srcdir)/android \
$(SBC_CFLAGS)
android_audio_a2dp_default_la_LIBADD = $(SBC_LIBS) -lrt
android_audio_a2dp_default_la_LDFLAGS = $(AM_LDFLAGS) -module -avoid-version \
-no-undefined -pthread
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_CPPFLAGS = $(AM_CPPFLAGS) -I$(srcdir)/android
android_audio_sco_default_la_LIBADD = $(SPEEXDSP_LIBS) -lrt
android_audio_sco_default_la_LDFLAGS = $(AM_LDFLAGS) -module -avoid-version \
-no-undefined
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
+454
View File
@@ -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.
@@ -0,0 +1,71 @@
// SPDX-License-Identifier: LGPL-2.1-or-later
/*
*
* BlueZ - Bluetooth protocol stack for Linux
*
* Copyright (C) 2014 Intel Corporation. All rights reserved.
*
*
*/
#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;
}
@@ -0,0 +1,12 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
/*
*
* BlueZ - Bluetooth protocol stack for Linux
*
* Copyright (C) 2014 Intel Corporation. All rights reserved.
*
*
*/
bool bt_a2dp_sink_register(struct ipc *ipc, const bdaddr_t *addr, uint8_t mode);
void bt_a2dp_sink_unregister(void);
File diff suppressed because it is too large Load Diff
+12
View File
@@ -0,0 +1,12 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
/*
*
* BlueZ - Bluetooth protocol stack for Linux
*
* Copyright (C) 2013-2014 Intel Corporation. All rights reserved.
*
*
*/
bool bt_a2dp_register(struct ipc *ipc, const bdaddr_t *addr, uint8_t mode);
void bt_a2dp_unregister(void);
@@ -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>
@@ -0,0 +1,69 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
/*
*
* BlueZ - Bluetooth protocol stack for Linux
*
* Copyright (C) 2014 Intel Corporation. All rights reserved.
*
*
*/
#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));
@@ -0,0 +1,260 @@
// SPDX-License-Identifier: Apache-2.0
/*
** Copyright 2011, The Android Open-Source Project
**
*/
//#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);
}
@@ -0,0 +1,99 @@
/* SPDX-License-Identifier: Apache-2.0 */
/*
** Copyright 2008, The Android Open-Source Project
**
*/
#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
File diff suppressed because it is too large Load Diff
+170
View File
@@ -0,0 +1,170 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
*
* BlueZ - Bluetooth protocol stack for Linux
*
* Copyright (C) 2006-2010 Nokia Corporation
* Copyright (C) 2004-2010 Marcel Holtmann <marcel@holtmann.org>
*
*
*/
#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);
File diff suppressed because it is too large Load Diff
+278
View File
@@ -0,0 +1,278 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
*
* BlueZ - Bluetooth protocol stack for Linux
*
* Copyright (C) 2006-2010 Nokia Corporation
* Copyright (C) 2004-2010 Marcel Holtmann <marcel@holtmann.org>
*
*
*/
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);
@@ -0,0 +1,897 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
*
* BlueZ - Bluetooth protocol stack for Linux
*
* Copyright (C) 2014 Intel Corporation
*
*
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#define _GNU_SOURCE
#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;
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,343 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
/*
*
* BlueZ - Bluetooth protocol stack for Linux
*
* Copyright (C) 2014 Intel Corporation. All rights reserved.
*
*
*/
/* 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);
File diff suppressed because it is too large Load Diff
+15
View File
@@ -0,0 +1,15 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
/*
*
* BlueZ - Bluetooth protocol stack for Linux
*
* Copyright (C) 2013-2014 Intel Corporation. All rights reserved.
*
*
*/
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);
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,102 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
/*
*
* BlueZ - Bluetooth protocol stack for Linux
*
* Copyright (C) 2013-2014 Intel Corporation. All rights reserved.
*
*
*/
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);
struct bt_ad;
struct adv_instance {
uint8_t instance;
int32_t timeout;
int32_t type;
struct bt_ad *ad;
struct bt_ad *sr;
unsigned include_tx_power:1;
};
/* Values below have no C API definition - only in Java (AdvertiseManager.java)
* and bluedroid
*/
enum android_adv_type {
ANDROID_ADVERTISING_EVENT_TYPE_CONNECTABLE = 0,
ANDROID_ADVERTISING_EVENT_TYPE_SCANNABLE = 2,
ANDROID_ADVERTISING_EVENT_TYPE_NON_CONNECTABLE = 3,
};
typedef void (*bt_le_addrm_advertising_done)(uint8_t status, void *user_data);
bool bt_le_add_advertising(struct adv_instance *adv,
bt_le_addrm_advertising_done cb, void *user_data);
bool bt_le_remove_advertising(struct adv_instance *adv,
bt_le_addrm_advertising_done cb, void *user_data);
@@ -0,0 +1,242 @@
// SPDX-License-Identifier: LGPL-2.1-or-later
/*
*
* BlueZ - Bluetooth protocol stack for Linux
*
* Copyright (C) 2013-2014 Intel Corporation. All rights reserved.
*
*
*/
#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, 0, monitor_buf, pktlen);
}
}
static int open_monitor(const char *path)
{
struct sockaddr_hci addr;
int opt = 1;
snoop = btsnoop_create(path, 0, 0, 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;
__btd_log_init(NULL, 0);
DBG("");
set_capabilities();
if (argc > 1)
path = argv[1];
else
path = DEFAULT_SNOOP_FILE;
mainloop_init();
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_with_signal(signal_callback, NULL);
close_monitor();
info("bluetoothd_snoop: stopped");
__btd_log_cleanup();
return EXIT_SUCCESS;
}
@@ -0,0 +1,83 @@
// SPDX-License-Identifier: Apache-2.0
/*
* Copyright (C) 2014 Intel Corporation
*
*/
#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;
}
@@ -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 };
@@ -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;
@@ -0,0 +1,467 @@
// SPDX-License-Identifier: Apache-2.0
/*
* Copyright (C) 2013 Intel Corporation
*
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#define _GNU_SOURCE
#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;
}
@@ -0,0 +1,87 @@
// SPDX-License-Identifier: Apache-2.0
/*
* Copyright (C) 2013 Intel Corporation
*
*/
#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;
}
@@ -0,0 +1,10 @@
/* SPDX-License-Identifier: Apache-2.0 */
/*
* Copyright (C) 2013 Intel Corporation
*
*/
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);
@@ -0,0 +1,525 @@
// SPDX-License-Identifier: Apache-2.0
/*
* Copyright (C) 2014 Intel Corporation
*
*/
#define _GNU_SOURCE
#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
};
@@ -0,0 +1,129 @@
// SPDX-License-Identifier: Apache-2.0
/*
* Copyright (C) 2014 Intel Corporation
*
*/
#define _GNU_SOURCE
#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
};
@@ -0,0 +1,133 @@
// SPDX-License-Identifier: Apache-2.0
/*
* Copyright (C) 2013 Intel Corporation
*
*/
#define _GNU_SOURCE
#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
};
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,658 @@
// SPDX-License-Identifier: Apache-2.0
/*
* Copyright (C) 2014 Intel Corporation
*
*/
#define _GNU_SOURCE
#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
};
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,444 @@
// SPDX-License-Identifier: Apache-2.0
/*
* Copyright (C) 2013 Intel Corporation
*
*/
#define _GNU_SOURCE
#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
};
@@ -0,0 +1,367 @@
// SPDX-License-Identifier: Apache-2.0
/*
* Copyright (C) 2014 Intel Corporation
*
*/
#define _GNU_SOURCE
#include <stdio.h>
#include <ctype.h>
#include <unistd.h>
#include <string.h>
#include <hardware/bluetooth.h>
#include <hardware/bt_hl.h>
#include "if-main.h"
#include "pollhandler.h"
#include "../hal-utils.h"
SINTMAP(bthl_mdep_role_t, -1, "(unknown)")
DELEMENT(BTHL_MDEP_ROLE_SOURCE),
DELEMENT(BTHL_MDEP_ROLE_SINK),
ENDMAP
SINTMAP(bthl_channel_type_t, -1, "(unknown)")
DELEMENT(BTHL_CHANNEL_TYPE_RELIABLE),
DELEMENT(BTHL_CHANNEL_TYPE_STREAMING),
DELEMENT(BTHL_CHANNEL_TYPE_ANY),
ENDMAP
SINTMAP(bthl_app_reg_state_t, -1, "(unknown)")
DELEMENT(BTHL_APP_REG_STATE_REG_SUCCESS),
DELEMENT(BTHL_APP_REG_STATE_REG_FAILED),
DELEMENT(BTHL_APP_REG_STATE_DEREG_SUCCESS),
DELEMENT(BTHL_APP_REG_STATE_DEREG_FAILED),
ENDMAP
SINTMAP(bthl_channel_state_t, -1, "(unknown)")
DELEMENT(BTHL_CONN_STATE_CONNECTING),
DELEMENT(BTHL_CONN_STATE_CONNECTED),
DELEMENT(BTHL_CONN_STATE_DISCONNECTING),
DELEMENT(BTHL_CONN_STATE_DISCONNECTED),
DELEMENT(BTHL_CONN_STATE_DESTROYED),
ENDMAP
#define APP_ID_SIZE 20
#define MDEP_CFG_SIZE 10
#define CHANNEL_ID_SIZE 50
struct channel_info {
int fd;
};
struct mdep_cfg {
uint8_t role;
struct channel_info channel[CHANNEL_ID_SIZE];
};
struct {
struct mdep_cfg mdep[MDEP_CFG_SIZE];
} app[APP_ID_SIZE];
const bthl_interface_t *if_hl = NULL;
static void app_reg_state_cb(int app_id, bthl_app_reg_state_t state)
{
haltest_info("%s: app_id=%d app_reg_state=%s\n", __func__,
app_id, bthl_app_reg_state_t2str(state));
}
static void channel_state_cb(int app_id, bt_bdaddr_t *bd_addr,
int index, int channel_id,
bthl_channel_state_t state, int fd)
{
char addr[MAX_ADDR_STR_LEN];
haltest_info("%s: app_id=%d bd_addr=%s mdep_cfg_index=%d\n"
"channel_id=%d channel_state=%s fd=%d\n", __func__,
app_id, bt_bdaddr_t2str(bd_addr, addr), index,
channel_id, bthl_channel_state_t2str(state), fd);
if (app_id >= APP_ID_SIZE || index >= MDEP_CFG_SIZE
|| channel_id >= CHANNEL_ID_SIZE) {
haltest_error("exceeds maximum limit");
return;
}
if (state == BTHL_CONN_STATE_CONNECTED) {
app[app_id].mdep[index].channel[channel_id].fd = fd;
/*
* PTS expects dummy data on fd when it
* connects in source role.
*/
if (app[app_id].mdep[index].role == BTHL_MDEP_ROLE_SOURCE)
if (write(fd, "0", sizeof("0")) < 0)
haltest_error("writing data on fd failed\n");
return;
}
if (state == BTHL_CONN_STATE_DISCONNECTED ||
state == BTHL_CONN_STATE_DESTROYED) {
if (app[app_id].mdep[index].channel[channel_id].fd >= 0) {
close(app[app_id].mdep[index].channel[channel_id].fd);
app[app_id].mdep[index].channel[channel_id].fd = -1;
}
}
}
static bthl_callbacks_t hl_cbacks = {
.size = sizeof(hl_cbacks),
.app_reg_state_cb = app_reg_state_cb,
.channel_state_cb = channel_state_cb,
};
/* init */
static void init_p(int argc, const char **argv)
{
int i, j, k;
for (i = 0; i < APP_ID_SIZE; i++) {
for (j = 0; j < MDEP_CFG_SIZE; j++) {
app[i].mdep[j].role = 0;
for (k = 0; k < CHANNEL_ID_SIZE; k++)
app[i].mdep[j].channel[k].fd = -1;
}
}
RETURN_IF_NULL(if_hl);
EXEC(if_hl->init, &hl_cbacks);
}
/* register_application */
static void register_application_p(int argc, const char **argv)
{
bthl_reg_param_t reg;
uint16_t mdep_argc_init, mdep_argc_off;
int app_id = -1;
int i;
RETURN_IF_NULL(if_hl);
if (argc <= 2) {
haltest_error("No app name is specified\n");
return;
}
if (argc <= 3) {
haltest_error("No provider is specified\n");
return;
}
if (argc <= 4) {
haltest_error("No service name is specified\n");
return;
}
if (argc <= 5) {
haltest_error("No service description is specified\n");
return;
}
if (argc <= 6) {
haltest_error("No num of mdeps is specified\n");
return;
}
memset(&reg, 0, sizeof(reg));
if (argc != ((atoi(argv[6]) * 4) + 7)) {
haltest_error("mdep cfg argumetns are not proper\n");
return;
}
reg.application_name = argv[2];
if (strcmp("-", argv[3]))
reg.provider_name = argv[3];
if (strcmp("-", argv[4]))
reg.srv_name = argv[4];
if (strcmp("-", argv[5]))
reg.srv_desp = argv[5];
reg.number_of_mdeps = atoi(argv[6]);
reg.mdep_cfg = malloc(reg.number_of_mdeps * sizeof(bthl_mdep_cfg_t));
if (!reg.mdep_cfg) {
haltest_error("malloc failed\n");
return;
}
mdep_argc_init = 7;
for (i = 0; i < reg.number_of_mdeps; i++) {
mdep_argc_off = mdep_argc_init + (4 * i);
reg.mdep_cfg[i].mdep_role =
str2bthl_mdep_role_t(argv[mdep_argc_off]);
reg.mdep_cfg[i].data_type = atoi(argv[mdep_argc_off + 1]);
reg.mdep_cfg[i].channel_type =
str2bthl_channel_type_t(argv[mdep_argc_off + 2]);
if (!strcmp("-", argv[mdep_argc_off + 3])) {
reg.mdep_cfg[i].mdep_description = NULL;
continue;
}
reg.mdep_cfg[i].mdep_description = argv[mdep_argc_off + 3];
}
EXEC(if_hl->register_application, &reg, &app_id);
for (i = 0; i < reg.number_of_mdeps; i++)
app[app_id].mdep[i].role = reg.mdep_cfg[i].mdep_role;
free(reg.mdep_cfg);
}
/* unregister_application */
static void unregister_application_p(int argc, const char **argv)
{
uint32_t app_id;
RETURN_IF_NULL(if_hl);
if (argc <= 2) {
haltest_error("No app id is specified");
return;
}
app_id = (uint32_t) atoi(argv[2]);
EXEC(if_hl->unregister_application, app_id);
}
/* connect_channel */
static void connect_channel_p(int argc, const char **argv)
{
uint32_t app_id, mdep_cfg_index;
int channel_id = -1;
bt_bdaddr_t bd_addr;
RETURN_IF_NULL(if_hl);
if (argc <= 2) {
haltest_error("No app id is specified");
return;
}
VERIFY_ADDR_ARG(3, &bd_addr);
if (argc <= 4) {
haltest_error("No mdep cfg index is specified");
return;
}
app_id = (uint32_t) atoi(argv[2]);
mdep_cfg_index = (uint32_t) atoi(argv[4]);
EXEC(if_hl->connect_channel, app_id, &bd_addr, mdep_cfg_index,
&channel_id);
}
/* destroy_channel */
static void destroy_channel_p(int argc, const char **argv)
{
uint32_t channel_id;
RETURN_IF_NULL(if_hl);
if (argc <= 2) {
haltest_error("No channel id is specified");
return;
}
channel_id = (uint32_t) atoi(argv[2]);
EXEC(if_hl->destroy_channel, channel_id);
}
/* close_channel */
static void close_channel_p(int argc, const char **argv)
{
uint32_t app_id;
uint8_t index;
int channel_id;
RETURN_IF_NULL(if_hl);
if (argc <= 2) {
haltest_error("No app id is specified");
return;
}
if (argc <= 3) {
haltest_error("No mdep_cfg_index is specified");
return;
}
if (argc <= 4) {
haltest_error("No channel_id is specified");
return;
}
app_id = (uint32_t) atoi(argv[2]);
if (app_id >= APP_ID_SIZE) {
haltest_error("Wrong app_id specified: %u\n", app_id);
return;
}
index = (uint8_t) atoi(argv[3]);
if (index >= MDEP_CFG_SIZE) {
haltest_error("Wrong mdep cfg index: %u\n", index);
return;
}
channel_id = atoi(argv[4]);
if (channel_id >= CHANNEL_ID_SIZE) {
haltest_error("Wrong channel id: %u\n", channel_id);
return;
}
if (app[app_id].mdep[index].channel[channel_id].fd >= 0) {
shutdown(app[app_id].mdep[index].channel[channel_id].fd,
SHUT_RDWR);
app[app_id].mdep[index].channel[channel_id].fd = -1;
}
}
/* cleanup */
static void cleanup_p(int argc, const char **argv)
{
RETURN_IF_NULL(if_hl);
EXECV(if_hl->cleanup);
if_hl = NULL;
}
static struct method methods[] = {
STD_METHOD(init),
STD_METHODH(register_application,
"<app_name> <provider_name> <srv_name> <srv_descr>\n"
"<num_of_mdeps>\n"
"[[<mdep_role>] [<data_type>] [<channel_type>] [<mdep_descr>]]"
"..."),
STD_METHODH(unregister_application, "<app_id>"),
STD_METHODH(connect_channel, "<app_id> <bd_addr> <mdep_cfg_index>"),
STD_METHODH(destroy_channel, "<channel_id>"),
STD_METHODH(close_channel, "<app_id> <mdep_cfg_index> <channel_id>"),
STD_METHOD(cleanup),
END_METHOD
};
const struct interface hl_if = {
.name = "hl",
.methods = methods
};
@@ -0,0 +1,187 @@
/* SPDX-License-Identifier: Apache-2.0 */
/*
* Copyright (C) 2013 Intel Corporation
*
*/
#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)
@@ -0,0 +1,77 @@
// SPDX-License-Identifier: Apache-2.0
/*
* Copyright (C) 2014 Intel Corporation
*
*/
#define _GNU_SOURCE
#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
};
@@ -0,0 +1,203 @@
// SPDX-License-Identifier: Apache-2.0
/*
* Copyright (C) 2013 Intel Corporation
*
*/
#define _GNU_SOURCE
#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
};
@@ -0,0 +1,104 @@
// SPDX-License-Identifier: Apache-2.0
/*
* Copyright (C) 2014 Intel Corporation
*
*/
#define _GNU_SOURCE
#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
};
@@ -0,0 +1,390 @@
// SPDX-License-Identifier: Apache-2.0
/*
* Copyright (C) 2014 Intel Corporation
*
*/
#define _GNU_SOURCE
#include <stdio.h>
#include <ctype.h>
#include <string.h>
#include <hardware/bluetooth.h>
#include <hardware/bt_hh.h>
#include "if-main.h"
#include "pollhandler.h"
#include "../hal-utils.h"
const btrc_interface_t *if_rc = NULL;
SINTMAP(btrc_play_status_t, -1, "(unknown)")
DELEMENT(BTRC_PLAYSTATE_STOPPED),
DELEMENT(BTRC_PLAYSTATE_PLAYING),
DELEMENT(BTRC_PLAYSTATE_PAUSED),
DELEMENT(BTRC_PLAYSTATE_FWD_SEEK),
DELEMENT(BTRC_PLAYSTATE_REV_SEEK),
DELEMENT(BTRC_PLAYSTATE_ERROR),
ENDMAP
SINTMAP(btrc_media_attr_t, -1, "(unknown)")
DELEMENT(BTRC_MEDIA_ATTR_TITLE),
DELEMENT(BTRC_MEDIA_ATTR_ARTIST),
DELEMENT(BTRC_MEDIA_ATTR_ALBUM),
DELEMENT(BTRC_MEDIA_ATTR_TRACK_NUM),
DELEMENT(BTRC_MEDIA_ATTR_NUM_TRACKS),
DELEMENT(BTRC_MEDIA_ATTR_GENRE),
DELEMENT(BTRC_MEDIA_ATTR_PLAYING_TIME),
ENDMAP
SINTMAP(btrc_status_t, -1, "(unknown)")
DELEMENT(BTRC_STS_BAD_CMD),
DELEMENT(BTRC_STS_BAD_PARAM),
DELEMENT(BTRC_STS_NOT_FOUND),
DELEMENT(BTRC_STS_INTERNAL_ERR),
DELEMENT(BTRC_STS_NO_ERROR),
ENDMAP
SINTMAP(btrc_event_id_t, -1, "(unknown)")
DELEMENT(BTRC_EVT_PLAY_STATUS_CHANGED),
DELEMENT(BTRC_EVT_TRACK_CHANGE),
DELEMENT(BTRC_EVT_TRACK_REACHED_END),
DELEMENT(BTRC_EVT_TRACK_REACHED_START),
DELEMENT(BTRC_EVT_PLAY_POS_CHANGED),
DELEMENT(BTRC_EVT_APP_SETTINGS_CHANGED),
ENDMAP
SINTMAP(btrc_notification_type_t, -1, "(unknown)")
DELEMENT(BTRC_NOTIFICATION_TYPE_INTERIM),
DELEMENT(BTRC_NOTIFICATION_TYPE_CHANGED),
ENDMAP
static char last_addr[MAX_ADDR_STR_LEN];
static void remote_features_cb(bt_bdaddr_t *bd_addr,
btrc_remote_features_t features)
{
haltest_info("%s: remote_bd_addr=%s features=%u\n", __func__,
bt_bdaddr_t2str(bd_addr, last_addr), features);
}
static void get_play_status_cb(void)
{
haltest_info("%s\n", __func__);
}
static void list_player_app_attr_cb(void)
{
haltest_info("%s\n", __func__);
}
static void list_player_app_values_cb(btrc_player_attr_t attr_id)
{
haltest_info("%s, attr_id=%d\n", __func__, attr_id);
}
static void get_player_app_value_cb(uint8_t num_attr,
btrc_player_attr_t *p_attrs)
{
int i;
haltest_info("%s, num_attr=%d\n", __func__, num_attr);
for (i = 0; i < num_attr; i++)
haltest_info("attribute=%u\n", p_attrs[i]);
}
static void get_player_app_attrs_text_cb(uint8_t num_attr,
btrc_player_attr_t *p_attrs)
{
int i;
haltest_info("%s, num_attr=%d\n", __func__, num_attr);
for (i = 0; i < num_attr; i++)
haltest_info("attribute=%u\n", p_attrs[i]);
}
static void get_player_app_values_text_cb(uint8_t attr_id, uint8_t num_val,
uint8_t *p_vals)
{
haltest_info("%s, attr_id=%d num_val=%d values=%p\n", __func__,
attr_id, num_val, p_vals);
}
static void set_player_app_value_cb(btrc_player_settings_t *p_vals)
{
int i;
haltest_info("%s, num_attr=%u\n", __func__, p_vals->num_attr);
for (i = 0; i < p_vals->num_attr; i++)
haltest_info("attr id=%u, values=%u\n", p_vals->attr_ids[i],
p_vals->attr_values[i]);
}
static void get_element_attr_cb(uint8_t num_attr, btrc_media_attr_t *attrs)
{
uint8_t i;
haltest_info("%s, num_of_attributes=%d\n", __func__, num_attr);
for (i = 0; i < num_attr; i++)
haltest_info("attr id=%s\n", btrc_media_attr_t2str(attrs[i]));
}
static void register_notification_cb(btrc_event_id_t event_id, uint32_t param)
{
haltest_info("%s, event=%u param=%u\n", __func__, event_id, param);
}
static void volume_change_cb(uint8_t volume, uint8_t ctype)
{
haltest_info("%s, volume=%d ctype=%d\n", __func__, volume, ctype);
}
static void passthrough_cmd_cb(int id, int key_state)
{
haltest_info("%s, id=%d key_state=%d\n", __func__, id, key_state);
}
static btrc_callbacks_t rc_cbacks = {
.size = sizeof(rc_cbacks),
.remote_features_cb = remote_features_cb,
.get_play_status_cb = get_play_status_cb,
.list_player_app_attr_cb = list_player_app_attr_cb,
.list_player_app_values_cb = list_player_app_values_cb,
.get_player_app_value_cb = get_player_app_value_cb,
.get_player_app_attrs_text_cb = get_player_app_attrs_text_cb,
.get_player_app_values_text_cb = get_player_app_values_text_cb,
.set_player_app_value_cb = set_player_app_value_cb,
.get_element_attr_cb = get_element_attr_cb,
.register_notification_cb = register_notification_cb,
.volume_change_cb = volume_change_cb,
.passthrough_cmd_cb = passthrough_cmd_cb,
};
/* init */
static void init_p(int argc, const char **argv)
{
RETURN_IF_NULL(if_rc);
EXEC(if_rc->init, &rc_cbacks);
}
/* get_play_status_rsp */
static void get_play_status_rsp_c(int argc, const char **argv,
enum_func *enum_func, void **user)
{
if (argc == 3) {
*user = TYPE_ENUM(btrc_play_status_t);
*enum_func = enum_defines;
}
}
static void get_play_status_rsp_p(int argc, const char **argv)
{
btrc_play_status_t play_status;
uint32_t song_len, song_pos;
RETURN_IF_NULL(if_rc);
if (argc <= 2) {
haltest_error("No play status specified");
return;
}
if (argc <= 3) {
haltest_error("No song length specified");
return;
}
if (argc <= 4) {
haltest_error("No song position specified");
return;
}
play_status = str2btrc_play_status_t(argv[2]);
song_len = (uint32_t) atoi(argv[3]);
song_pos = (uint32_t) atoi(argv[4]);
EXEC(if_rc->get_play_status_rsp, play_status, song_len, song_pos);
}
/* get_element_attr_rsp */
static void get_element_attr_rsp_c(int argc, const char **argv,
enum_func *enum_func, void **user)
{
if (argc == 4) {
*user = TYPE_ENUM(btrc_media_attr_t);
*enum_func = enum_defines;
}
}
static void get_element_attr_rsp_p(int argc, const char **argv)
{
uint8_t num_attr;
btrc_element_attr_val_t attrs;
RETURN_IF_NULL(if_rc);
if (argc <= 2) {
haltest_error("No number of attributes specified");
return;
}
if (argc <= 4) {
haltest_error("No attr id and value specified");
return;
}
num_attr = (uint8_t) atoi(argv[2]);
attrs.attr_id = str2btrc_media_attr_t(argv[3]);
strcpy((char *)attrs.text, argv[4]);
EXEC(if_rc->get_element_attr_rsp, num_attr, &attrs);
}
/* set_volume */
static void set_volume_c(int argc, const char **argv,
enum_func *enum_func, void **user)
{
}
static void set_volume_p(int argc, const char **argv)
{
uint8_t volume;
RETURN_IF_NULL(if_rc);
if (argc <= 2) {
haltest_error("No volume specified");
return;
}
volume = (uint8_t) atoi(argv[2]);
EXEC(if_rc->set_volume, volume);
}
/* set_player_app_value_rsp */
static void set_player_app_value_rsp_c(int argc, const char **argv,
enum_func *enum_func, void **user)
{
if (argc == 3) {
*user = TYPE_ENUM(btrc_status_t);
*enum_func = enum_defines;
}
}
static void set_player_app_value_rsp_p(int argc, const char **argv)
{
btrc_status_t rsp_status;
RETURN_IF_NULL(if_rc);
if (argc <= 2) {
haltest_error("No response status specified");
return;
}
rsp_status = str2btrc_status_t(argv[2]);
EXEC(if_rc->set_player_app_value_rsp, rsp_status);
}
/* register_notification_rsp */
static void register_notification_rsp_c(int argc, const char **argv,
enum_func *enum_func, void **user)
{
if (argc == 3) {
*user = TYPE_ENUM(btrc_event_id_t);
*enum_func = enum_defines;
}
if (argc == 4) {
*user = TYPE_ENUM(btrc_notification_type_t);
*enum_func = enum_defines;
}
}
static void register_notification_rsp_p(int argc, const char **argv)
{
btrc_event_id_t event_id;
btrc_notification_type_t type;
btrc_register_notification_t reg;
uint32_t song_pos;
uint64_t track;
RETURN_IF_NULL(if_rc);
memset(&reg, 0, sizeof(reg));
event_id = str2btrc_event_id_t(argv[2]);
type = str2btrc_notification_type_t(argv[3]);
switch (event_id) {
case BTRC_EVT_PLAY_STATUS_CHANGED:
reg.play_status = str2btrc_play_status_t(argv[4]);
break;
case BTRC_EVT_TRACK_CHANGE:
track = strtoull(argv[5], NULL, 10);
memcpy(reg.track, &track, sizeof(btrc_uid_t));
break;
case BTRC_EVT_TRACK_REACHED_END:
case BTRC_EVT_TRACK_REACHED_START:
break;
case BTRC_EVT_PLAY_POS_CHANGED:
song_pos = strtoul(argv[4], NULL, 10);
memcpy(&reg.song_pos, &song_pos, sizeof(uint32_t));
break;
case BTRC_EVT_APP_SETTINGS_CHANGED:
haltest_error("not supported");
return;
}
EXEC(if_rc->register_notification_rsp, event_id, type, &reg);
}
/* cleanup */
static void cleanup_p(int argc, const char **argv)
{
RETURN_IF_NULL(if_rc);
EXECV(if_rc->cleanup);
if_rc = NULL;
}
static struct method methods[] = {
STD_METHOD(init),
STD_METHODCH(get_play_status_rsp,
"<play_status> <song_len> <song_pos>"),
STD_METHODCH(get_element_attr_rsp, "<num_attr> <attrs_id> <value>"),
STD_METHODCH(set_player_app_value_rsp, "<rsp_status>"),
STD_METHODCH(set_volume, "<volume>"),
STD_METHODCH(register_notification_rsp,
"<event_id> <type> <respective_data...>\n"
"BTRC_EVT_PLAY_STATUS_CHANGED <type> <play_status>\n"
"BTRC_EVT_TRACK_CHANGE <type> <track>\n"
"BTRC_EVT_TRACK_REACHED_END <type>\n"
"BTRC_EVT_TRACK_REACHED_START <type>\n"
"BTRC_EVT_PLAY_POS_CHANGED <type> <song_pos>\n"),
STD_METHOD(cleanup),
END_METHOD
};
const struct interface rc_if = {
.name = "rc",
.methods = methods
};
@@ -0,0 +1,805 @@
// SPDX-License-Identifier: Apache-2.0
/*
* Copyright (C) 2014 Intel Corporation
*
*/
#define _GNU_SOURCE
#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
};
@@ -0,0 +1,340 @@
// SPDX-License-Identifier: Apache-2.0
/*
* Copyright (C) 2013 Intel Corporation
*
*/
#define _GNU_SOURCE
#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
};
@@ -0,0 +1,106 @@
// SPDX-License-Identifier: Apache-2.0
/*
* Copyright (C) 2013 Intel Corporation
*
*/
#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;
}
@@ -0,0 +1,15 @@
/* SPDX-License-Identifier: Apache-2.0 */
/*
* Copyright (C) 2013 Intel Corporation
*
*/
#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);
@@ -0,0 +1,364 @@
// SPDX-License-Identifier: Apache-2.0
/*
* Copyright (C) 2013 Intel Corporation
*
*/
#define _GNU_SOURCE
#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);
}
}
@@ -0,0 +1,813 @@
// SPDX-License-Identifier: Apache-2.0
/*
* Copyright (C) 2013 Intel Corporation
*
*/
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <stdbool.h>
#include <termios.h>
#include <stdlib.h>
#include "terminal.h"
#include "history.h"
/*
* Character sequences recognized by code in this file
* Leading ESC 0x1B is not included
*/
#define SEQ_INSERT "[2~"
#define SEQ_DELETE "[3~"
#define SEQ_HOME "OH"
#define SEQ_END "OF"
#define SEQ_PGUP "[5~"
#define SEQ_PGDOWN "[6~"
#define SEQ_LEFT "[D"
#define SEQ_RIGHT "[C"
#define SEQ_UP "[A"
#define SEQ_DOWN "[B"
#define SEQ_STAB "[Z"
#define SEQ_M_n "n"
#define SEQ_M_p "p"
#define SEQ_CLEFT "[1;5D"
#define SEQ_CRIGHT "[1;5C"
#define SEQ_CUP "[1;5A"
#define SEQ_CDOWN "[1;5B"
#define SEQ_SLEFT "[1;2D"
#define SEQ_SRIGHT "[1;2C"
#define SEQ_SUP "[1;2A"
#define SEQ_SDOWN "[1;2B"
#define SEQ_MLEFT "[1;3D"
#define SEQ_MRIGHT "[1;3C"
#define SEQ_MUP "[1;3A"
#define SEQ_MDOWN "[1;3B"
#define KEY_SEQUENCE(k) { KEY_##k, SEQ_##k }
struct ansii_sequence {
int code;
const char *sequence;
};
/* Table connects single int key codes with character sequences */
static const struct ansii_sequence ansii_sequnces[] = {
KEY_SEQUENCE(INSERT),
KEY_SEQUENCE(DELETE),
KEY_SEQUENCE(HOME),
KEY_SEQUENCE(END),
KEY_SEQUENCE(PGUP),
KEY_SEQUENCE(PGDOWN),
KEY_SEQUENCE(LEFT),
KEY_SEQUENCE(RIGHT),
KEY_SEQUENCE(UP),
KEY_SEQUENCE(DOWN),
KEY_SEQUENCE(CLEFT),
KEY_SEQUENCE(CRIGHT),
KEY_SEQUENCE(CUP),
KEY_SEQUENCE(CDOWN),
KEY_SEQUENCE(SLEFT),
KEY_SEQUENCE(SRIGHT),
KEY_SEQUENCE(SUP),
KEY_SEQUENCE(SDOWN),
KEY_SEQUENCE(MLEFT),
KEY_SEQUENCE(MRIGHT),
KEY_SEQUENCE(MUP),
KEY_SEQUENCE(MDOWN),
KEY_SEQUENCE(STAB),
KEY_SEQUENCE(M_p),
KEY_SEQUENCE(M_n),
{ 0, NULL }
};
#define KEY_SEQUNCE_NOT_FINISHED -1
#define KEY_C_C 3
#define KEY_C_D 4
#define KEY_C_L 12
#define isseqence(c) ((c) == 0x1B)
/*
* Number of characters that consist of ANSI sequence
* Should not be less then longest string in ansi_sequences
*/
#define MAX_ASCII_SEQUENCE 10
static char current_sequence[MAX_ASCII_SEQUENCE];
static int current_sequence_len = -1;
/* single line typed by user goes here */
static char line_buf[LINE_BUF_MAX];
/* index of cursor in input line */
static int line_buf_ix = 0;
/* current length of input line */
static int line_len = 0;
/* line index used for fetching lines from history */
static int line_index = 0;
static char prompt_buf[10] = "> ";
static const char *const noprompt = "";
static const char *current_prompt = prompt_buf;
static const char *prompt = prompt_buf;
/*
* Moves cursor to right or left
*
* n - positive - moves cursor right
* n - negative - moves cursor left
*/
static void terminal_move_cursor(int n)
{
if (n < 0) {
for (; n < 0; n++)
putchar('\b');
} else if (n > 0) {
printf("%*s", n, line_buf + line_buf_ix);
}
}
/* Draw command line */
void terminal_draw_command_line(void)
{
/*
* this needs to be checked here since line_buf is not cleared
* before parsing event though line_len and line_buf_ix are
*/
if (line_len > 0)
printf("%s%s", prompt, line_buf);
else
printf("%s", prompt);
/* move cursor to it's place */
terminal_move_cursor(line_buf_ix - line_len);
}
/* inserts string into command line at cursor position */
void terminal_insert_into_command_line(const char *p)
{
int len = strlen(p);
if (line_len == line_buf_ix) {
strcat(line_buf, p);
printf("%s", p);
line_len = line_len + len;
line_buf_ix = line_len;
} else {
memmove(line_buf + line_buf_ix + len,
line_buf + line_buf_ix, line_len - line_buf_ix + 1);
memmove(line_buf + line_buf_ix, p, len);
printf("%s", line_buf + line_buf_ix);
line_buf_ix += len;
line_len += len;
terminal_move_cursor(line_buf_ix - line_len);
}
}
/* Prints string and redraws command line */
int terminal_print(const char *format, ...)
{
va_list args;
int ret;
va_start(args, format);
ret = terminal_vprint(format, args);
va_end(args);
return ret;
}
/* Prints string and redraws command line */
int terminal_vprint(const char *format, va_list args)
{
int ret;
printf("\r%*s\r", (int) line_len + 1, " ");
ret = vprintf(format, args);
terminal_draw_command_line();
fflush(stdout);
return ret;
}
/*
* Call this when text in line_buf was changed
* and line needs to be redrawn
*/
static void terminal_line_replaced(void)
{
int len = strlen(line_buf);
/* line is shorter that previous */
if (len < line_len) {
/* if new line is shorter move cursor to end of new end */
while (line_buf_ix > len) {
putchar('\b');
line_buf_ix--;
}
/* If cursor was not at the end, move it to the end */
if (line_buf_ix < line_len)
printf("%.*s", line_len - line_buf_ix,
line_buf + line_buf_ix);
/* over write end of previous line */
while (line_len >= len++)
putchar(' ');
}
/* draw new line */
printf("\r%s%s", prompt, line_buf);
/* set up indexes to new line */
line_len = strlen(line_buf);
line_buf_ix = line_len;
fflush(stdout);
}
static void terminal_clear_line(void)
{
line_buf[0] = '\0';
terminal_line_replaced();
}
static void terminal_clear_screen(void)
{
line_buf[0] = '\0';
line_buf_ix = 0;
line_len = 0;
printf("\x1b[2J\x1b[1;1H%s", prompt);
}
static void terminal_delete_char(void)
{
/* delete character under cursor if not at the very end */
if (line_buf_ix >= line_len)
return;
/*
* Prepare buffer with one character missing
* trailing 0 is moved
*/
line_len--;
memmove(line_buf + line_buf_ix, line_buf + line_buf_ix + 1,
line_len - line_buf_ix + 1);
/* print rest of line from current cursor position */
printf("%s \b", line_buf + line_buf_ix);
/* move back cursor */
terminal_move_cursor(line_buf_ix - line_len);
}
/*
* Function tries to replace current line with specified line in history
* new_line_index - new line to show, -1 to show oldest
*/
static void terminal_get_line_from_history(int new_line_index)
{
new_line_index = history_get_line(new_line_index,
line_buf, LINE_BUF_MAX);
if (new_line_index >= 0) {
terminal_line_replaced();
line_index = new_line_index;
}
}
/*
* Function searches history back or forward for command line that starts
* with characters up to cursor position
*
* back - true - searches backward
* back - false - searches forward (more recent commands)
*/
static void terminal_match_hitory(bool back)
{
char buf[line_buf_ix + 1];
int line;
int matching_line = -1;
int dir = back ? 1 : -1;
line = line_index + dir;
while (matching_line == -1 && line >= 0) {
int new_line_index;
new_line_index = history_get_line(line, buf, line_buf_ix + 1);
if (new_line_index < 0)
break;
if (0 == strncmp(line_buf, buf, line_buf_ix))
matching_line = line;
line += dir;
}
if (matching_line >= 0) {
int pos = line_buf_ix;
terminal_get_line_from_history(matching_line);
/* move back to cursor position to original place */
line_buf_ix = pos;
terminal_move_cursor(pos - line_len);
}
}
/*
* Converts terminal character sequences to single value representing
* keyboard keys
*/
static int terminal_convert_sequence(int c)
{
int i;
/* Not in sequence yet? */
if (current_sequence_len == -1) {
/* Is ansi sequence detected by 0x1B ? */
if (isseqence(c)) {
current_sequence_len++;
return KEY_SEQUNCE_NOT_FINISHED;
}
return c;
}
/* Inside sequence */
current_sequence[current_sequence_len++] = c;
current_sequence[current_sequence_len] = '\0';
for (i = 0; ansii_sequnces[i].code; ++i) {
/* Matches so far? */
if (0 != strncmp(current_sequence, ansii_sequnces[i].sequence,
current_sequence_len))
continue;
/* Matches as a whole? */
if (ansii_sequnces[i].sequence[current_sequence_len] == 0) {
current_sequence_len = -1;
return ansii_sequnces[i].code;
}
/* partial match (not whole sequence yet) */
return KEY_SEQUNCE_NOT_FINISHED;
}
terminal_print("ansi char 0x%X %c\n", c);
/*
* Sequence does not match
* mark that no in sequence any more, return char
*/
current_sequence_len = -1;
return c;
}
typedef void (*terminal_action)(int c, line_callback process_line);
#define TERMINAL_ACTION(n) \
static void n(int c, void (*process_line)(char *line))
TERMINAL_ACTION(terminal_action_null)
{
}
/* Mapping between keys and function */
typedef struct {
int key;
terminal_action func;
} KeyAction;
int action_keys[] = {
KEY_SEQUNCE_NOT_FINISHED,
KEY_LEFT,
KEY_RIGHT,
KEY_HOME,
KEY_END,
KEY_DELETE,
KEY_CLEFT,
KEY_CRIGHT,
KEY_SUP,
KEY_SDOWN,
KEY_UP,
KEY_DOWN,
KEY_BACKSPACE,
KEY_INSERT,
KEY_PGUP,
KEY_PGDOWN,
KEY_CUP,
KEY_CDOWN,
KEY_SLEFT,
KEY_SRIGHT,
KEY_MLEFT,
KEY_MRIGHT,
KEY_MUP,
KEY_MDOWN,
KEY_STAB,
KEY_M_n,
KEY_M_p,
KEY_C_C,
KEY_C_D,
KEY_C_L,
'\t',
'\r',
'\n',
};
#define NELEM(x) ((int) (sizeof(x) / sizeof((x)[0])))
/*
* current_actions holds all recognizable kes and actions for them
* additional element (index 0) is used for default action
*/
static KeyAction current_actions[NELEM(action_keys) + 1];
/* KeyAction comparator by key, for qsort and bsearch */
static int KeyActionKeyCompare(const void *a, const void *b)
{
return ((const KeyAction *) a)->key - ((const KeyAction *) b)->key;
}
/* Find action by key, NULL if no action for this key */
static KeyAction *terminal_get_action(int key)
{
KeyAction a = { .key = key };
return bsearch(&a, current_actions + 1, NELEM(action_keys), sizeof(a),
KeyActionKeyCompare);
}
/* Sets new set of actions to use */
static void terminal_set_actions(const KeyAction *actions)
{
int i;
/* Make map with empty function for every key */
for (i = 0; i < NELEM(action_keys); ++i) {
/*
* + 1 due to 0 index reserved for default action that is
* called for non mapped key
*/
current_actions[i + 1].key = action_keys[i];
current_actions[i + 1].func = terminal_action_null;
}
/* Sort action from 1 (index 0 - default action) */
qsort(current_actions + 1, NELEM(action_keys), sizeof(KeyAction),
KeyActionKeyCompare);
/* Set default action (first in array) */
current_actions[0] = *actions++;
/* Copy rest of actions into their places */
for (; actions->key; ++actions) {
KeyAction *place = terminal_get_action(actions->key);
if (place)
place->func = actions->func;
}
}
TERMINAL_ACTION(terminal_action_left)
{
/* if not at the beginning move to previous character */
if (line_buf_ix <= 0)
return;
line_buf_ix--;
terminal_move_cursor(-1);
}
TERMINAL_ACTION(terminal_action_right)
{
/*
* If not at the end, just print current character
* and modify position
*/
if (line_buf_ix < line_len)
putchar(line_buf[line_buf_ix++]);
}
TERMINAL_ACTION(terminal_action_home)
{
/* move to beginning of line and update position */
printf("\r%s", prompt);
line_buf_ix = 0;
}
TERMINAL_ACTION(terminal_action_end)
{
/* if not at the end of line */
if (line_buf_ix < line_len) {
/* print everything from cursor */
printf("%s", line_buf + line_buf_ix);
/* just modify current position */
line_buf_ix = line_len;
}
}
TERMINAL_ACTION(terminal_action_del)
{
terminal_delete_char();
}
TERMINAL_ACTION(terminal_action_word_left)
{
int old_pos;
/*
* Move by word left
*
* Are we at the beginning of line?
*/
if (line_buf_ix <= 0)
return;
old_pos = line_buf_ix;
line_buf_ix--;
/* skip spaces left */
while (line_buf_ix && isspace(line_buf[line_buf_ix]))
line_buf_ix--;
/* skip all non spaces to the left */
while (line_buf_ix > 0 &&
!isspace(line_buf[line_buf_ix - 1]))
line_buf_ix--;
/* move cursor to new position */
terminal_move_cursor(line_buf_ix - old_pos);
}
TERMINAL_ACTION(terminal_action_word_right)
{
int old_pos;
/*
* Move by word right
*
* are we at the end of line?
*/
if (line_buf_ix >= line_len)
return;
old_pos = line_buf_ix;
/* skip all spaces */
while (line_buf_ix < line_len && isspace(line_buf[line_buf_ix]))
line_buf_ix++;
/* skip all non spaces */
while (line_buf_ix < line_len && !isspace(line_buf[line_buf_ix]))
line_buf_ix++;
/*
* Move cursor to right by printing text
* between old cursor and new
*/
if (line_buf_ix > old_pos)
printf("%.*s", (int) (line_buf_ix - old_pos),
line_buf + old_pos);
}
TERMINAL_ACTION(terminal_action_history_begin)
{
terminal_get_line_from_history(-1);
}
TERMINAL_ACTION(terminal_action_history_end)
{
if (line_index > 0)
terminal_get_line_from_history(0);
}
TERMINAL_ACTION(terminal_action_history_up)
{
terminal_get_line_from_history(line_index + 1);
}
TERMINAL_ACTION(terminal_action_history_down)
{
if (line_index > 0)
terminal_get_line_from_history(line_index - 1);
}
TERMINAL_ACTION(terminal_action_tab)
{
/* tab processing */
process_tab(line_buf, line_buf_ix);
}
TERMINAL_ACTION(terminal_action_backspace)
{
if (line_buf_ix <= 0)
return;
if (line_buf_ix == line_len) {
printf("\b \b");
line_len = --line_buf_ix;
line_buf[line_len] = 0;
} else {
putchar('\b');
line_buf_ix--;
line_len--;
memmove(line_buf + line_buf_ix,
line_buf + line_buf_ix + 1,
line_len - line_buf_ix + 1);
printf("%s \b", line_buf + line_buf_ix);
terminal_move_cursor(line_buf_ix - line_len);
}
}
TERMINAL_ACTION(terminal_action_find_history_forward)
{
/* Search history forward */
terminal_match_hitory(false);
}
TERMINAL_ACTION(terminal_action_find_history_backward)
{
/* Search history forward */
terminal_match_hitory(true);
}
TERMINAL_ACTION(terminal_action_ctrl_c)
{
terminal_clear_line();
}
TERMINAL_ACTION(terminal_action_ctrl_d)
{
if (line_len > 0) {
terminal_delete_char();
} else {
puts("");
exit(0);
}
}
TERMINAL_ACTION(terminal_action_clear_screen)
{
terminal_clear_screen();
}
TERMINAL_ACTION(terminal_action_enter)
{
/*
* On new line add line to history
* forget history position
*/
history_add_line(line_buf);
line_len = 0;
line_buf_ix = 0;
line_index = -1;
/* print new line */
putchar(c);
prompt = noprompt;
process_line(line_buf);
/* clear current line */
line_buf[0] = '\0';
prompt = current_prompt;
printf("%s", prompt);
}
TERMINAL_ACTION(terminal_action_default)
{
char str[2] = { c, 0 };
if (!isprint(c))
/*
* TODO: remove this print once all meaningful sequences
* are identified
*/
printf("char-0x%02x\n", c);
else if (line_buf_ix < LINE_BUF_MAX - 1)
terminal_insert_into_command_line(str);
}
/* Callback to call when user hit enter during prompt for */
static line_callback prompt_callback;
static KeyAction normal_actions[] = {
{ 0, terminal_action_default },
{ KEY_LEFT, terminal_action_left },
{ KEY_RIGHT, terminal_action_right },
{ KEY_HOME, terminal_action_home },
{ KEY_END, terminal_action_end },
{ KEY_DELETE, terminal_action_del },
{ KEY_CLEFT, terminal_action_word_left },
{ KEY_CRIGHT, terminal_action_word_right },
{ KEY_SUP, terminal_action_history_begin },
{ KEY_SDOWN, terminal_action_history_end },
{ KEY_UP, terminal_action_history_up },
{ KEY_DOWN, terminal_action_history_down },
{ '\t', terminal_action_tab },
{ KEY_BACKSPACE, terminal_action_backspace },
{ KEY_M_n, terminal_action_find_history_forward },
{ KEY_M_p, terminal_action_find_history_backward },
{ KEY_C_C, terminal_action_ctrl_c },
{ KEY_C_D, terminal_action_ctrl_d },
{ KEY_C_L, terminal_action_clear_screen },
{ '\r', terminal_action_enter },
{ '\n', terminal_action_enter },
{ 0, NULL },
};
TERMINAL_ACTION(terminal_action_answer)
{
putchar(c);
terminal_set_actions(normal_actions);
/* Restore default prompt */
current_prompt = prompt_buf;
/* No prompt for prints */
prompt = noprompt;
line_buf_ix = 0;
line_len = 0;
/* Call user function with what was typed */
prompt_callback(line_buf);
line_buf[0] = 0;
/* promot_callback could change current_prompt */
prompt = current_prompt;
printf("%s", prompt);
}
TERMINAL_ACTION(terminal_action_prompt_ctrl_c)
{
printf("^C\n");
line_buf_ix = 0;
line_len = 0;
line_buf[0] = 0;
current_prompt = prompt_buf;
prompt = current_prompt;
terminal_set_actions(normal_actions);
printf("%s", prompt);
}
static KeyAction prompt_actions[] = {
{ 0, terminal_action_default },
{ KEY_LEFT, terminal_action_left },
{ KEY_RIGHT, terminal_action_right },
{ KEY_HOME, terminal_action_home },
{ KEY_END, terminal_action_end },
{ KEY_DELETE, terminal_action_del },
{ KEY_CLEFT, terminal_action_word_left },
{ KEY_CRIGHT, terminal_action_word_right },
{ KEY_BACKSPACE, terminal_action_backspace },
{ KEY_C_C, terminal_action_prompt_ctrl_c },
{ KEY_C_D, terminal_action_ctrl_d },
{ '\r', terminal_action_answer },
{ '\n', terminal_action_answer },
{ 0, NULL },
};
void terminal_process_char(int c, line_callback process_line)
{
KeyAction *a;
c = terminal_convert_sequence(c);
/* Get action for this key */
a = terminal_get_action(c);
/* No action found, get default one */
if (a == NULL)
a = &current_actions[0];
a->func(c, process_line);
fflush(stdout);
}
void terminal_prompt_for(const char *s, line_callback process_line)
{
current_prompt = s;
if (prompt != noprompt) {
prompt = s;
terminal_clear_line();
}
prompt_callback = process_line;
terminal_set_actions(prompt_actions);
}
static struct termios origianl_tios;
static void terminal_cleanup(void)
{
tcsetattr(0, TCSANOW, &origianl_tios);
}
void terminal_setup(void)
{
struct termios tios;
terminal_set_actions(normal_actions);
tcgetattr(0, &origianl_tios);
tios = origianl_tios;
/*
* Turn off echo since all editing is done by hand,
* Ctrl-c handled internally
*/
tios.c_lflag &= ~(ICANON | ECHO | BRKINT | IGNBRK);
tcsetattr(0, TCSANOW, &tios);
/* Restore terminal at exit */
atexit(terminal_cleanup);
printf("%s", prompt);
fflush(stdout);
}
@@ -0,0 +1,51 @@
/* SPDX-License-Identifier: Apache-2.0 */
/*
* Copyright (C) 2013 Intel Corporation
*
*/
#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);
@@ -0,0 +1,18 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
*
* BlueZ - Bluetooth protocol stack for Linux
*
* Copyright (C) 1987-2011 Free Software Foundation, Inc.
*
*
*/
#ifndef _HISTORY_H_
#define _HISTORY_H_
static inline void add_history(const char *c)
{
}
#endif
@@ -0,0 +1,97 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
*
* BlueZ - Bluetooth protocol stack for Linux
*
* Copyright (C) 1987-2011 Free Software Foundation, Inc.
*
*
*/
#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
@@ -0,0 +1,31 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
/*
*
* BlueZ - Bluetooth protocol stack for Linux
*
* Copyright (C) 1991-2013 Free Software Foundation, Inc.
*
*
*/
#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
+58
View File
@@ -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
-------------------------------------------------------------------------------
@@ -0,0 +1,82 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
/*
*
* BlueZ - Bluetooth protocol stack for Linux
*
* Copyright (C) 2013 Intel Corporation. All rights reserved.
*
*
*/
#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;
}
File diff suppressed because it is too large Load Diff
+30
View File
@@ -0,0 +1,30 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
/*
*
* BlueZ - Bluetooth protocol stack for Linux
*
* Copyright (C) 2014 Intel Corporation. All rights reserved.
*
*
*/
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);
@@ -0,0 +1,152 @@
// SPDX-License-Identifier: Apache-2.0
/*
* Copyright (C) 2014 Intel Corporation
*
*/
#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;
}
@@ -0,0 +1,154 @@
// SPDX-License-Identifier: Apache-2.0
/*
* Copyright (C) 2013 Intel Corporation
*
*/
#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;
}
@@ -0,0 +1,260 @@
// SPDX-License-Identifier: Apache-2.0
/*
* Copyright (C) 2014 Tieto Poland
*
*/
#define _GNU_SOURCE
#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 =
A2DP_SET_VENDOR_ID_CODEC_ID(APTX_VENDOR_ID, APTX_CODEC_ID),
.frequency = APTX_SAMPLING_FREQ_44100 |
APTX_SAMPLING_FREQ_48000,
.channel_mode = APTX_CHANNEL_MODE_STEREO,
},
{
.info =
A2DP_SET_VENDOR_ID_CODEC_ID(APTX_VENDOR_ID, APTX_CODEC_ID),
.frequency = APTX_SAMPLING_FREQ_48000,
.channel_mode = APTX_CHANNEL_MODE_STEREO,
},
{
.info =
A2DP_SET_VENDOR_ID_CODEC_ID(APTX_VENDOR_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;
}
@@ -0,0 +1,418 @@
// SPDX-License-Identifier: Apache-2.0
/*
* Copyright (C) 2013 Intel Corporation
*
*/
#define _GNU_SOURCE
#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 = SBC_MIN_BITPOOL,
.max_bitpool = SBC_BITPOOL_HQ_JOINT_STEREO_44100,
},
{
.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 = SBC_MIN_BITPOOL,
.max_bitpool = SBC_BITPOOL_HQ_JOINT_STEREO_44100,
},
{
.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 = SBC_MIN_BITPOOL,
.max_bitpool = SBC_BITPOOL_HQ_JOINT_STEREO_48000,
},
};
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;
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,91 @@
/* SPDX-License-Identifier: Apache-2.0 */
/*
* Copyright (C) 2013 Intel Corporation
*
*/
#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);
@@ -0,0 +1,135 @@
// SPDX-License-Identifier: Apache-2.0
/*
* Copyright (C) 2014 Intel Corporation
*
*/
#define _GNU_SOURCE
#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;
}
@@ -0,0 +1,678 @@
// SPDX-License-Identifier: Apache-2.0
/*
* Copyright (C) 2014 Intel Corporation
*
*/
#define _GNU_SOURCE
#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;
}
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,642 @@
// SPDX-License-Identifier: Apache-2.0
/*
* Copyright (C) 2014 Intel Corporation
*
*/
#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;
}
@@ -0,0 +1,882 @@
// SPDX-License-Identifier: Apache-2.0
/*
* Copyright (C) 2014 Intel Corporation
*
*/
#define _GNU_SOURCE
#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;
}
@@ -0,0 +1,286 @@
// SPDX-License-Identifier: Apache-2.0
/*
* Copyright (C) 2014 Intel Corporation
*
*/
#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;
}
@@ -0,0 +1,393 @@
// SPDX-License-Identifier: Apache-2.0
/*
* Copyright (C) 2013 Intel Corporation
*
*/
#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;
}
File diff suppressed because it is too large Load Diff
+460
View File
@@ -0,0 +1,460 @@
// SPDX-License-Identifier: Apache-2.0
/*
* Copyright (C) 2013 Intel Corporation
*
*/
#include <pthread.h>
#include <errno.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <stdbool.h>
#include <poll.h>
#include <unistd.h>
#include <stdint.h>
#include <stdlib.h>
#include <cutils/properties.h>
#include "hal.h"
#include "hal-msg.h"
#include "hal-log.h"
#include "ipc-common.h"
#include "hal-ipc.h"
#define CONNECT_TIMEOUT (10 * 1000)
static int listen_sk = -1;
static int cmd_sk = -1;
static int notif_sk = -1;
static pthread_mutex_t cmd_sk_mutex = PTHREAD_MUTEX_INITIALIZER;
static pthread_t notif_th = 0;
struct service_handler {
const struct hal_ipc_handler *handler;
uint8_t size;
};
static struct service_handler services[HAL_SERVICE_ID_MAX + 1];
void hal_ipc_register(uint8_t service, const struct hal_ipc_handler *handlers,
uint8_t size)
{
services[service].handler = handlers;
services[service].size = size;
}
void hal_ipc_unregister(uint8_t service)
{
services[service].handler = NULL;
services[service].size = 0;
}
static bool handle_msg(void *buf, ssize_t len, int fd)
{
struct ipc_hdr *msg = buf;
const struct hal_ipc_handler *handler;
uint8_t opcode;
if (len < (ssize_t) sizeof(*msg)) {
error("IPC: message too small (%zd bytes)", len);
return false;
}
if (len != (ssize_t) (sizeof(*msg) + msg->len)) {
error("IPC: message malformed (%zd bytes)", len);
return false;
}
/* if service is valid */
if (msg->service_id > HAL_SERVICE_ID_MAX) {
error("IPC: unknown service (0x%x)", msg->service_id);
return false;
}
/* if service is registered */
if (!services[msg->service_id].handler) {
error("IPC: unregistered service (0x%x)", msg->service_id);
return false;
}
/* if opcode fit valid range */
if (msg->opcode < HAL_MINIMUM_EVENT) {
error("IPC: invalid opcode for service 0x%x (0x%x)",
msg->service_id, msg->opcode);
return false;
}
/*
* opcode is used as table offset and must be adjusted as events start
* with HAL_MINIMUM_EVENT offset
*/
opcode = msg->opcode - HAL_MINIMUM_EVENT;
/* if opcode is valid */
if (opcode >= services[msg->service_id].size) {
error("IPC: invalid opcode for service 0x%x (0x%x)",
msg->service_id, msg->opcode);
return false;
}
handler = &services[msg->service_id].handler[opcode];
/* if payload size is valid */
if ((handler->var_len && handler->data_len > msg->len) ||
(!handler->var_len && handler->data_len != msg->len)) {
error("IPC: message size invalid for service 0x%x opcode 0x%x "
"(%u bytes)",
msg->service_id, msg->opcode, msg->len);
return false;
}
handler->handler(msg->payload, msg->len, fd);
return true;
}
static void *notification_handler(void *data)
{
struct msghdr msg;
struct iovec iv;
struct cmsghdr *cmsg;
char cmsgbuf[CMSG_SPACE(sizeof(int))];
char buf[IPC_MTU];
ssize_t ret;
int fd;
bt_thread_associate();
while (true) {
memset(&msg, 0, sizeof(msg));
memset(buf, 0, sizeof(buf));
memset(cmsgbuf, 0, sizeof(cmsgbuf));
iv.iov_base = buf;
iv.iov_len = sizeof(buf);
msg.msg_iov = &iv;
msg.msg_iovlen = 1;
msg.msg_control = cmsgbuf;
msg.msg_controllen = sizeof(cmsgbuf);
ret = recvmsg(notif_sk, &msg, 0);
if (ret < 0) {
error("Receiving notifications failed: %s",
strerror(errno));
goto failed;
}
/* socket was shutdown */
if (ret == 0) {
pthread_mutex_lock(&cmd_sk_mutex);
if (cmd_sk == -1) {
pthread_mutex_unlock(&cmd_sk_mutex);
break;
}
pthread_mutex_unlock(&cmd_sk_mutex);
error("Notification socket closed");
goto failed;
}
fd = -1;
/* Receive auxiliary data in msg */
for (cmsg = CMSG_FIRSTHDR(&msg); cmsg;
cmsg = CMSG_NXTHDR(&msg, cmsg)) {
if (cmsg->cmsg_level == SOL_SOCKET
&& cmsg->cmsg_type == SCM_RIGHTS) {
memcpy(&fd, CMSG_DATA(cmsg), sizeof(int));
break;
}
}
if (!handle_msg(buf, ret, fd))
goto failed;
}
close(notif_sk);
notif_sk = -1;
bt_thread_disassociate();
DBG("exit");
return NULL;
failed:
exit(EXIT_FAILURE);
}
static int accept_connection(int sk)
{
int err;
struct pollfd pfd;
int new_sk;
memset(&pfd, 0 , sizeof(pfd));
pfd.fd = sk;
pfd.events = POLLIN;
err = poll(&pfd, 1, CONNECT_TIMEOUT);
if (err < 0) {
err = errno;
error("Failed to poll: %d (%s)", err, strerror(err));
return -1;
}
if (err == 0) {
error("bluetoothd connect timeout");
return -1;
}
new_sk = accept(sk, NULL, NULL);
if (new_sk < 0) {
err = errno;
error("Failed to accept socket: %d (%s)", err, strerror(err));
return -1;
}
return new_sk;
}
bool hal_ipc_accept(void)
{
int err;
cmd_sk = accept_connection(listen_sk);
if (cmd_sk < 0)
return false;
notif_sk = accept_connection(listen_sk);
if (notif_sk < 0) {
close(cmd_sk);
cmd_sk = -1;
return false;
}
err = pthread_create(&notif_th, NULL, notification_handler, NULL);
if (err) {
notif_th = 0;
error("Failed to start notification thread: %d (%s)", err,
strerror(err));
close(cmd_sk);
cmd_sk = -1;
close(notif_sk);
notif_sk = -1;
return false;
}
info("IPC connected");
return true;
}
bool hal_ipc_init(const char *path, size_t size)
{
struct sockaddr_un addr;
int sk;
int err;
sk = socket(AF_LOCAL, SOCK_SEQPACKET, 0);
if (sk < 0) {
err = errno;
error("Failed to create socket: %d (%s)", err,
strerror(err));
return false;
}
memset(&addr, 0, sizeof(addr));
addr.sun_family = AF_UNIX;
memcpy(addr.sun_path, path, size);
if (bind(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
err = errno;
error("Failed to bind socket: %d (%s)", err, strerror(err));
close(sk);
return false;
}
if (listen(sk, 2) < 0) {
err = errno;
error("Failed to listen on socket: %d (%s)", err,
strerror(err));
close(sk);
return false;
}
listen_sk = sk;
return true;
}
void hal_ipc_cleanup(void)
{
close(listen_sk);
listen_sk = -1;
pthread_mutex_lock(&cmd_sk_mutex);
if (cmd_sk >= 0) {
close(cmd_sk);
cmd_sk = -1;
}
pthread_mutex_unlock(&cmd_sk_mutex);
if (notif_sk < 0)
return;
shutdown(notif_sk, SHUT_RD);
pthread_join(notif_th, NULL);
notif_th = 0;
}
int hal_ipc_cmd(uint8_t service_id, uint8_t opcode, uint16_t len, void *param,
size_t *rsp_len, void *rsp, int *fd)
{
ssize_t ret;
struct msghdr msg;
struct iovec iv[2];
struct ipc_hdr cmd;
char cmsgbuf[CMSG_SPACE(sizeof(int))];
struct ipc_status s;
size_t s_len = sizeof(s);
if (cmd_sk < 0) {
error("Invalid cmd socket passed to hal_ipc_cmd");
goto failed;
}
if (!rsp || !rsp_len) {
memset(&s, 0, s_len);
rsp_len = &s_len;
rsp = &s;
}
memset(&msg, 0, sizeof(msg));
memset(&cmd, 0, sizeof(cmd));
cmd.service_id = service_id;
cmd.opcode = opcode;
cmd.len = len;
iv[0].iov_base = &cmd;
iv[0].iov_len = sizeof(cmd);
iv[1].iov_base = param;
iv[1].iov_len = len;
msg.msg_iov = iv;
msg.msg_iovlen = 2;
pthread_mutex_lock(&cmd_sk_mutex);
ret = sendmsg(cmd_sk, &msg, 0);
if (ret < 0) {
error("Sending command failed:%s", strerror(errno));
pthread_mutex_unlock(&cmd_sk_mutex);
goto failed;
}
/* socket was shutdown */
if (ret == 0) {
error("Command socket closed");
pthread_mutex_unlock(&cmd_sk_mutex);
goto failed;
}
memset(&msg, 0, sizeof(msg));
memset(&cmd, 0, sizeof(cmd));
iv[0].iov_base = &cmd;
iv[0].iov_len = sizeof(cmd);
iv[1].iov_base = rsp;
iv[1].iov_len = *rsp_len;
msg.msg_iov = iv;
msg.msg_iovlen = 2;
if (fd) {
memset(cmsgbuf, 0, sizeof(cmsgbuf));
msg.msg_control = cmsgbuf;
msg.msg_controllen = sizeof(cmsgbuf);
}
ret = recvmsg(cmd_sk, &msg, 0);
pthread_mutex_unlock(&cmd_sk_mutex);
if (ret < 0) {
error("Receiving command response failed: %s", strerror(errno));
goto failed;
}
if (ret < (ssize_t) sizeof(cmd)) {
error("Too small response received(%zd bytes)", ret);
goto failed;
}
if (cmd.service_id != service_id) {
error("Invalid service id (0x%x vs 0x%x)",
cmd.service_id, service_id);
goto failed;
}
if (ret != (ssize_t) (sizeof(cmd) + cmd.len)) {
error("Malformed response received(%zd bytes)", ret);
goto failed;
}
if (cmd.opcode != opcode && cmd.opcode != HAL_OP_STATUS) {
error("Invalid opcode received (0x%x vs 0x%x)",
cmd.opcode, opcode);
goto failed;
}
if (cmd.opcode == HAL_OP_STATUS) {
struct ipc_status *s = rsp;
if (sizeof(*s) != cmd.len) {
error("Invalid status length");
goto failed;
}
if (s->code == HAL_STATUS_SUCCESS) {
error("Invalid success status response");
goto failed;
}
return s->code;
}
/* Receive auxiliary data in msg */
if (fd) {
struct cmsghdr *cmsg;
*fd = -1;
for (cmsg = CMSG_FIRSTHDR(&msg); cmsg;
cmsg = CMSG_NXTHDR(&msg, cmsg)) {
if (cmsg->cmsg_level == SOL_SOCKET
&& cmsg->cmsg_type == SCM_RIGHTS) {
memcpy(fd, CMSG_DATA(cmsg), sizeof(int));
break;
}
}
}
*rsp_len = cmd.len;
return BT_STATUS_SUCCESS;
failed:
exit(EXIT_FAILURE);
}
@@ -0,0 +1,22 @@
/* SPDX-License-Identifier: Apache-2.0 */
/*
* Copyright (C) 2013 Intel Corporation
*
*/
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);
@@ -0,0 +1,24 @@
/* SPDX-License-Identifier: Apache-2.0 */
/*
* Copyright (C) 2013 Intel Corporation
*
*/
#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)
@@ -0,0 +1,149 @@
// SPDX-License-Identifier: Apache-2.0
/*
* Copyright (C) 2014 Intel Corporation
*
*/
#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;
}
File diff suppressed because it is too large Load Diff

Some files were not shown because too many files have changed in this diff Show More