mirror of
https://github.com/IllusionMan1212/ShockPair.git
synced 2026-10-24 10:12:26 +00:00
initial commit
This commit is contained in:
@@ -0,0 +1 @@
|
||||
/build
|
||||
@@ -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
|
||||
Vendored
+21
@@ -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)
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
@@ -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})
|
||||
@@ -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>
|
||||
@@ -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.
|
||||
@@ -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
@@ -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.
|
||||
|
||||
@@ -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
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
])
|
||||
Vendored
+10499
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
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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(®, 0, sizeof(reg));
|
||||
|
||||
if (argc != ((atoi(argv[6]) * 4) + 7)) {
|
||||
haltest_error("mdep cfg argumetns are not proper\n");
|
||||
return;
|
||||
}
|
||||
|
||||
reg.application_name = argv[2];
|
||||
|
||||
if (strcmp("-", argv[3]))
|
||||
reg.provider_name = argv[3];
|
||||
|
||||
if (strcmp("-", argv[4]))
|
||||
reg.srv_name = argv[4];
|
||||
|
||||
if (strcmp("-", argv[5]))
|
||||
reg.srv_desp = argv[5];
|
||||
|
||||
reg.number_of_mdeps = atoi(argv[6]);
|
||||
|
||||
reg.mdep_cfg = malloc(reg.number_of_mdeps * sizeof(bthl_mdep_cfg_t));
|
||||
if (!reg.mdep_cfg) {
|
||||
haltest_error("malloc failed\n");
|
||||
return;
|
||||
}
|
||||
mdep_argc_init = 7;
|
||||
|
||||
for (i = 0; i < reg.number_of_mdeps; i++) {
|
||||
mdep_argc_off = mdep_argc_init + (4 * i);
|
||||
reg.mdep_cfg[i].mdep_role =
|
||||
str2bthl_mdep_role_t(argv[mdep_argc_off]);
|
||||
reg.mdep_cfg[i].data_type = atoi(argv[mdep_argc_off + 1]);
|
||||
reg.mdep_cfg[i].channel_type =
|
||||
str2bthl_channel_type_t(argv[mdep_argc_off + 2]);
|
||||
|
||||
if (!strcmp("-", argv[mdep_argc_off + 3])) {
|
||||
reg.mdep_cfg[i].mdep_description = NULL;
|
||||
continue;
|
||||
}
|
||||
|
||||
reg.mdep_cfg[i].mdep_description = argv[mdep_argc_off + 3];
|
||||
}
|
||||
|
||||
EXEC(if_hl->register_application, ®, &app_id);
|
||||
|
||||
for (i = 0; i < reg.number_of_mdeps; i++)
|
||||
app[app_id].mdep[i].role = reg.mdep_cfg[i].mdep_role;
|
||||
|
||||
free(reg.mdep_cfg);
|
||||
}
|
||||
|
||||
/* unregister_application */
|
||||
|
||||
static void unregister_application_p(int argc, const char **argv)
|
||||
{
|
||||
uint32_t app_id;
|
||||
|
||||
RETURN_IF_NULL(if_hl);
|
||||
|
||||
if (argc <= 2) {
|
||||
haltest_error("No app id is specified");
|
||||
return;
|
||||
}
|
||||
|
||||
app_id = (uint32_t) atoi(argv[2]);
|
||||
|
||||
EXEC(if_hl->unregister_application, app_id);
|
||||
}
|
||||
|
||||
/* connect_channel */
|
||||
|
||||
static void connect_channel_p(int argc, const char **argv)
|
||||
{
|
||||
uint32_t app_id, mdep_cfg_index;
|
||||
int channel_id = -1;
|
||||
bt_bdaddr_t bd_addr;
|
||||
|
||||
RETURN_IF_NULL(if_hl);
|
||||
|
||||
if (argc <= 2) {
|
||||
haltest_error("No app id is specified");
|
||||
return;
|
||||
}
|
||||
|
||||
VERIFY_ADDR_ARG(3, &bd_addr);
|
||||
|
||||
if (argc <= 4) {
|
||||
haltest_error("No mdep cfg index is specified");
|
||||
return;
|
||||
}
|
||||
|
||||
app_id = (uint32_t) atoi(argv[2]);
|
||||
mdep_cfg_index = (uint32_t) atoi(argv[4]);
|
||||
|
||||
EXEC(if_hl->connect_channel, app_id, &bd_addr, mdep_cfg_index,
|
||||
&channel_id);
|
||||
}
|
||||
|
||||
/* destroy_channel */
|
||||
|
||||
static void destroy_channel_p(int argc, const char **argv)
|
||||
{
|
||||
uint32_t channel_id;
|
||||
|
||||
RETURN_IF_NULL(if_hl);
|
||||
|
||||
if (argc <= 2) {
|
||||
haltest_error("No channel id is specified");
|
||||
return;
|
||||
}
|
||||
|
||||
channel_id = (uint32_t) atoi(argv[2]);
|
||||
|
||||
EXEC(if_hl->destroy_channel, channel_id);
|
||||
}
|
||||
|
||||
/* close_channel */
|
||||
|
||||
static void close_channel_p(int argc, const char **argv)
|
||||
{
|
||||
uint32_t app_id;
|
||||
uint8_t index;
|
||||
int channel_id;
|
||||
|
||||
RETURN_IF_NULL(if_hl);
|
||||
|
||||
if (argc <= 2) {
|
||||
haltest_error("No app id is specified");
|
||||
return;
|
||||
}
|
||||
|
||||
if (argc <= 3) {
|
||||
haltest_error("No mdep_cfg_index is specified");
|
||||
return;
|
||||
}
|
||||
|
||||
if (argc <= 4) {
|
||||
haltest_error("No channel_id is specified");
|
||||
return;
|
||||
}
|
||||
|
||||
app_id = (uint32_t) atoi(argv[2]);
|
||||
if (app_id >= APP_ID_SIZE) {
|
||||
haltest_error("Wrong app_id specified: %u\n", app_id);
|
||||
return;
|
||||
}
|
||||
|
||||
index = (uint8_t) atoi(argv[3]);
|
||||
if (index >= MDEP_CFG_SIZE) {
|
||||
haltest_error("Wrong mdep cfg index: %u\n", index);
|
||||
return;
|
||||
}
|
||||
|
||||
channel_id = atoi(argv[4]);
|
||||
if (channel_id >= CHANNEL_ID_SIZE) {
|
||||
haltest_error("Wrong channel id: %u\n", channel_id);
|
||||
return;
|
||||
}
|
||||
|
||||
if (app[app_id].mdep[index].channel[channel_id].fd >= 0) {
|
||||
shutdown(app[app_id].mdep[index].channel[channel_id].fd,
|
||||
SHUT_RDWR);
|
||||
app[app_id].mdep[index].channel[channel_id].fd = -1;
|
||||
}
|
||||
}
|
||||
|
||||
/* cleanup */
|
||||
|
||||
static void cleanup_p(int argc, const char **argv)
|
||||
{
|
||||
RETURN_IF_NULL(if_hl);
|
||||
|
||||
EXECV(if_hl->cleanup);
|
||||
if_hl = NULL;
|
||||
}
|
||||
|
||||
static struct method methods[] = {
|
||||
STD_METHOD(init),
|
||||
STD_METHODH(register_application,
|
||||
"<app_name> <provider_name> <srv_name> <srv_descr>\n"
|
||||
"<num_of_mdeps>\n"
|
||||
"[[<mdep_role>] [<data_type>] [<channel_type>] [<mdep_descr>]]"
|
||||
"..."),
|
||||
STD_METHODH(unregister_application, "<app_id>"),
|
||||
STD_METHODH(connect_channel, "<app_id> <bd_addr> <mdep_cfg_index>"),
|
||||
STD_METHODH(destroy_channel, "<channel_id>"),
|
||||
STD_METHODH(close_channel, "<app_id> <mdep_cfg_index> <channel_id>"),
|
||||
STD_METHOD(cleanup),
|
||||
END_METHOD
|
||||
};
|
||||
|
||||
const struct interface hl_if = {
|
||||
.name = "hl",
|
||||
.methods = methods
|
||||
};
|
||||
@@ -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(®, 0, sizeof(reg));
|
||||
event_id = str2btrc_event_id_t(argv[2]);
|
||||
type = str2btrc_notification_type_t(argv[3]);
|
||||
|
||||
switch (event_id) {
|
||||
case BTRC_EVT_PLAY_STATUS_CHANGED:
|
||||
reg.play_status = str2btrc_play_status_t(argv[4]);
|
||||
break;
|
||||
|
||||
case BTRC_EVT_TRACK_CHANGE:
|
||||
track = strtoull(argv[5], NULL, 10);
|
||||
memcpy(reg.track, &track, sizeof(btrc_uid_t));
|
||||
break;
|
||||
|
||||
case BTRC_EVT_TRACK_REACHED_END:
|
||||
case BTRC_EVT_TRACK_REACHED_START:
|
||||
break;
|
||||
|
||||
case BTRC_EVT_PLAY_POS_CHANGED:
|
||||
song_pos = strtoul(argv[4], NULL, 10);
|
||||
memcpy(®.song_pos, &song_pos, sizeof(uint32_t));
|
||||
break;
|
||||
|
||||
case BTRC_EVT_APP_SETTINGS_CHANGED:
|
||||
haltest_error("not supported");
|
||||
return;
|
||||
}
|
||||
|
||||
EXEC(if_rc->register_notification_rsp, event_id, type, ®);
|
||||
}
|
||||
|
||||
/* cleanup */
|
||||
|
||||
static void cleanup_p(int argc, const char **argv)
|
||||
{
|
||||
RETURN_IF_NULL(if_rc);
|
||||
|
||||
EXECV(if_rc->cleanup);
|
||||
if_rc = NULL;
|
||||
}
|
||||
|
||||
static struct method methods[] = {
|
||||
STD_METHOD(init),
|
||||
STD_METHODCH(get_play_status_rsp,
|
||||
"<play_status> <song_len> <song_pos>"),
|
||||
STD_METHODCH(get_element_attr_rsp, "<num_attr> <attrs_id> <value>"),
|
||||
STD_METHODCH(set_player_app_value_rsp, "<rsp_status>"),
|
||||
STD_METHODCH(set_volume, "<volume>"),
|
||||
STD_METHODCH(register_notification_rsp,
|
||||
"<event_id> <type> <respective_data...>\n"
|
||||
"BTRC_EVT_PLAY_STATUS_CHANGED <type> <play_status>\n"
|
||||
"BTRC_EVT_TRACK_CHANGE <type> <track>\n"
|
||||
"BTRC_EVT_TRACK_REACHED_END <type>\n"
|
||||
"BTRC_EVT_TRACK_REACHED_START <type>\n"
|
||||
"BTRC_EVT_PLAY_POS_CHANGED <type> <song_pos>\n"),
|
||||
STD_METHOD(cleanup),
|
||||
END_METHOD
|
||||
};
|
||||
|
||||
const struct interface rc_if = {
|
||||
.name = "rc",
|
||||
.methods = methods
|
||||
};
|
||||
@@ -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 = ¤t_actions[0];
|
||||
|
||||
a->func(c, process_line);
|
||||
fflush(stdout);
|
||||
}
|
||||
|
||||
void terminal_prompt_for(const char *s, line_callback process_line)
|
||||
{
|
||||
current_prompt = s;
|
||||
if (prompt != noprompt) {
|
||||
prompt = s;
|
||||
terminal_clear_line();
|
||||
}
|
||||
prompt_callback = process_line;
|
||||
terminal_set_actions(prompt_actions);
|
||||
}
|
||||
|
||||
static struct termios origianl_tios;
|
||||
|
||||
static void terminal_cleanup(void)
|
||||
{
|
||||
tcsetattr(0, TCSANOW, &origianl_tios);
|
||||
}
|
||||
|
||||
void terminal_setup(void)
|
||||
{
|
||||
struct termios tios;
|
||||
|
||||
terminal_set_actions(normal_actions);
|
||||
|
||||
tcgetattr(0, &origianl_tios);
|
||||
tios = origianl_tios;
|
||||
|
||||
/*
|
||||
* Turn off echo since all editing is done by hand,
|
||||
* Ctrl-c handled internally
|
||||
*/
|
||||
tios.c_lflag &= ~(ICANON | ECHO | BRKINT | IGNBRK);
|
||||
tcsetattr(0, TCSANOW, &tios);
|
||||
|
||||
/* Restore terminal at exit */
|
||||
atexit(terminal_cleanup);
|
||||
|
||||
printf("%s", prompt);
|
||||
fflush(stdout);
|
||||
}
|
||||
@@ -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
|
||||
@@ -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
@@ -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
@@ -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(¬if_th, NULL, notification_handler, NULL);
|
||||
if (err) {
|
||||
notif_th = 0;
|
||||
error("Failed to start notification thread: %d (%s)", err,
|
||||
strerror(err));
|
||||
close(cmd_sk);
|
||||
cmd_sk = -1;
|
||||
close(notif_sk);
|
||||
notif_sk = -1;
|
||||
return false;
|
||||
}
|
||||
|
||||
info("IPC connected");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool hal_ipc_init(const char *path, size_t size)
|
||||
{
|
||||
struct sockaddr_un addr;
|
||||
int sk;
|
||||
int err;
|
||||
|
||||
sk = socket(AF_LOCAL, SOCK_SEQPACKET, 0);
|
||||
if (sk < 0) {
|
||||
err = errno;
|
||||
error("Failed to create socket: %d (%s)", err,
|
||||
strerror(err));
|
||||
return false;
|
||||
}
|
||||
|
||||
memset(&addr, 0, sizeof(addr));
|
||||
addr.sun_family = AF_UNIX;
|
||||
|
||||
memcpy(addr.sun_path, path, size);
|
||||
|
||||
if (bind(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
|
||||
err = errno;
|
||||
error("Failed to bind socket: %d (%s)", err, strerror(err));
|
||||
close(sk);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (listen(sk, 2) < 0) {
|
||||
err = errno;
|
||||
error("Failed to listen on socket: %d (%s)", err,
|
||||
strerror(err));
|
||||
close(sk);
|
||||
return false;
|
||||
}
|
||||
|
||||
listen_sk = sk;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void hal_ipc_cleanup(void)
|
||||
{
|
||||
close(listen_sk);
|
||||
listen_sk = -1;
|
||||
|
||||
pthread_mutex_lock(&cmd_sk_mutex);
|
||||
if (cmd_sk >= 0) {
|
||||
close(cmd_sk);
|
||||
cmd_sk = -1;
|
||||
}
|
||||
pthread_mutex_unlock(&cmd_sk_mutex);
|
||||
|
||||
if (notif_sk < 0)
|
||||
return;
|
||||
|
||||
shutdown(notif_sk, SHUT_RD);
|
||||
|
||||
pthread_join(notif_th, NULL);
|
||||
notif_th = 0;
|
||||
}
|
||||
|
||||
int hal_ipc_cmd(uint8_t service_id, uint8_t opcode, uint16_t len, void *param,
|
||||
size_t *rsp_len, void *rsp, int *fd)
|
||||
{
|
||||
ssize_t ret;
|
||||
struct msghdr msg;
|
||||
struct iovec iv[2];
|
||||
struct ipc_hdr cmd;
|
||||
char cmsgbuf[CMSG_SPACE(sizeof(int))];
|
||||
struct ipc_status s;
|
||||
size_t s_len = sizeof(s);
|
||||
|
||||
if (cmd_sk < 0) {
|
||||
error("Invalid cmd socket passed to hal_ipc_cmd");
|
||||
goto failed;
|
||||
}
|
||||
|
||||
if (!rsp || !rsp_len) {
|
||||
memset(&s, 0, s_len);
|
||||
rsp_len = &s_len;
|
||||
rsp = &s;
|
||||
}
|
||||
|
||||
memset(&msg, 0, sizeof(msg));
|
||||
memset(&cmd, 0, sizeof(cmd));
|
||||
|
||||
cmd.service_id = service_id;
|
||||
cmd.opcode = opcode;
|
||||
cmd.len = len;
|
||||
|
||||
iv[0].iov_base = &cmd;
|
||||
iv[0].iov_len = sizeof(cmd);
|
||||
|
||||
iv[1].iov_base = param;
|
||||
iv[1].iov_len = len;
|
||||
|
||||
msg.msg_iov = iv;
|
||||
msg.msg_iovlen = 2;
|
||||
|
||||
pthread_mutex_lock(&cmd_sk_mutex);
|
||||
|
||||
ret = sendmsg(cmd_sk, &msg, 0);
|
||||
if (ret < 0) {
|
||||
error("Sending command failed:%s", strerror(errno));
|
||||
pthread_mutex_unlock(&cmd_sk_mutex);
|
||||
goto failed;
|
||||
}
|
||||
|
||||
/* socket was shutdown */
|
||||
if (ret == 0) {
|
||||
error("Command socket closed");
|
||||
pthread_mutex_unlock(&cmd_sk_mutex);
|
||||
goto failed;
|
||||
}
|
||||
|
||||
memset(&msg, 0, sizeof(msg));
|
||||
memset(&cmd, 0, sizeof(cmd));
|
||||
|
||||
iv[0].iov_base = &cmd;
|
||||
iv[0].iov_len = sizeof(cmd);
|
||||
|
||||
iv[1].iov_base = rsp;
|
||||
iv[1].iov_len = *rsp_len;
|
||||
|
||||
msg.msg_iov = iv;
|
||||
msg.msg_iovlen = 2;
|
||||
|
||||
if (fd) {
|
||||
memset(cmsgbuf, 0, sizeof(cmsgbuf));
|
||||
msg.msg_control = cmsgbuf;
|
||||
msg.msg_controllen = sizeof(cmsgbuf);
|
||||
}
|
||||
|
||||
ret = recvmsg(cmd_sk, &msg, 0);
|
||||
|
||||
pthread_mutex_unlock(&cmd_sk_mutex);
|
||||
|
||||
if (ret < 0) {
|
||||
error("Receiving command response failed: %s", strerror(errno));
|
||||
goto failed;
|
||||
}
|
||||
|
||||
|
||||
if (ret < (ssize_t) sizeof(cmd)) {
|
||||
error("Too small response received(%zd bytes)", ret);
|
||||
goto failed;
|
||||
}
|
||||
|
||||
if (cmd.service_id != service_id) {
|
||||
error("Invalid service id (0x%x vs 0x%x)",
|
||||
cmd.service_id, service_id);
|
||||
goto failed;
|
||||
}
|
||||
|
||||
if (ret != (ssize_t) (sizeof(cmd) + cmd.len)) {
|
||||
error("Malformed response received(%zd bytes)", ret);
|
||||
goto failed;
|
||||
}
|
||||
|
||||
if (cmd.opcode != opcode && cmd.opcode != HAL_OP_STATUS) {
|
||||
error("Invalid opcode received (0x%x vs 0x%x)",
|
||||
cmd.opcode, opcode);
|
||||
goto failed;
|
||||
}
|
||||
|
||||
if (cmd.opcode == HAL_OP_STATUS) {
|
||||
struct ipc_status *s = rsp;
|
||||
|
||||
if (sizeof(*s) != cmd.len) {
|
||||
error("Invalid status length");
|
||||
goto failed;
|
||||
}
|
||||
|
||||
if (s->code == HAL_STATUS_SUCCESS) {
|
||||
error("Invalid success status response");
|
||||
goto failed;
|
||||
}
|
||||
|
||||
return s->code;
|
||||
}
|
||||
|
||||
/* Receive auxiliary data in msg */
|
||||
if (fd) {
|
||||
struct cmsghdr *cmsg;
|
||||
|
||||
*fd = -1;
|
||||
|
||||
for (cmsg = CMSG_FIRSTHDR(&msg); cmsg;
|
||||
cmsg = CMSG_NXTHDR(&msg, cmsg)) {
|
||||
if (cmsg->cmsg_level == SOL_SOCKET
|
||||
&& cmsg->cmsg_type == SCM_RIGHTS) {
|
||||
memcpy(fd, CMSG_DATA(cmsg), sizeof(int));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
*rsp_len = cmd.len;
|
||||
|
||||
return BT_STATUS_SUCCESS;
|
||||
|
||||
failed:
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
@@ -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
Reference in New Issue
Block a user