eclair snapshot master tegra-9.12.5-baseline tegra-9.12.6-baseline
Jean-Baptiste Queru [Fri, 13 Nov 2009 02:45:21 +0000 (18:45 -0800)]
348 files changed:
.gitignore [new file with mode: 0644]
AUTHORS [new file with mode: 0644]
Android.mk [new file with mode: 0755]
COPYING [new file with mode: 0644]
COPYING.LIB [new file with mode: 0644]
ChangeLog [new file with mode: 0644]
INSTALL [new file with mode: 0644]
Makefile.am [new file with mode: 0644]
NEWS [new file with mode: 0644]
README [new file with mode: 0644]
acinclude.m4 [new file with mode: 0644]
audio/Android.mk [new file with mode: 0755]
audio/Makefile.am [new file with mode: 0644]
audio/a2dp.c [new file with mode: 0644]
audio/a2dp.h [new file with mode: 0644]
audio/audio.conf [new file with mode: 0644]
audio/avdtp.c [new file with mode: 0644]
audio/avdtp.h [new file with mode: 0644]
audio/bluetooth.conf [new file with mode: 0644]
audio/control.c [new file with mode: 0644]
audio/control.h [new file with mode: 0644]
audio/ctl_bluetooth.c [new file with mode: 0644]
audio/device.c [new file with mode: 0644]
audio/device.h [new file with mode: 0644]
audio/gateway.c [new file with mode: 0644]
audio/gateway.h [new file with mode: 0644]
audio/gsta2dpsink.c [new file with mode: 0644]
audio/gsta2dpsink.h [new file with mode: 0644]
audio/gstavdtpsink.c [new file with mode: 0644]
audio/gstavdtpsink.h [new file with mode: 0644]
audio/gstbluetooth.c [new file with mode: 0644]
audio/gstrtpsbcpay.c [new file with mode: 0644]
audio/gstrtpsbcpay.h [new file with mode: 0644]
audio/gstsbcdec.c [new file with mode: 0644]
audio/gstsbcdec.h [new file with mode: 0644]
audio/gstsbcenc.c [new file with mode: 0644]
audio/gstsbcenc.h [new file with mode: 0644]
audio/gstsbcparse.c [new file with mode: 0644]
audio/gstsbcparse.h [new file with mode: 0644]
audio/gstsbcutil.c [new file with mode: 0644]
audio/gstsbcutil.h [new file with mode: 0644]
audio/headset.c [new file with mode: 0644]
audio/headset.h [new file with mode: 0644]
audio/ipc.c [new file with mode: 0644]
audio/ipc.h [new file with mode: 0644]
audio/ipctest-a2dp-easy.test [new file with mode: 0644]
audio/ipctest-a2dp-resume-fast.test [new file with mode: 0644]
audio/ipctest-hsp-a2dp-switch.test [new file with mode: 0644]
audio/ipctest-hsp-easy.test [new file with mode: 0644]
audio/ipctest-init-shutdown.test [new file with mode: 0644]
audio/ipctest.c [new file with mode: 0644]
audio/liba2dp.c [new file with mode: 0755]
audio/liba2dp.h [new file with mode: 0644]
audio/main.c [new file with mode: 0644]
audio/manager.c [new file with mode: 0644]
audio/manager.h [new file with mode: 0644]
audio/module-bluetooth-sink.c [new file with mode: 0644]
audio/pcm_bluetooth.c [new file with mode: 0644]
audio/rtp.h [new file with mode: 0644]
audio/sink.c [new file with mode: 0644]
audio/sink.h [new file with mode: 0644]
audio/source.c [new file with mode: 0644]
audio/source.h [new file with mode: 0644]
audio/telephony-dummy.c [new file with mode: 0644]
audio/telephony-maemo.c [new file with mode: 0644]
audio/telephony-ofono.c [new file with mode: 0644]
audio/telephony.h [new file with mode: 0644]
audio/unix.c [new file with mode: 0644]
audio/unix.h [new file with mode: 0644]
bluez.m4 [new file with mode: 0644]
bluez.pc.in [new file with mode: 0644]
bootstrap [new file with mode: 0755]
bootstrap-configure [new file with mode: 0755]
client/Makefile.am [new file with mode: 0644]
common/Android.mk [new file with mode: 0755]
common/Makefile.am [new file with mode: 0644]
common/android_bluez.c [new file with mode: 0644]
common/btio.c [new file with mode: 0644]
common/btio.h [new file with mode: 0644]
common/glib-helper.c [new file with mode: 0644]
common/glib-helper.h [new file with mode: 0644]
common/logging.c [new file with mode: 0644]
common/logging.h [new file with mode: 0644]
common/oui.c [new file with mode: 0644]
common/oui.h [new file with mode: 0644]
common/ppoll.h [new file with mode: 0644]
common/sdp-xml.c [new file with mode: 0644]
common/sdp-xml.h [new file with mode: 0644]
common/test_textfile.c [new file with mode: 0644]
common/textfile.c [new file with mode: 0644]
common/textfile.h [new file with mode: 0644]
common/uinput.h [new file with mode: 0644]
compat/Makefile.am [new file with mode: 0644]
compat/bnep.c [new file with mode: 0644]
compat/dun.c [new file with mode: 0644]
compat/dund.1 [new file with mode: 0644]
compat/dund.c [new file with mode: 0644]
compat/dund.h [new file with mode: 0644]
compat/fakehid.c [new file with mode: 0644]
compat/fakehid.txt [new file with mode: 0644]
compat/hidd.1 [new file with mode: 0644]
compat/hidd.c [new file with mode: 0644]
compat/hidd.h [new file with mode: 0644]
compat/lib.h [new file with mode: 0644]
compat/msdun.c [new file with mode: 0644]
compat/pand.1 [new file with mode: 0644]
compat/pand.c [new file with mode: 0644]
compat/pand.h [new file with mode: 0644]
compat/sdp.c [new file with mode: 0644]
compat/sdp.h [new file with mode: 0644]
configure.ac [new file with mode: 0644]
cups/Makefile.am [new file with mode: 0644]
cups/cups.h [new file with mode: 0644]
cups/hcrp.c [new file with mode: 0644]
cups/main.c [new file with mode: 0644]
cups/sdp.c [new file with mode: 0644]
cups/spp.c [new file with mode: 0644]
doc/Makefile.am [new file with mode: 0644]
doc/adapter-api.txt [new file with mode: 0644]
doc/agent-api.txt [new file with mode: 0644]
doc/audio-api.txt [new file with mode: 0644]
doc/bluez-docs.xml [new file with mode: 0644]
doc/control-api.txt [new file with mode: 0644]
doc/device-api.txt [new file with mode: 0644]
doc/gtk-doc.make [new file with mode: 0644]
doc/input-api.txt [new file with mode: 0644]
doc/manager-api.txt [new file with mode: 0644]
doc/network-api.txt [new file with mode: 0644]
doc/node-api.txt [new file with mode: 0644]
doc/serial-api.txt [new file with mode: 0644]
doc/service-api.txt [new file with mode: 0644]
doc/version.xml.in [new file with mode: 0644]
gdbus/Android.mk [new file with mode: 0755]
gdbus/Makefile.am [new file with mode: 0644]
gdbus/gdbus.h [new file with mode: 0644]
gdbus/mainloop.c [new file with mode: 0644]
gdbus/object.c [new file with mode: 0644]
gdbus/watch.c [new file with mode: 0644]
include/bluetooth/Makefile.am [new file with mode: 0644]
include/bluetooth/bluetooth.h [new file with mode: 0644]
include/bluetooth/bnep.h [new file with mode: 0644]
include/bluetooth/cmtp.h [new file with mode: 0644]
include/bluetooth/hci.h [new file with mode: 0644]
include/bluetooth/hci_lib.h [new file with mode: 0644]
include/bluetooth/hidp.h [new file with mode: 0644]
include/bluetooth/l2cap.h [new file with mode: 0644]
include/bluetooth/rfcomm.h [new file with mode: 0644]
include/bluetooth/sco.h [new file with mode: 0644]
include/bluetooth/sdp.h [new file with mode: 0644]
include/bluetooth/sdp_lib.h [new file with mode: 0644]
input/Android.mk [new file with mode: 0755]
input/Makefile.am [new file with mode: 0644]
input/device.c [new file with mode: 0644]
input/device.h [new file with mode: 0644]
input/fakehid.c [new file with mode: 0644]
input/fakehid.h [new file with mode: 0644]
input/input.conf [new file with mode: 0644]
input/main.c [new file with mode: 0644]
input/manager.c [new file with mode: 0644]
input/manager.h [new file with mode: 0644]
input/server.c [new file with mode: 0644]
input/server.h [new file with mode: 0644]
input/sixpair.c [new file with mode: 0644]
lib/Android.mk [new file with mode: 0755]
lib/Makefile.am [new file with mode: 0644]
lib/bluetooth.c [new file with mode: 0644]
lib/hci.c [new file with mode: 0644]
lib/sdp.c [new file with mode: 0644]
network/Makefile.am [new file with mode: 0644]
network/bridge.c [new file with mode: 0644]
network/bridge.h [new file with mode: 0644]
network/common.c [new file with mode: 0644]
network/common.h [new file with mode: 0644]
network/connection.c [new file with mode: 0644]
network/connection.h [new file with mode: 0644]
network/main.c [new file with mode: 0644]
network/manager.c [new file with mode: 0644]
network/manager.h [new file with mode: 0644]
network/network.conf [new file with mode: 0644]
network/server.c [new file with mode: 0644]
network/server.h [new file with mode: 0644]
plugins/Android.mk [new file with mode: 0755]
plugins/Makefile.am [new file with mode: 0644]
plugins/builtin.h [new file with mode: 0644]
plugins/echo.c [new file with mode: 0644]
plugins/hal.c [new file with mode: 0644]
plugins/hciops.c [new file with mode: 0644]
plugins/netlink.c [new file with mode: 0644]
plugins/service.c [new file with mode: 0644]
plugins/storage.c [new file with mode: 0644]
rfcomm/Android.mk [new file with mode: 0755]
rfcomm/Makefile.am [new file with mode: 0644]
rfcomm/kword.c [new file with mode: 0644]
rfcomm/kword.h [new file with mode: 0644]
rfcomm/lexer.c [new file with mode: 0644]
rfcomm/lexer.l [new file with mode: 0644]
rfcomm/main.c [new file with mode: 0644]
rfcomm/parser.c [new file with mode: 0644]
rfcomm/parser.h [new file with mode: 0644]
rfcomm/parser.y [new file with mode: 0644]
rfcomm/rfcomm.1 [new file with mode: 0644]
rfcomm/rfcomm.conf [new file with mode: 0644]
sbc/Makefile.am [new file with mode: 0644]
sbc/formats.h [new file with mode: 0644]
sbc/sbc.c [new file with mode: 0644]
sbc/sbc.h [new file with mode: 0644]
sbc/sbc_math.h [new file with mode: 0644]
sbc/sbc_primitives.c [new file with mode: 0644]
sbc/sbc_primitives.h [new file with mode: 0644]
sbc/sbc_primitives_mmx.c [new file with mode: 0644]
sbc/sbc_primitives_mmx.h [new file with mode: 0644]
sbc/sbc_primitives_neon.c [new file with mode: 0644]
sbc/sbc_primitives_neon.h [new file with mode: 0644]
sbc/sbc_tables.h [new file with mode: 0644]
sbc/sbcdec.c [new file with mode: 0644]
sbc/sbcenc.c [new file with mode: 0644]
sbc/sbcinfo.c [new file with mode: 0644]
sbc/sbctester.c [new file with mode: 0644]
scripts/Makefile.am [new file with mode: 0644]
scripts/bluetooth-hid2hci.rules [new file with mode: 0644]
scripts/bluetooth-serial.rules [new file with mode: 0644]
scripts/bluetooth.rules.in [new file with mode: 0644]
scripts/bluetooth_serial [new file with mode: 0644]
serial/Makefile.am [new file with mode: 0644]
serial/main.c [new file with mode: 0644]
serial/manager.c [new file with mode: 0644]
serial/manager.h [new file with mode: 0644]
serial/port.c [new file with mode: 0644]
serial/port.h [new file with mode: 0644]
serial/proxy.c [new file with mode: 0644]
serial/proxy.h [new file with mode: 0644]
serial/serial.conf [new file with mode: 0644]
src/Android.mk [new file with mode: 0755]
src/Makefile.am [new file with mode: 0644]
src/adapter.c [new file with mode: 0644]
src/adapter.h [new file with mode: 0644]
src/agent.c [new file with mode: 0644]
src/agent.h [new file with mode: 0644]
src/bluetooth.conf [new file with mode: 0644]
src/bluetoothd.8.in [new file with mode: 0644]
src/dbus-common.c [new file with mode: 0644]
src/dbus-common.h [new file with mode: 0644]
src/dbus-hci.c [new file with mode: 0644]
src/dbus-hci.h [new file with mode: 0644]
src/device.c [new file with mode: 0644]
src/device.h [new file with mode: 0644]
src/error.c [new file with mode: 0644]
src/error.h [new file with mode: 0644]
src/hcid.conf [new file with mode: 0644]
src/hcid.conf.5.in [new file with mode: 0644]
src/hcid.h [new file with mode: 0644]
src/main.c [new file with mode: 0644]
src/main.conf [new file with mode: 0644]
src/manager.c [new file with mode: 0644]
src/manager.h [new file with mode: 0644]
src/plugin.c [new file with mode: 0644]
src/plugin.h [new file with mode: 0644]
src/rfkill.c [new file with mode: 0644]
src/sdpd-database.c [new file with mode: 0644]
src/sdpd-request.c [new file with mode: 0644]
src/sdpd-server.c [new file with mode: 0644]
src/sdpd-service.c [new file with mode: 0644]
src/sdpd.h [new file with mode: 0644]
src/security.c [new file with mode: 0644]
src/storage.c [new file with mode: 0644]
src/storage.h [new file with mode: 0644]
test/Android.mk [new file with mode: 0755]
test/Makefile.am [new file with mode: 0644]
test/agent.c [new file with mode: 0644]
test/apitest [new file with mode: 0755]
test/attest.c [new file with mode: 0644]
test/avtest.c [new file with mode: 0644]
test/bdaddr.8 [new file with mode: 0644]
test/bdaddr.c [new file with mode: 0644]
test/btiotest.c [new file with mode: 0644]
test/dbusdef.py [new file with mode: 0644]
test/hciemu.1 [new file with mode: 0644]
test/hciemu.c [new file with mode: 0644]
test/hsmicro [new file with mode: 0755]
test/hsplay [new file with mode: 0755]
test/hstest.c [new file with mode: 0644]
test/l2test.c [new file with mode: 0644]
test/list-devices [new file with mode: 0755]
test/lmptest.c [new file with mode: 0644]
test/monitor-bluetooth [new file with mode: 0755]
test/rctest.1 [new file with mode: 0644]
test/rctest.c [new file with mode: 0644]
test/scotest.c [new file with mode: 0644]
test/sdptest.c [new file with mode: 0644]
test/service-did.xml [new file with mode: 0644]
test/service-ftp.xml [new file with mode: 0644]
test/service-opp.xml [new file with mode: 0644]
test/service-record.dtd [new file with mode: 0644]
test/service-spp.xml [new file with mode: 0644]
test/simple-agent [new file with mode: 0755]
test/simple-service [new file with mode: 0755]
test/test-adapter [new file with mode: 0755]
test/test-device [new file with mode: 0755]
test/test-discovery [new file with mode: 0755]
test/test-manager [new file with mode: 0755]
test/test-network [new file with mode: 0755]
test/test-serial [new file with mode: 0755]
test/test-service [new file with mode: 0755]
test/test-telephony [new file with mode: 0755]
tools/Android.mk [new file with mode: 0755]
tools/Makefile.am [new file with mode: 0644]
tools/avctrl.8 [new file with mode: 0644]
tools/avctrl.c [new file with mode: 0644]
tools/avinfo.c [new file with mode: 0644]
tools/bccmd.8 [new file with mode: 0644]
tools/bccmd.c [new file with mode: 0644]
tools/ciptool.1 [new file with mode: 0644]
tools/ciptool.c [new file with mode: 0644]
tools/csr.c [new file with mode: 0644]
tools/csr.h [new file with mode: 0644]
tools/csr_3wire.c [new file with mode: 0644]
tools/csr_bcsp.c [new file with mode: 0644]
tools/csr_h4.c [new file with mode: 0644]
tools/csr_hci.c [new file with mode: 0644]
tools/csr_usb.c [new file with mode: 0644]
tools/dfu.c [new file with mode: 0644]
tools/dfu.h [new file with mode: 0644]
tools/dfubabel.1 [new file with mode: 0644]
tools/dfubabel.c [new file with mode: 0644]
tools/dfutool.1 [new file with mode: 0644]
tools/dfutool.c [new file with mode: 0644]
tools/example.psr [new file with mode: 0644]
tools/hciattach.8 [new file with mode: 0644]
tools/hciattach.c [new file with mode: 0644]
tools/hciattach.h [new file with mode: 0644]
tools/hciattach_st.c [new file with mode: 0644]
tools/hciattach_ti.c [new file with mode: 0644]
tools/hciattach_tialt.c [new file with mode: 0644]
tools/hciconfig.8 [new file with mode: 0644]
tools/hciconfig.c [new file with mode: 0644]
tools/hcieventmask.c [new file with mode: 0644]
tools/hcisecfilter.c [new file with mode: 0644]
tools/hcitool.1 [new file with mode: 0644]
tools/hcitool.c [new file with mode: 0644]
tools/hid2hci.8 [new file with mode: 0644]
tools/hid2hci.c [new file with mode: 0644]
tools/l2ping.8 [new file with mode: 0644]
tools/l2ping.c [new file with mode: 0644]
tools/ppporc.c [new file with mode: 0644]
tools/sdptool.1 [new file with mode: 0644]
tools/sdptool.c [new file with mode: 0644]
tools/ubcsp.c [new file with mode: 0644]
tools/ubcsp.h [new file with mode: 0644]

diff --git a/.gitignore b/.gitignore
new file mode 100644 (file)
index 0000000..d5b85d3
--- /dev/null
@@ -0,0 +1,83 @@
+*.o
+*.a
+*.lo
+*.la
+*.so
+.deps
+.libs
+Makefile
+Makefile.in
+aclocal.m4
+config.guess
+config.h
+config.h.in
+config.log
+config.status
+config.sub
+configure
+depcomp
+compile
+install-sh
+libtool
+ltmain.sh
+missing
+stamp-h1
+autom4te.cache
+
+ylwrap
+bluez.pc
+include/bluetooth
+src/bluetoothd
+audio/telephony.c
+scripts/bluetooth.rules
+
+sbc/sbcdec
+sbc/sbcenc
+sbc/sbcinfo
+sbc/sbctester
+
+tools/avctrl
+tools/avinfo
+tools/bccmd
+tools/ciptool
+tools/dfubabel
+tools/dfutool
+tools/hciattach
+tools/hciconfig
+tools/hcieventmask
+tools/hcisecfilter
+tools/hcitool
+tools/hid2hci
+tools/l2ping
+tools/ppporc
+tools/sdptool
+audio/ipctest
+cups/bluetooth
+test/agent
+test/bdaddr
+test/hciemu
+test/attest
+test/hstest
+test/avtest
+test/l2test
+test/rctest
+test/scotest
+test/sdptest
+test/lmptest
+test/btiotest
+rfcomm/rfcomm
+compat/dund
+compat/hidd
+compat/pand
+common/test_textfile
+
+doc/*.bak
+doc/*.stamp
+doc/bluez.*
+doc/bluez-*.txt
+doc/*.sgml
+doc/version.xml
+doc/xml
+doc/html
+src/bluetoothd.8
+src/hcid.conf.5
diff --git a/AUTHORS b/AUTHORS
new file mode 100644 (file)
index 0000000..cd0189e
--- /dev/null
+++ b/AUTHORS
@@ -0,0 +1,46 @@
+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@nokia.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 F. Padovan <gustavo@las.ic.unicamp.br>
+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>
diff --git a/Android.mk b/Android.mk
new file mode 100755 (executable)
index 0000000..456efdd
--- /dev/null
@@ -0,0 +1,20 @@
+#
+# Copyright (C) 2008 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+ifneq ($(TARGET_SIMULATOR),true)
+ifeq ($(BOARD_HAVE_BLUETOOTH),true)
+  include $(all-subdir-makefiles)
+endif
+endif
diff --git a/COPYING b/COPYING
new file mode 100644 (file)
index 0000000..6d45519
--- /dev/null
+++ b/COPYING
@@ -0,0 +1,340 @@
+                   GNU GENERAL PUBLIC LICENSE
+                      Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+                       51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+                           Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users.  This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it.  (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.)  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+  To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have.  You must make sure that they, too, receive or can get the
+source code.  And you must show them these terms so they know their
+rights.
+
+  We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+  Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software.  If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+  Finally, any free program is threatened constantly by software
+patents.  We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary.  To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+
+                   GNU GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License.  The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language.  (Hereinafter, translation is included without limitation in
+the term "modification".)  Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+  1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+  2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) You must cause the modified files to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    b) You must cause any work that you distribute or publish, that in
+    whole or in part contains or is derived from the Program or any
+    part thereof, to be licensed as a whole at no charge to all third
+    parties under the terms of this License.
+
+    c) If the modified program normally reads commands interactively
+    when run, you must cause it, when started running for such
+    interactive use in the most ordinary way, to print or display an
+    announcement including an appropriate copyright notice and a
+    notice that there is no warranty (or else, saying that you provide
+    a warranty) and that users may redistribute the program under
+    these conditions, and telling the user how to view a copy of this
+    License.  (Exception: if the Program itself is interactive but
+    does not normally print such an announcement, your work based on
+    the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+    a) Accompany it with the complete corresponding machine-readable
+    source code, which must be distributed under the terms of Sections
+    1 and 2 above on a medium customarily used for software interchange; or,
+
+    b) Accompany it with a written offer, valid for at least three
+    years, to give any third party, for a charge no more than your
+    cost of physically performing source distribution, a complete
+    machine-readable copy of the corresponding source code, to be
+    distributed under the terms of Sections 1 and 2 above on a medium
+    customarily used for software interchange; or,
+
+    c) Accompany it with the information you received as to the offer
+    to distribute corresponding source code.  (This alternative is
+    allowed only for noncommercial distribution and only if you
+    received the program in object code or executable form with such
+    an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it.  For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable.  However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+  4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License.  Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+  5. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Program or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+  6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+  7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+  8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded.  In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+  9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation.  If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+  10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission.  For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this.  Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+                           NO WARRANTY
+
+  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+                    END OF TERMS AND CONDITIONS
+
+           How to Apply These Terms to Your New Programs
+
+  If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+  To do so, attach the following notices to the program.  It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+    Gnomovision version 69, Copyright (C) year name of author
+    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+    This is free software, and you are welcome to redistribute it
+    under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License.  Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary.  Here is a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+  `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+  <signature of Ty Coon>, 1 April 1989
+  Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs.  If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library.  If this is what you want to do, use the GNU Library General
+Public License instead of this License.
diff --git a/COPYING.LIB b/COPYING.LIB
new file mode 100644 (file)
index 0000000..1f7c8cc
--- /dev/null
@@ -0,0 +1,504 @@
+                 GNU LESSER GENERAL PUBLIC LICENSE
+                      Version 2.1, February 1999
+
+ Copyright (C) 1991, 1999 Free Software Foundation, Inc.
+     51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+[This is the first released version of the Lesser GPL.  It also counts
+ as the successor of the GNU Library Public License, version 2, hence
+ the version number 2.1.]
+
+                           Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+Licenses are intended to guarantee your freedom to share and change
+free software--to make sure the software is free for all its users.
+
+  This license, the Lesser General Public License, applies to some
+specially designated software packages--typically libraries--of the
+Free Software Foundation and other authors who decide to use it.  You
+can use it too, but we suggest you first think carefully about whether
+this license or the ordinary General Public License is the better
+strategy to use in any particular case, based on the explanations below.
+
+  When we speak of free software, we are referring to freedom of use,
+not price.  Our General Public Licenses are designed to make sure that
+you have the freedom to distribute copies of free software (and charge
+for this service if you wish); that you receive source code or can get
+it if you want it; that you can change the software and use pieces of
+it in new free programs; and that you are informed that you can do
+these things.
+
+  To protect your rights, we need to make restrictions that forbid
+distributors to deny you these rights or to ask you to surrender these
+rights.  These restrictions translate to certain responsibilities for
+you if you distribute copies of the library or if you modify it.
+
+  For example, if you distribute copies of the library, whether gratis
+or for a fee, you must give the recipients all the rights that we gave
+you.  You must make sure that they, too, receive or can get the source
+code.  If you link other code with the library, you must provide
+complete object files to the recipients, so that they can relink them
+with the library after making changes to the library and recompiling
+it.  And you must show them these terms so they know their rights.
+
+  We protect your rights with a two-step method: (1) we copyright the
+library, and (2) we offer you this license, which gives you legal
+permission to copy, distribute and/or modify the library.
+
+  To protect each distributor, we want to make it very clear that
+there is no warranty for the free library.  Also, if the library is
+modified by someone else and passed on, the recipients should know
+that what they have is not the original version, so that the original
+author's reputation will not be affected by problems that might be
+introduced by others.
+
+  Finally, software patents pose a constant threat to the existence of
+any free program.  We wish to make sure that a company cannot
+effectively restrict the users of a free program by obtaining a
+restrictive license from a patent holder.  Therefore, we insist that
+any patent license obtained for a version of the library must be
+consistent with the full freedom of use specified in this license.
+
+  Most GNU software, including some libraries, is covered by the
+ordinary GNU General Public License.  This license, the GNU Lesser
+General Public License, applies to certain designated libraries, and
+is quite different from the ordinary General Public License.  We use
+this license for certain libraries in order to permit linking those
+libraries into non-free programs.
+
+  When a program is linked with a library, whether statically or using
+a shared library, the combination of the two is legally speaking a
+combined work, a derivative of the original library.  The ordinary
+General Public License therefore permits such linking only if the
+entire combination fits its criteria of freedom.  The Lesser General
+Public License permits more lax criteria for linking other code with
+the library.
+
+  We call this license the "Lesser" General Public License because it
+does Less to protect the user's freedom than the ordinary General
+Public License.  It also provides other free software developers Less
+of an advantage over competing non-free programs.  These disadvantages
+are the reason we use the ordinary General Public License for many
+libraries.  However, the Lesser license provides advantages in certain
+special circumstances.
+
+  For example, on rare occasions, there may be a special need to
+encourage the widest possible use of a certain library, so that it becomes
+a de-facto standard.  To achieve this, non-free programs must be
+allowed to use the library.  A more frequent case is that a free
+library does the same job as widely used non-free libraries.  In this
+case, there is little to gain by limiting the free library to free
+software only, so we use the Lesser General Public License.
+
+  In other cases, permission to use a particular library in non-free
+programs enables a greater number of people to use a large body of
+free software.  For example, permission to use the GNU C Library in
+non-free programs enables many more people to use the whole GNU
+operating system, as well as its variant, the GNU/Linux operating
+system.
+
+  Although the Lesser General Public License is Less protective of the
+users' freedom, it does ensure that the user of a program that is
+linked with the Library has the freedom and the wherewithal to run
+that program using a modified version of the Library.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.  Pay close attention to the difference between a
+"work based on the library" and a "work that uses the library".  The
+former contains code derived from the library, whereas the latter must
+be combined with the library in order to run.
+
+                 GNU LESSER GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License Agreement applies to any software library or other
+program which contains a notice placed by the copyright holder or
+other authorized party saying it may be distributed under the terms of
+this Lesser General Public License (also called "this License").
+Each licensee is addressed as "you".
+
+  A "library" means a collection of software functions and/or data
+prepared so as to be conveniently linked with application programs
+(which use some of those functions and data) to form executables.
+
+  The "Library", below, refers to any such software library or work
+which has been distributed under these terms.  A "work based on the
+Library" means either the Library or any derivative work under
+copyright law: that is to say, a work containing the Library or a
+portion of it, either verbatim or with modifications and/or translated
+straightforwardly into another language.  (Hereinafter, translation is
+included without limitation in the term "modification".)
+
+  "Source code" for a work means the preferred form of the work for
+making modifications to it.  For a library, complete source code means
+all the source code for all modules it contains, plus any associated
+interface definition files, plus the scripts used to control compilation
+and installation of the library.
+
+  Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running a program using the Library is not restricted, and output from
+such a program is covered only if its contents constitute a work based
+on the Library (independent of the use of the Library in a tool for
+writing it).  Whether that is true depends on what the Library does
+and what the program that uses the Library does.
+  
+  1. You may copy and distribute verbatim copies of the Library's
+complete source code as you receive it, in any medium, provided that
+you conspicuously and appropriately publish on each copy an
+appropriate copyright notice and disclaimer of warranty; keep intact
+all the notices that refer to this License and to the absence of any
+warranty; and distribute a copy of this License along with the
+Library.
+
+  You may charge a fee for the physical act of transferring a copy,
+and you may at your option offer warranty protection in exchange for a
+fee.
+
+  2. You may modify your copy or copies of the Library or any portion
+of it, thus forming a work based on the Library, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) The modified work must itself be a software library.
+
+    b) You must cause the files modified to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    c) You must cause the whole of the work to be licensed at no
+    charge to all third parties under the terms of this License.
+
+    d) If a facility in the modified Library refers to a function or a
+    table of data to be supplied by an application program that uses
+    the facility, other than as an argument passed when the facility
+    is invoked, then you must make a good faith effort to ensure that,
+    in the event an application does not supply such function or
+    table, the facility still operates, and performs whatever part of
+    its purpose remains meaningful.
+
+    (For example, a function in a library to compute square roots has
+    a purpose that is entirely well-defined independent of the
+    application.  Therefore, Subsection 2d requires that any
+    application-supplied function or table used by this function must
+    be optional: if the application does not supply it, the square
+    root function must still compute square roots.)
+
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Library,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Library, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote
+it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Library.
+
+In addition, mere aggregation of another work not based on the Library
+with the Library (or with a work based on the Library) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may opt to apply the terms of the ordinary GNU General Public
+License instead of this License to a given copy of the Library.  To do
+this, you must alter all the notices that refer to this License, so
+that they refer to the ordinary GNU General Public License, version 2,
+instead of to this License.  (If a newer version than version 2 of the
+ordinary GNU General Public License has appeared, then you can specify
+that version instead if you wish.)  Do not make any other change in
+these notices.
+
+  Once this change is made in a given copy, it is irreversible for
+that copy, so the ordinary GNU General Public License applies to all
+subsequent copies and derivative works made from that copy.
+
+  This option is useful when you wish to copy part of the code of
+the Library into a program that is not a library.
+
+  4. You may copy and distribute the Library (or a portion or
+derivative of it, under Section 2) in object code or executable form
+under the terms of Sections 1 and 2 above provided that you accompany
+it with the complete corresponding machine-readable source code, which
+must be distributed under the terms of Sections 1 and 2 above on a
+medium customarily used for software interchange.
+
+  If distribution of object code is made by offering access to copy
+from a designated place, then offering equivalent access to copy the
+source code from the same place satisfies the requirement to
+distribute the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+  5. A program that contains no derivative of any portion of the
+Library, but is designed to work with the Library by being compiled or
+linked with it, is called a "work that uses the Library".  Such a
+work, in isolation, is not a derivative work of the Library, and
+therefore falls outside the scope of this License.
+
+  However, linking a "work that uses the Library" with the Library
+creates an executable that is a derivative of the Library (because it
+contains portions of the Library), rather than a "work that uses the
+library".  The executable is therefore covered by this License.
+Section 6 states terms for distribution of such executables.
+
+  When a "work that uses the Library" uses material from a header file
+that is part of the Library, the object code for the work may be a
+derivative work of the Library even though the source code is not.
+Whether this is true is especially significant if the work can be
+linked without the Library, or if the work is itself a library.  The
+threshold for this to be true is not precisely defined by law.
+
+  If such an object file uses only numerical parameters, data
+structure layouts and accessors, and small macros and small inline
+functions (ten lines or less in length), then the use of the object
+file is unrestricted, regardless of whether it is legally a derivative
+work.  (Executables containing this object code plus portions of the
+Library will still fall under Section 6.)
+
+  Otherwise, if the work is a derivative of the Library, you may
+distribute the object code for the work under the terms of Section 6.
+Any executables containing that work also fall under Section 6,
+whether or not they are linked directly with the Library itself.
+
+  6. As an exception to the Sections above, you may also combine or
+link a "work that uses the Library" with the Library to produce a
+work containing portions of the Library, and distribute that work
+under terms of your choice, provided that the terms permit
+modification of the work for the customer's own use and reverse
+engineering for debugging such modifications.
+
+  You must give prominent notice with each copy of the work that the
+Library is used in it and that the Library and its use are covered by
+this License.  You must supply a copy of this License.  If the work
+during execution displays copyright notices, you must include the
+copyright notice for the Library among them, as well as a reference
+directing the user to the copy of this License.  Also, you must do one
+of these things:
+
+    a) Accompany the work with the complete corresponding
+    machine-readable source code for the Library including whatever
+    changes were used in the work (which must be distributed under
+    Sections 1 and 2 above); and, if the work is an executable linked
+    with the Library, with the complete machine-readable "work that
+    uses the Library", as object code and/or source code, so that the
+    user can modify the Library and then relink to produce a modified
+    executable containing the modified Library.  (It is understood
+    that the user who changes the contents of definitions files in the
+    Library will not necessarily be able to recompile the application
+    to use the modified definitions.)
+
+    b) Use a suitable shared library mechanism for linking with the
+    Library.  A suitable mechanism is one that (1) uses at run time a
+    copy of the library already present on the user's computer system,
+    rather than copying library functions into the executable, and (2)
+    will operate properly with a modified version of the library, if
+    the user installs one, as long as the modified version is
+    interface-compatible with the version that the work was made with.
+
+    c) Accompany the work with a written offer, valid for at
+    least three years, to give the same user the materials
+    specified in Subsection 6a, above, for a charge no more
+    than the cost of performing this distribution.
+
+    d) If distribution of the work is made by offering access to copy
+    from a designated place, offer equivalent access to copy the above
+    specified materials from the same place.
+
+    e) Verify that the user has already received a copy of these
+    materials or that you have already sent this user a copy.
+
+  For an executable, the required form of the "work that uses the
+Library" must include any data and utility programs needed for
+reproducing the executable from it.  However, as a special exception,
+the materials to be distributed need not include anything that is
+normally distributed (in either source or binary form) with the major
+components (compiler, kernel, and so on) of the operating system on
+which the executable runs, unless that component itself accompanies
+the executable.
+
+  It may happen that this requirement contradicts the license
+restrictions of other proprietary libraries that do not normally
+accompany the operating system.  Such a contradiction means you cannot
+use both them and the Library together in an executable that you
+distribute.
+
+  7. You may place library facilities that are a work based on the
+Library side-by-side in a single library together with other library
+facilities not covered by this License, and distribute such a combined
+library, provided that the separate distribution of the work based on
+the Library and of the other library facilities is otherwise
+permitted, and provided that you do these two things:
+
+    a) Accompany the combined library with a copy of the same work
+    based on the Library, uncombined with any other library
+    facilities.  This must be distributed under the terms of the
+    Sections above.
+
+    b) Give prominent notice with the combined library of the fact
+    that part of it is a work based on the Library, and explaining
+    where to find the accompanying uncombined form of the same work.
+
+  8. You may not copy, modify, sublicense, link with, or distribute
+the Library except as expressly provided under this License.  Any
+attempt otherwise to copy, modify, sublicense, link with, or
+distribute the Library is void, and will automatically terminate your
+rights under this License.  However, parties who have received copies,
+or rights, from you under this License will not have their licenses
+terminated so long as such parties remain in full compliance.
+
+  9. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Library or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Library (or any work based on the
+Library), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Library or works based on it.
+
+  10. Each time you redistribute the Library (or any work based on the
+Library), the recipient automatically receives a license from the
+original licensor to copy, distribute, link with or modify the Library
+subject to these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties with
+this License.
+
+  11. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Library at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Library by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Library.
+
+If any portion of this section is held invalid or unenforceable under any
+particular circumstance, the balance of the section is intended to apply,
+and the section as a whole is intended to apply in other circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+  12. If the distribution and/or use of the Library is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Library under this License may add
+an explicit geographical distribution limitation excluding those countries,
+so that distribution is permitted only in or among countries not thus
+excluded.  In such case, this License incorporates the limitation as if
+written in the body of this License.
+
+  13. The Free Software Foundation may publish revised and/or new
+versions of the Lesser General Public License from time to time.
+Such new versions will be similar in spirit to the present version,
+but may differ in detail to address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Library
+specifies a version number of this License which applies to it and
+"any later version", you have the option of following the terms and
+conditions either of that version or of any later version published by
+the Free Software Foundation.  If the Library does not specify a
+license version number, you may choose any version ever published by
+the Free Software Foundation.
+
+  14. If you wish to incorporate parts of the Library into other free
+programs whose distribution conditions are incompatible with these,
+write to the author to ask for permission.  For software which is
+copyrighted by the Free Software Foundation, write to the Free
+Software Foundation; we sometimes make exceptions for this.  Our
+decision will be guided by the two goals of preserving the free status
+of all derivatives of our free software and of promoting the sharing
+and reuse of software generally.
+
+                           NO WARRANTY
+
+  15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
+WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
+EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
+OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
+KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
+LIBRARY IS WITH YOU.  SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
+THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+  16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
+WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
+AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
+FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
+CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
+LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
+RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
+FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
+SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+DAMAGES.
+
+                    END OF TERMS AND CONDITIONS
+
+           How to Apply These Terms to Your New Libraries
+
+  If you develop a new library, and you want it to be of the greatest
+possible use to the public, we recommend making it free software that
+everyone can redistribute and change.  You can do so by permitting
+redistribution under these terms (or, alternatively, under the terms of the
+ordinary General Public License).
+
+  To apply these terms, attach the following notices to the library.  It is
+safest to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least the
+"copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the library's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Lesser General Public
+    License as published by the Free Software Foundation; either
+    version 2.1 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Lesser General Public License for more details.
+
+    You should have received a copy of the GNU Lesser General Public
+    License along with this library; if not, write to the Free Software
+    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+
+Also add information on how to contact you by electronic and paper mail.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the library, if
+necessary.  Here is a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the
+  library `Frob' (a library for tweaking knobs) written by James Random Hacker.
+
+  <signature of Ty Coon>, 1 April 1990
+  Ty Coon, President of Vice
+
+That's all there is to it!
+
+
diff --git a/ChangeLog b/ChangeLog
new file mode 100644 (file)
index 0000000..e7066b8
--- /dev/null
+++ b/ChangeLog
@@ -0,0 +1,1230 @@
+ver 4.47:
+       Add support for RFKILL unblock handling.
+       Add support for serial proxy configurations.
+       Add support for caching service class updates.
+       Fix issues with updating SDP service records.
+       Fix usage of limited discoverable mode.
+       Remove deprecated methods and signals for AudioSource.
+
+ver 4.46:
+       Add support for A2DP sink role.
+       Fix clearing svc_cache before the adapter is up.
+       Fix various pointer after free usages.
+       Fix various memory leaks.
+
+ver 4.45:
+       Fix UDEV_DATADIR fallback if pkg-config fails.
+       Fix adapter cleanup and setup prototypes.
+       Fix double-free with out-of-range devices.
+       Fix inband ring setting to be per-headset.
+       Fix handling of Maemo CSD startup.
+
+ver 4.44:
+       Add some missing manual pages.
+       Fix missing number prefix when installing udev rules.
+       Fix program prefix used in Bluetooth udev rules.
+       Fix three-way calling indicator order.
+       Fix downgrade/upgrade of callheld indicator.
+       Fix +CIEV sending when indicator value changes.
+       Fix signal handling for Maemo telephony driver.
+       Fix parsing issues with messages from Maemo CSD.
+       Fix issue with duplicate active calls.
+
+ver 4.43:
+       Add support for udev based on-demand startup.
+       Fix verbose error reporting of CUPS backend.
+       Fix various string length issues.
+       Fix issues with Maemo telephony driver.
+       Fix another device setup and temporary flag issue.
+       Fix and update example agent implementation.
+
+ver 4.42:
+       Add TI WL1271 to Texas Instruments chip list.
+       Add special udev mode to bluetoothd.
+       Fix regression when there is no agent registered.
+       Fix error return when bonding socket hang up.
+       Fix SCO server socket for HFP handsfree role.
+       Fix shutdown on SCO socket before closing.
+       Fix shutdown on A2DP audio stream channel before closing.
+       Fix issue with asserting on AVDTP reference count bugs.
+       Fix authorization denied issue with certain headsets.
+       Fix AVRCP UNITINFO and SUBUNIT INFO responses.
+       Fix discovery cancel issues in case SDP discovery fails.
+
+ver 4.41:
+       Fix pairing even if the ACL gets dropped before successful SDP.
+       Fix regression which caused device to be removed after pairing.
+       Fix HSP record fetching when remote device doesn't support it.
+       Fix SDP discovery canceling when clearing hs->pending.
+       Fix headset never connecting on the first attempt.
+       Fix headset state tracking if bt_search_service() fails.
+       Fix maximum headset connection count check.
+       Fix AVDTP Discover timeout handling.
+       Fix also UI_SET_KEYBIT for the new pause and play key codes.
+
+ver 4.40:
+       Add telephony driver for oFono telephony stack.
+       Add support for Dell specific HID proxy switching.
+       Add support for running hid2hci from udev.
+       Add mapping for AVRCP Play and Pause to dedicated key codes.
+       Fix AVRCP keycodes to better match existing X keymap support.
+       Fix various quoting issues within telephony support.
+       Fix memory allocation issue when generating PDUs for SDP.
+       Fix race condition on device removal.
+       Fix non-cancelable issue with CreateDevice method.
+       Fix non-working CancelDiscovery method call.
+
+ver 4.39:
+       Add workaround for dealing with unknown inquiry complete.
+       Fix discovering when using software scheduler.
+       Fix wrong NoInputNoOutput IO capability string.
+       Fix race condition with agent during pairing.
+       Fix agent cancellation for security mode 3 acceptor failure.
+       Fix temporary flag removal when device creation fails.
+       Fix hciattach to use ppoll instead of poll.
+       Fix service class update when adapter is down.
+       Fix service classes race condition during startup.
+       Fix release of audio client before freeing the device.
+
+ver 4.38:
+       Add support for builtin plugins.
+       Add framework for adapter operations.
+       Add constants for Enhanced Retransmission modes.
+       Fix HCI socket leak in device_remove_bonding.
+       Fix various format string issues.
+       Fix crashes with various free functions.
+       Fix issues with Headset and A2DP drivers to load again.
+       Fix sending AVRCP button released passthrough messages
+       Fix bug which prevent input devices to work after restart.
+       Fix issue with interpretation of UUID-128 as channel.
+
+ver 4.37:
+       Add version value for Bluetooth 3.0 devices.
+       Add additional L2CAP extended feature mask bits.
+       Add support for loading plugins in priority order.
+       Add support for more detailed usage of disconnect watches.
+       Add support for AVRCP volume control.
+       Add saturated clipping of SBC decoder output to 16-bit.
+       Fix potentially infinite recursion of adapter_up.
+       Fix SCO handling in the case of an incoming call.
+       Fix input service to use confirm callback.
+       Fix cleanup of temporary device entries from storage.
+
+ver 4.36:
+       Add proper tracking of AVCTP connect attempts.
+       Add support to channel pattern in Serial interface.
+       Fix A2DP sink crash if removing device while connecting.
+       Fix error handling if HFP indicators aren't initialized.
+       Fix segfault while handling an incoming SCO connection.
+       Fix Serial.Disconnect to abort connection attempt.
+
+ver 4.35:
+       Add support for Handsfree profile headset role.
+       Add additional checks for open SEIDs from clients.
+       Fix device removal while audio IPC client is connected.
+       Fix device removal when an authorization request is pending.
+       Fix incoming AVDTP connect while authorization in progress.
+       Fix disconnection timers for audio support.
+       Fix various potential NULL pointer deferences.
+       Fix callheld indicator value for multiple calls.
+       Fix voice number type usage.
+       Fix GDBus watch handling.
+
+ver 4.34:
+       Add support for version checks of plugins.
+       Add support for class property on adapter interface.
+       Add support for second SDP attempt after connection reset.
+       Add support for more detailed audio states.
+       Add support for HFP+A2DP auto connection feature.
+       Add support for new and improved audio IPC.
+       Add program for testing audio IPC interface.
+       Fix various AVDTP qualification related issues.
+       Fix broken SDP AttributeIdList parsing.
+       Fix invalid memory access of SDP URL handling.
+       Fix local class of device race conditions.
+       Fix issue with periodic inquiry on startup.
+       Fix missing temporary devices in some situations.
+       Fix SBC alignment issue for encoding with four subbands.
+
+ver 4.33:
+       Add Paired property to the DeviceFound signals.
+       Add support for Headset profile 1.2 version.
+       Fix broken network configuration when IPv6 is disabled.
+       Fix network regression that caused disconnection.
+       Fix SDP truncation of strings with NULL values.
+       Fix service discovery handling of CUPS helper.
+
+ver 4.32:
+       Fix broken SDP record handling.
+       Fix SDP data buffer parsing.
+       Fix more SDP memory leaks.
+       Fix read scan enable calls.
+       Fix A2DP stream handling.
+
+ver 4.31:
+       Add support for new BtIO helper library.
+       Fix AVDTP session close issue.
+       Fix SDP memory leaks.
+       Fix various uninitialized memory issues.
+       Fix duplicate signal emissions.
+       Fix property changes request handling.
+       Fix class of device storage handling.
+
+ver 4.30:
+       Add CID field to L2CAP socket address structure.
+       Fix reset of authentication requirements after bonding.
+       Fix storing of link keys when using dedicated bonding.
+       Fix storing of pre-Bluetooth 2.1 link keys.
+       Fix resetting trust settings on every reboot.
+       Fix handling of local name changes.
+       Fix memory leaks in hciconfig and hcitool
+
+ver 4.29:
+       Use AVRCP version 1.0 for now.
+       Decrease AVDTP idle timeout to one second.
+       Delay AVRCP connection when remote device connects A2DP.
+       Add workaround for AVDTP stream setup with broken headsets.
+       Add missing three-way calling feature bit for Handsfree.
+       Fix handsfree callheld indicator updating.
+       Fix parsing of all AT commands within the buffer.
+       Fix authentication replies when disconnected.
+       Fix handling of debug combination keys.
+       Fix handling of changed combination keys.
+       Fix handling of link keys when using no bonding.
+       Fix handling of invalid/unknown authentication requirements.
+       Fix closing of L2CAP raw socket used for dedicated bonding.
+
+ver 4.28:
+       Add AVDTP signal fragmentation support.
+       Add more SBC performance optimizations.
+       Add more SBC audio quality improvements.
+       Use native byte order for audio plugins.
+       Set the adapter alias only after checking the EIR data.
+       Fix auto-disconnect issue with explicit A2DP connections.
+       Fix invalid memory access of ALSA plugin.
+       Fix compilation with -Wsign-compare.
+
+ver 4.27:
+       Add more SBC optimization (MMX and ARM NEON).
+       Add BT_SECURITY and BT_DEFER_SETUP definitions.
+       Add support for deferred connection setup.
+       Add support for fragmentation of data packets.
+       Add option to trigger dedicated bonding.
+       Follow MITM requirements from remote device.
+       Require MITM for dedicated bonding if capabilities allow it.
+       Fix IO capabilities for non-pairing and pairing cases.
+       Fix no-bonding connections in non-bondable mode.
+       Fix new pairing detection with SSP.
+       Fix bonding with pre-2.1 devices and newer kernels.
+       Fix LIAC setting while toggling Pairable property.
+       Fix device creation for incoming security mode 3 connects.
+       Fix crash within A2DP with bogus pointer.
+       Fix issue with sdp_copy_record() function.
+       Fix crash with extract_des() if sdp_uuid_extract() fails.
+
+ver 4.26:
+       Use of constant shift in SBC quantization code.
+       Add possibility to analyze 4 blocks at once in encoder.
+       Fix correct handling of frame sizes in the encoder.
+       Fix for big endian problems in SBC codec.
+       Fix audio client socket to always be non-blocking.
+       Update telephony support for Maemo.
+
+ver 4.25:
+       Fix receiving data over the audio control socket.
+       Fix subbands selection for joint-stereo in SBC encoder.
+       Add new SBC analysis filter function.
+
+ver 4.24:
+       Fix signal emissions when removing adapters.
+       Fix missing adapter signals on exit.
+       Add support for bringing adapters down on exit.
+       Add support for RememberPowered option.
+       Add support for verbose compiler warnings.
+       Add more options to SBC encoder.
+
+ver 4.23:
+       Update audio IPC for better codec handling.
+       Fix bitstream optimization for SBC encoder.
+       Fix length header values of IPC messages.
+       Fix multiple coding style violations.
+       Fix FindDevice to handle temporary devices.
+       Add configuration option for DeviceID.
+       Add support for InitiallyPowered option.
+       Add missing signals for manager properties.
+       Add telephony support for Maemo.
+
+ver 4.22:
+       Add deny statements to D-Bus access policy.
+       Add support for LegacyPairing property.
+       Add support for global properties.
+       Add more commands to telephony testing script.
+       Add sender checks for serial and network interfaces.
+       Remove deprecated methods and signals from input interface.
+       Remove deprecated methods and signals from network interface.
+       Remove OffMode option and always use device down.
+
+ver 4.21:
+       Fix adapter initialization logic.
+       Fix adapter setup and start security manager early.
+       Fix usage issue with first_init variable.
+
+ver 4.20:
+       Cleanup session handling.
+       Cleanup mode setting handling.
+       Fix issue with concurrent audio clients.
+       Fix issue with HFP/HSP suspending.
+       Fix AT result code syntax handling.
+       Add Handsfree support for AT+NREC.
+       Add PairableTimeout adapter property.
+
+ver 4.19:
+       Fix installation of manual pages for old daemons.
+       Fix D-Bus signal emmissions for CreateDevice.
+       Fix issues with UUID probing.
+       Fix +BSRF syntax issue.
+       Add Pairable adapter property.
+       Add sdp_copy_record() library function.
+
+ver 4.18:
+       Fix release before close issue with RFCOMM TTYs.
+       Fix Connected property on input interface.
+       Fix DeviceFound signals during initial name resolving.
+       Fix service discovery handling.
+       Fix duplicate UUID detection.
+       Fix SBC gain mismatch and decoding handling.
+       Add more options to SBC encoder and decoder.
+       Add special any adapter object for service interface.
+       Add variable prefix to adapter and device object paths.
+
+ver 4.17:
+       Fix SBC encoder not writing last frame.
+       Fix missing timer for A2DP suspend.
+       Add more supported devices to hid2hci utility.
+       Add additional functionality to Handsfree support.
+
+ver 4.16:
+       Fix wrong parameter usage of watch callbacks.
+       Fix parameters for callback upon path removal.
+       Fix unloading of adapter drivers.
+
+ver 4.15:
+       Fix various A2DP state machine issues.
+       Fix some issues with the Handsfree error reporting.
+       Fix format string warnings with recent GCC versions.
+       Remove dependency on GModule.
+
+ver 4.14:
+       Fix types of property arrays.
+       Fix potential crash with input devices.
+       Fix PS3 BD remote input event generation.
+       Allow dynamic adapter driver registration.
+       Update udev rules.
+
+ver 4.13:
+       Fix service discovery and UUID handling.
+       Fix bonding issues with Simple Pairing.
+       Fix file descriptor misuse of SCO connections.
+       Fix various memory leaks in the device handling.
+       Fix AVCTP disconnect handling.
+       Fix GStreamer modes for MP3 encoding.
+       Add operator selection to Handsfree support.
+
+ver 4.12:
+       Fix crash with missing icon value.
+       Fix error checks of HAL plugin.
+       Fix SCO server socket cleanup on exit.
+       Fix memory leaks from DBusPendingCall.
+       Fix handling of pending authorization requests.
+       Fix missing protocol UUIDs in record pattern.
+
+ver 4.11:
+       Change SCO server socket into a generic one.
+       Add test script for dummy telephony plugin.
+       Fix uninitialized reply of multiple GetProperties methods.
+
+ver 4.10:
+       Fix memory leaks with HAL messages.
+       Add more advanced handsfree features.
+       Add properties to audio, input and network interfaces.
+       Stop device discovery timer on device removal.
+
+ver 4.9:
+       Fix signals for Powered and Discoverable properties.
+       Fix handling of Alias and Icon properties.
+       Fix duplicate entries for service UUIDs.
+
+ver 4.8:
+       Fix retrieving of formfactor value.
+       Fix retrieving of local and remote extended features.
+       Fix potential NULL pointer dereference during pairing.
+       Fix crash with browsing due to a remotely initated pairing.
+
+ver 4.7:
+       Fix pairing and service discovery logic.
+       Fix crashes during suspend and resume.
+       Fix race condition within devdown mode.
+       Add RequestSession and ReleaseSession methods.
+       Add Powered and Discoverable properties.
+       Add Devices property and deprecate ListDevices.
+       Add workaround for a broken carkit from Nokia.
+
+ver 4.6:
+       Fix Device ID record handling.
+       Fix service browsing and storage.
+       Fix authentication and encryption for input devices.
+       Fix adapter name initialization.
+
+ver 4.5:
+       Fix initialization issue with new adapters.
+       Send HID authentication request without blocking.
+       Hide the verbose SDP debug behind SDP_DEBUG.
+       Add extra UUIDs for service discovery.
+       Add SCO server socket listener.
+       Add authorization support to service plugin.
+
+ver 4.4:
+       Add temporary fix for the CUPS compile issue.
+       Add service-api.txt to distribution.
+       Mention the variable prefix of an object path
+
+ver 4.3:
+       Add dummy driver for telephony support.
+       Add support for discovery sessions.
+       Add service plugin for external services.
+       Various cleanups.
+
+ver 4.2:
+       Avoid memory copies in A2DP write routine.
+       Fix broken logic with Simple Pairing check and old kernels.
+       Allow non-bondable and outgoing SDP without agent.
+       Only remove the bonding for non-temporary devices.
+       Cleanup various unnecessary includes.
+       Make more unexported functions static.
+       Add basic infrastructure for gtk-doc support.
+
+ver 4.1:
+       Add 30 seconds timeout to BNEP connection setup phase.
+       Avoid memory copies in A2DP write routine for ALSA.
+       Make sure to include compat/sdp.h in the distribution.
+
+ver 4.0:
+       Initial public release.
+
+ver 3.36:
+       Add init routines for TI BRF chips.
+       Add extra attributes to the serial port record.
+       Add example record for headset audio gateway record.
+       Use Handsfree version 0x0105 for the gateway role.
+       Fix SDP record registration with specific record handles.
+       Fix BCSP sent/receive handling.
+       Fix various includes for cross-compilation.
+       Allow link mode settings for outgoing connections.
+       Allow bonding during periodic inquiry.
+
+ver 3.35:
+       Add two additional company identifiers.
+       Add UUID-128 support for service discovery.
+       Fix usage of friendly names for service discovery.
+       Fix authorization when experiemental is disabled.
+       Fix uninitialized variable in passkey request handling.
+       Enable output of timestamps for l2test and rctest.
+
+ver 3.34:
+       Replace various SDP functions with safe versions.
+       Add additional length validation for incoming SDP packets.
+       Use safe function versions for SDP client handling.
+       Fix issue with RemoveDevice during discovery procedure.
+       Fix collect for non-persistent service records.
+
+ver 3.33:
+       Add functions for reading and writing the link policy settings.
+       Add definition for authentication requirements.
+       Add support for handling Simple Pairing.
+       Add Simple Pairing support to Agent interface.
+       Add ReleaseMode method to Adapter interface.
+       Add DiscoverServices method to Device interface.
+       Remove obsolete code and cleanup the repository.
+       Move over to use the libgdbus API.
+       Enable PIE by default if supported.
+
+ver 3.32:
+       Add OCF constants for synchronous flow control enabling.
+       Add support for switching HID proxy devices from Dell.
+       Add more Bluetooth client/server helper functions.
+       Add support for input service idle timeout option.
+       Fix BNEP reconnection handling.
+       Fix return value for snd_pcm_hw_params() calls.
+       Use upper-case addresses for object paths.
+       Remove HAL support helpers.
+       Remove inotify support.
+       Remove service daemon activation handling.
+       Remove uneeded D-Bus API extension.
+
+ver 3.31:
+       Create device object for all pairing cases.
+       Convert authorization to internal function calls.
+       Add initial support for Headset Audio Gateway role.
+       Add generic Bluetooth helper functions for GLib.
+       Fix endiannes handling of connection handles.
+       Don't optimize when debug is enabled.
+
+ver 3.30:
+       Convert audio service into a plugin.
+       Convert input service into a plugin.
+       Convert serial service into a plugin.
+       Convert network service into a plugin.
+       Emit old device signals when a property is changed.
+       Fix missing DiscoverDevices and CancelDiscovery methods.
+       Add another company identifier.
+       Add basic support for Bluetooth sessions.
+       Add avinfo utility for AVDTP/A2DP classification.
+       Remove build option for deprecated sdpd binary.
+
+ver 3.29:
+       Introduce new D-Bus based API.
+       Add more SBC optimizations.
+       Add support for PS3 remote devices.
+       Fix alignment trap in SDP server.
+       Fix memory leak in sdp_get_uuidseq_attr function.
+
+ver 3.28:
+       Add support for MCAP UUIDs.
+       Add support for role switch for audio service.
+       Add disconnect timer for audio service.
+       Add disconnect detection to ALSA plugin.
+       Add more SBC optimizations.
+       Fix alignment issue of SDP server.
+       Remove support for SDP parsing via expat.
+
+ver 3.27:
+       Update uinput.h with extra key definitions.
+       Add support for input connect/disconnect callbacks.
+       Add ifdefs around some baud rate definitions.
+       Add another company identifier.
+       Add proper HFP service level connection handling.
+       Add basic headset automatic disconnect support.
+       Add support for new SBC API.
+       Fix SBC decoder noise at high bitpools.
+       Use 32-bit multipliers for further SBC optimization.
+       Check for RFCOMM connection state in SCO connect callback.
+       Make use of parameters selected in ALSA plugin.
+
+ver 3.26:
+       Fix compilation issues with UCHAR_MAX, USHRT_MAX and UINT_MAX.
+       Improve handling of different audio transports.
+       Enable services by default and keep old daemons disabled.
+
+ver 3.25:
+       Add limited support for Handsfree profile.
+       Add limited support for MPEG12/MP3 codec.
+       Add basic support for UNITINFO and SUBUNITINFO.
+       Add more SBC optimizations.
+       Fix external service (un)registration.
+       Allow GetInfo and GetAddress to fail.
+
+ver 3.24:
+       Add definitions for MDP.
+       Add TCP connection support for serial proxy.
+       Add fix for Logitech HID proxy switching.
+       Add missing macros, MIN, MAX, ABS and CLAMP.
+       Add more SBC encoder optimizations.
+       Add initial mechanism to handle headset commands.
+       Fix connecting to handsfree profile headsets.
+       Use proper function for checking signal name.
+
+ver 3.23:
+       Fix remote name request handling bug.
+       Fix key search function to honor the mmap area size.
+       Fix Avahi integration of network service.
+       Add new plugin communication for audio service.
+       Enable basic AVRCP support by default.
+       More optimizations to the SBC library.
+       Create common error definitions.
+
+ver 3.22:
+       Add missing include file from audio service.
+       Add SBC conformance test utility.
+       Add basic uinput support for AVRCP.
+       Fix L2CAP socket leak in audio service.
+       Fix buffer usage in GStreamer plugin.
+       Fix remote name request event handling.
+
+ver 3.21:
+       Add constant for Bluetooth socket options level.
+       Add initial AVRCP support.
+       Add A2DP sink support to GStreamer plugin.
+       Fix interoperability with A2DP suspend.
+       Fix sign error in 8-subband encoder.
+       Fix handling of service classes length size.
+       Store Extended Inquiry Response data information.
+       Publish device id information through EIR.
+       Support higher baud rates for Ericcson based chips.
+
+ver 3.20:
+       Fix GStreamer plugin file type detection.
+       Fix potential infinite loop in inotify support.
+       Fix D-Bus signatures for dict handling.
+       Fix issues with service activation.
+       Fix SDP failure handling of audio service.
+       Fix various memory leaks in input service.
+       Add secure device creation method to input service.
+       Add service information methods to serial service.
+       Add config file support to network service.
+       Add scripting capability to network service.
+       Add special on-mode handling.
+       Add optimization for SBC encoder.
+       Add tweaks for D-Bus 1.1.x libraries.
+       Add support for inquiry transmit power level.
+
+ver 3.19:
+       Limit range of bitpool announced while in ACP side.
+       Use poll instead of usleep to wait for worker thread.
+       Use default event mask from the specification.
+       Add L2CAP mode constants.
+       Add HID proxy support for Logitech diNovo Edge dongle.
+       Add refresh option to re-request device names.
+       Show correct connection link type.
+
+ver 3.18:
+       Don't allocate memory for the Bluetooth base UUID.
+       Implement proper locking for headsets.
+       Fix various A2DP SEP locking issues.
+       Fix and cleanup audio stream handling.
+       Fix stream starting if suspend request is pending.
+       Fix A2DP and AVDTP endianess problems.
+       Add network timeout and retransmission support.
+       Add more detailed decoding of EIR elements.
+
+ver 3.17:
+       Fix supported commands bit calculation.
+       Fix crashes in audio and network services.
+       Check PAN source and destination roles.
+       Only export the needed symbols for the plugins.
+
+ver 3.16:
+       Update company identifier list.
+       Add support for headsets with SCO audio over HCI.
+       Add support for auto-create through ALSA plugin.
+       Add support for ALSA plugin parameters.
+       Add GStreamer plugin with SBC decoder and encoder.
+       Fix network service NAP, GN and PANU servers.
+       Set EIR information from SDP database.
+
+ver 3.15:
+       Add A2DP support to the audio service.
+       Add proxy support to the serial service.
+       Extract main service class for later use.
+       Set service classes value from SDP database.
+
+ver 3.14:
+       Add missing signals for the adapter interface.
+       Add definitions and functions for Simple Pairing.
+       Add basic commands for Simple Pairing.
+       Add correct Simple Pairing and EIR interaction.
+       Add missing properties for remote information.
+       Add EPoX endian quirk to the input service.
+       Fix HID descriptor import and storage functions.
+       Fix handling of adapters in raw mode.
+       Fix remote device listing methods.
+
+ver 3.13:
+       Fix some issues with the headset support.
+       Fix concurrent pending connection attempts.
+       Fix usage of devname instead of netdev.
+       Add identifier for Nokia SyncML records.
+       Add command for reading the CSR chip revision.
+       Add generic CSR radio test support.
+       Update HCI command table.
+
+ver 3.12:
+       Add missing HCI command text descriptions
+       Add missing HCI commands structures.
+       Add missing HCI event structures.
+       Add common bachk() function.
+       Add support for limited discovery mode.
+       Add support for setting of event mask.
+       Add GetRemoteServiceIdentifiers method.
+       Add skeleton for local D-Bus server.
+       Add headset gain control methods.
+       Fix various headset implementation issues.
+       Fix various serial port service issues.
+       Fix various input service issues.
+       Let CUPS plugin discover printers in range.
+       Improve the BCM2035 UART init routine.
+       Ignore connection events for non-ACL links.
+
+ver 3.11:
+       Update API documentation.
+       Minimize SDP root records and browse groups.
+       Use same decoder for text and URL strings.
+       Fix URL data size handling.
+       Fix SDP pattern extraction for XML.
+       Fix network connection persistent state.
+       Add network connection helper methods.
+       Add initial version of serial port support.
+       Add class of device tracking.
+
+ver 3.10.1:
+       Add option to disable installation of manual pages.
+       Fix input service encryption setup.
+       Fix serial service methods.
+       Fix network service connection handling.
+       Provide a simple init script.
+
+ver 3.10:
+       Add initial version of network service.
+       Add initial version of serial service.
+       Add initial version of input service.
+       Add initial version of audio service.
+       Add authorization framework.
+       Add integer based SBC library.
+       Add version code for Bluetooth 2.1 specification.
+       Add ESCO_LINK connection type constant.
+       Export sdp_uuid32_to_uuid128() function.
+
+ver 3.9:
+       Add RemoteDeviceDisconnectRequested signal.
+       Add updated service framework.
+       Add embedded GLib library.
+       Add support for using system GLib library.
+       Create internal SDP server library.
+
+ver 3.8:
+       Sort discovered devices list based on their RSSI.
+       Send DiscoverableTimeoutChanged signal.
+       Fix local and remote name validity checking.
+       Add ListRemoteDevices and ListRecentRemoteDevices methods.
+       Add basic integration of confirmation concept.
+       Add support for service record description via XML.
+       Add support for external commands to the RFCOMM utility.
+       Add experimental service and authorization API.
+       Add functions for registering binary records.
+
+ver 3.7:
+       Fix class of device handling.
+       Fix error replies with pairing and security mode 3.
+       Fix disconnect method for RFCOMM connections.
+       Add match pattern for service searches.
+       Add support for prioritized watches.
+       Add additional PDU length checks.
+       Fix CSRC value for partial responses.
+
+ver 3.6.1:
+       Fix IO channel race conditions.
+       Fix pairing issues on big endian systems.
+       Fix pairing issues with page timeout errors.
+       Fix pairing state for security mode 3 requests.
+       Switch to user as default security manager mode.
+
+ver 3.6:
+       Update D-Bus based RFCOMM interface support.
+       Use L2CAP raw sockets for HCI connection creation.
+       Add periodic discovery support to the D-Bus interface.
+       Add initial support for device names via EIR.
+       Add proper UTF-8 validation of device names.
+       Add support for the J-Three keyboard.
+       Fix issues with the asynchronous API for SDP.
+
+ver 3.5:
+       Fix and cleanup watch functionality.
+       Add support for periodic inquiry mode.
+       Add support for asynchronous SDP requests.
+       Add more request owner tracking.
+       Add asynchronous API for SDP.
+       Document pageto and discovto options.
+
+ver 3.4:
+       Improve error reporting for failed HCI commands.
+       Improve handling of CancelBonding.
+       Fixed bonding reply message when disconnected.
+       Fix UUID128 string lookup handling.
+       Fix malloc() versus bt_malloc() usage.
+
+ver 3.3:
+       Don't change inquiry mode for Bluetooth 1.1 adapters.
+       Add udev rules for Bluetooth serial PCMCIA cards.
+       Add Cancel and Release methods for passkey agents.
+       Add GetRemoteClass method.
+       Convert to using ppoll() and pselect().
+       Initialize allocated memory to zero.
+       Remove bcm203x firmware loader.
+       Remove kernel specific timeouts.
+       Add additional private data field for SDP sessions.
+       Add host controller to host flow control defines.
+       Add host number of completed packets defines.
+       Initialize various memory to zero before usage.
+
+ver 3.2:
+       Only check for the low-level D-Bus library.
+       Update possible device minor classes.
+       Fix timeout for pending reply.
+       Add more Inquiry with RSSI quirks.
+       Sleep only 100 msecs for device detection.
+       Don't send BondingCreated on link key renewal.
+       Allow storing of all UTF-8 remote device names.
+       Create storage filenames with a generic function.
+       Fix handling of SDP strings.
+       Add adapter type for SDIO cards.
+       Add features bit for link supervision timeout.
+
+ver 3.1:
+       Add missing placeholders for feature bits.
+       Fix handling of raw mode devices.
+       Fix busy loop in UUID extraction routine.
+       Remove inquiry mode setting.
+       Remove auth and encrypt settings.
+
+ver 3.0:
+       Implement the new BlueZ D-Bus API.
+       Fix broken behavior with EVT_CMD_STATUS.
+       Add features bit for pause encryption.
+       Add additional EIR error code.
+       Add more company identifiers.
+       Add another Phonebook Access identifier.
+       Update sniff subrating data structures.
+
+ver 2.25:
+       Use %jx instead of %llx for uint64_t and int64_t.
+       Allow null-terminated text strings.
+       Add UUID for N-Gage games.
+       Add UUID for Apple Macintosh Attributes.
+       Add Apple attributes and iSync records.
+       Add definitions for Apple Agent.
+       Add support for the Handsfree Audio Gateway service.
+       Add support for choosing a specific record handle.
+       Add support for dialup/telephone connections.
+       Add definitions for Apple Agent.
+       Add support for record handle on service registration.
+
+ver 2.24:
+       Fix display of SDP text and data strings.
+       Add support for device scan property.
+       Add support for additional access protocols.
+       Update the D-Bus policy configuration file.
+
+ver 2.23:
+       Update the new D-Bus interface.
+       Make dfutool ready for big endian architectures.
+       Add support for AVRCP specific service records.
+       Add support for writing complex BCCMD commands.
+       Add the new BCCMD interface utility.
+       Add MicroBCSP implementation from CSR.
+       Add constants and definitions for sniff subrating.
+       Add support for allocation of binary text elements.
+       Add HCI emulation tool.
+       Add fake HID support for old EPoX presenters.
+       Reject connections from unknown HID devices.
+       Fix service discovery deadlocks with Samsung D600 phones.
+
+ver 2.22:
+       Remove D-Bus 0.23 support.
+       Add initial version of the new D-Bus interface.
+       Add support for extended inquiry response commands.
+       Add support for the Logitech diNovo Media Desktop Laser.
+       Add compile time buffer checks (FORTIFY SOURCE).
+       Decode reserved LMP feature bits.
+       Fix errno overwrite problems.
+       Fix profile descriptor problem with Samsung phones.
+
+ver 2.21:
+       Move create_dirs() and create_file() into the textfile library.
+       Let textfile_put() also replace the last key value pair.
+       Fix memory leaks with textfile_get() usage.
+       Fix infinite loops and false positive matches.
+       Don't retrieve stored link keys for RAW devices.
+       Document the putkey and delkey commands.
+       Show supported commands also in clear text.
+       Support volatile changes of the BD_ADDR for CSR chips.
+       Add support for identification of supported commands.
+       Add missing OCF declarations for the security filter.
+       Add two new company identifiers.
+
+ver 2.20:
+       Add UUIDs for video distribution profile.
+       Add UUIDs for phonebook access profile.
+       Add attribute identifier for supported repositories.
+       Add definitions for extended inquiry response.
+       Add functions for extended inquiry response.
+       Add support for extended inquiry response.
+       Add support for HotSync service record.
+       Add support for ActiveSync service record.
+       Add ActiveSync networking support.
+       Fix D-Bus crashes with new API versions.
+
+ver 2.19:
+       Fix the GCC 4.0 warnings.
+       Fix the routing for dealing with raw devices.
+       Fix off by one memory allocation error.
+       Fix security problem with escape characters in device name.
+       Add per device service record functions.
+       Send D-Bus signals for inquiry results and remote name resolves.
+       Add support for device specific SDP records.
+
+ver 2.18:
+       Support D-Bus 0.23 and 0.33 API versions.
+       Support reading of complex BCCMD values.
+       Support minimum and maximum encryption key length.
+       Add support for reading and writing the inquiry scan type.
+       Add definitions for connection accept timeout and scan enable.
+       Add support for inquiry scan type.
+       Add tool for the CSR BCCMD interface.
+       Add first draft of the Audio/Video control utility.
+       Add disconnect timer support for the A2DP ALSA plugin.
+       Make SBC parameters configurable.
+       Replace non-printable characters in device names.
+       Remove hci_vhci.h header file.
+       Remove hci_uart.h header file.
+
+ver 2.17:
+       Set the storage directory through ${localstatedir}.
+       Add the textfile library for ASCII based file access.
+       Add support for return link keys event.
+       Add support for voice setting configuration.
+       Add support for page scan timeout configuration.
+       Add support for storing and deleting of stored link keys.
+       Add support for searching for services with UUID-128.
+       Add support for retrieving all possible service records.
+       Add support for a raw mode view of service records.
+       Add support for HID information caching in hidd.
+       Add support for authentication in pand and dund.
+       Add support for changing BD_ADDR of CSR chips.
+       Add pskey utility for changing CSR persistent storage values.
+       Add the firmware upgrade utility.
+       Add connection caching for the A2DP ALSA plugin.
+       Add functions for stored link keys.
+       Add definitions for PIN type and unit key.
+       Add SDP_WAIT_ON_CLOSE flag for sdp_connect().
+       Include stdio.h in bluetooth.h header file.
+       Include sys/socket.h in the header files.
+
+ver 2.16:
+       Store link keys in ASCII based file format.
+       Support device name caching.
+       Support zero length data sizes in l2test.
+       Change default l2ping data size to 44 bytes.
+       Hide the server record and the public browse group root.
+       Read BD_ADDR if not set and if it is a raw device.
+       Add SDP language attributes.
+       Add support for browsing the L2CAP group.
+       Add support for stored pin codes for outgoing connections.
+       Add support for local commands and extended features.
+       Add support for reading CSR panic and fault codes.
+       Add config option for setting the inquiry mode.
+       Add OUI decoding support.
+       Use unlimited inquiry responses as default.
+       Use cached device names for PIN request.
+       Use the clock offset when getting the remote names.
+       Add function for reading local supported commands.
+       Add function for reading local extended features.
+       Add function for reading remote extended features.
+       Add function for getting the remote name with a clock offset.
+       Add function for extracting the OUI from a BD_ADDR.
+       Add inquiry info structure with RSSI and page scan mode.
+       Fix buffer allocation for features to string conversion.
+       Support inquiry with unlimited number of responses.
+
+ver 2.15:
+       Enable the RFCOMM service level security.
+       Add deprecated functions for reading the name.
+       Add command for reading the clock offset.
+       Add command for reading the clock.
+       Add function for reading the clock.
+       Add function for reading the local Bluetooth address.
+       Add function for reading the local supported features.
+       Don't configure raw devices.
+       Don't set inquiry scan or page scan on raw devices.
+       Don't show extended information for raw devices.
+       Support L2CAP signal sizes bigger than 2048 bytes.
+       Cleanup of the socket handling code of the test programs.
+       Use better way for unaligned access.
+       Remove sdp_internal.h and its usage.
+
+ver 2.14:
+       Make use of additional connection information.
+       Use library function for reading the RSSI.
+       Use library function for reading the link quality.
+       Use library function for reading the transmit power level.
+       Use library functions for the link supervision timeout.
+       Add tool for changing the device address.
+       Add function for reading the RSSI.
+       Add function for reading the link quality.
+       Add function for reading the transmit power level.
+       Add functions for the link supervision timeout.
+       Remove deprecated functions.
+       Update AM_PATH_BLUEZ macro.
+
+ver 2.13:
+       Use file permission 0600 for the link key file.
+       Add support for HID attribute descriptions.
+       Add support for Device ID attributes.
+       Add Device ID and HID attribute definitions.
+       Update the UUID constants and its translations.
+       Update L2CAP socket option definitions.
+       Update connection information definitions.
+       Various whitespace cleanups.
+
+ver 2.12:
+       Inherit the device specific options from the default.
+       Use --device for selecting the source device.
+       Add --nosdp option for devices with resource limitation.
+       Add support and parameter option for secure mode.
+       Add a lot of build ids and hardware revisions.
+       Add service classes and profile ids for WAP.
+       Add simple AM_PATH_BLUEZ macro.
+       Update UUID translation tables.
+       Correct kernel interface for CMTP and HIDP support.
+
+ver 2.11:
+       Initial support for the kernel security manager.
+       Various cleanups to avoid inclusion of kernel headers.
+       Fix output when the CUPS backend is called without arguments.
+       Fix problems with a 64 bit userland.
+       Use Bluetooth library functions if available.
+       Use standard numbering scheme of SDP record handles.
+       Use bit zero for vendor packets in the filter type bitmask.
+       Add SIM Access types for service discovery.
+       Add more audio/video profile translations.
+       Add another company identifier.
+       Add the missing HCI error codes.
+       Add RFCOMM socket options.
+       Add definition for the SECURE link mode.
+       Add functions for reading and writing the inquiry mode.
+       Add functions for AFH related settings and information.
+       Add version identifier for the Bluetooth 2.0 specification.
+       Add a master option to the hidd.
+       Add support for changing the link key of a connection.
+       Add support for requesting encryption on keyboards.
+       Add support for revision information of Digianswer devices.
+       Add support for the Zoom, IBM and TDK PCMCIA cards.
+       Add checks for the OpenOBEX and the ALSA libraries.
+       Add experimental mRouter support.
+
+ver 2.10:
+       Use a define for the configuration directory.
+       Fix string initialization for flags translation.
+       Fix and extend the unaligned access macros.
+       Make compiling with debug information optional.
+       Don't override CFLAGS from configure.
+       Check for usb_get_busses() and usb_interrupt_read().
+       Add optional support for compiling with PIE.
+       Make installation of the init scripts optional.
+       Make compiling with debug information optional.
+       Don't override CFLAGS from configure.
+
+ver 2.9:
+       Retry SDP connect if busy in the CUPS backend.
+       Use packet type and allow role switch in hcitool.
+       Use the functions from the USB library for hid2hci.
+       Add Broadcom firmware loader.
+       Add EPoX endian quirk for buggy keyboards.
+       Add L2CAP info type and info result definitions.
+       Add value for L2CAP_CONF_RFC_MODE.
+       Change RSSI value to signed instead of unsigned.
+       Allow UUID32 values as protocol identifiers.
+       Update the autoconf/automake scripts.
+
+ver 2.8:
+       Use LIBS and LDADD instead of LDFLAGS.
+       Use HIDP subclass field for HID boot protocol.
+       Set olen before calling getsockopt() in pand.
+       Restore signals for dev-up script.
+       Add PID file support for pand.
+       Add size parameter to expand_name() in hcid.
+       Add support for audio source and audio sink SDP records.
+       Add support for HID virtual cable unplug.
+       Add support for AmbiCom BT2000C card.
+       Add defines and UUID's for audio/video profiles.
+       Add AVDTP protocol identifier.
+       Add HIDP subclass field.
+       Add PKGConfig support.
+       Fix the event code of inquiry with RSSI.
+       Remove dummy SDP library.
+
+ver 2.7:
+       Fix display of decoded LMP features.
+       Update company identifiers.
+       Add AFH related types.
+       Add first bits from EDR prototyping specification.
+       Add support for inquiry with RSSI.
+       Add HCRP related SDP functions.
+       Add HIDP header file.
+       Add support for getting the AFH channel map.
+       Add support for AFH mode.
+       Add support for inquiry mode.
+       Add Bluetooth backend for CUPS.
+       Add the hid2hci utility.
+       Add the hidd utility.
+       Add the pand utility.
+       Add the dund utility.
+       More endian bug fixes.
+       Give udev some time to create the RFCOMM device nodes.
+       Release the TTY if no device node is found.
+       New startup script for the Bluetooth subsystem.
+       Update to the autoconf stuff.
+
+ver 2.6:
+       Change default prefix to /usr.
+       Add manpages for hcid and hcid.conf.
+       Add the sdpd server daemon.
+       Add the sdptool utility.
+       Add the ciptool utility.
+       Add new company identifiers.
+       Add BNEP and CMTP header files.
+       Add the SDP library.
+       Use R2 for default value of pscan_rep_mode.
+
+ver 2.5:
+       Add decoding of Bluetooth 1.2 features.
+       Add link manager version parameter for Bluetooth 1.2.
+       Add new company identifiers.
+       Add D-Bus support for PIN request.
+       Support for transmit power level.
+       Support for park, sniff and hold mode.
+       Support for role switch.
+       Support for reading the clock offset.
+       Support for requesting authentication.
+       Support for setting connection encryption.
+       Show revision information for Broadcom devices.
+       Replace unprintable characters in device name.
+       Use R1 for default value of pscan_rep_mode.
+       Fix some 64-bit problems.
+       Fix some endian problems.
+       Report an error on PIN helper failure.
+       Update bluepin script for GTK2.
+
+ver 2.4:
+       Increase number of inquiry responses.
+       Support for transmit power level.
+       Display all 8 bytes of the features.
+       Add support for reading and writing of IAC.
+       Correct decoding class of device.
+       Use Ericsson revision command for ST Microelectronics devices.
+       Display AVM firmware version with 'revision' command.
+       New code for CSR specific revision information.
+       Support for ST Microelectronics specific initialization.
+       Support for 3Com card version 3.0.
+       Support for TDK, IBM and Socket cards.
+       Support for initial baud rate.
+       Update man pages.
+       Fixes for some memory leaks.
+
+ver 2.3:
+       Added const qualifiers to appropriate function arguments.
+       Minor fixes.
+       CSR firmware version is now displayed by 'revision' command.
+       Voice command is working properly on big endian machines.
+       Added support for Texas Bluetooth modules.
+       Added support for high UART baud rates on Ericsson modules.
+       BCSP initialization fixes.
+       Support for role switch command (hcitool).
+       RFCOMM config file parser fixes.
+       Update man pages.
+       Removed GLib dependency.
+
+ver 2.2:
+       Updated RFCOMM header file.
+       Additional HCI command and event defines.
+       Support for voice settings (hciconfig).
+       Minor hcitool fixes.
+       Improved configure script.
+       Added Headset testing tool.
+       Updated man pages.
+       RPM package.
+
+ver 2.1.1:
+       Resurrect hci_remote_name.
+
+ver 2.1:
+       Added hci_{read, write}_class_of_dev().
+       Added hci_{read, write}_current_iac_lap().
+       Added hci_write_local_name().
+       Added RFCOMM header file.
+       Minor fixes.
+       Improved BCSP initialization (hciattach).
+       Support for displaying link quality (hcitool).
+       Support for changing link supervision timeout (hcitool).
+       New RFCOMM TTY configuration tool (rfcomm).
+       Minor fixes and updates.
+
+ver 2.0:
+       Additional company IDs.
+       BCSP initialization (hciattach).
+       Minor hciconfig fixes.
+
+ver 2.0-pr13:
+       Support for multiple pairing modes.
+       Link key database handling fixes.
+
+ver 2.0-pre12:
+       Removed max link key limit. Keys never expire.
+       Link key database is always updated. Reread PIN on SIGHUP (hcid).
+       Bluetooth script starts SDPd, if installed.
+       Other minor fixes.
+
+ver 2.0-pre11:
+       Improved link key management and more verbose logging (hcid).
+       Fixed scan command (hcitool).
+
+ver 2.0-pre10:
+       Fix hci_inquiry function to return errors and accept user buffers.
+       New functions hci_devba, hci_devid, hci_for_each_dev and hci_get_route.
+       Additional company IDs.
+       Makefile and other minor fixes.
+       Support for reading RSSI, remote name and changing
+       connection type (hcitool). 
+       Device initialization fixes (hcid).
+       Other minor fixes and improvements.
+       Build environment cleanup and fixes.
+
+ver 2.0-pre9:
+       Improved bluepin. Working X authentication.
+       Improved hcitool. New flexible cmd syntax, additional commands.
+       Human readable display of the device features.
+       LMP features to string translation support.
+       Additional HCI command and event defines.
+       Extended hci_filter API.
+
+ver 2.0-pre8:
+       Additional HCI ioctls and defines.
+       All strings and buffers are allocated dynamically.
+       ba2str, str2ba automatically swap bdaddress.
+       Additional hciconfig commands. Support for ACL and SCO MTU ioctls.
+       Support for Inventel and COM1 UART based devices.
+       Minor hcitool fixes.
+       Improved l2test. New L2CAP test modes.
+       Minor fixes and cleanup.
+
+ver 2.0-pre7:
+       Bluetooth libraries and header files is now a separate package.
+       New build environment uses automake and libtool.
+       Massive header files cleanup.
+       Bluetooth utilities is now a separate package.
+       New build environment uses automake.
+       Moved all config files and security data to /etc/bluetooth.
+       Various cleanups.
+
+ver 2.0-pre6:
+       API cleanup and additions.
+       Improved hcitool.
+       l2test minor output fixes.
+       hciattach opt to display list of supported devices.
+
+ver 2.0-pre4:
+       HCI filter enhancements.
+
+ver 2.0-pre3:
+       Cleanup.
+
+ver 2.0-pre2:
+       Additional HCI library functions.
+       Improved CSR baud rate initialization.
+       PCMCIA scripts fixes and enhancements.
+       Documentation update.
+
+ver 2.0-pre1:
+       New UART initialization utility.
+       Hot plugging support for UART based PCMCIA devices.
+       SCO testing utility.
+       New authentication utility (bluepin).
+       Minor fixes and improvements.
diff --git a/INSTALL b/INSTALL
new file mode 100644 (file)
index 0000000..56b077d
--- /dev/null
+++ b/INSTALL
@@ -0,0 +1,236 @@
+Installation Instructions
+*************************
+
+Copyright (C) 1994, 1995, 1996, 1999, 2000, 2001, 2002, 2004, 2005 Free
+Software Foundation, Inc.
+
+This file is free documentation; the Free Software Foundation gives
+unlimited permission to copy, distribute and modify it.
+
+Basic Installation
+==================
+
+These are generic installation instructions.
+
+   The `configure' shell script attempts to guess correct values for
+various system-dependent variables used during compilation.  It uses
+those values to create a `Makefile' in each directory of the package.
+It may also create one or more `.h' files containing system-dependent
+definitions.  Finally, it creates a shell script `config.status' that
+you can run in the future to recreate the current configuration, and a
+file `config.log' containing compiler output (useful mainly for
+debugging `configure').
+
+   It can also use an optional file (typically called `config.cache'
+and enabled with `--cache-file=config.cache' or simply `-C') that saves
+the results of its tests to speed up reconfiguring.  (Caching is
+disabled by default to prevent problems with accidental use of stale
+cache files.)
+
+   If you need to do unusual things to compile the package, please try
+to figure out how `configure' could check whether to do them, and mail
+diffs or instructions to the address given in the `README' so they can
+be considered for the next release.  If you are using the cache, and at
+some point `config.cache' contains results you don't want to keep, you
+may remove or edit it.
+
+   The file `configure.ac' (or `configure.in') is used to create
+`configure' by a program called `autoconf'.  You only need
+`configure.ac' if you want to change it or regenerate `configure' using
+a newer version of `autoconf'.
+
+The simplest way to compile this package is:
+
+  1. `cd' to the directory containing the package's source code and type
+     `./configure' to configure the package for your system.  If you're
+     using `csh' on an old version of System V, you might need to type
+     `sh ./configure' instead to prevent `csh' from trying to execute
+     `configure' itself.
+
+     Running `configure' takes awhile.  While running, it prints some
+     messages telling which features it is checking for.
+
+  2. Type `make' to compile the package.
+
+  3. Optionally, type `make check' to run any self-tests that come with
+     the package.
+
+  4. Type `make install' to install the programs and any data files and
+     documentation.
+
+  5. You can remove the program binaries and object files from the
+     source code directory by typing `make clean'.  To also remove the
+     files that `configure' created (so you can compile the package for
+     a different kind of computer), type `make distclean'.  There is
+     also a `make maintainer-clean' target, but that is intended mainly
+     for the package's developers.  If you use it, you may have to get
+     all sorts of other programs in order to regenerate files that came
+     with the distribution.
+
+Compilers and Options
+=====================
+
+Some systems require unusual options for compilation or linking that the
+`configure' script does not know about.  Run `./configure --help' for
+details on some of the pertinent environment variables.
+
+   You can give `configure' initial values for configuration parameters
+by setting variables in the command line or in the environment.  Here
+is an example:
+
+     ./configure CC=c89 CFLAGS=-O2 LIBS=-lposix
+
+   *Note Defining Variables::, for more details.
+
+Compiling For Multiple Architectures
+====================================
+
+You can compile the package for more than one kind of computer at the
+same time, by placing the object files for each architecture in their
+own directory.  To do this, you must use a version of `make' that
+supports the `VPATH' variable, such as GNU `make'.  `cd' to the
+directory where you want the object files and executables to go and run
+the `configure' script.  `configure' automatically checks for the
+source code in the directory that `configure' is in and in `..'.
+
+   If you have to use a `make' that does not support the `VPATH'
+variable, you have to compile the package for one architecture at a
+time in the source code directory.  After you have installed the
+package for one architecture, use `make distclean' before reconfiguring
+for another architecture.
+
+Installation Names
+==================
+
+By default, `make install' will install the package's files in
+`/usr/local/bin', `/usr/local/man', etc.  You can specify an
+installation prefix other than `/usr/local' by giving `configure' the
+option `--prefix=PREFIX'.
+
+   You can specify separate installation prefixes for
+architecture-specific files and architecture-independent files.  If you
+give `configure' the option `--exec-prefix=PREFIX', the package will
+use PREFIX as the prefix for installing programs and libraries.
+Documentation and other data files will still use the regular prefix.
+
+   In addition, if you use an unusual directory layout you can give
+options like `--bindir=DIR' to specify different values for particular
+kinds of files.  Run `configure --help' for a list of the directories
+you can set and what kinds of files go in them.
+
+   If the package supports it, you can cause programs to be installed
+with an extra prefix or suffix on their names by giving `configure' the
+option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'.
+
+Optional Features
+=================
+
+Some packages pay attention to `--enable-FEATURE' options to
+`configure', where FEATURE indicates an optional part of the package.
+They may also pay attention to `--with-PACKAGE' options, where PACKAGE
+is something like `gnu-as' or `x' (for the X Window System).  The
+`README' should mention any `--enable-' and `--with-' options that the
+package recognizes.
+
+   For packages that use the X Window System, `configure' can usually
+find the X include and library files automatically, but if it doesn't,
+you can use the `configure' options `--x-includes=DIR' and
+`--x-libraries=DIR' to specify their locations.
+
+Specifying the System Type
+==========================
+
+There may be some features `configure' cannot figure out automatically,
+but needs to determine by the type of machine the package will run on.
+Usually, assuming the package is built to be run on the _same_
+architectures, `configure' can figure that out, but if it prints a
+message saying it cannot guess the machine type, give it the
+`--build=TYPE' option.  TYPE can either be a short name for the system
+type, such as `sun4', or a canonical name which has the form:
+
+     CPU-COMPANY-SYSTEM
+
+where SYSTEM can have one of these forms:
+
+     OS KERNEL-OS
+
+   See the file `config.sub' for the possible values of each field.  If
+`config.sub' isn't included in this package, then this package doesn't
+need to know the machine type.
+
+   If you are _building_ compiler tools for cross-compiling, you should
+use the `--target=TYPE' option to select the type of system they will
+produce code for.
+
+   If you want to _use_ a cross compiler, that generates code for a
+platform different from the build platform, you should specify the
+"host" platform (i.e., that on which the generated programs will
+eventually be run) with `--host=TYPE'.
+
+Sharing Defaults
+================
+
+If you want to set default values for `configure' scripts to share, you
+can create a site shell script called `config.site' that gives default
+values for variables like `CC', `cache_file', and `prefix'.
+`configure' looks for `PREFIX/share/config.site' if it exists, then
+`PREFIX/etc/config.site' if it exists.  Or, you can set the
+`CONFIG_SITE' environment variable to the location of the site script.
+A warning: not all `configure' scripts look for a site script.
+
+Defining Variables
+==================
+
+Variables not defined in a site shell script can be set in the
+environment passed to `configure'.  However, some packages may run
+configure again during the build, and the customized values of these
+variables may be lost.  In order to avoid this problem, you should set
+them in the `configure' command line, using `VAR=value'.  For example:
+
+     ./configure CC=/usr/local2/bin/gcc
+
+causes the specified `gcc' to be used as the C compiler (unless it is
+overridden in the site shell script).  Here is a another example:
+
+     /bin/bash ./configure CONFIG_SHELL=/bin/bash
+
+Here the `CONFIG_SHELL=/bin/bash' operand causes subsequent
+configuration-related scripts to be executed by `/bin/bash'.
+
+`configure' Invocation
+======================
+
+`configure' recognizes the following options to control how it operates.
+
+`--help'
+`-h'
+     Print a summary of the options to `configure', and exit.
+
+`--version'
+`-V'
+     Print the version of Autoconf used to generate the `configure'
+     script, and exit.
+
+`--cache-file=FILE'
+     Enable the cache: use and save the results of the tests in FILE,
+     traditionally `config.cache'.  FILE defaults to `/dev/null' to
+     disable caching.
+
+`--config-cache'
+`-C'
+     Alias for `--cache-file=config.cache'.
+
+`--quiet'
+`--silent'
+`-q'
+     Do not print messages saying which checks are being made.  To
+     suppress all normal output, redirect it to `/dev/null' (any error
+     messages will still be shown).
+
+`--srcdir=DIR'
+     Look for the package's source code in directory DIR.  Usually
+     `configure' can determine that directory automatically.
+
+`configure' also accepts some other, not widely useful, options.  Run
+`configure --help' for more details.
+
diff --git a/Makefile.am b/Makefile.am
new file mode 100644 (file)
index 0000000..c4c6322
--- /dev/null
@@ -0,0 +1,20 @@
+
+SUBDIRS = include lib sbc gdbus common plugins src client\
+                       network serial input audio tools \
+                       rfcomm compat cups test scripts doc
+
+EXTRA_DIST = bluez.m4
+
+pkgconfigdir = $(libdir)/pkgconfig
+
+pkgconfig_DATA = bluez.pc
+
+DISTCHECK_CONFIGURE_FLAGS = --disable-gtk-doc \
+                               --disable-udevrules
+
+DISTCLEANFILES = $(pkgconfig_DATA)
+
+MAINTAINERCLEANFILES = Makefile.in \
+       aclocal.m4 configure config.h.in config.sub config.guess \
+       ltmain.sh depcomp compile missing install-sh mkinstalldirs ylwrap
+
diff --git a/NEWS b/NEWS
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/README b/README
new file mode 100644 (file)
index 0000000..3cc6547
--- /dev/null
+++ b/README
@@ -0,0 +1,38 @@
+BlueZ - Bluetooth protocol stack for Linux
+******************************************
+
+Copyright (C) 2000-2001  Qualcomm Incorporated
+Copyright (C) 2002-2003  Maxim Krasnyansky <maxk@qualcomm.com>
+Copyright (C) 2002-2009  Marcel Holtmann <marcel@holtmann.org>
+
+
+Compilation and installation
+============================
+
+In order to compile Bluetooth utilities you need following software packages:
+       - Linux Bluetooth protocol stack (BlueZ)
+       - GCC compiler
+       - D-Bus library
+       - GLib library
+       - USB library (optional)
+       - Lexical Analyzer (flex, lex)
+       - YACC (yacc, bison, byacc)
+
+To configure run:
+       ./configure --prefix=/usr --mandir=/usr/share/man \
+               --sysconfdir=/etc --localstatedir=/var --libexecdir=/lib
+
+Configure automatically searches for all required components and packages.
+
+To compile and install run:
+       make && make install
+
+
+Information
+===========
+
+Mailing lists:
+       linux-bluetooth@vger.kernel.org
+
+For additional information about the project visit BlueZ web site:
+       http://www.bluez.org
diff --git a/acinclude.m4 b/acinclude.m4
new file mode 100644 (file)
index 0000000..248fed3
--- /dev/null
@@ -0,0 +1,380 @@
+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], [
+       if (test "${CFLAGS}" = ""); then
+               CFLAGS="-Wall -O2"
+       fi
+       if (test "$USE_MAINTAINER_MODE" = "yes"); then
+               CFLAGS+=" -Werror -Wextra"
+               CFLAGS+=" -Wno-unused-parameter"
+               CFLAGS+=" -Wno-missing-field-initializers"
+               CFLAGS+=" -Wdeclaration-after-statement"
+               CFLAGS+=" -Wmissing-declarations"
+               CFLAGS+=" -Wredundant-decls"
+               CFLAGS+=" -Wcast-align"
+       fi
+])
+
+AC_DEFUN([GTK_DOC_CHECK], [
+       AC_ARG_WITH([html-dir],
+               AS_HELP_STRING([--with-html-dir=PATH], [path to installed docs]),,
+                                       [with_html_dir='${datadir}/gtk-doc/html'])
+       HTML_DIR="$with_html_dir"
+       AC_SUBST([HTML_DIR])
+
+       AC_ARG_ENABLE([gtk-doc],
+               AS_HELP_STRING([--enable-gtk-doc], [use gtk-doc to build documentation [[default=no]]]),,
+                                       [enable_gtk_doc=no])
+
+       if test x$enable_gtk_doc = xyes; then
+               ifelse([$1],[],
+                       [PKG_CHECK_EXISTS([gtk-doc],,
+                               AC_MSG_ERROR([gtk-doc not installed and --enable-gtk-doc requested]))],
+                       [PKG_CHECK_EXISTS([gtk-doc >= $1],,
+                               AC_MSG_ERROR([You need to have gtk-doc >= $1 installed to build gtk-doc]))])
+       fi
+
+       AC_MSG_CHECKING([whether to build gtk-doc documentation])
+       AC_MSG_RESULT($enable_gtk_doc)
+
+       AC_PATH_PROGS(GTKDOC_CHECK,gtkdoc-check,)
+
+       AM_CONDITIONAL([ENABLE_GTK_DOC], [test x$enable_gtk_doc = xyes])
+       AM_CONDITIONAL([GTK_DOC_USE_LIBTOOL], [test -n "$LIBTOOL"])
+])
+
+AC_DEFUN([AC_FUNC_PPOLL], [
+       AC_CHECK_FUNC(ppoll, dummy=yes, AC_DEFINE(NEED_PPOLL, 1,
+                       [Define to 1 if you need the ppoll() function.]))
+])
+
+AC_DEFUN([AC_INIT_BLUEZ], [
+       AC_PREFIX_DEFAULT(/usr/local)
+
+       if (test "${prefix}" = "NONE"); then
+               dnl no prefix and no sysconfdir, so default to /etc
+               if (test "$sysconfdir" = '${prefix}/etc'); then
+                       AC_SUBST([sysconfdir], ['/etc'])
+               fi
+
+               dnl no prefix and no localstatedir, so default to /var
+               if (test "$localstatedir" = '${prefix}/var'); then
+                       AC_SUBST([localstatedir], ['/var'])
+               fi
+
+               dnl no prefix and no libexecdir, so default to /lib
+               if (test "$libexecdir" = '${exec_prefix}/libexec'); then
+                       AC_SUBST([libexecdir], ['/lib'])
+               fi
+
+               dnl no prefix and no mandir, so use ${prefix}/share/man as default
+               if (test "$mandir" = '${prefix}/man'); then
+                       AC_SUBST([mandir], ['${prefix}/share/man'])
+               fi
+
+               prefix="${ac_default_prefix}"
+       fi
+
+       if (test "${libdir}" = '${exec_prefix}/lib'); then
+               libdir="${prefix}/lib"
+       fi
+
+       plugindir="${libdir}/bluetooth/plugins"
+
+       if (test "$sysconfdir" = '${prefix}/etc'); then
+               configdir="${prefix}/etc/bluetooth"
+       else
+               configdir="${sysconfdir}/bluetooth"
+       fi
+
+       if (test "$localstatedir" = '${prefix}/var'); then
+               storagedir="${prefix}/var/lib/bluetooth"
+       else
+               storagedir="${localstatedir}/lib/bluetooth"
+       fi
+
+       AC_DEFINE_UNQUOTED(CONFIGDIR, "${configdir}",
+                               [Directory for the configuration files])
+       AC_DEFINE_UNQUOTED(STORAGEDIR, "${storagedir}",
+                               [Directory for the storage files])
+
+       AC_SUBST(CONFIGDIR, "${configdir}")
+       AC_SUBST(STORAGEDIR, "${storagedir}")
+
+       UDEV_DATADIR="`$PKG_CONFIG --variable=udevdir udev`"
+       if (test -z "${UDEV_DATADIR}"); then
+               UDEV_DATADIR="${sysconfdir}/udev/rules.d"
+       else
+               UDEV_DATADIR="${UDEV_DATADIR}/rules.d"
+       fi
+       AC_SUBST(UDEV_DATADIR)
+])
+
+AC_DEFUN([AC_PATH_DBUS], [
+       PKG_CHECK_MODULES(DBUS, dbus-1 >= 1.0, dummy=yes,
+                               AC_MSG_ERROR(D-Bus library is required))
+       AC_CHECK_LIB(dbus-1, dbus_watch_get_unix_fd, dummy=yes,
+               AC_DEFINE(NEED_DBUS_WATCH_GET_UNIX_FD, 1,
+                       [Define to 1 if you need the dbus_watch_get_unix_fd() function.]))
+       AC_SUBST(DBUS_CFLAGS)
+       AC_SUBST(DBUS_LIBS)
+])
+
+AC_DEFUN([AC_PATH_GLIB], [
+       PKG_CHECK_MODULES(GLIB, glib-2.0 >= 2.14, dummy=yes,
+                               AC_MSG_ERROR(GLib library version 2.14 or later is required))
+       AC_SUBST(GLIB_CFLAGS)
+       AC_SUBST(GLIB_LIBS)
+])
+
+AC_DEFUN([AC_PATH_GSTREAMER], [
+       PKG_CHECK_MODULES(GSTREAMER, gstreamer-0.10 gstreamer-plugins-base-0.10, gstreamer_found=yes, gstreamer_found=no)
+       AC_SUBST(GSTREAMER_CFLAGS)
+       AC_SUBST(GSTREAMER_LIBS)
+       GSTREAMER_PLUGINSDIR=`$PKG_CONFIG --variable=pluginsdir gstreamer-0.10`
+       AC_SUBST(GSTREAMER_PLUGINSDIR)
+])
+
+AC_DEFUN([AC_PATH_PULSE], [
+       PKG_CHECK_MODULES(PULSE, libpulse, pulse_found=yes, pulse_found=no)
+       AC_SUBST(PULSE_CFLAGS)
+       AC_SUBST(PULSE_LIBS)
+])
+
+AC_DEFUN([AC_PATH_ALSA], [
+       PKG_CHECK_MODULES(ALSA, alsa, alsa_found=yes, alsa_found=no)
+       AC_CHECK_LIB(rt, clock_gettime, ALSA_LIBS="$ALSA_LIBS -lrt", alsa_found=no)
+       AC_SUBST(ALSA_CFLAGS)
+       AC_SUBST(ALSA_LIBS)
+])
+
+AC_DEFUN([AC_PATH_USB], [
+       PKG_CHECK_MODULES(USB, libusb, usb_found=yes, usb_found=no)
+       AC_SUBST(USB_CFLAGS)
+       AC_SUBST(USB_LIBS)
+       AC_CHECK_LIB(usb, usb_get_busses, dummy=yes,
+               AC_DEFINE(NEED_USB_GET_BUSSES, 1,
+                       [Define to 1 if you need the usb_get_busses() function.]))
+       AC_CHECK_LIB(usb, usb_interrupt_read, dummy=yes,
+               AC_DEFINE(NEED_USB_INTERRUPT_READ, 1,
+                       [Define to 1 if you need the usb_interrupt_read() function.]))
+])
+
+AC_DEFUN([AC_PATH_NETLINK], [
+       PKG_CHECK_MODULES(NETLINK, libnl-1, netlink_found=yes, netlink_found=no)
+       AC_SUBST(NETLINK_CFLAGS)
+       AC_SUBST(NETLINK_LIBS)
+])
+
+AC_DEFUN([AC_PATH_SNDFILE], [
+       PKG_CHECK_MODULES(SNDFILE, sndfile, sndfile_found=yes, sndfile_found=no)
+       AC_SUBST(SNDFILE_CFLAGS)
+       AC_SUBST(SNDFILE_LIBS)
+])
+
+AC_DEFUN([AC_ARG_BLUEZ], [
+       debug_enable=no
+       optimization_enable=yes
+       fortify_enable=yes
+       pie_enable=yes
+       sndfile_enable=${sndfile_found}
+       netlink_enable=no
+       hal_enable=${hal_found}
+       usb_enable=${usb_found}
+       alsa_enable=${alsa_found}
+       gstreamer_enable=${gstreamer_found}
+       audio_enable=yes
+       input_enable=yes
+       serial_enable=yes
+       network_enable=yes
+       service_enable=yes
+       tools_enable=yes
+       hidd_enable=no
+       pand_enable=no
+       dund_enable=no
+       cups_enable=no
+       test_enable=no
+       bccmd_enable=no
+       pcmcia_enable=no
+       hid2hci_enable=no
+       dfutool_enable=no
+       manpages_enable=yes
+       udevrules_enable=yes
+       configfiles_enable=yes
+       telephony_driver=dummy
+
+       AC_ARG_ENABLE(optimization, AC_HELP_STRING([--disable-optimization], [disable code optimization]), [
+               optimization_enable=${enableval}
+       ])
+
+       AC_ARG_ENABLE(fortify, AC_HELP_STRING([--disable-fortify], [disable compile time buffer checks]), [
+               fortify_enable=${enableval}
+       ])
+
+       AC_ARG_ENABLE(pie, AC_HELP_STRING([--disable-pie], [disable position independent executables flag]), [
+               pie_enable=${enableval}
+       ])
+
+       AC_ARG_ENABLE(network, AC_HELP_STRING([--disable-network], [disable network plugin]), [
+               network_enable=${enableval}
+       ])
+
+       AC_ARG_ENABLE(serial, AC_HELP_STRING([--disable-serial], [disable serial plugin]), [
+               serial_enable=${enableval}
+       ])
+
+       AC_ARG_ENABLE(input, AC_HELP_STRING([--disable-input], [disable input plugin]), [
+               input_enable=${enableval}
+       ])
+
+       AC_ARG_ENABLE(audio, AC_HELP_STRING([--disable-audio], [disable audio plugin]), [
+               audio_enable=${enableval}
+       ])
+
+       AC_ARG_ENABLE(service, AC_HELP_STRING([--disable-service], [disable service plugin]), [
+               service_enable=${enableval}
+       ])
+
+       AC_ARG_ENABLE(gstreamer, AC_HELP_STRING([--enable-gstreamer], [enable GStreamer support]), [
+               gstreamer_enable=${enableval}
+       ])
+
+       AC_ARG_ENABLE(alsa, AC_HELP_STRING([--enable-alsa], [enable ALSA support]), [
+               alsa_enable=${enableval}
+       ])
+
+       AC_ARG_ENABLE(usb, AC_HELP_STRING([--enable-usb], [enable USB support]), [
+               usb_enable=${enableval}
+       ])
+
+       AC_ARG_ENABLE(netlink, AC_HELP_STRING([--enable-netlink], [enable NETLINK support]), [
+               netlink_enable=${enableval}
+       ])
+
+       AC_ARG_ENABLE(tools, AC_HELP_STRING([--enable-tools], [install Bluetooth utilities]), [
+               tools_enable=${enableval}
+       ])
+
+       AC_ARG_ENABLE(bccmd, AC_HELP_STRING([--enable-bccmd], [install BCCMD interface utility]), [
+               bccmd_enable=${enableval}
+       ])
+
+       AC_ARG_ENABLE(pcmcia, AC_HELP_STRING([--enable-pcmcia], [install PCMCIA serial script]), [
+               pcmcia_enable=${enableval}
+       ])
+
+       AC_ARG_ENABLE(hid2hci, AC_HELP_STRING([--enable-hid2hci], [install HID mode switching utility]), [
+               hid2hci_enable=${enableval}
+       ])
+
+       AC_ARG_ENABLE(dfutool, AC_HELP_STRING([--enable-dfutool], [install DFU firmware upgrade utility]), [
+               dfutool_enable=${enableval}
+       ])
+
+       AC_ARG_ENABLE(hidd, AC_HELP_STRING([--enable-hidd], [install HID daemon]), [
+               hidd_enable=${enableval}
+       ])
+
+       AC_ARG_ENABLE(pand, AC_HELP_STRING([--enable-pand], [install PAN daemon]), [
+               pand_enable=${enableval}
+       ])
+
+       AC_ARG_ENABLE(dund, AC_HELP_STRING([--enable-dund], [install DUN daemon]), [
+               dund_enable=${enableval}
+       ])
+
+       AC_ARG_ENABLE(cups, AC_HELP_STRING([--enable-cups], [install CUPS backend support]), [
+               cups_enable=${enableval}
+       ])
+
+       AC_ARG_ENABLE(test, AC_HELP_STRING([--enable-test], [install test programs]), [
+               test_enable=${enableval}
+       ])
+
+       AC_ARG_ENABLE(manpages, AC_HELP_STRING([--enable-manpages], [install Bluetooth manual pages]), [
+               manpages_enable=${enableval}
+       ])
+
+       AC_ARG_ENABLE(udevrules, AC_HELP_STRING([--enable-udevrules], [install Bluetooth udev rules]), [
+               udevrules_enable=${enableval}
+       ])
+
+       AC_ARG_ENABLE(configfiles, AC_HELP_STRING([--enable-configfiles], [install Bluetooth configuration files]), [
+               configfiles_enable=${enableval}
+       ])
+
+       AC_ARG_ENABLE(debug, AC_HELP_STRING([--enable-debug], [enable compiling with debugging information]), [
+               debug_enable=${enableval}
+       ])
+
+       AC_ARG_WITH(telephony, AC_HELP_STRING([--with-telephony=DRIVER], [select telephony driver]), [
+               telephony_driver=${withval}
+       ])
+
+       AC_SUBST([TELEPHONY_DRIVER], [telephony-${telephony_driver}.c])
+
+       if (test "${fortify_enable}" = "yes"); then
+               CFLAGS="$CFLAGS -D_FORTIFY_SOURCE=2"
+       fi
+
+       if (test "${pie_enable}" = "yes" && test "${ac_cv_prog_cc_pie}" = "yes"); then
+               CFLAGS="$CFLAGS -fPIC"
+               LDFLAGS="$LDFLAGS -pie"
+       fi
+
+       if (test "${debug_enable}" = "yes" && test "${ac_cv_prog_cc_g}" = "yes"); then
+               CFLAGS="$CFLAGS -g"
+       fi
+
+       if (test "${optimization_enable}" = "no"); then
+               CFLAGS="$CFLAGS -O0"
+       fi
+
+       if (test "${usb_enable}" = "yes" && test "${usb_found}" = "yes"); then
+               AC_DEFINE(HAVE_LIBUSB, 1, [Define to 1 if you have USB library.])
+       fi
+
+       AC_SUBST([BLUEZ_CFLAGS], ['-I$(top_builddir)/include'])
+       AC_SUBST([BLUEZ_LIBS], ['$(top_builddir)/lib/libbluetooth.la'])
+
+       AC_SUBST([GDBUS_CFLAGS], ['-I$(top_srcdir)/gdbus'])
+       AC_SUBST([GDBUS_LIBS], ['$(top_builddir)/gdbus/libgdbus.la'])
+
+       AC_SUBST([SBC_CFLAGS], ['-I$(top_srcdir)/sbc'])
+       AC_SUBST([SBC_LIBS], ['$(top_builddir)/sbc/libsbc.la'])
+
+       AM_CONDITIONAL(SNDFILE, test "${sndfile_enable}" = "yes" && test "${sndfile_found}" = "yes")
+       AM_CONDITIONAL(NETLINK, test "${netlink_enable}" = "yes" && test "${netlink_found}" = "yes")
+       AM_CONDITIONAL(USB, test "${usb_enable}" = "yes" && test "${usb_found}" = "yes")
+       AM_CONDITIONAL(SBC, test "${alsa_enable}" = "yes" || test "${gstreamer_enable}" = "yes")
+       AM_CONDITIONAL(ALSA, test "${alsa_enable}" = "yes" && test "${alsa_found}" = "yes")
+       AM_CONDITIONAL(GSTREAMER, test "${gstreamer_enable}" = "yes" && test "${gstreamer_found}" = "yes")
+       AM_CONDITIONAL(AUDIOPLUGIN, test "${audio_enable}" = "yes")
+       AM_CONDITIONAL(INPUTPLUGIN, test "${input_enable}" = "yes")
+       AM_CONDITIONAL(SERIALPLUGIN, test "${serial_enable}" = "yes")
+       AM_CONDITIONAL(NETWORKPLUGIN, test "${network_enable}" = "yes")
+       AM_CONDITIONAL(SERVICEPLUGIN, test "${service_enable}" = "yes")
+       AM_CONDITIONAL(HIDD, test "${hidd_enable}" = "yes")
+       AM_CONDITIONAL(PAND, test "${pand_enable}" = "yes")
+       AM_CONDITIONAL(DUND, test "${dund_enable}" = "yes")
+       AM_CONDITIONAL(CUPS, test "${cups_enable}" = "yes")
+       AM_CONDITIONAL(TEST, test "${test_enable}" = "yes")
+       AM_CONDITIONAL(TOOLS, test "${tools_enable}" = "yes")
+       AM_CONDITIONAL(BCCMD, test "${bccmd_enable}" = "yes")
+       AM_CONDITIONAL(PCMCIA, test "${pcmcia_enable}" = "yes")
+       AM_CONDITIONAL(HID2HCI, test "${hid2hci_enable}" = "yes" && test "${usb_found}" = "yes")
+       AM_CONDITIONAL(DFUTOOL, test "${dfutool_enable}" = "yes" && test "${usb_found}" = "yes")
+       AM_CONDITIONAL(MANPAGES, test "${manpages_enable}" = "yes")
+       AM_CONDITIONAL(UDEVRULES, test "${udevrules_enable}" = "yes")
+       AM_CONDITIONAL(CONFIGFILES, test "${configfiles_enable}" = "yes")
+])
diff --git a/audio/Android.mk b/audio/Android.mk
new file mode 100755 (executable)
index 0000000..9537e33
--- /dev/null
@@ -0,0 +1,74 @@
+LOCAL_PATH:= $(call my-dir)
+
+# A2DP plugin
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES:= \
+       a2dp.c \
+       avdtp.c \
+       control.c \
+       device.c \
+       gateway.c \
+       headset.c \
+       ipc.c \
+       main.c \
+       manager.c \
+       module-bluetooth-sink.c \
+       sink.c \
+       source.c \
+       telephony-dummy.c \
+       unix.c
+
+LOCAL_CFLAGS:= \
+       -DVERSION=\"4.47\" \
+       -DSTORAGEDIR=\"/data/misc/bluetoothd\" \
+       -DCONFIGDIR=\"/etc/bluez\" \
+       -DANDROID \
+       -D__S_IFREG=0100000  # missing from bionic stat.h
+
+LOCAL_C_INCLUDES:= \
+       $(LOCAL_PATH)/../include \
+       $(LOCAL_PATH)/../common \
+       $(LOCAL_PATH)/../gdbus \
+       $(LOCAL_PATH)/../src \
+       $(call include-path-for, glib) \
+       $(call include-path-for, dbus)
+
+LOCAL_SHARED_LIBRARIES := \
+       libbluetooth \
+       libbluetoothd \
+       libdbus
+
+
+LOCAL_MODULE_PATH := $(TARGET_OUT_SHARED_LIBRARIES)/bluez-plugin
+LOCAL_UNSTRIPPED_PATH := $(TARGET_OUT_SHARED_LIBRARIES_UNSTRIPPED)/bluez-plugin
+LOCAL_MODULE := audio
+
+include $(BUILD_SHARED_LIBRARY)
+
+#
+# liba2dp
+# This is linked to Audioflinger so **LGPL only**
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES:= \
+       liba2dp.c \
+       ipc.c \
+       ../sbc/sbc.c.arm \
+       ../sbc/sbc_primitives.c \
+       ../sbc/sbc_primitives_neon.c
+
+# to improve SBC performance
+LOCAL_CFLAGS:= -funroll-loops
+
+LOCAL_C_INCLUDES:= \
+       $(LOCAL_PATH)/../sbc \
+
+LOCAL_SHARED_LIBRARIES := \
+       libcutils
+
+LOCAL_MODULE := liba2dp
+
+include $(BUILD_SHARED_LIBRARY)
diff --git a/audio/Makefile.am b/audio/Makefile.am
new file mode 100644 (file)
index 0000000..52ad212
--- /dev/null
@@ -0,0 +1,87 @@
+
+BUILT_SOURCES = telephony.c
+
+if AUDIOPLUGIN
+plugindir = $(libdir)/bluetooth/plugins
+
+plugin_LTLIBRARIES = audio.la
+
+audio_la_SOURCES = main.c \
+       ipc.h ipc.c unix.h unix.c manager.h manager.c telephony.h \
+       device.h device.c headset.h headset.c gateway.h gateway.c \
+       avdtp.h avdtp.c a2dp.h a2dp.c sink.h sink.c source.h source.c \
+       control.h control.c
+
+nodist_audio_la_SOURCES = $(BUILT_SOURCES)
+
+audio_la_LDFLAGS = -module -avoid-version -no-undefined
+
+LDADD = $(top_builddir)/common/libhelper.a \
+               @GDBUS_LIBS@ @GLIB_LIBS@ @DBUS_LIBS@ @BLUEZ_LIBS@
+
+if ALSA
+alsadir = $(libdir)/alsa-lib
+
+alsa_LTLIBRARIES = libasound_module_pcm_bluetooth.la libasound_module_ctl_bluetooth.la
+
+libasound_module_pcm_bluetooth_la_SOURCES = pcm_bluetooth.c rtp.h ipc.h ipc.c
+libasound_module_pcm_bluetooth_la_LDFLAGS = -module -avoid-version #-export-symbols-regex [_]*snd_pcm_.*
+libasound_module_pcm_bluetooth_la_LIBADD = @SBC_LIBS@ @BLUEZ_LIBS@ @ALSA_LIBS@
+libasound_module_pcm_bluetooth_la_CFLAGS = @ALSA_CFLAGS@ @BLUEZ_CFLAGS@ @SBC_CFLAGS@
+
+libasound_module_ctl_bluetooth_la_SOURCES = ctl_bluetooth.c rtp.h ipc.h ipc.c
+libasound_module_ctl_bluetooth_la_LDFLAGS = -module -avoid-version #-export-symbols-regex [_]*snd_ctl_.*
+libasound_module_ctl_bluetooth_la_LIBADD = @BLUEZ_LIBS@ @ALSA_LIBS@
+libasound_module_ctl_bluetooth_la_CFLAGS = @ALSA_CFLAGS@ @BLUEZ_CFLAGS@
+
+if CONFIGFILES
+alsaconfdir = $(sysconfdir)/alsa
+
+alsaconf_DATA = bluetooth.conf
+endif
+endif
+
+if GSTREAMER
+gstreamerdir = $(libdir)/gstreamer-0.10
+
+gstreamer_LTLIBRARIES = libgstbluetooth.la
+
+libgstbluetooth_la_SOURCES = gstbluetooth.c \
+                               gstsbcenc.h gstsbcenc.c \
+                               gstsbcdec.h gstsbcdec.c \
+                               gstsbcparse.h gstsbcparse.c \
+                               gstavdtpsink.h gstavdtpsink.c \
+                               gsta2dpsink.h gsta2dpsink.c \
+                               gstsbcutil.h gstsbcutil.c \
+                               gstrtpsbcpay.h gstrtpsbcpay.c \
+                               rtp.h ipc.h ipc.c
+libgstbluetooth_la_LDFLAGS = -module -avoid-version
+libgstbluetooth_la_LIBADD = @SBC_LIBS@ @BLUEZ_LIBS@ @GSTREAMER_LIBS@ \
+                                               -lgstaudio-0.10 -lgstrtp-0.10
+libgstbluetooth_la_CFLAGS = -fvisibility=hidden -fno-strict-aliasing \
+                        @GSTREAMER_CFLAGS@ @BLUEZ_CFLAGS@ @SBC_CFLAGS@
+endif
+endif
+
+noinst_LTLIBRARIES = libipc.la
+
+libipc_la_SOURCES = ipc.h ipc.c
+
+noinst_PROGRAMS = ipctest
+
+ipctest_LDADD= libipc.la @SBC_LIBS@ @GLIB_LIBS@
+
+AM_CFLAGS = -fvisibility=hidden @SBC_CFLAGS@ \
+               @BLUEZ_CFLAGS@ @DBUS_CFLAGS@ @GLIB_CFLAGS@ @GDBUS_CFLAGS@
+
+CLEANFILES = $(BUILT_SOURCES)
+
+INCLUDES = -I$(top_srcdir)/common -I$(top_srcdir)/src
+
+EXTRA_DIST = audio.conf telephony-dummy.c telephony-maemo.c telephony-ofono.c \
+               bluetooth.conf
+
+MAINTAINERCLEANFILES = Makefile.in
+
+telephony.c: @TELEPHONY_DRIVER@
+       @if [ ! -e $@ ] ; then $(LN_S) $< $@ ; fi
diff --git a/audio/a2dp.c b/audio/a2dp.c
new file mode 100644 (file)
index 0000000..45be5d4
--- /dev/null
@@ -0,0 +1,1626 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2006-2007  Nokia Corporation
+ *  Copyright (C) 2004-2009  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdlib.h>
+#include <errno.h>
+
+#include <dbus/dbus.h>
+#include <glib.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/sdp.h>
+#include <bluetooth/sdp_lib.h>
+
+#include "logging.h"
+#include "device.h"
+#include "manager.h"
+#include "avdtp.h"
+#include "sink.h"
+#include "source.h"
+#include "a2dp.h"
+#include "sdpd.h"
+
+/* The duration that streams without users are allowed to stay in
+ * STREAMING state. */
+#define SUSPEND_TIMEOUT 5
+#define RECONFIGURE_TIMEOUT 500
+
+#ifndef MIN
+# define MIN(x, y) ((x) < (y) ? (x) : (y))
+#endif
+
+#ifndef MAX
+# define MAX(x, y) ((x) > (y) ? (x) : (y))
+#endif
+
+struct a2dp_sep {
+       uint8_t type;
+       uint8_t codec;
+       struct avdtp_local_sep *sep;
+       struct avdtp *session;
+       struct avdtp_stream *stream;
+       guint suspend_timer;
+       gboolean locked;
+       gboolean suspending;
+       gboolean starting;
+};
+
+struct a2dp_setup_cb {
+       a2dp_config_cb_t config_cb;
+       a2dp_stream_cb_t resume_cb;
+       a2dp_stream_cb_t suspend_cb;
+       void *user_data;
+       unsigned int id;
+};
+
+struct a2dp_setup {
+       struct audio_device *dev;
+       struct avdtp *session;
+       struct a2dp_sep *sep;
+       struct avdtp_stream *stream;
+       struct avdtp_error *err;
+       GSList *client_caps;
+       gboolean reconfigure;
+       gboolean canceled;
+       gboolean start;
+       GSList *cb;
+       int ref;
+};
+
+static DBusConnection *connection = NULL;
+
+struct a2dp_server {
+       bdaddr_t src;
+       GSList *sinks;
+       GSList *sources;
+       uint32_t source_record_id;
+       uint32_t sink_record_id;
+};
+
+static GSList *servers = NULL;
+static GSList *setups = NULL;
+static unsigned int cb_id = 0;
+
+static struct a2dp_setup *setup_ref(struct a2dp_setup *setup)
+{
+       setup->ref++;
+
+       debug("setup_ref(%p): ref=%d", setup, setup->ref);
+
+       return setup;
+}
+
+static void setup_free(struct a2dp_setup *s)
+{
+       debug("setup_free(%p)", s);
+       setups = g_slist_remove(setups, s);
+       if (s->session)
+               avdtp_unref(s->session);
+       g_slist_foreach(s->cb, (GFunc) g_free, NULL);
+       g_slist_free(s->cb);
+       g_free(s);
+}
+
+static void setup_unref(struct a2dp_setup *setup)
+{
+       setup->ref--;
+
+       debug("setup_unref(%p): ref=%d", setup, setup->ref);
+
+       if (setup->ref <= 0)
+               setup_free(setup);
+}
+
+static struct audio_device *a2dp_get_dev(struct avdtp *session)
+{
+       bdaddr_t src, dst;
+
+       avdtp_get_peers(session, &src, &dst);
+
+       return manager_find_device(NULL, &src, &dst, NULL, FALSE);
+}
+
+static gboolean finalize_config(struct a2dp_setup *s)
+{
+       GSList *l;
+
+       setup_ref(s);
+       for (l = s->cb; l != NULL; l = l->next) {
+               struct a2dp_setup_cb *cb = l->data;
+               struct avdtp_stream *stream = s->err ? NULL : s->stream;
+
+               if (!cb->config_cb)
+                       continue;
+
+               cb->config_cb(s->session, s->sep, stream, s->err,
+                                                       cb->user_data);
+               cb->config_cb = NULL;
+               setup_unref(s);
+       }
+
+       setup_unref(s);
+       return FALSE;
+}
+
+static gboolean finalize_config_errno(struct a2dp_setup *s, int err)
+{
+       struct avdtp_error avdtp_err;
+
+       avdtp_error_init(&avdtp_err, AVDTP_ERROR_ERRNO, -err);
+       s->err = err ? &avdtp_err : NULL;
+
+       return finalize_config(s);
+}
+
+static gboolean finalize_resume(struct a2dp_setup *s)
+{
+       GSList *l;
+
+       setup_ref(s);
+       for (l = s->cb; l != NULL; l = l->next) {
+               struct a2dp_setup_cb *cb = l->data;
+
+               if (cb && cb->resume_cb) {
+                       cb->resume_cb(s->session, s->err, cb->user_data);
+                       cb->resume_cb = NULL;
+                       setup_unref(s);
+               }
+       }
+
+       setup_unref(s);
+       return FALSE;
+}
+
+static gboolean finalize_suspend(struct a2dp_setup *s)
+{
+       GSList *l;
+
+       setup_ref(s);
+       for (l = s->cb; l != NULL; l = l->next) {
+               struct a2dp_setup_cb *cb = l->data;
+
+               if (cb->suspend_cb) {
+                       cb->suspend_cb(s->session, s->err, cb->user_data);
+                       cb->suspend_cb = NULL;
+                       setup_unref(s);
+               }
+       }
+
+       setup_unref(s);
+       return FALSE;
+}
+
+static gboolean finalize_suspend_errno(struct a2dp_setup *s, int err)
+{
+       struct avdtp_error avdtp_err;
+
+       avdtp_error_init(&avdtp_err, AVDTP_ERROR_ERRNO, -err);
+       s->err = err ? &avdtp_err : NULL;
+
+       return finalize_suspend(s);
+}
+
+static struct a2dp_setup *find_setup_by_session(struct avdtp *session)
+{
+       GSList *l;
+
+       for (l = setups; l != NULL; l = l->next) {
+               struct a2dp_setup *setup = l->data;
+
+               if (setup->session == session)
+                       return setup;
+       }
+
+       return NULL;
+}
+
+static struct a2dp_setup *find_setup_by_dev(struct audio_device *dev)
+{
+       GSList *l;
+
+       for (l = setups; l != NULL; l = l->next) {
+               struct a2dp_setup *setup = l->data;
+
+               if (setup->dev == dev)
+                       return setup;
+       }
+
+       return NULL;
+}
+
+static void stream_state_changed(struct avdtp_stream *stream,
+                                       avdtp_state_t old_state,
+                                       avdtp_state_t new_state,
+                                       struct avdtp_error *err,
+                                       void *user_data)
+{
+       struct a2dp_sep *sep = user_data;
+
+       if (new_state != AVDTP_STATE_IDLE)
+               return;
+
+       if (sep->suspend_timer) {
+               g_source_remove(sep->suspend_timer);
+               sep->suspend_timer = 0;
+       }
+
+       if (sep->session) {
+               avdtp_unref(sep->session);
+               sep->session = NULL;
+       }
+
+       sep->stream = NULL;
+
+}
+
+static gboolean sbc_setconf_ind(struct avdtp *session,
+                               struct avdtp_local_sep *sep,
+                               struct avdtp_stream *stream,
+                               GSList *caps, uint8_t *err,
+                               uint8_t *category, void *user_data)
+{
+       struct a2dp_sep *a2dp_sep = user_data;
+       struct audio_device *dev;
+       struct avdtp_service_capability *cap;
+       struct avdtp_media_codec_capability *codec_cap;
+       struct sbc_codec_cap *sbc_cap;
+
+       if (a2dp_sep->type == AVDTP_SEP_TYPE_SINK)
+               debug("Sink %p: Set_Configuration_Ind", sep);
+       else
+               debug("Source %p: Set_Configuration_Ind", sep);
+
+       dev = a2dp_get_dev(session);
+       if (!dev) {
+               *err = AVDTP_UNSUPPORTED_CONFIGURATION;
+               *category = 0x00;
+               return FALSE;
+       }
+
+       /* Check bipool range */
+       for (codec_cap = NULL; caps; caps = g_slist_next(caps)) {
+               cap = caps->data;
+               if (cap->category != AVDTP_MEDIA_CODEC)
+                       continue;
+
+               if (cap->length < sizeof(struct sbc_codec_cap))
+                       continue;
+
+               codec_cap = (void *) cap->data;
+
+               if (codec_cap->media_codec_type != A2DP_CODEC_SBC)
+                       continue;
+
+               sbc_cap = (void *) codec_cap;
+
+               if (sbc_cap->min_bitpool < MIN_BITPOOL ||
+                                       sbc_cap->max_bitpool > MAX_BITPOOL) {
+                       *err = AVDTP_UNSUPPORTED_CONFIGURATION;
+                       *category = AVDTP_MEDIA_CODEC;
+                       return FALSE;
+               }
+
+               break;
+       }
+
+       avdtp_stream_add_cb(session, stream, stream_state_changed, a2dp_sep);
+       a2dp_sep->stream = stream;
+
+       if (a2dp_sep->type == AVDTP_SEP_TYPE_SOURCE)
+               sink_new_stream(dev, session, stream);
+
+       return TRUE;
+}
+
+static gboolean sbc_getcap_ind(struct avdtp *session, struct avdtp_local_sep *sep,
+                               GSList **caps, uint8_t *err, void *user_data)
+{
+       struct a2dp_sep *a2dp_sep = user_data;
+       struct avdtp_service_capability *media_transport, *media_codec;
+       struct sbc_codec_cap sbc_cap;
+
+       if (a2dp_sep->type == AVDTP_SEP_TYPE_SINK)
+               debug("Sink %p: Get_Capability_Ind", sep);
+       else
+               debug("Source %p: Get_Capability_Ind", sep);
+
+       *caps = NULL;
+
+       media_transport = avdtp_service_cap_new(AVDTP_MEDIA_TRANSPORT,
+                                               NULL, 0);
+
+       *caps = g_slist_append(*caps, media_transport);
+
+       memset(&sbc_cap, 0, sizeof(struct sbc_codec_cap));
+
+       sbc_cap.cap.media_type = AVDTP_MEDIA_TYPE_AUDIO;
+       sbc_cap.cap.media_codec_type = A2DP_CODEC_SBC;
+
+#ifdef ANDROID
+       sbc_cap.frequency = SBC_SAMPLING_FREQ_44100;
+#else
+       sbc_cap.frequency = ( SBC_SAMPLING_FREQ_48000 |
+                               SBC_SAMPLING_FREQ_44100 |
+                               SBC_SAMPLING_FREQ_32000 |
+                               SBC_SAMPLING_FREQ_16000 );
+#endif
+
+       sbc_cap.channel_mode = ( SBC_CHANNEL_MODE_JOINT_STEREO |
+                                       SBC_CHANNEL_MODE_STEREO |
+                                       SBC_CHANNEL_MODE_DUAL_CHANNEL |
+                                       SBC_CHANNEL_MODE_MONO );
+
+       sbc_cap.block_length = ( SBC_BLOCK_LENGTH_16 |
+                                       SBC_BLOCK_LENGTH_12 |
+                                       SBC_BLOCK_LENGTH_8 |
+                                       SBC_BLOCK_LENGTH_4 );
+
+       sbc_cap.subbands = ( SBC_SUBBANDS_8 | SBC_SUBBANDS_4 );
+
+       sbc_cap.allocation_method = ( SBC_ALLOCATION_LOUDNESS |
+                                       SBC_ALLOCATION_SNR );
+
+       sbc_cap.min_bitpool = MIN_BITPOOL;
+       sbc_cap.max_bitpool = MAX_BITPOOL;
+
+       media_codec = avdtp_service_cap_new(AVDTP_MEDIA_CODEC, &sbc_cap,
+                                               sizeof(sbc_cap));
+
+       *caps = g_slist_append(*caps, media_codec);
+
+       return TRUE;
+}
+
+static gboolean mpeg_setconf_ind(struct avdtp *session,
+                               struct avdtp_local_sep *sep,
+                               struct avdtp_stream *stream,
+                               GSList *caps, uint8_t *err,
+                               uint8_t *category, void *user_data)
+{
+       struct a2dp_sep *a2dp_sep = user_data;
+       struct audio_device *dev;
+
+       if (a2dp_sep->type == AVDTP_SEP_TYPE_SINK)
+               debug("Sink %p: Set_Configuration_Ind", sep);
+       else
+               debug("Source %p: Set_Configuration_Ind", sep);
+
+       dev = a2dp_get_dev(session);
+       if (!dev) {
+               *err = AVDTP_UNSUPPORTED_CONFIGURATION;
+               *category = 0x00;
+               return FALSE;
+       }
+
+       avdtp_stream_add_cb(session, stream, stream_state_changed, a2dp_sep);
+       a2dp_sep->stream = stream;
+
+       if (a2dp_sep->type == AVDTP_SEP_TYPE_SOURCE)
+               sink_new_stream(dev, session, stream);
+
+       return TRUE;
+}
+
+static gboolean mpeg_getcap_ind(struct avdtp *session,
+                               struct avdtp_local_sep *sep,
+                               GSList **caps, uint8_t *err, void *user_data)
+{
+       struct a2dp_sep *a2dp_sep = user_data;
+       struct avdtp_service_capability *media_transport, *media_codec;
+       struct mpeg_codec_cap mpeg_cap;
+
+       if (a2dp_sep->type == AVDTP_SEP_TYPE_SINK)
+               debug("Sink %p: Get_Capability_Ind", sep);
+       else
+               debug("Source %p: Get_Capability_Ind", sep);
+
+       *caps = NULL;
+
+       media_transport = avdtp_service_cap_new(AVDTP_MEDIA_TRANSPORT,
+                                               NULL, 0);
+
+       *caps = g_slist_append(*caps, media_transport);
+
+       memset(&mpeg_cap, 0, sizeof(struct mpeg_codec_cap));
+
+       mpeg_cap.cap.media_type = AVDTP_MEDIA_TYPE_AUDIO;
+       mpeg_cap.cap.media_codec_type = A2DP_CODEC_MPEG12;
+
+       mpeg_cap.frequency = ( MPEG_SAMPLING_FREQ_48000 |
+                               MPEG_SAMPLING_FREQ_44100 |
+                               MPEG_SAMPLING_FREQ_32000 |
+                               MPEG_SAMPLING_FREQ_24000 |
+                               MPEG_SAMPLING_FREQ_22050 |
+                               MPEG_SAMPLING_FREQ_16000 );
+
+       mpeg_cap.channel_mode = ( MPEG_CHANNEL_MODE_JOINT_STEREO |
+                                       MPEG_CHANNEL_MODE_STEREO |
+                                       MPEG_CHANNEL_MODE_DUAL_CHANNEL |
+                                       MPEG_CHANNEL_MODE_MONO );
+
+       mpeg_cap.layer = ( MPEG_LAYER_MP3 | MPEG_LAYER_MP2 | MPEG_LAYER_MP1 );
+
+       mpeg_cap.bitrate = 0xFFFF;
+
+       media_codec = avdtp_service_cap_new(AVDTP_MEDIA_CODEC, &mpeg_cap,
+                                               sizeof(mpeg_cap));
+
+       *caps = g_slist_append(*caps, media_codec);
+
+       return TRUE;
+}
+
+static void setconf_cfm(struct avdtp *session, struct avdtp_local_sep *sep,
+                               struct avdtp_stream *stream,
+                               struct avdtp_error *err, void *user_data)
+{
+       struct a2dp_sep *a2dp_sep = user_data;
+       struct a2dp_setup *setup;
+       struct audio_device *dev;
+       int ret;
+
+       if (a2dp_sep->type == AVDTP_SEP_TYPE_SINK)
+               debug("Sink %p: Set_Configuration_Cfm", sep);
+       else
+               debug("Source %p: Set_Configuration_Cfm", sep);
+
+       setup = find_setup_by_session(session);
+
+       if (err) {
+               if (setup) {
+                       setup->err = err;
+                       finalize_config(setup);
+               }
+               return;
+       }
+
+       avdtp_stream_add_cb(session, stream, stream_state_changed, a2dp_sep);
+       a2dp_sep->stream = stream;
+
+       if (!setup)
+               return;
+
+       dev = a2dp_get_dev(session);
+
+       /* Notify D-Bus interface of the new stream */
+       if (a2dp_sep->type == AVDTP_SEP_TYPE_SOURCE)
+               sink_new_stream(dev, session, setup->stream);
+       else
+               source_new_stream(dev, session, setup->stream);
+
+       ret = avdtp_open(session, stream);
+       if (ret < 0) {
+               error("Error on avdtp_open %s (%d)", strerror(-ret), -ret);
+               setup->stream = NULL;
+               finalize_config_errno(setup, ret);
+       }
+}
+
+static gboolean getconf_ind(struct avdtp *session, struct avdtp_local_sep *sep,
+                               uint8_t *err, void *user_data)
+{
+       struct a2dp_sep *a2dp_sep = user_data;
+
+       if (a2dp_sep->type == AVDTP_SEP_TYPE_SINK)
+               debug("Sink %p: Get_Configuration_Ind", sep);
+       else
+               debug("Source %p: Get_Configuration_Ind", sep);
+       return TRUE;
+}
+
+static void getconf_cfm(struct avdtp *session, struct avdtp_local_sep *sep,
+                       struct avdtp_stream *stream, struct avdtp_error *err,
+                       void *user_data)
+{
+       struct a2dp_sep *a2dp_sep = user_data;
+
+       if (a2dp_sep->type == AVDTP_SEP_TYPE_SINK)
+               debug("Sink %p: Set_Configuration_Cfm", sep);
+       else
+               debug("Source %p: Set_Configuration_Cfm", sep);
+}
+
+static gboolean open_ind(struct avdtp *session, struct avdtp_local_sep *sep,
+                               struct avdtp_stream *stream, uint8_t *err,
+                               void *user_data)
+{
+       struct a2dp_sep *a2dp_sep = user_data;
+
+       if (a2dp_sep->type == AVDTP_SEP_TYPE_SINK)
+               debug("Sink %p: Open_Ind", sep);
+       else
+               debug("Source %p: Open_Ind", sep);
+       return TRUE;
+}
+
+static void open_cfm(struct avdtp *session, struct avdtp_local_sep *sep,
+                       struct avdtp_stream *stream, struct avdtp_error *err,
+                       void *user_data)
+{
+       struct a2dp_sep *a2dp_sep = user_data;
+       struct a2dp_setup *setup;
+
+       if (a2dp_sep->type == AVDTP_SEP_TYPE_SINK)
+               debug("Sink %p: Open_Cfm", sep);
+       else
+               debug("Source %p: Open_Cfm", sep);
+
+       setup = find_setup_by_session(session);
+       if (!setup)
+               return;
+
+       if (setup->canceled) {
+               if (!err)
+                       avdtp_close(session, stream);
+               setup_unref(setup);
+               return;
+       }
+
+       if (setup->reconfigure)
+               setup->reconfigure = FALSE;
+
+       if (err) {
+               setup->stream = NULL;
+               setup->err = err;
+       }
+
+       finalize_config(setup);
+}
+
+static gboolean suspend_timeout(struct a2dp_sep *sep)
+{
+       if (avdtp_suspend(sep->session, sep->stream) == 0)
+               sep->suspending = TRUE;
+
+       sep->suspend_timer = 0;
+
+       avdtp_unref(sep->session);
+       sep->session = NULL;
+
+       return FALSE;
+}
+
+static gboolean start_ind(struct avdtp *session, struct avdtp_local_sep *sep,
+                               struct avdtp_stream *stream, uint8_t *err,
+                               void *user_data)
+{
+       struct a2dp_sep *a2dp_sep = user_data;
+       struct a2dp_setup *setup;
+
+       if (a2dp_sep->type == AVDTP_SEP_TYPE_SINK)
+               debug("Sink %p: Start_Ind", sep);
+       else
+               debug("Source %p: Start_Ind", sep);
+
+       setup = find_setup_by_session(session);
+       if (setup) {
+               if (setup->canceled)
+                       setup_unref(setup);
+               else
+                       finalize_resume(setup);
+       }
+
+       if (!a2dp_sep->locked) {
+               a2dp_sep->session = avdtp_ref(session);
+               a2dp_sep->suspend_timer = g_timeout_add_seconds(SUSPEND_TIMEOUT,
+                                               (GSourceFunc) suspend_timeout,
+                                               a2dp_sep);
+       }
+
+       return TRUE;
+}
+
+static void start_cfm(struct avdtp *session, struct avdtp_local_sep *sep,
+                       struct avdtp_stream *stream, struct avdtp_error *err,
+                       void *user_data)
+{
+       struct a2dp_sep *a2dp_sep = user_data;
+       struct a2dp_setup *setup;
+
+       if (a2dp_sep->type == AVDTP_SEP_TYPE_SINK)
+               debug("Sink %p: Start_Cfm", sep);
+       else
+               debug("Source %p: Start_Cfm", sep);
+
+       setup = find_setup_by_session(session);
+       if (!setup)
+               return;
+
+       if (setup->canceled) {
+               if (!err)
+                       avdtp_close(session, stream);
+               setup_unref(setup);
+               return;
+       }
+
+       if (err) {
+               setup->stream = NULL;
+               setup->err = err;
+       }
+
+       finalize_resume(setup);
+}
+
+static gboolean suspend_ind(struct avdtp *session, struct avdtp_local_sep *sep,
+                               struct avdtp_stream *stream, uint8_t *err,
+                               void *user_data)
+{
+       struct a2dp_sep *a2dp_sep = user_data;
+
+       if (a2dp_sep->type == AVDTP_SEP_TYPE_SINK)
+               debug("Sink %p: Suspend_Ind", sep);
+       else
+               debug("Source %p: Suspend_Ind", sep);
+
+       if (a2dp_sep->suspend_timer) {
+               g_source_remove(a2dp_sep->suspend_timer);
+               a2dp_sep->suspend_timer = 0;
+               avdtp_unref(a2dp_sep->session);
+               a2dp_sep->session = NULL;
+       }
+
+       return TRUE;
+}
+
+static void suspend_cfm(struct avdtp *session, struct avdtp_local_sep *sep,
+                       struct avdtp_stream *stream, struct avdtp_error *err,
+                       void *user_data)
+{
+       struct a2dp_sep *a2dp_sep = user_data;
+       struct a2dp_setup *setup;
+       gboolean start;
+
+       if (a2dp_sep->type == AVDTP_SEP_TYPE_SINK)
+               debug("Sink %p: Suspend_Cfm", sep);
+       else
+               debug("Source %p: Suspend_Cfm", sep);
+
+       a2dp_sep->suspending = FALSE;
+
+       setup = find_setup_by_session(session);
+       if (!setup)
+               return;
+
+       start = setup->start;
+       setup->start = FALSE;
+
+       if (err) {
+               setup->stream = NULL;
+               setup->err = err;
+               finalize_suspend(setup);
+       }
+       else
+               finalize_suspend_errno(setup, 0);
+
+       if (!start)
+               return;
+
+       if (err) {
+               setup->err = err;
+               finalize_suspend(setup);
+       } else if (avdtp_start(session, a2dp_sep->stream) < 0) {
+               struct avdtp_error start_err;
+               error("avdtp_start failed");
+               avdtp_error_init(&start_err, AVDTP_ERROR_ERRNO, EIO);
+               setup->err = err;
+               finalize_suspend(setup);
+       }
+}
+
+static gboolean close_ind(struct avdtp *session, struct avdtp_local_sep *sep,
+                               struct avdtp_stream *stream, uint8_t *err,
+                               void *user_data)
+{
+       struct a2dp_sep *a2dp_sep = user_data;
+
+       if (a2dp_sep->type == AVDTP_SEP_TYPE_SINK)
+               debug("Sink %p: Close_Ind", sep);
+       else
+               debug("Source %p: Close_Ind", sep);
+
+       return TRUE;
+}
+
+static gboolean a2dp_reconfigure(gpointer data)
+{
+       struct a2dp_setup *setup = data;
+       struct avdtp_local_sep *lsep;
+       struct avdtp_remote_sep *rsep;
+       struct avdtp_service_capability *cap;
+       struct avdtp_media_codec_capability *codec_cap = NULL;
+       GSList *l;
+       int posix_err;
+
+       for (l = setup->client_caps; l != NULL; l = l->next) {
+               cap = l->data;
+
+               if (cap->category != AVDTP_MEDIA_CODEC)
+                       continue;
+
+               codec_cap = (void *) cap->data;
+               break;
+       }
+
+       if (!codec_cap) {
+               error("Cannot find capabilities to reconfigure");
+               posix_err = -EINVAL;
+               goto failed;
+       }
+
+       posix_err = avdtp_get_seps(setup->session, AVDTP_SEP_TYPE_SINK,
+                                       codec_cap->media_type,
+                                       codec_cap->media_codec_type,
+                                       &lsep, &rsep);
+       if (posix_err < 0) {
+               error("No matching ACP and INT SEPs found");
+               goto failed;
+       }
+
+       posix_err = avdtp_set_configuration(setup->session, rsep, lsep,
+                                               setup->client_caps,
+                                               &setup->stream);
+       if (posix_err < 0) {
+               error("avdtp_set_configuration: %s", strerror(-posix_err));
+               goto failed;
+       }
+
+       return FALSE;
+
+failed:
+       finalize_config_errno(setup, posix_err);
+       return FALSE;
+}
+
+static void close_cfm(struct avdtp *session, struct avdtp_local_sep *sep,
+                       struct avdtp_stream *stream, struct avdtp_error *err,
+                       void *user_data)
+{
+       struct a2dp_sep *a2dp_sep = user_data;
+       struct a2dp_setup *setup;
+
+       if (a2dp_sep->type == AVDTP_SEP_TYPE_SINK)
+               debug("Sink %p: Close_Cfm", sep);
+       else
+               debug("Source %p: Close_Cfm", sep);
+
+       setup = find_setup_by_session(session);
+       if (!setup)
+               return;
+
+       if (setup->canceled) {
+               setup_unref(setup);
+               return;
+       }
+
+       if (err) {
+               setup->stream = NULL;
+               setup->err = err;
+               finalize_config(setup);
+               return;
+       }
+
+       if (setup->reconfigure)
+               g_timeout_add(RECONFIGURE_TIMEOUT, a2dp_reconfigure, setup);
+}
+
+static gboolean abort_ind(struct avdtp *session, struct avdtp_local_sep *sep,
+                               struct avdtp_stream *stream, uint8_t *err,
+                               void *user_data)
+{
+       struct a2dp_sep *a2dp_sep = user_data;
+
+       if (a2dp_sep->type == AVDTP_SEP_TYPE_SINK)
+               debug("Sink %p: Abort_Ind", sep);
+       else
+               debug("Source %p: Abort_Ind", sep);
+
+       a2dp_sep->stream = NULL;
+
+       return TRUE;
+}
+
+static void abort_cfm(struct avdtp *session, struct avdtp_local_sep *sep,
+                       struct avdtp_stream *stream, struct avdtp_error *err,
+                       void *user_data)
+{
+       struct a2dp_sep *a2dp_sep = user_data;
+       struct a2dp_setup *setup;
+
+       if (a2dp_sep->type == AVDTP_SEP_TYPE_SINK)
+               debug("Sink %p: Abort_Cfm", sep);
+       else
+               debug("Source %p: Abort_Cfm", sep);
+
+       setup = find_setup_by_session(session);
+       if (!setup)
+               return;
+
+       setup_unref(setup);
+}
+
+static gboolean reconf_ind(struct avdtp *session, struct avdtp_local_sep *sep,
+                               uint8_t *err, void *user_data)
+{
+       struct a2dp_sep *a2dp_sep = user_data;
+
+       if (a2dp_sep->type == AVDTP_SEP_TYPE_SINK)
+               debug("Sink %p: ReConfigure_Ind", sep);
+       else
+               debug("Source %p: ReConfigure_Ind", sep);
+       return TRUE;
+}
+
+static void reconf_cfm(struct avdtp *session, struct avdtp_local_sep *sep,
+                       struct avdtp_stream *stream, struct avdtp_error *err,
+                       void *user_data)
+{
+       struct a2dp_sep *a2dp_sep = user_data;
+       struct a2dp_setup *setup;
+
+       if (a2dp_sep->type == AVDTP_SEP_TYPE_SINK)
+               debug("Sink %p: ReConfigure_Cfm", sep);
+       else
+               debug("Source %p: ReConfigure_Cfm", sep);
+
+       setup = find_setup_by_session(session);
+       if (!setup)
+               return;
+
+       if (setup->canceled) {
+               if (!err)
+                       avdtp_close(session, stream);
+               setup_unref(setup);
+               return;
+       }
+
+       if (err) {
+               setup->stream = NULL;
+               setup->err = err;
+       }
+
+       finalize_config(setup);
+}
+
+static struct avdtp_sep_cfm cfm = {
+       .set_configuration      = setconf_cfm,
+       .get_configuration      = getconf_cfm,
+       .open                   = open_cfm,
+       .start                  = start_cfm,
+       .suspend                = suspend_cfm,
+       .close                  = close_cfm,
+       .abort                  = abort_cfm,
+       .reconfigure            = reconf_cfm
+};
+
+static struct avdtp_sep_ind sbc_ind = {
+       .get_capability         = sbc_getcap_ind,
+       .set_configuration      = sbc_setconf_ind,
+       .get_configuration      = getconf_ind,
+       .open                   = open_ind,
+       .start                  = start_ind,
+       .suspend                = suspend_ind,
+       .close                  = close_ind,
+       .abort                  = abort_ind,
+       .reconfigure            = reconf_ind
+};
+
+static struct avdtp_sep_ind mpeg_ind = {
+       .get_capability         = mpeg_getcap_ind,
+       .set_configuration      = mpeg_setconf_ind,
+       .get_configuration      = getconf_ind,
+       .open                   = open_ind,
+       .start                  = start_ind,
+       .suspend                = suspend_ind,
+       .close                  = close_ind,
+       .abort                  = abort_ind,
+       .reconfigure            = reconf_ind
+};
+
+static sdp_record_t *a2dp_record(uint8_t type)
+{
+       sdp_list_t *svclass_id, *pfseq, *apseq, *root;
+       uuid_t root_uuid, l2cap_uuid, avdtp_uuid, a2dp_uuid;
+       sdp_profile_desc_t profile[1];
+       sdp_list_t *aproto, *proto[2];
+       sdp_record_t *record;
+       sdp_data_t *psm, *version, *features;
+       uint16_t lp = AVDTP_UUID, ver = 0x0100, feat = 0x000F;
+
+       record = sdp_record_alloc();
+       if (!record)
+               return NULL;
+
+       sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
+       root = sdp_list_append(0, &root_uuid);
+       sdp_set_browse_groups(record, root);
+
+       if (type == AVDTP_SEP_TYPE_SOURCE)
+               sdp_uuid16_create(&a2dp_uuid, AUDIO_SOURCE_SVCLASS_ID);
+       else
+               sdp_uuid16_create(&a2dp_uuid, AUDIO_SINK_SVCLASS_ID);
+       svclass_id = sdp_list_append(0, &a2dp_uuid);
+       sdp_set_service_classes(record, svclass_id);
+
+       sdp_uuid16_create(&profile[0].uuid, ADVANCED_AUDIO_PROFILE_ID);
+       profile[0].version = 0x0100;
+       pfseq = sdp_list_append(0, &profile[0]);
+       sdp_set_profile_descs(record, pfseq);
+
+       sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID);
+       proto[0] = sdp_list_append(0, &l2cap_uuid);
+       psm = sdp_data_alloc(SDP_UINT16, &lp);
+       proto[0] = sdp_list_append(proto[0], psm);
+       apseq = sdp_list_append(0, proto[0]);
+
+       sdp_uuid16_create(&avdtp_uuid, AVDTP_UUID);
+       proto[1] = sdp_list_append(0, &avdtp_uuid);
+       version = sdp_data_alloc(SDP_UINT16, &ver);
+       proto[1] = sdp_list_append(proto[1], version);
+       apseq = sdp_list_append(apseq, proto[1]);
+
+       aproto = sdp_list_append(0, apseq);
+       sdp_set_access_protos(record, aproto);
+
+       features = sdp_data_alloc(SDP_UINT16, &feat);
+       sdp_attr_add(record, SDP_ATTR_SUPPORTED_FEATURES, features);
+
+       if (type == AVDTP_SEP_TYPE_SOURCE)
+               sdp_set_info_attr(record, "Audio Source", 0, 0);
+       else
+               sdp_set_info_attr(record, "Audio Sink", 0, 0);
+
+       free(psm);
+       free(version);
+       sdp_list_free(proto[0], 0);
+       sdp_list_free(proto[1], 0);
+       sdp_list_free(apseq, 0);
+       sdp_list_free(pfseq, 0);
+       sdp_list_free(aproto, 0);
+       sdp_list_free(root, 0);
+       sdp_list_free(svclass_id, 0);
+
+       return record;
+}
+
+static struct a2dp_sep *a2dp_add_sep(struct a2dp_server *server, uint8_t type,
+                                       uint8_t codec)
+{
+       struct a2dp_sep *sep;
+       GSList **l;
+       uint32_t *record_id;
+       sdp_record_t *record;
+       struct avdtp_sep_ind *ind;
+
+       sep = g_new0(struct a2dp_sep, 1);
+
+       ind = (codec == A2DP_CODEC_MPEG12) ? &mpeg_ind : &sbc_ind;
+       sep->sep = avdtp_register_sep(&server->src, type,
+                                       AVDTP_MEDIA_TYPE_AUDIO, codec, ind,
+                                       &cfm, sep);
+       if (sep->sep == NULL) {
+               g_free(sep);
+               return NULL;
+       }
+
+       sep->codec = codec;
+       sep->type = type;
+
+       if (type == AVDTP_SEP_TYPE_SOURCE) {
+               l = &server->sources;
+               record_id = &server->source_record_id;
+       } else {
+               l = &server->sinks;
+               record_id = &server->sink_record_id;
+       }
+
+       if (*record_id != 0)
+               goto add;
+
+       record = a2dp_record(type);
+       if (!record) {
+               error("Unable to allocate new service record");
+               avdtp_unregister_sep(sep->sep);
+               g_free(sep);
+               return NULL;
+       }
+
+       if (add_record_to_server(&server->src, record) < 0) {
+               error("Unable to register A2DP service record");\
+               sdp_record_free(record);
+               avdtp_unregister_sep(sep->sep);
+               g_free(sep);
+               return NULL;
+       }
+       *record_id = record->handle;
+
+add:
+       *l = g_slist_append(*l, sep);
+
+       return sep;
+}
+
+static struct a2dp_server *find_server(GSList *list, const bdaddr_t *src)
+{
+       GSList *l;
+
+       for (l = list; l; l = l->next) {
+               struct a2dp_server *server = l->data;
+
+               if (bacmp(&server->src, src) == 0)
+                       return server;
+       }
+
+       return NULL;
+}
+
+int a2dp_register(DBusConnection *conn, const bdaddr_t *src, GKeyFile *config)
+{
+       int sbc_srcs = 1, sbc_sinks = 1;
+       int mpeg12_srcs = 0, mpeg12_sinks = 0;
+       gboolean source = TRUE, sink = FALSE;
+       char *str;
+       GError *err = NULL;
+       int i;
+       struct a2dp_server *server;
+
+       if (!config)
+               goto proceed;
+
+       str = g_key_file_get_string(config, "General", "Enable", &err);
+
+       if (err) {
+               debug("audio.conf: %s", err->message);
+               g_clear_error(&err);
+       } else {
+               if (strstr(str, "Sink"))
+                       source = TRUE;
+               if (strstr(str, "Source"))
+                       sink = TRUE;
+               g_free(str);
+       }
+
+       str = g_key_file_get_string(config, "General", "Disable", &err);
+
+       if (err) {
+               debug("audio.conf: %s", err->message);
+               g_clear_error(&err);
+       } else {
+               if (strstr(str, "Sink"))
+                       source = FALSE;
+               if (strstr(str, "Source"))
+                       sink = FALSE;
+               g_free(str);
+       }
+
+       str = g_key_file_get_string(config, "A2DP", "SBCSources", &err);
+       if (err) {
+               debug("audio.conf: %s", err->message);
+               g_clear_error(&err);
+       } else {
+               sbc_srcs = atoi(str);
+               g_free(str);
+       }
+
+       str = g_key_file_get_string(config, "A2DP", "MPEG12Sources", &err);
+       if (err) {
+               debug("audio.conf: %s", err->message);
+               g_clear_error(&err);
+       } else {
+               mpeg12_srcs = atoi(str);
+               g_free(str);
+       }
+
+       str = g_key_file_get_string(config, "A2DP", "SBCSinks", &err);
+       if (err) {
+               debug("audio.conf: %s", err->message);
+               g_clear_error(&err);
+       } else {
+               sbc_sinks = atoi(str);
+               g_free(str);
+       }
+
+       str = g_key_file_get_string(config, "A2DP", "MPEG12Sinks", &err);
+       if (err) {
+               debug("audio.conf: %s", err->message);
+               g_clear_error(&err);
+       } else {
+               mpeg12_sinks = atoi(str);
+               g_free(str);
+       }
+
+proceed:
+       if (!connection)
+               connection = dbus_connection_ref(conn);
+
+       server = find_server(servers, src);
+       if (!server) {
+               int av_err;
+
+               server = g_new0(struct a2dp_server, 1);
+               if (!server)
+                       return -ENOMEM;
+
+               av_err = avdtp_init(src, config);
+               if (av_err < 0)
+                       return av_err;
+
+               bacpy(&server->src, src);
+               servers = g_slist_append(servers, server);
+       }
+
+       if (source) {
+               for (i = 0; i < sbc_srcs; i++)
+                       a2dp_add_sep(server, AVDTP_SEP_TYPE_SOURCE,
+                                       A2DP_CODEC_SBC);
+
+               for (i = 0; i < mpeg12_srcs; i++)
+                       a2dp_add_sep(server, AVDTP_SEP_TYPE_SOURCE,
+                                       A2DP_CODEC_MPEG12);
+       }
+
+       if (sink) {
+               for (i = 0; i < sbc_sinks; i++)
+                       a2dp_add_sep(server, AVDTP_SEP_TYPE_SINK,
+                                       A2DP_CODEC_SBC);
+
+               for (i = 0; i < mpeg12_sinks; i++)
+                       a2dp_add_sep(server, AVDTP_SEP_TYPE_SINK,
+                                       A2DP_CODEC_MPEG12);
+       }
+
+       return 0;
+}
+
+static void a2dp_unregister_sep(struct a2dp_sep *sep)
+{
+       avdtp_unregister_sep(sep->sep);
+       g_free(sep);
+}
+
+void a2dp_unregister(const bdaddr_t *src)
+{
+       struct a2dp_server *server;
+
+       server = find_server(servers, src);
+       if (!server)
+               return;
+
+       g_slist_foreach(server->sinks, (GFunc) a2dp_unregister_sep, NULL);
+       g_slist_free(server->sinks);
+
+       g_slist_foreach(server->sources, (GFunc) a2dp_unregister_sep, NULL);
+       g_slist_free(server->sources);
+
+       avdtp_exit(src);
+
+       if (server->source_record_id)
+               remove_record_from_server(server->source_record_id);
+
+       if (server->sink_record_id)
+               remove_record_from_server(server->sink_record_id);
+
+       servers = g_slist_remove(servers, server);
+       g_free(server);
+
+       if (servers)
+               return;
+
+       dbus_connection_unref(connection);
+       connection = NULL;
+}
+
+struct a2dp_sep *a2dp_get(struct avdtp *session,
+                               struct avdtp_remote_sep *rsep)
+{
+       GSList *l;
+       struct a2dp_server *server;
+       struct avdtp_service_capability *cap;
+       struct avdtp_media_codec_capability *codec_cap = NULL;
+       bdaddr_t src;
+
+       avdtp_get_peers(session, &src, NULL);
+       server = find_server(servers, &src);
+       if (!server)
+               return NULL;
+
+       cap = avdtp_get_codec(rsep);
+       codec_cap = (void *) cap->data;
+
+       if (avdtp_get_type(rsep) == AVDTP_SEP_TYPE_SINK)
+               l = server->sources;
+       else
+               l = server->sinks;
+
+       for (; l != NULL; l = l->next) {
+               struct a2dp_sep *sep = l->data;
+
+               if (sep->locked)
+                       continue;
+
+               if (sep->codec != codec_cap->media_codec_type)
+                       continue;
+
+               if (!sep->stream || avdtp_has_stream(session, sep->stream))
+                       return sep;
+       }
+
+       return NULL;
+}
+
+unsigned int a2dp_config(struct avdtp *session, struct a2dp_sep *sep,
+                               a2dp_config_cb_t cb, GSList *caps,
+                               void *user_data)
+{
+       struct a2dp_setup_cb *cb_data;
+       GSList *l;
+       struct a2dp_server *server;
+       struct a2dp_setup *setup;
+       struct a2dp_sep *tmp;
+       struct avdtp_local_sep *lsep;
+       struct avdtp_remote_sep *rsep;
+       struct avdtp_service_capability *cap;
+       struct avdtp_media_codec_capability *codec_cap = NULL;
+       int posix_err;
+       bdaddr_t src;
+       uint8_t remote_type;
+
+       avdtp_get_peers(session, &src, NULL);
+       server = find_server(servers, &src);
+       if (!server)
+               return 0;
+
+       for (l = caps; l != NULL; l = l->next) {
+               cap = l->data;
+
+               if (cap->category != AVDTP_MEDIA_CODEC)
+                       continue;
+
+               codec_cap = (void *) cap->data;
+               break;
+       }
+
+       if (!codec_cap)
+               return 0;
+
+       if (sep->codec != codec_cap->media_codec_type)
+               return 0;
+
+       debug("a2dp_config: selected SEP %p", sep->sep);
+
+       cb_data = g_new0(struct a2dp_setup_cb, 1);
+       cb_data->config_cb = cb;
+       cb_data->user_data = user_data;
+       cb_data->id = ++cb_id;
+
+       setup = find_setup_by_session(session);
+       if (!setup) {
+               setup = g_new0(struct a2dp_setup, 1);
+               setup->session = avdtp_ref(session);
+               setup->dev = a2dp_get_dev(session);
+               setups = g_slist_append(setups, setup);
+       }
+
+       setup_ref(setup);
+       setup->cb = g_slist_append(setup->cb, cb_data);
+       setup->sep = sep;
+       setup->stream = sep->stream;
+       setup->client_caps = caps;
+
+       switch (avdtp_sep_get_state(sep->sep)) {
+       case AVDTP_STATE_IDLE:
+               if (sep->type == AVDTP_SEP_TYPE_SOURCE) {
+                       l = server->sources;
+                       remote_type = AVDTP_SEP_TYPE_SINK;
+               } else {
+                       remote_type = AVDTP_SEP_TYPE_SOURCE;
+                       l = server->sinks;
+               }
+
+               for (; l != NULL; l = l->next) {
+                       tmp = l->data;
+                       if (avdtp_has_stream(session, tmp->stream))
+                               break;
+               }
+
+               if (l != NULL) {
+                       setup->reconfigure = TRUE;
+                       if (avdtp_close(session, tmp->stream) < 0) {
+                               error("avdtp_close failed");
+                               goto failed;
+                       }
+                       break;
+               }
+
+               if (avdtp_get_seps(session, remote_type,
+                               codec_cap->media_type,
+                               codec_cap->media_codec_type,
+                               &lsep, &rsep) < 0) {
+                       error("No matching ACP and INT SEPs found");
+                       goto failed;
+               }
+
+               posix_err = avdtp_set_configuration(session, rsep, lsep,
+                                                       caps, &setup->stream);
+               if (posix_err < 0) {
+                       error("avdtp_set_configuration: %s",
+                               strerror(-posix_err));
+                       goto failed;
+               }
+               break;
+       case AVDTP_STATE_OPEN:
+       case AVDTP_STATE_STREAMING:
+               if (avdtp_stream_has_capabilities(setup->stream, caps)) {
+                       debug("Configuration match: resuming");
+                       g_idle_add((GSourceFunc) finalize_config, setup);
+               } else if (!setup->reconfigure) {
+                       setup->reconfigure = TRUE;
+                       if (avdtp_close(session, sep->stream) < 0) {
+                               error("avdtp_close failed");
+                               goto failed;
+                       }
+               }
+               break;
+       default:
+               error("SEP in bad state for requesting a new stream");
+               goto failed;
+       }
+
+       return cb_data->id;
+
+failed:
+       setup_unref(setup);
+       cb_id--;
+       return 0;
+}
+
+unsigned int a2dp_resume(struct avdtp *session, struct a2dp_sep *sep,
+                               a2dp_stream_cb_t cb, void *user_data)
+{
+       struct a2dp_setup_cb *cb_data;
+       struct a2dp_setup *setup;
+
+       cb_data = g_new0(struct a2dp_setup_cb, 1);
+       cb_data->resume_cb = cb;
+       cb_data->user_data = user_data;
+       cb_data->id = ++cb_id;
+
+       setup = find_setup_by_session(session);
+       if (!setup) {
+               setup = g_new0(struct a2dp_setup, 1);
+               setup->session = avdtp_ref(session);
+               setup->dev = a2dp_get_dev(session);
+               setups = g_slist_append(setups, setup);
+       }
+
+       setup_ref(setup);
+       setup->cb = g_slist_append(setup->cb, cb_data);
+       setup->sep = sep;
+       setup->stream = sep->stream;
+
+       switch (avdtp_sep_get_state(sep->sep)) {
+       case AVDTP_STATE_IDLE:
+               goto failed;
+               break;
+       case AVDTP_STATE_OPEN:
+               if (avdtp_start(session, sep->stream) < 0) {
+                       error("avdtp_start failed");
+                       goto failed;
+               }
+               break;
+       case AVDTP_STATE_STREAMING:
+               if (!sep->suspending && sep->suspend_timer) {
+                       g_source_remove(sep->suspend_timer);
+                       sep->suspend_timer = 0;
+                       avdtp_unref(sep->session);
+                       sep->session = NULL;
+               }
+               if (sep->suspending)
+                       setup->start = TRUE;
+               else
+                       g_idle_add((GSourceFunc) finalize_resume, setup);
+               break;
+       default:
+               error("SEP in bad state for resume");
+               goto failed;
+       }
+
+       return cb_data->id;
+
+failed:
+       setup_unref(setup);
+       cb_id--;
+       return 0;
+}
+
+unsigned int a2dp_suspend(struct avdtp *session, struct a2dp_sep *sep,
+                               a2dp_stream_cb_t cb, void *user_data)
+{
+       struct a2dp_setup_cb *cb_data;
+       struct a2dp_setup *setup;
+
+       cb_data = g_new0(struct a2dp_setup_cb, 1);
+       cb_data->suspend_cb = cb;
+       cb_data->user_data = user_data;
+       cb_data->id = ++cb_id;
+
+       setup = find_setup_by_session(session);
+       if (!setup) {
+               setup = g_new0(struct a2dp_setup, 1);
+               setup->session = avdtp_ref(session);
+               setup->dev = a2dp_get_dev(session);
+               setups = g_slist_append(setups, setup);
+       }
+
+       setup_ref(setup);
+       setup->cb = g_slist_append(setup->cb, cb_data);
+       setup->sep = sep;
+       setup->stream = sep->stream;
+
+       switch (avdtp_sep_get_state(sep->sep)) {
+       case AVDTP_STATE_IDLE:
+               error("a2dp_suspend: no stream to suspend");
+               goto failed;
+               break;
+       case AVDTP_STATE_OPEN:
+               g_idle_add((GSourceFunc) finalize_suspend, setup);
+               break;
+       case AVDTP_STATE_STREAMING:
+               if (avdtp_suspend(session, sep->stream) < 0) {
+                       error("avdtp_suspend failed");
+                       goto failed;
+               }
+               break;
+       default:
+               error("SEP in bad state for suspend");
+               goto failed;
+       }
+
+       return cb_data->id;
+
+failed:
+       setup_unref(setup);
+       cb_id--;
+       return 0;
+}
+
+gboolean a2dp_cancel(struct audio_device *dev, unsigned int id)
+{
+       struct a2dp_setup_cb *cb_data;
+       struct a2dp_setup *setup;
+       GSList *l;
+
+       debug("a2dp_cancel()");
+
+       setup = find_setup_by_dev(dev);
+       if (!setup)
+               return FALSE;
+
+       for (cb_data = NULL, l = setup->cb; l != NULL; l = g_slist_next(l)) {
+               struct a2dp_setup_cb *cb = l->data;
+
+               if (cb->id == id) {
+                       cb_data = cb;
+                       break;
+               }
+       }
+
+       if (!cb_data)
+               error("a2dp_cancel: no matching callback with id %u", id);
+
+       setup->cb = g_slist_remove(setup->cb, cb_data);
+       g_free(cb_data);
+
+       if (!setup->cb) {
+               setup->canceled = TRUE;
+               setup->sep = NULL;
+       }
+
+       return TRUE;
+}
+
+gboolean a2dp_sep_lock(struct a2dp_sep *sep, struct avdtp *session)
+{
+       if (sep->locked)
+               return FALSE;
+
+       debug("SEP %p locked", sep->sep);
+       sep->locked = TRUE;
+
+       return TRUE;
+}
+
+gboolean a2dp_sep_unlock(struct a2dp_sep *sep, struct avdtp *session)
+{
+       avdtp_state_t state;
+
+       state = avdtp_sep_get_state(sep->sep);
+
+       sep->locked = FALSE;
+
+       debug("SEP %p unlocked", sep->sep);
+
+       if (!sep->stream || state == AVDTP_STATE_IDLE)
+               return TRUE;
+
+       switch (state) {
+       case AVDTP_STATE_OPEN:
+               /* Set timer here */
+               break;
+       case AVDTP_STATE_STREAMING:
+               if (avdtp_suspend(session, sep->stream) == 0)
+                       sep->suspending = TRUE;
+               break;
+       default:
+               break;
+       }
+
+       return TRUE;
+}
+
+gboolean a2dp_sep_get_lock(struct a2dp_sep *sep)
+{
+       return sep->locked;
+}
+
+static int stream_cmp(gconstpointer data, gconstpointer user_data)
+{
+       const struct a2dp_sep *sep = data;
+       const struct avdtp_stream *stream = user_data;
+
+       return (sep->stream != stream);
+}
+
+struct a2dp_sep *a2dp_get_sep(struct avdtp *session,
+                               struct avdtp_stream *stream)
+{
+       struct a2dp_server *server;
+       bdaddr_t src, dst;
+       GSList *l;
+
+       avdtp_get_peers(session, &src, &dst);
+
+       for (l = servers; l; l = l->next) {
+               server = l->data;
+
+               if (bacmp(&src, &server->src) == 0)
+                       break;
+       }
+
+       if (!l)
+               return NULL;
+
+       l = g_slist_find_custom(server->sources, stream, stream_cmp);
+       if (l)
+               return l->data;
+
+       l = g_slist_find_custom(server->sinks, stream, stream_cmp);
+       if (l)
+               return l->data;
+
+       return NULL;
+}
diff --git a/audio/a2dp.h b/audio/a2dp.h
new file mode 100644 (file)
index 0000000..f08c643
--- /dev/null
@@ -0,0 +1,149 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2006-2007  Nokia Corporation
+ *  Copyright (C) 2004-2009  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#define A2DP_CODEC_SBC                 0x00
+#define A2DP_CODEC_MPEG12              0x01
+#define A2DP_CODEC_MPEG24              0x02
+#define A2DP_CODEC_ATRAC               0x03
+
+#define SBC_SAMPLING_FREQ_16000                (1 << 3)
+#define SBC_SAMPLING_FREQ_32000                (1 << 2)
+#define SBC_SAMPLING_FREQ_44100                (1 << 1)
+#define SBC_SAMPLING_FREQ_48000                1
+
+#define SBC_CHANNEL_MODE_MONO          (1 << 3)
+#define SBC_CHANNEL_MODE_DUAL_CHANNEL  (1 << 2)
+#define SBC_CHANNEL_MODE_STEREO                (1 << 1)
+#define SBC_CHANNEL_MODE_JOINT_STEREO  1
+
+#define SBC_BLOCK_LENGTH_4             (1 << 3)
+#define SBC_BLOCK_LENGTH_8             (1 << 2)
+#define SBC_BLOCK_LENGTH_12            (1 << 1)
+#define SBC_BLOCK_LENGTH_16            1
+
+#define SBC_SUBBANDS_4                 (1 << 1)
+#define SBC_SUBBANDS_8                 1
+
+#define SBC_ALLOCATION_SNR             (1 << 1)
+#define SBC_ALLOCATION_LOUDNESS                1
+
+#define MPEG_CHANNEL_MODE_MONO         (1 << 3)
+#define MPEG_CHANNEL_MODE_DUAL_CHANNEL (1 << 2)
+#define MPEG_CHANNEL_MODE_STEREO       (1 << 1)
+#define MPEG_CHANNEL_MODE_JOINT_STEREO 1
+
+#define MPEG_LAYER_MP1                 (1 << 2)
+#define MPEG_LAYER_MP2                 (1 << 1)
+#define MPEG_LAYER_MP3                 1
+
+#define MPEG_SAMPLING_FREQ_16000       (1 << 5)
+#define MPEG_SAMPLING_FREQ_22050       (1 << 4)
+#define MPEG_SAMPLING_FREQ_24000       (1 << 3)
+#define MPEG_SAMPLING_FREQ_32000       (1 << 2)
+#define MPEG_SAMPLING_FREQ_44100       (1 << 1)
+#define MPEG_SAMPLING_FREQ_48000       1
+
+#define MAX_BITPOOL 64
+#define MIN_BITPOOL 2
+
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+
+struct sbc_codec_cap {
+       struct avdtp_media_codec_capability cap;
+       uint8_t channel_mode:4;
+       uint8_t frequency:4;
+       uint8_t allocation_method:2;
+       uint8_t subbands:2;
+       uint8_t block_length:4;
+       uint8_t min_bitpool;
+       uint8_t max_bitpool;
+} __attribute__ ((packed));
+
+struct mpeg_codec_cap {
+       struct avdtp_media_codec_capability cap;
+       uint8_t channel_mode:4;
+       uint8_t crc:1;
+       uint8_t layer:3;
+       uint8_t frequency:6;
+       uint8_t mpf:1;
+       uint8_t rfa:1;
+       uint16_t bitrate;
+} __attribute__ ((packed));
+
+#elif __BYTE_ORDER == __BIG_ENDIAN
+
+struct sbc_codec_cap {
+       struct avdtp_media_codec_capability cap;
+       uint8_t frequency:4;
+       uint8_t channel_mode:4;
+       uint8_t block_length:4;
+       uint8_t subbands:2;
+       uint8_t allocation_method:2;
+       uint8_t min_bitpool;
+       uint8_t max_bitpool;
+} __attribute__ ((packed));
+
+struct mpeg_codec_cap {
+       struct avdtp_media_codec_capability cap;
+       uint8_t layer:3;
+       uint8_t crc:1;
+       uint8_t channel_mode:4;
+       uint8_t rfa:1;
+       uint8_t mpf:1;
+       uint8_t frequency:6;
+       uint16_t bitrate;
+} __attribute__ ((packed));
+
+#else
+#error "Unknown byte order"
+#endif
+
+struct a2dp_sep;
+
+typedef void (*a2dp_config_cb_t) (struct avdtp *session, struct a2dp_sep *sep,
+                                       struct avdtp_stream *stream,
+                                       struct avdtp_error *err,
+                                       void *user_data);
+typedef void (*a2dp_stream_cb_t) (struct avdtp *session,
+                                       struct avdtp_error *err,
+                                       void *user_data);
+
+int a2dp_register(DBusConnection *conn, const bdaddr_t *src, GKeyFile *config);
+void a2dp_unregister(const bdaddr_t *src);
+
+struct a2dp_sep *a2dp_get(struct avdtp *session, struct avdtp_remote_sep *sep);
+unsigned int a2dp_config(struct avdtp *session, struct a2dp_sep *sep,
+                               a2dp_config_cb_t cb, GSList *caps,
+                               void *user_data);
+unsigned int a2dp_resume(struct avdtp *session, struct a2dp_sep *sep,
+                               a2dp_stream_cb_t cb, void *user_data);
+unsigned int a2dp_suspend(struct avdtp *session, struct a2dp_sep *sep,
+                               a2dp_stream_cb_t cb, void *user_data);
+gboolean a2dp_cancel(struct audio_device *dev, unsigned int id);
+
+gboolean a2dp_sep_lock(struct a2dp_sep *sep, struct avdtp *session);
+gboolean a2dp_sep_unlock(struct a2dp_sep *sep, struct avdtp *session);
+gboolean a2dp_sep_get_lock(struct a2dp_sep *sep);
+struct a2dp_sep *a2dp_get_sep(struct avdtp *session,
+                               struct avdtp_stream *stream);
diff --git a/audio/audio.conf b/audio/audio.conf
new file mode 100644 (file)
index 0000000..a093ffb
--- /dev/null
@@ -0,0 +1,43 @@
+# Configuration file for the audio service
+
+# This section contains options which are not specific to any
+# particular interface
+[General]
+Enable=Source,Control,Sink
+Disable=Headset,Gateway
+
+# Switch to master role for incoming connections (defaults to true)
+#Master=true
+
+# If we want to disable support for specific services
+# Defaults to supporting all implemented services
+#Disable=Control,Source
+
+# SCO routing. Either PCM or HCI (in which case audio is routed to/from ALSA)
+# Defaults to HCI
+#SCORouting=PCM
+
+# Automatically connect both A2DP and HFP/HSP profiles for incoming
+# connections. Some headsets that support both profiles will only connect the
+# other one automatically so the default setting of true is usually a good
+# idea.
+#AutoConnect=true
+
+# Headset interface specific options (i.e. options which affect how the audio
+# service interacts with remote headset devices)
+#[Headset]
+
+# Set to true to support HFP (in addition to HSP only which is the default)
+# Defaults to false
+#HFP=true
+
+# Maximum number of connected HSP/HFP devices per adapter. Defaults to 1
+#MaxConnections=1
+
+# Just an example of potential config options for the other interfaces
+[A2DP]
+SBCSources=1
+MPEG12Sources=0
+
+[AVRCP]
+InputDeviceName=AVRCP
diff --git a/audio/avdtp.c b/audio/avdtp.c
new file mode 100644 (file)
index 0000000..939db10
--- /dev/null
@@ -0,0 +1,3510 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2006-2007  Nokia Corporation
+ *  Copyright (C) 2004-2009  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <stdint.h>
+#include <errno.h>
+#include <unistd.h>
+#include <assert.h>
+#include <signal.h>
+#include <netinet/in.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/sdp.h>
+
+#include <glib.h>
+#include <dbus/dbus.h>
+
+#include "logging.h"
+
+#include "../src/adapter.h"
+#include "../src/device.h"
+
+#include "device.h"
+#include "manager.h"
+#include "control.h"
+#include "avdtp.h"
+#include "glib-helper.h"
+#include "btio.h"
+#include "sink.h"
+#include "source.h"
+
+#include <bluetooth/l2cap.h>
+
+#define AVDTP_PSM 25
+
+#define MAX_SEID 0x3E
+
+#define AVDTP_DISCOVER                         0x01
+#define AVDTP_GET_CAPABILITIES                 0x02
+#define AVDTP_SET_CONFIGURATION                        0x03
+#define AVDTP_GET_CONFIGURATION                        0x04
+#define AVDTP_RECONFIGURE                      0x05
+#define AVDTP_OPEN                             0x06
+#define AVDTP_START                            0x07
+#define AVDTP_CLOSE                            0x08
+#define AVDTP_SUSPEND                          0x09
+#define AVDTP_ABORT                            0x0A
+#define AVDTP_SECURITY_CONTROL                 0x0B
+
+#define AVDTP_PKT_TYPE_SINGLE                  0x00
+#define AVDTP_PKT_TYPE_START                   0x01
+#define AVDTP_PKT_TYPE_CONTINUE                        0x02
+#define AVDTP_PKT_TYPE_END                     0x03
+
+#define AVDTP_MSG_TYPE_COMMAND                 0x00
+#define AVDTP_MSG_TYPE_ACCEPT                  0x02
+#define AVDTP_MSG_TYPE_REJECT                  0x03
+
+#define REQ_TIMEOUT 4
+#define DISCONNECT_TIMEOUT 1
+#define STREAM_TIMEOUT 20
+
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+
+struct avdtp_common_header {
+       uint8_t message_type:2;
+       uint8_t packet_type:2;
+       uint8_t transaction:4;
+} __attribute__ ((packed));
+
+struct avdtp_single_header {
+       uint8_t message_type:2;
+       uint8_t packet_type:2;
+       uint8_t transaction:4;
+       uint8_t signal_id:6;
+       uint8_t rfa0:2;
+} __attribute__ ((packed));
+
+struct avdtp_start_header {
+       uint8_t message_type:2;
+       uint8_t packet_type:2;
+       uint8_t transaction:4;
+       uint8_t no_of_packets;
+       uint8_t signal_id:6;
+       uint8_t rfa0:2;
+} __attribute__ ((packed));
+
+struct avdtp_continue_header {
+       uint8_t message_type:2;
+       uint8_t packet_type:2;
+       uint8_t transaction:4;
+} __attribute__ ((packed));
+
+struct seid_info {
+       uint8_t rfa0:1;
+       uint8_t inuse:1;
+       uint8_t seid:6;
+       uint8_t rfa2:3;
+       uint8_t type:1;
+       uint8_t media_type:4;
+} __attribute__ ((packed));
+
+struct seid {
+       uint8_t rfa0:2;
+       uint8_t seid:6;
+} __attribute__ ((packed));
+
+#elif __BYTE_ORDER == __BIG_ENDIAN
+
+struct avdtp_common_header {
+       uint8_t transaction:4;
+       uint8_t packet_type:2;
+       uint8_t message_type:2;
+} __attribute__ ((packed));
+
+struct avdtp_single_header {
+       uint8_t transaction:4;
+       uint8_t packet_type:2;
+       uint8_t message_type:2;
+       uint8_t rfa0:2;
+       uint8_t signal_id:6;
+} __attribute__ ((packed));
+
+struct avdtp_start_header {
+       uint8_t transaction:4;
+       uint8_t packet_type:2;
+       uint8_t message_type:2;
+       uint8_t no_of_packets;
+       uint8_t rfa0:2;
+       uint8_t signal_id:6;
+} __attribute__ ((packed));
+
+struct avdtp_continue_header {
+       uint8_t transaction:4;
+       uint8_t packet_type:2;
+       uint8_t message_type:2;
+} __attribute__ ((packed));
+
+struct seid_info {
+       uint8_t seid:6;
+       uint8_t inuse:1;
+       uint8_t rfa0:1;
+       uint8_t media_type:4;
+       uint8_t type:1;
+       uint8_t rfa2:3;
+} __attribute__ ((packed));
+
+struct seid {
+       uint8_t seid:6;
+       uint8_t rfa0:2;
+} __attribute__ ((packed));
+
+#else
+#error "Unknown byte order"
+#endif
+
+/* packets */
+
+struct discover_resp {
+       struct seid_info seps[0];
+} __attribute__ ((packed));
+
+struct getcap_resp {
+       uint8_t caps[0];
+} __attribute__ ((packed));
+
+struct start_req {
+       struct seid first_seid;
+       struct seid other_seids[0];
+} __attribute__ ((packed));
+
+struct suspend_req {
+       struct seid first_seid;
+       struct seid other_seids[0];
+} __attribute__ ((packed));
+
+struct seid_rej {
+       uint8_t error;
+} __attribute__ ((packed));
+
+struct conf_rej {
+       uint8_t category;
+       uint8_t error;
+} __attribute__ ((packed));
+
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+
+struct seid_req {
+       uint8_t rfa0:2;
+       uint8_t acp_seid:6;
+} __attribute__ ((packed));
+
+struct setconf_req {
+       uint8_t rfa0:2;
+       uint8_t acp_seid:6;
+       uint8_t rfa1:2;
+       uint8_t int_seid:6;
+
+       uint8_t caps[0];
+} __attribute__ ((packed));
+
+struct stream_rej {
+       uint8_t rfa0:2;
+       uint8_t acp_seid:6;
+       uint8_t error;
+} __attribute__ ((packed));
+
+struct reconf_req {
+       uint8_t rfa0:2;
+       uint8_t acp_seid:6;
+
+       uint8_t serv_cap;
+       uint8_t serv_cap_len;
+
+       uint8_t caps[0];
+} __attribute__ ((packed));
+
+#elif __BYTE_ORDER == __BIG_ENDIAN
+
+struct seid_req {
+       uint8_t acp_seid:6;
+       uint8_t rfa0:2;
+} __attribute__ ((packed));
+
+struct setconf_req {
+       uint8_t acp_seid:6;
+       uint8_t rfa0:2;
+       uint8_t int_seid:6;
+       uint8_t rfa1:2;
+
+       uint8_t caps[0];
+} __attribute__ ((packed));
+
+struct stream_rej {
+       uint8_t acp_seid:6;
+       uint8_t rfa0:2;
+       uint8_t error;
+} __attribute__ ((packed));
+
+struct reconf_req {
+       uint8_t acp_seid:6;
+       uint8_t rfa0:2;
+
+       uint8_t serv_cap;
+       uint8_t serv_cap_len;
+
+       uint8_t caps[0];
+} __attribute__ ((packed));
+
+#else
+#error "Unknown byte order"
+#endif
+
+struct in_buf {
+       gboolean active;
+       int no_of_packets;
+       uint8_t transaction;
+       uint8_t message_type;
+       uint8_t signal_id;
+       uint8_t buf[1024];
+       uint8_t data_size;
+};
+
+struct pending_req {
+       uint8_t transaction;
+       uint8_t signal_id;
+       void *data;
+       size_t data_size;
+       struct avdtp_stream *stream; /* Set if the request targeted a stream */
+       guint timeout;
+};
+
+struct avdtp_remote_sep {
+       uint8_t seid;
+       uint8_t type;
+       uint8_t media_type;
+       struct avdtp_service_capability *codec;
+       GSList *caps; /* of type struct avdtp_service_capability */
+       struct avdtp_stream *stream;
+};
+
+struct avdtp_server {
+       bdaddr_t src;
+       GIOChannel *io;
+       GSList *seps;
+       GSList *sessions;
+};
+
+struct avdtp_local_sep {
+       avdtp_state_t state;
+       struct avdtp_stream *stream;
+       struct seid_info info;
+       uint8_t codec;
+       GSList *caps;
+       struct avdtp_sep_ind *ind;
+       struct avdtp_sep_cfm *cfm;
+       void *user_data;
+       struct avdtp_server *server;
+};
+
+struct stream_callback {
+       avdtp_stream_state_cb cb;
+       void *user_data;
+       unsigned int id;
+};
+
+struct avdtp_state_callback {
+       avdtp_session_state_cb cb;
+       void *user_data;
+       unsigned int id;
+};
+
+struct avdtp_stream {
+       GIOChannel *io;
+       uint16_t imtu;
+       uint16_t omtu;
+       struct avdtp *session;
+       struct avdtp_local_sep *lsep;
+       uint8_t rseid;
+       GSList *caps;
+       GSList *callbacks;
+       struct avdtp_service_capability *codec;
+       guint io_id;            /* Transport GSource ID */
+       guint timer;            /* Waiting for other side to close or open
+                                * the transport channel */
+       gboolean open_acp;      /* If we are in ACT role for Open */
+       gboolean close_int;     /* If we are in INT role for Close */
+       gboolean abort_int;     /* If we are in INT role for Abort */
+       guint idle_timer;
+};
+
+/* Structure describing an AVDTP connection between two devices */
+
+struct avdtp {
+       int ref;
+       int free_lock;
+
+       struct avdtp_server *server;
+       bdaddr_t dst;
+
+       avdtp_session_state_t state;
+
+       /* True if the session should be automatically disconnected */
+       gboolean auto_dc;
+
+       GIOChannel *io;
+       guint io_id;
+
+       GSList *seps; /* Elements of type struct avdtp_remote_sep * */
+
+       GSList *streams; /* Elements of type struct avdtp_stream * */
+
+       GSList *req_queue; /* Elements of type struct pending_req * */
+       GSList *prio_queue; /* Same as req_queue but is processed before it */
+
+       struct avdtp_stream *pending_open;
+
+       uint16_t imtu;
+       uint16_t omtu;
+
+       struct in_buf in;
+
+       char *buf;
+
+       avdtp_discover_cb_t discov_cb;
+       void *user_data;
+
+       struct pending_req *req;
+
+       guint dc_timer;
+
+       /* Attempt stream setup instead of disconnecting */
+       gboolean stream_setup;
+
+       DBusPendingCall *pending_auth;
+};
+
+static GSList *servers = NULL;
+
+static GSList *avdtp_callbacks = NULL;
+
+static gboolean auto_connect = TRUE;
+
+static int send_request(struct avdtp *session, gboolean priority,
+                       struct avdtp_stream *stream, uint8_t signal_id,
+                       void *buffer, size_t size);
+static gboolean avdtp_parse_resp(struct avdtp *session,
+                                       struct avdtp_stream *stream,
+                                       uint8_t transaction, uint8_t signal_id,
+                                       void *buf, int size);
+static gboolean avdtp_parse_rej(struct avdtp *session,
+                                       struct avdtp_stream *stream,
+                                       uint8_t transaction, uint8_t signal_id,
+                                       void *buf, int size);
+static int process_queue(struct avdtp *session);
+static void connection_lost(struct avdtp *session, int err);
+static void avdtp_sep_set_state(struct avdtp *session,
+                               struct avdtp_local_sep *sep,
+                               avdtp_state_t state);
+
+static struct avdtp_server *find_server(GSList *list, const bdaddr_t *src)
+{
+       GSList *l;
+
+       for (l = list; l; l = l->next) {
+               struct avdtp_server *server = l->data;
+
+               if (bacmp(&server->src, src) == 0)
+                       return server;
+       }
+
+       return NULL;
+}
+
+static const char *avdtp_statestr(avdtp_state_t state)
+{
+       switch (state) {
+       case AVDTP_STATE_IDLE:
+               return "IDLE";
+       case AVDTP_STATE_CONFIGURED:
+               return "CONFIGURED";
+       case AVDTP_STATE_OPEN:
+               return "OPEN";
+       case AVDTP_STATE_STREAMING:
+               return "STREAMING";
+       case AVDTP_STATE_CLOSING:
+               return "CLOSING";
+       case AVDTP_STATE_ABORTING:
+               return "ABORTING";
+       default:
+               return "<unknown state>";
+       }
+}
+
+static gboolean try_send(int sk, void *data, size_t len)
+{
+       int err;
+
+       do {
+               err = send(sk, data, len, 0);
+       } while (err < 0 && errno == EINTR);
+
+       if (err < 0) {
+               error("send: %s (%d)", strerror(errno), errno);
+               return FALSE;
+       } else if ((size_t) err != len) {
+               error("try_send: complete buffer not sent (%d/%zu bytes)",
+                                                               err, len);
+               return FALSE;
+       }
+
+       return TRUE;
+}
+
+static gboolean avdtp_send(struct avdtp *session, uint8_t transaction,
+                               uint8_t message_type, uint8_t signal_id,
+                               void *data, size_t len)
+{
+       unsigned int cont_fragments, sent;
+       struct avdtp_start_header start;
+       struct avdtp_continue_header cont;
+       int sock;
+
+       if (session->io == NULL) {
+               error("avdtp_send: session is closed");
+               return FALSE;
+       }
+
+       sock = g_io_channel_unix_get_fd(session->io);
+
+       /* Single packet - no fragmentation */
+       if (sizeof(struct avdtp_single_header) + len <= session->omtu) {
+               struct avdtp_single_header single;
+
+               memset(&single, 0, sizeof(single));
+
+               single.transaction = transaction;
+               single.packet_type = AVDTP_PKT_TYPE_SINGLE;
+               single.message_type = message_type;
+               single.signal_id = signal_id;
+
+               memcpy(session->buf, &single, sizeof(single));
+               memcpy(session->buf + sizeof(single), data, len);
+
+               return try_send(sock, session->buf, sizeof(single) + len);
+       }
+
+       /* Count the number of needed fragments */
+       cont_fragments = (len - (session->omtu - sizeof(start))) /
+                                       (session->omtu - sizeof(cont)) + 1;
+
+       debug("avdtp_send: %zu bytes split into %d fragments", len,
+                                                       cont_fragments + 1);
+
+       /* Send the start packet */
+       memset(&start, 0, sizeof(start));
+       start.transaction = transaction;
+       start.packet_type = AVDTP_PKT_TYPE_START;
+       start.message_type = message_type;
+       start.no_of_packets = cont_fragments + 1;
+       start.signal_id = signal_id;
+
+       memcpy(session->buf, &start, sizeof(start));
+       memcpy(session->buf + sizeof(start), data,
+                                       session->omtu - sizeof(start));
+
+       if (!try_send(sock, session->buf, session->omtu))
+               return FALSE;
+
+       debug("avdtp_send: first packet with %zu bytes sent",
+                                               session->omtu - sizeof(start));
+
+       sent = session->omtu - sizeof(start);
+
+       /* Send the continue fragments and the end packet */
+       while (sent < len) {
+               int left, to_copy;
+
+               left = len - sent;
+               if (left + sizeof(cont) > session->omtu) {
+                       cont.packet_type = AVDTP_PKT_TYPE_CONTINUE;
+                       to_copy = session->omtu - sizeof(cont);
+                       debug("avdtp_send: sending continue with %d bytes",
+                                                               to_copy);
+               } else {
+                       cont.packet_type = AVDTP_PKT_TYPE_END;
+                       to_copy = left;
+                       debug("avdtp_send: sending end with %d bytes",
+                                                               to_copy);
+               }
+
+               cont.transaction = transaction;
+               cont.message_type = message_type;
+
+               memcpy(session->buf, &cont, sizeof(cont));
+               memcpy(session->buf + sizeof(cont), data + sent, to_copy);
+
+               if (!try_send(sock, session->buf, to_copy + sizeof(cont)))
+                       return FALSE;
+
+               sent += to_copy;
+       }
+
+       return TRUE;
+}
+
+static void pending_req_free(struct pending_req *req)
+{
+       if (req->timeout)
+               g_source_remove(req->timeout);
+       g_free(req->data);
+       g_free(req);
+}
+
+static void close_stream(struct avdtp_stream *stream)
+{
+       int sock;
+
+       if (stream->io == NULL)
+               return;
+
+       sock = g_io_channel_unix_get_fd(stream->io);
+
+       shutdown(sock, SHUT_RDWR);
+
+       g_io_channel_shutdown(stream->io, FALSE, NULL);
+
+       g_io_channel_unref(stream->io);
+       stream->io = NULL;
+}
+
+static gboolean stream_close_timeout(gpointer user_data)
+{
+       struct avdtp_stream *stream = user_data;
+
+       debug("Timed out waiting for peer to close the transport channel");
+
+       stream->timer = 0;
+
+       close_stream(stream);
+
+       return FALSE;
+}
+
+static gboolean stream_open_timeout(gpointer user_data)
+{
+       struct avdtp_stream *stream = user_data;
+
+       debug("Timed out waiting for peer to open the transport channel");
+
+       stream->timer = 0;
+
+       stream->session->pending_open = NULL;
+
+       avdtp_abort(stream->session, stream);
+
+       return FALSE;
+}
+
+static gboolean disconnect_timeout(gpointer user_data)
+{
+       struct avdtp *session = user_data;
+       struct audio_device *dev;
+       gboolean stream_setup;
+
+       session->dc_timer = 0;
+       stream_setup = session->stream_setup;
+       session->stream_setup = FALSE;
+
+       dev = manager_get_device(&session->server->src, &session->dst, FALSE);
+
+       if (dev && dev->sink && stream_setup)
+               sink_setup_stream(dev->sink, session);
+       else if (dev && dev->source && stream_setup)
+               source_setup_stream(dev->source, session);
+       else
+               connection_lost(session, ETIMEDOUT);
+
+       return FALSE;
+}
+
+static void remove_disconnect_timer(struct avdtp *session)
+{
+       g_source_remove(session->dc_timer);
+       session->dc_timer = 0;
+       session->stream_setup = FALSE;
+}
+
+static void set_disconnect_timer(struct avdtp *session)
+{
+       if (session->dc_timer)
+               remove_disconnect_timer(session);
+
+       session->dc_timer = g_timeout_add_seconds(DISCONNECT_TIMEOUT,
+                                               disconnect_timeout,
+                                               session);
+}
+
+void avdtp_error_init(struct avdtp_error *err, uint8_t type, int id)
+{
+       err->type = type;
+       switch (type) {
+       case AVDTP_ERROR_ERRNO:
+               err->err.posix_errno = id;
+               break;
+       case AVDTP_ERROR_ERROR_CODE:
+               err->err.error_code = id;
+               break;
+       }
+}
+
+avdtp_error_type_t avdtp_error_type(struct avdtp_error *err)
+{
+       return err->type;
+}
+
+int avdtp_error_error_code(struct avdtp_error *err)
+{
+       assert(err->type == AVDTP_ERROR_ERROR_CODE);
+       return err->err.error_code;
+}
+
+int avdtp_error_posix_errno(struct avdtp_error *err)
+{
+       assert(err->type == AVDTP_ERROR_ERRNO);
+       return err->err.posix_errno;
+}
+
+static struct avdtp_stream *find_stream_by_rseid(struct avdtp *session,
+                                                       uint8_t rseid)
+{
+       GSList *l;
+
+       for (l = session->streams; l != NULL; l = g_slist_next(l)) {
+               struct avdtp_stream *stream = l->data;
+
+               if (stream->rseid == rseid)
+                       return stream;
+       }
+
+       return NULL;
+}
+
+static struct avdtp_remote_sep *find_remote_sep(GSList *seps, uint8_t seid)
+{
+       GSList *l;
+
+       for (l = seps; l != NULL; l = g_slist_next(l)) {
+               struct avdtp_remote_sep *sep = l->data;
+
+               if (sep->seid == seid)
+                       return sep;
+       }
+
+       return NULL;
+}
+
+static void avdtp_set_state(struct avdtp *session,
+                                       avdtp_session_state_t new_state)
+{
+       GSList *l;
+       struct audio_device *dev;
+       bdaddr_t src, dst;
+       avdtp_session_state_t old_state = session->state;
+
+       session->state = new_state;
+
+       avdtp_get_peers(session, &src, &dst);
+       dev = manager_get_device(&src, &dst, FALSE);
+       if (dev == NULL) {
+               error("avdtp_set_state(): no matching audio device");
+               return;
+       }
+
+       for (l = avdtp_callbacks; l != NULL; l = l->next) {
+               struct avdtp_state_callback *cb = l->data;
+               cb->cb(dev, session, old_state, new_state, cb->user_data);
+       }
+}
+
+static void stream_free(struct avdtp_stream *stream)
+{
+       struct avdtp_remote_sep *rsep;
+
+       stream->lsep->info.inuse = 0;
+       stream->lsep->stream = NULL;
+
+       rsep = find_remote_sep(stream->session->seps, stream->rseid);
+       if (rsep)
+               rsep->stream = NULL;
+
+       if (stream->timer)
+               g_source_remove(stream->timer);
+
+       if (stream->io)
+               g_io_channel_unref(stream->io);
+
+       if (stream->io_id)
+               g_source_remove(stream->io_id);
+
+       g_slist_foreach(stream->callbacks, (GFunc) g_free, NULL);
+       g_slist_free(stream->callbacks);
+
+       g_slist_foreach(stream->caps, (GFunc) g_free, NULL);
+       g_slist_free(stream->caps);
+
+       g_free(stream);
+}
+
+static gboolean stream_timeout(gpointer user_data)
+{
+       struct avdtp_stream *stream = user_data;
+       struct avdtp *session = stream->session;
+
+       avdtp_close(session, stream);
+
+       stream->idle_timer = 0;
+
+       return FALSE;
+}
+
+static gboolean transport_cb(GIOChannel *chan, GIOCondition cond,
+                               gpointer data)
+{
+       struct avdtp_stream *stream = data;
+       struct avdtp_local_sep *sep = stream->lsep;
+
+       if (stream->close_int && sep->cfm && sep->cfm->close)
+               sep->cfm->close(stream->session, sep, stream, NULL,
+                               sep->user_data);
+
+       if (!(cond & G_IO_NVAL))
+               close_stream(stream);
+
+       stream->io_id = 0;
+
+       if (!stream->abort_int)
+               avdtp_sep_set_state(stream->session, sep, AVDTP_STATE_IDLE);
+
+       return FALSE;
+}
+
+static void handle_transport_connect(struct avdtp *session, GIOChannel *io,
+                                       uint16_t imtu, uint16_t omtu)
+{
+       struct avdtp_stream *stream = session->pending_open;
+       struct avdtp_local_sep *sep = stream->lsep;
+
+       session->pending_open = NULL;
+
+       if (stream->timer) {
+               g_source_remove(stream->timer);
+               stream->timer = 0;
+       }
+
+       if (io == NULL) {
+               if (!stream->open_acp && sep->cfm && sep->cfm->open) {
+                       struct avdtp_error err;
+                       avdtp_error_init(&err, AVDTP_ERROR_ERRNO, EIO);
+                       sep->cfm->open(session, sep, NULL, &err,
+                                       sep->user_data);
+               }
+               return;
+       }
+
+       stream->io = g_io_channel_ref(io);
+       stream->omtu = omtu;
+       stream->imtu = imtu;
+
+       if (!stream->open_acp && sep->cfm && sep->cfm->open)
+               sep->cfm->open(session, sep, stream, NULL, sep->user_data);
+
+       avdtp_sep_set_state(session, sep, AVDTP_STATE_OPEN);
+
+       stream->io_id = g_io_add_watch(io, G_IO_ERR | G_IO_HUP | G_IO_NVAL,
+                                       (GIOFunc) transport_cb, stream);
+}
+
+static int pending_req_cmp(gconstpointer a, gconstpointer b)
+{
+       const struct pending_req *req = a;
+       const struct avdtp_stream *stream = b;
+
+       if (req->stream == stream)
+               return 0;
+
+       return -1;
+}
+
+static void cleanup_queue(struct avdtp *session, struct avdtp_stream *stream)
+{
+       GSList *l;
+       struct pending_req *req;
+
+       while ((l = g_slist_find_custom(session->prio_queue, stream,
+                                                       pending_req_cmp))) {
+               req = l->data;
+               pending_req_free(req);
+               session->prio_queue = g_slist_remove(session->prio_queue, req);
+       }
+
+       while ((l = g_slist_find_custom(session->req_queue, stream,
+                                                       pending_req_cmp))) {
+               req = l->data;
+               pending_req_free(req);
+               session->req_queue = g_slist_remove(session->req_queue, req);
+       }
+}
+
+static void avdtp_sep_set_state(struct avdtp *session,
+                               struct avdtp_local_sep *sep,
+                               avdtp_state_t state)
+{
+       struct avdtp_stream *stream = sep->stream;
+       avdtp_state_t old_state;
+       struct avdtp_error err, *err_ptr = NULL;
+       GSList *l;
+
+       if (!stream) {
+               error("Error changing sep state: stream not available");
+               return;
+       }
+
+       if (sep->state == state) {
+               avdtp_error_init(&err, AVDTP_ERROR_ERRNO, EIO);
+               debug("stream state change failed: %s", avdtp_strerror(&err));
+               err_ptr = &err;
+       } else {
+               err_ptr = NULL;
+               debug("stream state changed: %s -> %s",
+                               avdtp_statestr(sep->state),
+                               avdtp_statestr(state));
+       }
+
+       old_state = sep->state;
+       sep->state = state;
+
+       for (l = stream->callbacks; l != NULL; l = g_slist_next(l)) {
+               struct stream_callback *cb = l->data;
+               cb->cb(stream, old_state, state, err_ptr, cb->user_data);
+       }
+
+       switch (state) {
+       case AVDTP_STATE_OPEN:
+               if (old_state > AVDTP_STATE_OPEN && session->auto_dc)
+                       stream->idle_timer = g_timeout_add_seconds(STREAM_TIMEOUT,
+                                                               stream_timeout,
+                                                               stream);
+               break;
+       case AVDTP_STATE_STREAMING:
+       case AVDTP_STATE_CLOSING:
+       case AVDTP_STATE_ABORTING:
+               if (stream->idle_timer) {
+                       g_source_remove(stream->idle_timer);
+                       stream->idle_timer = 0;
+               }
+               break;
+       case AVDTP_STATE_IDLE:
+               if (stream->idle_timer) {
+                       g_source_remove(stream->idle_timer);
+                       stream->idle_timer = 0;
+               }
+               session->streams = g_slist_remove(session->streams, stream);
+               if (session->pending_open == stream)
+                       handle_transport_connect(session, NULL, 0, 0);
+               if (session->req && session->req->stream == stream)
+                       session->req->stream = NULL;
+               /* Remove pending commands for this stream from the queue */
+               cleanup_queue(session, stream);
+               stream_free(stream);
+               if (session->ref == 1 && !session->streams)
+                       set_disconnect_timer(session);
+               break;
+       default:
+               break;
+       }
+}
+
+static void finalize_discovery(struct avdtp *session, int err)
+{
+       struct avdtp_error avdtp_err;
+
+       avdtp_error_init(&avdtp_err, AVDTP_ERROR_ERRNO, err);
+
+       if (!session->discov_cb)
+               return;
+
+       session->discov_cb(session, session->seps,
+                               err ? &avdtp_err : NULL,
+                               session->user_data);
+
+       session->discov_cb = NULL;
+       session->user_data = NULL;
+}
+
+static void release_stream(struct avdtp_stream *stream, struct avdtp *session)
+{
+       struct avdtp_local_sep *sep = stream->lsep;
+
+       if (sep->cfm && sep->cfm->abort &&
+                               (sep->state != AVDTP_STATE_ABORTING ||
+                                                       stream->abort_int))
+               sep->cfm->abort(session, sep, stream, NULL, sep->user_data);
+
+       avdtp_sep_set_state(session, sep, AVDTP_STATE_IDLE);
+}
+
+static void connection_lost(struct avdtp *session, int err)
+{
+       char address[18];
+
+       ba2str(&session->dst, address);
+       debug("Disconnected from %s", address);
+
+       if (session->state == AVDTP_SESSION_STATE_CONNECTING && err != EACCES)
+               btd_cancel_authorization(&session->server->src, &session->dst);
+
+       session->free_lock = 1;
+
+       finalize_discovery(session, err);
+
+       g_slist_foreach(session->streams, (GFunc) release_stream, session);
+       session->streams = NULL;
+
+       session->free_lock = 0;
+
+       if (session->io) {
+               g_io_channel_shutdown(session->io, FALSE, NULL);
+               g_io_channel_unref(session->io);
+               session->io = NULL;
+       }
+
+       avdtp_set_state(session, AVDTP_SESSION_STATE_DISCONNECTED);
+
+       if (session->io_id) {
+               g_source_remove(session->io_id);
+               session->io_id = 0;
+       }
+
+       if (session->dc_timer)
+               remove_disconnect_timer(session);
+
+       session->auto_dc = TRUE;
+
+       if (session->ref != 1)
+               error("connection_lost: ref count not 1 after all callbacks");
+       else
+               avdtp_unref(session);
+}
+
+void avdtp_unref(struct avdtp *session)
+{
+       struct avdtp_server *server;
+
+       if (!session)
+               return;
+
+       session->ref--;
+
+       debug("avdtp_unref(%p): ref=%d", session, session->ref);
+
+       if (session->ref == 1) {
+               if (session->state == AVDTP_SESSION_STATE_CONNECTING &&
+                                                               session->io) {
+                       btd_cancel_authorization(&session->server->src,
+                                                       &session->dst);
+                       g_io_channel_shutdown(session->io, TRUE, NULL);
+                       g_io_channel_unref(session->io);
+                       session->io = NULL;
+               }
+
+               if (session->io)
+                       set_disconnect_timer(session);
+               else if (!session->free_lock) /* Drop the local ref if we
+                                                aren't connected */
+                       session->ref--;
+       }
+
+       if (session->ref > 0)
+               return;
+
+       server = session->server;
+
+       debug("avdtp_unref(%p): freeing session and removing from list",
+                       session);
+
+       if (session->dc_timer)
+               remove_disconnect_timer(session);
+
+       server->sessions = g_slist_remove(server->sessions, session);
+
+       if (session->req)
+               pending_req_free(session->req);
+
+       g_slist_foreach(session->seps, (GFunc) g_free, NULL);
+       g_slist_free(session->seps);
+
+       g_free(session->buf);
+
+       g_free(session);
+}
+
+struct avdtp *avdtp_ref(struct avdtp *session)
+{
+       session->ref++;
+       debug("avdtp_ref(%p): ref=%d", session, session->ref);
+       if (session->dc_timer)
+               remove_disconnect_timer(session);
+       return session;
+}
+
+static struct avdtp_local_sep *find_local_sep_by_seid(struct avdtp_server *server,
+                                                       uint8_t seid)
+{
+       GSList *l;
+
+       for (l = server->seps; l != NULL; l = g_slist_next(l)) {
+               struct avdtp_local_sep *sep = l->data;
+
+               if (sep->info.seid == seid)
+                       return sep;
+       }
+
+       return NULL;
+}
+
+static struct avdtp_local_sep *find_local_sep(struct avdtp_server *server,
+                                               uint8_t type,
+                                               uint8_t media_type,
+                                               uint8_t codec)
+{
+       GSList *l;
+
+       for (l = server->seps; l != NULL; l = g_slist_next(l)) {
+               struct avdtp_local_sep *sep = l->data;
+
+               if (sep->info.inuse)
+                       continue;
+
+               if (sep->info.type == type &&
+                               sep->info.media_type == media_type &&
+                               sep->codec == codec)
+                       return sep;
+       }
+
+       return NULL;
+}
+
+static GSList *caps_to_list(uint8_t *data, int size,
+                               struct avdtp_service_capability **codec)
+{
+       GSList *caps;
+       int processed;
+
+       for (processed = 0, caps = NULL; processed + 2 < size;) {
+               struct avdtp_service_capability *cap;
+               uint8_t length, category;
+
+               category = data[0];
+               length = data[1];
+
+               if (processed + 2 + length > size) {
+                       error("Invalid capability data in getcap resp");
+                       break;
+               }
+
+               cap = g_malloc(sizeof(struct avdtp_service_capability) +
+                                       length);
+               memcpy(cap, data, 2 + length);
+
+               processed += 2 + length;
+               data += 2 + length;
+
+               caps = g_slist_append(caps, cap);
+
+               if (category == AVDTP_MEDIA_CODEC &&
+                               length >=
+                               sizeof(struct avdtp_media_codec_capability))
+                       *codec = cap;
+       }
+
+       return caps;
+}
+
+static gboolean avdtp_unknown_cmd(struct avdtp *session, uint8_t transaction,
+                                                       void *buf, int size)
+{
+       return avdtp_send(session, transaction, AVDTP_MSG_TYPE_REJECT,
+                                                               0, NULL, 0);
+}
+
+static gboolean avdtp_discover_cmd(struct avdtp *session, uint8_t transaction,
+                                                       void *buf, int size)
+{
+       GSList *l;
+       unsigned int rsp_size, sep_count, i;
+       struct seid_info *seps;
+       gboolean ret;
+
+       sep_count = g_slist_length(session->server->seps);
+
+       if (sep_count == 0) {
+               uint8_t err = AVDTP_NOT_SUPPORTED_COMMAND;
+               return avdtp_send(session, transaction, AVDTP_MSG_TYPE_REJECT,
+                                       AVDTP_DISCOVER, &err, sizeof(err));
+       }
+
+       rsp_size = sep_count * sizeof(struct seid_info);
+
+       seps = g_new0(struct seid_info, sep_count);
+
+       for (l = session->server->seps, i = 0; l != NULL; l = l->next, i++) {
+               struct avdtp_local_sep *sep = l->data;
+
+               memcpy(&seps[i], &sep->info, sizeof(struct seid_info));
+       }
+
+       ret = avdtp_send(session, transaction, AVDTP_MSG_TYPE_ACCEPT,
+                               AVDTP_DISCOVER, seps, rsp_size);
+       g_free(seps);
+
+       return ret;
+}
+
+static gboolean avdtp_getcap_cmd(struct avdtp *session, uint8_t transaction,
+                                       struct seid_req *req, unsigned int size)
+{
+       GSList *l, *caps;
+       struct avdtp_local_sep *sep = NULL;
+       unsigned int rsp_size;
+       uint8_t err, buf[1024], *ptr = buf;
+
+       if (size < sizeof(struct seid_req)) {
+               err = AVDTP_BAD_LENGTH;
+               goto failed;
+       }
+
+       sep = find_local_sep_by_seid(session->server, req->acp_seid);
+       if (!sep) {
+               err = AVDTP_BAD_ACP_SEID;
+               goto failed;
+       }
+
+       if (!sep->ind->get_capability(session, sep, &caps, &err,
+                                       sep->user_data))
+               goto failed;
+
+       for (l = caps, rsp_size = 0; l != NULL; l = g_slist_next(l)) {
+               struct avdtp_service_capability *cap = l->data;
+
+               if (rsp_size + cap->length + 2 > sizeof(buf))
+                       break;
+
+               memcpy(ptr, cap, cap->length + 2);
+               rsp_size += cap->length + 2;
+               ptr += cap->length + 2;
+
+               g_free(cap);
+       }
+
+       g_slist_free(caps);
+
+       return avdtp_send(session, transaction, AVDTP_MSG_TYPE_ACCEPT,
+                               AVDTP_GET_CAPABILITIES, buf, rsp_size);
+
+failed:
+       return avdtp_send(session, transaction, AVDTP_MSG_TYPE_REJECT,
+                               AVDTP_GET_CAPABILITIES, &err, sizeof(err));
+}
+
+static gboolean avdtp_setconf_cmd(struct avdtp *session, uint8_t transaction,
+                               struct setconf_req *req, unsigned int size)
+{
+       struct conf_rej rej;
+       struct avdtp_local_sep *sep;
+       struct avdtp_stream *stream;
+       uint8_t err, category = 0x00;
+       struct audio_device *dev;
+       bdaddr_t src, dst;
+       GSList *l;
+
+       if (size < sizeof(struct setconf_req)) {
+               error("Too short getcap request");
+               return FALSE;
+       }
+
+       sep = find_local_sep_by_seid(session->server, req->acp_seid);
+       if (!sep) {
+               err = AVDTP_BAD_ACP_SEID;
+               goto failed;
+       }
+
+       if (sep->stream) {
+               err = AVDTP_SEP_IN_USE;
+               goto failed;
+       }
+
+       avdtp_get_peers(session, &src, &dst);
+       dev = manager_get_device(&src, &dst, FALSE);
+       if (!dev) {
+               error("Unable to get a audio device object");
+               goto failed;
+       }
+
+       switch (sep->info.type) {
+       case AVDTP_SEP_TYPE_SOURCE:
+               if (!dev->sink) {
+                       btd_device_add_uuid(dev->btd_dev, A2DP_SINK_UUID);
+                       if (!dev->sink) {
+                               error("Unable to get a audio sink object");
+                               goto failed;
+                       }
+               }
+               break;
+       case AVDTP_SEP_TYPE_SINK:
+               /* Do source_init() here when it's implemented */
+               break;
+       }
+
+       stream = g_new0(struct avdtp_stream, 1);
+       stream->session = session;
+       stream->lsep = sep;
+       stream->rseid = req->int_seid;
+       stream->caps = caps_to_list(req->caps,
+                                       size - sizeof(struct setconf_req),
+                                       &stream->codec);
+
+       /* Verify that the Media Transport capability's length = 0. Reject otherwise */
+       for (l = stream->caps; l != NULL; l = g_slist_next(l)) {
+               struct avdtp_service_capability *cap = l->data;
+
+               if (cap->category == AVDTP_MEDIA_TRANSPORT && cap->length != 0) {
+                       err = AVDTP_BAD_MEDIA_TRANSPORT_FORMAT;
+                       goto failed_stream;
+               }
+       }
+
+       if (sep->ind && sep->ind->set_configuration) {
+               if (!sep->ind->set_configuration(session, sep, stream,
+                                                       stream->caps, &err,
+                                                       &category,
+                                                       sep->user_data))
+                       goto failed_stream;
+       }
+
+       if (!avdtp_send(session, transaction, AVDTP_MSG_TYPE_ACCEPT,
+                                       AVDTP_SET_CONFIGURATION, NULL, 0)) {
+               stream_free(stream);
+               return FALSE;
+       }
+
+       sep->stream = stream;
+       session->streams = g_slist_append(session->streams, stream);
+
+       avdtp_sep_set_state(session, sep, AVDTP_STATE_CONFIGURED);
+
+       return TRUE;
+
+failed_stream:
+       stream_free(stream);
+failed:
+       rej.error = err;
+       rej.category = category;
+       return avdtp_send(session, transaction, AVDTP_MSG_TYPE_REJECT,
+                               AVDTP_SET_CONFIGURATION, &rej, sizeof(rej));
+}
+
+static gboolean avdtp_getconf_cmd(struct avdtp *session, uint8_t transaction,
+                                       struct seid_req *req, int size)
+{
+       GSList *l;
+       struct avdtp_local_sep *sep = NULL;
+       int rsp_size;
+       uint8_t err;
+       uint8_t buf[1024];
+       uint8_t *ptr = buf;
+
+       if (size < (int) sizeof(struct seid_req)) {
+               error("Too short getconf request");
+               return FALSE;
+       }
+
+       memset(buf, 0, sizeof(buf));
+
+       sep = find_local_sep_by_seid(session->server, req->acp_seid);
+       if (!sep) {
+               err = AVDTP_BAD_ACP_SEID;
+               goto failed;
+       }
+       if (!sep->stream || !sep->stream->caps) {
+               err = AVDTP_UNSUPPORTED_CONFIGURATION;
+               goto failed;
+       }
+
+       for (l = sep->stream->caps, rsp_size = 0; l != NULL; l = g_slist_next(l)) {
+               struct avdtp_service_capability *cap = l->data;
+
+               if (rsp_size + cap->length + 2 > (int) sizeof(buf))
+                       break;
+
+               memcpy(ptr, cap, cap->length + 2);
+               rsp_size += cap->length + 2;
+               ptr += cap->length + 2;
+       }
+
+       return avdtp_send(session, transaction, AVDTP_MSG_TYPE_ACCEPT,
+                               AVDTP_GET_CONFIGURATION, buf, rsp_size);
+
+failed:
+       return avdtp_send(session, transaction, AVDTP_MSG_TYPE_REJECT,
+                               AVDTP_GET_CONFIGURATION, &err, sizeof(err));
+}
+
+static gboolean avdtp_reconf_cmd(struct avdtp *session, uint8_t transaction,
+                                       struct seid_req *req, int size)
+{
+       return avdtp_unknown_cmd(session, transaction, (void *) req, size);
+}
+
+static gboolean avdtp_open_cmd(struct avdtp *session, uint8_t transaction,
+                               struct seid_req *req, unsigned int size)
+{
+       struct avdtp_local_sep *sep;
+       struct avdtp_stream *stream;
+       uint8_t err;
+
+       if (size < sizeof(struct seid_req)) {
+               error("Too short abort request");
+               return FALSE;
+       }
+
+       sep = find_local_sep_by_seid(session->server, req->acp_seid);
+       if (!sep) {
+               err = AVDTP_BAD_ACP_SEID;
+               goto failed;
+       }
+
+       if (sep->state != AVDTP_STATE_CONFIGURED) {
+               err = AVDTP_BAD_STATE;
+               goto failed;
+       }
+
+       stream = sep->stream;
+
+       if (sep->ind && sep->ind->open) {
+               if (!sep->ind->open(session, sep, stream, &err,
+                                       sep->user_data))
+                       goto failed;
+       }
+
+       if (!avdtp_send(session, transaction, AVDTP_MSG_TYPE_ACCEPT,
+                                               AVDTP_OPEN, NULL, 0))
+               return FALSE;
+
+       stream->open_acp = TRUE;
+       session->pending_open = stream;
+       stream->timer = g_timeout_add_seconds(REQ_TIMEOUT,
+                                               stream_open_timeout,
+                                               stream);
+
+       return TRUE;
+
+failed:
+       return avdtp_send(session, transaction, AVDTP_MSG_TYPE_REJECT,
+                               AVDTP_OPEN, &err, sizeof(err));
+}
+
+static gboolean avdtp_start_cmd(struct avdtp *session, uint8_t transaction,
+                               struct start_req *req, unsigned int size)
+{
+       struct avdtp_local_sep *sep;
+       struct avdtp_stream *stream;
+       struct stream_rej rej;
+       struct seid *seid;
+       uint8_t err, failed_seid;
+       int seid_count, i;
+
+       if (size < sizeof(struct start_req)) {
+               error("Too short start request");
+               return FALSE;
+       }
+
+       seid_count = 1 + size - sizeof(struct start_req);
+
+       seid = &req->first_seid;
+
+       for (i = 0; i < seid_count; i++, seid++) {
+               failed_seid = seid->seid;
+
+               sep = find_local_sep_by_seid(session->server,
+                                       req->first_seid.seid);
+               if (!sep || !sep->stream) {
+                       err = AVDTP_BAD_ACP_SEID;
+                       goto failed;
+               }
+
+               stream = sep->stream;
+
+               if (sep->state != AVDTP_STATE_OPEN) {
+                       err = AVDTP_BAD_STATE;
+                       goto failed;
+               }
+
+               if (sep->ind && sep->ind->start) {
+                       if (!sep->ind->start(session, sep, stream, &err,
+                                               sep->user_data))
+                               goto failed;
+               }
+
+               avdtp_sep_set_state(session, sep, AVDTP_STATE_STREAMING);
+       }
+
+       return avdtp_send(session, transaction, AVDTP_MSG_TYPE_ACCEPT,
+                                               AVDTP_START, NULL, 0);
+
+failed:
+       memset(&rej, 0, sizeof(rej));
+       rej.acp_seid = failed_seid;
+       rej.error = err;
+       return avdtp_send(session, transaction, AVDTP_MSG_TYPE_REJECT,
+                               AVDTP_START, &rej, sizeof(rej));
+}
+
+static gboolean avdtp_close_cmd(struct avdtp *session, uint8_t transaction,
+                               struct seid_req *req, unsigned int size)
+{
+       struct avdtp_local_sep *sep;
+       struct avdtp_stream *stream;
+       uint8_t err;
+
+       if (size < sizeof(struct seid_req)) {
+               error("Too short close request");
+               return FALSE;
+       }
+
+       sep = find_local_sep_by_seid(session->server, req->acp_seid);
+       if (!sep || !sep->stream) {
+               err = AVDTP_BAD_ACP_SEID;
+               goto failed;
+       }
+
+       if (sep->state != AVDTP_STATE_OPEN &&
+                       sep->state != AVDTP_STATE_STREAMING) {
+               err = AVDTP_BAD_STATE;
+               goto failed;
+       }
+
+       stream = sep->stream;
+
+       if (sep->ind && sep->ind->close) {
+               if (!sep->ind->close(session, sep, stream, &err,
+                                       sep->user_data))
+                       goto failed;
+       }
+
+       avdtp_sep_set_state(session, sep, AVDTP_STATE_CLOSING);
+
+       if (!avdtp_send(session, transaction, AVDTP_MSG_TYPE_ACCEPT,
+                                               AVDTP_CLOSE, NULL, 0))
+               return FALSE;
+
+       stream->timer = g_timeout_add_seconds(REQ_TIMEOUT,
+                                       stream_close_timeout,
+                                       stream);
+
+       return TRUE;
+
+failed:
+       return avdtp_send(session, transaction, AVDTP_MSG_TYPE_REJECT,
+                                       AVDTP_CLOSE, &err, sizeof(err));
+}
+
+static gboolean avdtp_suspend_cmd(struct avdtp *session, uint8_t transaction,
+                               struct suspend_req *req, unsigned int size)
+{
+       struct avdtp_local_sep *sep;
+       struct avdtp_stream *stream;
+       struct stream_rej rej;
+       struct seid *seid;
+       uint8_t err, failed_seid;
+       int seid_count, i;
+
+       if (size < sizeof(struct suspend_req)) {
+               error("Too short suspend request");
+               return FALSE;
+       }
+
+       seid_count = 1 + size - sizeof(struct suspend_req);
+
+       seid = &req->first_seid;
+
+       for (i = 0; i < seid_count; i++, seid++) {
+               failed_seid = seid->seid;
+
+               sep = find_local_sep_by_seid(session->server,
+                                       req->first_seid.seid);
+               if (!sep || !sep->stream) {
+                       err = AVDTP_BAD_ACP_SEID;
+                       goto failed;
+               }
+
+               stream = sep->stream;
+
+               if (sep->state != AVDTP_STATE_STREAMING) {
+                       err = AVDTP_BAD_STATE;
+                       goto failed;
+               }
+
+               if (sep->ind && sep->ind->suspend) {
+                       if (!sep->ind->suspend(session, sep, stream, &err,
+                                               sep->user_data))
+                               goto failed;
+               }
+
+               avdtp_sep_set_state(session, sep, AVDTP_STATE_OPEN);
+       }
+
+       return avdtp_send(session, transaction, AVDTP_MSG_TYPE_ACCEPT,
+                                               AVDTP_SUSPEND, NULL, 0);
+
+failed:
+       memset(&rej, 0, sizeof(rej));
+       rej.acp_seid = failed_seid;
+       rej.error = err;
+       return avdtp_send(session, transaction, AVDTP_MSG_TYPE_REJECT,
+                               AVDTP_SUSPEND, &rej, sizeof(rej));
+}
+
+static gboolean avdtp_abort_cmd(struct avdtp *session, uint8_t transaction,
+                               struct seid_req *req, unsigned int size)
+{
+       struct avdtp_local_sep *sep;
+       uint8_t err;
+       gboolean ret;
+
+       if (size < sizeof(struct seid_req)) {
+               error("Too short abort request");
+               return FALSE;
+       }
+
+       sep = find_local_sep_by_seid(session->server, req->acp_seid);
+       if (!sep || !sep->stream) {
+               err = AVDTP_BAD_ACP_SEID;
+               goto failed;
+       }
+
+       if (sep->ind && sep->ind->abort) {
+               if (!sep->ind->abort(session, sep, sep->stream, &err,
+                                       sep->user_data))
+                       goto failed;
+       }
+
+       ret = avdtp_send(session, transaction, AVDTP_MSG_TYPE_ACCEPT,
+                                               AVDTP_ABORT, NULL, 0);
+       if (ret)
+               avdtp_sep_set_state(session, sep, AVDTP_STATE_ABORTING);
+
+       return ret;
+
+failed:
+       return avdtp_send(session, transaction, AVDTP_MSG_TYPE_REJECT,
+                                       AVDTP_ABORT, &err, sizeof(err));
+}
+
+static gboolean avdtp_secctl_cmd(struct avdtp *session, uint8_t transaction,
+                                       struct seid_req *req, int size)
+{
+       return avdtp_unknown_cmd(session, transaction, (void *) req, size);
+}
+
+static gboolean avdtp_parse_cmd(struct avdtp *session, uint8_t transaction,
+                               uint8_t signal_id, void *buf, int size)
+{
+       switch (signal_id) {
+       case AVDTP_DISCOVER:
+               debug("Received DISCOVER_CMD");
+               return avdtp_discover_cmd(session, transaction, buf, size);
+       case AVDTP_GET_CAPABILITIES:
+               debug("Received  GET_CAPABILITIES_CMD");
+               return avdtp_getcap_cmd(session, transaction, buf, size);
+       case AVDTP_SET_CONFIGURATION:
+               debug("Received SET_CONFIGURATION_CMD");
+               return avdtp_setconf_cmd(session, transaction, buf, size);
+       case AVDTP_GET_CONFIGURATION:
+               debug("Received GET_CONFIGURATION_CMD");
+               return avdtp_getconf_cmd(session, transaction, buf, size);
+       case AVDTP_RECONFIGURE:
+               debug("Received RECONFIGURE_CMD");
+               return avdtp_reconf_cmd(session, transaction, buf, size);
+       case AVDTP_OPEN:
+               debug("Received OPEN_CMD");
+               return avdtp_open_cmd(session, transaction, buf, size);
+       case AVDTP_START:
+               debug("Received START_CMD");
+               return avdtp_start_cmd(session, transaction, buf, size);
+       case AVDTP_CLOSE:
+               debug("Received CLOSE_CMD");
+               return avdtp_close_cmd(session, transaction, buf, size);
+       case AVDTP_SUSPEND:
+               debug("Received SUSPEND_CMD");
+               return avdtp_suspend_cmd(session, transaction, buf, size);
+       case AVDTP_ABORT:
+               debug("Received ABORT_CMD");
+               return avdtp_abort_cmd(session, transaction, buf, size);
+       case AVDTP_SECURITY_CONTROL:
+               debug("Received SECURITY_CONTROL_CMD");
+               return avdtp_secctl_cmd(session, transaction, buf, size);
+       default:
+               debug("Received unknown request id %u", signal_id);
+               return avdtp_unknown_cmd(session, transaction, buf, size);
+       }
+}
+
+enum avdtp_parse_result { PARSE_ERROR, PARSE_FRAGMENT, PARSE_SUCCESS };
+
+static enum avdtp_parse_result avdtp_parse_data(struct avdtp *session,
+                                                       void *buf, size_t size)
+{
+       struct avdtp_common_header *header = buf;
+       struct avdtp_single_header *single = (void *) session->buf;
+       struct avdtp_start_header *start = (void *) session->buf;
+       void *payload;
+       gsize payload_size;
+
+       switch (header->packet_type) {
+       case AVDTP_PKT_TYPE_SINGLE:
+               if (size < sizeof(*single)) {
+                       error("Received too small single packet (%zu bytes)", size);
+                       return PARSE_ERROR;
+               }
+               if (session->in.active) {
+                       error("SINGLE: Invalid AVDTP packet fragmentation");
+                       return PARSE_ERROR;
+               }
+
+               payload = session->buf + sizeof(*single);
+               payload_size = size - sizeof(*single);
+
+               session->in.active = TRUE;
+               session->in.data_size = 0;
+               session->in.no_of_packets = 1;
+               session->in.transaction = header->transaction;
+               session->in.message_type = header->message_type;
+               session->in.signal_id = single->signal_id;
+
+               break;
+       case AVDTP_PKT_TYPE_START:
+               if (size < sizeof(*start)) {
+                       error("Received too small start packet (%zu bytes)", size);
+                       return PARSE_ERROR;
+               }
+               if (session->in.active) {
+                       error("START: Invalid AVDTP packet fragmentation");
+                       return PARSE_ERROR;
+               }
+
+               session->in.active = TRUE;
+               session->in.data_size = 0;
+               session->in.transaction = header->transaction;
+               session->in.message_type = header->message_type;
+               session->in.no_of_packets = start->no_of_packets;
+               session->in.signal_id = start->signal_id;
+
+               payload = session->buf + sizeof(*start);
+               payload_size = size - sizeof(*start);
+
+               break;
+       case AVDTP_PKT_TYPE_CONTINUE:
+               if (size < sizeof(struct avdtp_continue_header)) {
+                       error("Received too small continue packet (%zu bytes)",
+                                                                       size);
+                       return PARSE_ERROR;
+               }
+               if (!session->in.active) {
+                       error("CONTINUE: Invalid AVDTP packet fragmentation");
+                       return PARSE_ERROR;
+               }
+               if (session->in.transaction != header->transaction) {
+                       error("Continue transaction id doesn't match");
+                       return PARSE_ERROR;
+               }
+               if (session->in.no_of_packets <= 1) {
+                       error("Too few continue packets");
+                       return PARSE_ERROR;
+               }
+
+               payload = session->buf + sizeof(struct avdtp_continue_header);
+               payload_size = size - sizeof(struct avdtp_continue_header);
+
+               break;
+       case AVDTP_PKT_TYPE_END:
+               if (size < sizeof(struct avdtp_continue_header)) {
+                       error("Received too small end packet (%zu bytes)", size);
+                       return PARSE_ERROR;
+               }
+               if (!session->in.active) {
+                       error("END: Invalid AVDTP packet fragmentation");
+                       return PARSE_ERROR;
+               }
+               if (session->in.transaction != header->transaction) {
+                       error("End transaction id doesn't match");
+                       return PARSE_ERROR;
+               }
+               if (session->in.no_of_packets > 1) {
+                       error("Got an end packet too early");
+                       return PARSE_ERROR;
+               }
+
+               payload = session->buf + sizeof(struct avdtp_continue_header);
+               payload_size = size - sizeof(struct avdtp_continue_header);
+
+               break;
+       default:
+               error("Invalid AVDTP packet type 0x%02X", header->packet_type);
+               return PARSE_ERROR;
+       }
+
+       if (session->in.data_size + payload_size >
+                                       sizeof(session->in.buf)) {
+               error("Not enough incoming buffer space!");
+               return PARSE_ERROR;
+       }
+
+       memcpy(session->in.buf + session->in.data_size, payload, payload_size);
+       session->in.data_size += payload_size;
+
+       if (session->in.no_of_packets > 1) {
+               session->in.no_of_packets--;
+               debug("Received AVDTP fragment. %d to go",
+                                               session->in.no_of_packets);
+               return PARSE_FRAGMENT;
+       }
+
+       session->in.active = FALSE;
+
+       return PARSE_SUCCESS;
+}
+
+static gboolean session_cb(GIOChannel *chan, GIOCondition cond,
+                               gpointer data)
+{
+       struct avdtp *session = data;
+       struct avdtp_common_header *header;
+       gsize size;
+
+       debug("session_cb");
+
+       if (cond & G_IO_NVAL)
+               return FALSE;
+
+       header = (void *) session->buf;
+
+       if (cond & (G_IO_HUP | G_IO_ERR))
+               goto failed;
+
+       if (g_io_channel_read(chan, session->buf, session->imtu, &size)
+                                                       != G_IO_ERROR_NONE) {
+               error("IO Channel read error");
+               goto failed;
+       }
+
+       if (size < sizeof(struct avdtp_common_header)) {
+               error("Received too small packet (%zu bytes)", size);
+               goto failed;
+       }
+
+       switch (avdtp_parse_data(session, session->buf, size)) {
+       case PARSE_ERROR:
+               goto failed;
+       case PARSE_FRAGMENT:
+               return TRUE;
+       case PARSE_SUCCESS:
+               break;
+       }
+
+       if (session->in.message_type == AVDTP_MSG_TYPE_COMMAND) {
+               if (!avdtp_parse_cmd(session, session->in.transaction,
+                                       session->in.signal_id,
+                                       session->in.buf,
+                                       session->in.data_size)) {
+                       error("Unable to handle command. Disconnecting");
+                       goto failed;
+               }
+
+               if (session->ref == 1 && !session->streams && !session->req)
+                       set_disconnect_timer(session);
+
+               if (session->streams && session->dc_timer)
+                       remove_disconnect_timer(session);
+
+               return TRUE;
+       }
+
+       if (session->req == NULL) {
+               error("No pending request, ignoring message");
+               return TRUE;
+       }
+
+       if (header->transaction != session->req->transaction) {
+               error("Transaction label doesn't match");
+               return TRUE;
+       }
+
+       if (session->in.signal_id != session->req->signal_id) {
+               error("Reponse signal doesn't match");
+               return TRUE;
+       }
+
+       g_source_remove(session->req->timeout);
+       session->req->timeout = 0;
+
+       switch (header->message_type) {
+       case AVDTP_MSG_TYPE_ACCEPT:
+               if (!avdtp_parse_resp(session, session->req->stream,
+                                               session->in.transaction,
+                                               session->in.signal_id,
+                                               session->in.buf,
+                                               session->in.data_size)) {
+                       error("Unable to parse accept response");
+                       goto failed;
+               }
+               break;
+       case AVDTP_MSG_TYPE_REJECT:
+               if (!avdtp_parse_rej(session, session->req->stream,
+                                               session->in.transaction,
+                                               session->in.signal_id,
+                                               session->in.buf,
+                                               session->in.data_size)) {
+                       error("Unable to parse reject response");
+                       goto failed;
+               }
+               break;
+       default:
+               error("Unknown message type 0x%02X", header->message_type);
+               break;
+       }
+
+       pending_req_free(session->req);
+       session->req = NULL;
+
+       process_queue(session);
+
+       return TRUE;
+
+failed:
+       connection_lost(session, EIO);
+
+       return FALSE;
+}
+
+static struct avdtp *find_session(GSList *list, const bdaddr_t *dst)
+{
+       GSList *l;
+
+       for (l = list; l != NULL; l = g_slist_next(l)) {
+               struct avdtp *s = l->data;
+
+               if (bacmp(dst, &s->dst))
+                       continue;
+
+               return s;
+       }
+
+       return NULL;
+}
+
+static struct avdtp *avdtp_get_internal(const bdaddr_t *src, const bdaddr_t *dst)
+{
+       struct avdtp_server *server;
+       struct avdtp *session;
+
+       assert(src != NULL);
+       assert(dst != NULL);
+
+       server = find_server(servers, src);
+       if (server == NULL)
+               return NULL;
+
+       session = find_session(server->sessions, dst);
+       if (session) {
+               if (session->pending_auth)
+                       return NULL;
+               else
+                       return session;
+       }
+
+       session = g_new0(struct avdtp, 1);
+
+       session->server = server;
+       bacpy(&session->dst, dst);
+       session->ref = 1;
+       /* We don't use avdtp_set_state() here since this isn't a state change
+        * but just setting of the initial state */
+       session->state = AVDTP_SESSION_STATE_DISCONNECTED;
+       session->auto_dc = TRUE;
+
+       server->sessions = g_slist_append(server->sessions, session);
+
+       return session;
+}
+
+struct avdtp *avdtp_get(bdaddr_t *src, bdaddr_t *dst)
+{
+       struct avdtp *session;
+
+       session = avdtp_get_internal(src, dst);
+
+       if (!session)
+               return NULL;
+
+       return avdtp_ref(session);
+}
+
+static void avdtp_connect_cb(GIOChannel *chan, GError *err, gpointer user_data)
+{
+       struct avdtp *session = user_data;
+       char address[18];
+       GError *gerr = NULL;
+
+       if (err) {
+               error("%s", err->message);
+               goto failed;
+       }
+
+       if (!session->io)
+               session->io = g_io_channel_ref(chan);
+
+       bt_io_get(chan, BT_IO_L2CAP, &gerr,
+                       BT_IO_OPT_OMTU, &session->omtu,
+                       BT_IO_OPT_IMTU, &session->imtu,
+                       BT_IO_OPT_INVALID);
+       if (gerr) {
+               error("%s", gerr->message);
+               g_error_free(gerr);
+               goto failed;
+       }
+
+       ba2str(&session->dst, address);
+       debug("AVDTP: connected %s channel to %s",
+                       session->pending_open ? "transport" : "signaling",
+                       address);
+
+       if (session->state == AVDTP_SESSION_STATE_CONNECTING) {
+               debug("AVDTP imtu=%u, omtu=%u", session->imtu, session->omtu);
+
+               session->buf = g_malloc0(session->imtu);
+               avdtp_set_state(session, AVDTP_SESSION_STATE_CONNECTED);
+
+               if (session->io_id)
+                       g_source_remove(session->io_id);
+
+               /* This watch should be low priority since otherwise the
+                * connect callback might be dispatched before the session
+                * callback if the kernel wakes us up at the same time for
+                * them. This could happen if a headset is very quick in
+                * sending the Start command after connecting the stream
+                * transport channel.
+                */
+               session->io_id = g_io_add_watch_full(chan,
+                                               G_PRIORITY_LOW,
+                                               G_IO_IN | G_IO_ERR | G_IO_HUP
+                                               | G_IO_NVAL,
+                                               (GIOFunc) session_cb, session,
+                                               NULL);
+
+               if (session->stream_setup) {
+                       set_disconnect_timer(session);
+                       avdtp_set_auto_disconnect(session, FALSE);
+               }
+       } else if (session->pending_open)
+               handle_transport_connect(session, chan, session->imtu,
+                                                               session->omtu);
+       else
+               goto failed;
+
+       process_queue(session);
+
+       return;
+
+failed:
+       if (session->pending_open) {
+               struct avdtp_stream *stream = session->pending_open;
+
+               handle_transport_connect(session, NULL, 0, 0);
+
+               if (avdtp_abort(session, stream) < 0)
+                       avdtp_sep_set_state(session, stream->lsep,
+                                               AVDTP_STATE_IDLE);
+       } else
+               connection_lost(session, EIO);
+
+       return;
+}
+
+static void auth_cb(DBusError *derr, void *user_data)
+{
+       struct avdtp *session = user_data;
+       GError *err = NULL;
+
+       if (derr && dbus_error_is_set(derr)) {
+               error("Access denied: %s", derr->message);
+               connection_lost(session, EACCES);
+               return;
+       }
+
+       if (!bt_io_accept(session->io, avdtp_connect_cb, session, NULL,
+                                                               &err)) {
+               error("bt_io_accept: %s", err->message);
+               connection_lost(session, EACCES);
+               g_error_free(err);
+               return;
+       }
+
+       /* This is so that avdtp_connect_cb will know to do the right thing
+        * with respect to the disconnect timer */
+       session->stream_setup = TRUE;
+}
+
+static void avdtp_confirm_cb(GIOChannel *chan, gpointer data)
+{
+       struct avdtp *session;
+       struct audio_device *dev;
+       char address[18];
+       bdaddr_t src, dst;
+       int perr;
+       GError *err = NULL;
+
+       bt_io_get(chan, BT_IO_L2CAP, &err,
+                       BT_IO_OPT_SOURCE_BDADDR, &src,
+                       BT_IO_OPT_DEST_BDADDR, &dst,
+                       BT_IO_OPT_DEST, address,
+                       BT_IO_OPT_INVALID);
+       if (err) {
+               error("%s", err->message);
+               g_error_free(err);
+               goto drop;
+       }
+
+       debug("AVDTP: incoming connect from %s", address);
+
+       session = avdtp_get_internal(&src, &dst);
+       if (!session)
+               goto drop;
+
+       /* This state (ie, session is already *connecting*) happens when the
+        * device initiates a connect (really a config'd L2CAP channel) even
+        * though there is a connect we initiated in progress. In sink.c &
+        * source.c, this state is referred to as XCASE connect:connect.
+        * Abort the device's channel in favor of our own.
+        */
+       if (session->state == AVDTP_SESSION_STATE_CONNECTING) {
+               debug("avdtp_confirm_cb: connect already in progress"
+                                               " (XCASE connect:connect)");
+               goto drop;
+       }
+
+       if (session->pending_open && session->pending_open->open_acp) {
+               if (!bt_io_accept(chan, avdtp_connect_cb, session, NULL, NULL))
+                       goto drop;
+               return;
+       }
+
+       if (session->io) {
+               error("Refusing unexpected connect from %s", address);
+               goto drop;
+       }
+
+       dev = manager_get_device(&src, &dst, FALSE);
+       if (!dev) {
+               dev = manager_get_device(&src, &dst, TRUE);
+               if (!dev) {
+                       error("Unable to get audio device object for %s",
+                                       address);
+                       goto drop;
+               }
+               btd_device_add_uuid(dev->btd_dev, ADVANCED_AUDIO_UUID);
+       }
+
+       session->io = g_io_channel_ref(chan);
+       avdtp_set_state(session, AVDTP_SESSION_STATE_CONNECTING);
+
+       session->io_id = g_io_add_watch(chan, G_IO_ERR | G_IO_HUP | G_IO_NVAL,
+                                       (GIOFunc) session_cb, session);
+
+       perr = audio_device_request_authorization(dev, ADVANCED_AUDIO_UUID,
+                                                       auth_cb, session);
+       if (perr < 0) {
+               avdtp_unref(session);
+               goto drop;
+       }
+
+       dev->auto_connect = auto_connect;
+
+       return;
+
+drop:
+       g_io_channel_shutdown(chan, TRUE, NULL);
+}
+
+static int l2cap_connect(struct avdtp *session)
+{
+       GError *err = NULL;
+       GIOChannel *io;
+
+       io = bt_io_connect(BT_IO_L2CAP, avdtp_connect_cb, session,
+                               NULL, &err,
+                               BT_IO_OPT_SOURCE_BDADDR, &session->server->src,
+                               BT_IO_OPT_DEST_BDADDR, &session->dst,
+                               BT_IO_OPT_PSM, AVDTP_PSM,
+                               BT_IO_OPT_INVALID);
+       if (!io) {
+               error("%s", err->message);
+               g_error_free(err);
+               return -EIO;
+       }
+
+       g_io_channel_unref(io);
+
+       return 0;
+}
+
+static void queue_request(struct avdtp *session, struct pending_req *req,
+                       gboolean priority)
+{
+       if (priority)
+               session->prio_queue = g_slist_append(session->prio_queue, req);
+       else
+               session->req_queue = g_slist_append(session->req_queue, req);
+}
+
+static uint8_t req_get_seid(struct pending_req *req)
+{
+       if (req->signal_id == AVDTP_DISCOVER)
+               return 0;
+
+       return ((struct seid_req *) (req->data))->acp_seid;
+}
+
+static gboolean request_timeout(gpointer user_data)
+{
+       struct avdtp *session = user_data;
+       struct pending_req *req;
+       struct seid_req sreq;
+       struct avdtp_local_sep *lsep;
+       struct avdtp_stream *stream;
+       uint8_t seid;
+       struct avdtp_error err;
+
+       req = session->req;
+       session->req = NULL;
+
+       avdtp_error_init(&err, AVDTP_ERROR_ERRNO, ETIMEDOUT);
+
+       seid = req_get_seid(req);
+       if (seid)
+               stream = find_stream_by_rseid(session, seid);
+       else
+               stream = NULL;
+
+       if (stream)
+               lsep = stream->lsep;
+       else
+               lsep = NULL;
+
+       switch (req->signal_id) {
+       case AVDTP_RECONFIGURE:
+               error("Reconfigure request timed out");
+               if (lsep && lsep->cfm && lsep->cfm->reconfigure)
+                       lsep->cfm->reconfigure(session, lsep, stream, &err,
+                                               lsep->user_data);
+               break;
+       case AVDTP_OPEN:
+               error("Open request timed out");
+               if (lsep && lsep->cfm && lsep->cfm->open)
+                       lsep->cfm->open(session, lsep, stream, &err,
+                                       lsep->user_data);
+               break;
+       case AVDTP_START:
+               error("Start request timed out");
+               if (lsep && lsep->cfm && lsep->cfm->start)
+                       lsep->cfm->start(session, lsep, stream, &err,
+                                               lsep->user_data);
+               break;
+       case AVDTP_SUSPEND:
+               error("Suspend request timed out");
+               if (lsep && lsep->cfm && lsep->cfm->suspend)
+                       lsep->cfm->suspend(session, lsep, stream, &err,
+                                               lsep->user_data);
+               break;
+       case AVDTP_CLOSE:
+               error("Close request timed out");
+               if (lsep && lsep->cfm && lsep->cfm->close) {
+                       lsep->cfm->close(session, lsep, stream, &err,
+                                               lsep->user_data);
+                       if (stream)
+                               stream->close_int = FALSE;
+               }
+               break;
+       case AVDTP_SET_CONFIGURATION:
+               error("SetConfiguration request timed out");
+               if (lsep && lsep->cfm && lsep->cfm->set_configuration)
+                       lsep->cfm->set_configuration(session, lsep, stream,
+                                                       &err, lsep->user_data);
+               goto failed;
+       case AVDTP_DISCOVER:
+               error("Discover request timed out");
+               goto failed;
+       case AVDTP_GET_CAPABILITIES:
+               error("GetCapabilities request timed out");
+               goto failed;
+       case AVDTP_ABORT:
+               error("Abort request timed out");
+               goto failed;
+       }
+
+       if (!stream)
+               goto failed;
+
+       memset(&sreq, 0, sizeof(sreq));
+       sreq.acp_seid = seid;
+
+       if (send_request(session, TRUE, stream, AVDTP_ABORT,
+                                               &sreq, sizeof(sreq)) < 0) {
+               error("Unable to send abort request");
+               goto failed;
+       }
+
+       stream->abort_int = TRUE;
+
+       goto done;
+
+failed:
+       connection_lost(session, ETIMEDOUT);
+done:
+       pending_req_free(req);
+       return FALSE;
+}
+
+static int send_req(struct avdtp *session, gboolean priority,
+                       struct pending_req *req)
+{
+       static int transaction = 0;
+       int err;
+
+       if (session->state == AVDTP_SESSION_STATE_DISCONNECTED) {
+               err = l2cap_connect(session);
+               if (err < 0)
+                       goto failed;
+               avdtp_set_state(session, AVDTP_SESSION_STATE_CONNECTING);
+       }
+
+       if (session->state < AVDTP_SESSION_STATE_CONNECTED ||
+                       session->req != NULL) {
+               queue_request(session, req, priority);
+               return 0;
+       }
+
+       req->transaction = transaction++;
+       transaction %= 16;
+
+       /* FIXME: Should we retry to send if the buffer
+       was not totally sent or in case of EINTR? */
+       if (!avdtp_send(session, req->transaction, AVDTP_MSG_TYPE_COMMAND,
+                               req->signal_id, req->data, req->data_size)) {
+               err = -EIO;
+               goto failed;
+       }
+
+
+       session->req = req;
+
+       req->timeout = g_timeout_add_seconds(REQ_TIMEOUT,
+                                       request_timeout,
+                                       session);
+       return 0;
+
+failed:
+       g_free(req->data);
+       g_free(req);
+       return err;
+}
+
+static int send_request(struct avdtp *session, gboolean priority,
+                       struct avdtp_stream *stream, uint8_t signal_id,
+                       void *buffer, size_t size)
+{
+       struct pending_req *req;
+
+       req = g_new0(struct pending_req, 1);
+       req->signal_id = signal_id;
+       req->data = g_malloc(size);
+       memcpy(req->data, buffer, size);
+       req->data_size = size;
+       req->stream = stream;
+
+       return send_req(session, priority, req);
+}
+
+static gboolean avdtp_discover_resp(struct avdtp *session,
+                                       struct discover_resp *resp, int size)
+{
+       int sep_count, i;
+
+       sep_count = size / sizeof(struct seid_info);
+
+       for (i = 0; i < sep_count; i++) {
+               struct avdtp_remote_sep *sep;
+               struct avdtp_stream *stream;
+               struct seid_req req;
+               int ret;
+
+ &n